diff --git a/src/plugins/visualizations/public/expressions/visualization_function.ts b/src/plugins/visualizations/public/expressions/visualization_function.ts index 222479158934b7d..68a153f4272a306 100644 --- a/src/plugins/visualizations/public/expressions/visualization_function.ts +++ b/src/plugins/visualizations/public/expressions/visualization_function.ts @@ -21,7 +21,7 @@ import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; import { VisResponseValue, PersistedState } from '../../../../plugins/visualizations/public'; import { ExpressionFunctionDefinition, Render } from '../../../../plugins/expressions/public'; -import { getTypes, getIndexPatterns, getFilterManager } from '../services'; +import { getTypes, getIndexPatterns, getFilterManager, getSearch } from '../services'; interface Arguments { index?: string | null; @@ -31,6 +31,7 @@ interface Arguments { schemas?: string; visConfig?: string; uiState?: string; + aggConfigs?: string; } export type ExpressionFunctionVisualization = ExpressionFunctionDefinition< @@ -84,6 +85,11 @@ export const visualization = (): ExpressionFunctionVisualization => ({ default: '"{}"', help: 'User interface state', }, + aggConfigs: { + types: ['string'], + default: '"{}"', + help: 'Aggregation configurations', + }, }, async fn(input, args, { inspectorAdapters }) { const visConfigParams = args.visConfig ? JSON.parse(args.visConfig) : {}; @@ -94,6 +100,11 @@ export const visualization = (): ExpressionFunctionVisualization => ({ const uiStateParams = args.uiState ? JSON.parse(args.uiState) : {}; const uiState = new PersistedState(uiStateParams); + const aggConfigsState = args.aggConfigs ? JSON.parse(args.aggConfigs) : []; + const aggs = indexPattern + ? getSearch().aggs.createAggConfigs(indexPattern, aggConfigsState) + : undefined; + if (typeof visType.requestHandler === 'function') { input = await visType.requestHandler({ partialRows: args.partialRows, @@ -107,6 +118,7 @@ export const visualization = (): ExpressionFunctionVisualization => ({ inspectorAdapters, queryFilter: getFilterManager(), forceFetch: true, + aggs, }); } diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts index 2ef07bf18c91cea..e74a83d91fabfad 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.ts @@ -535,7 +535,10 @@ export const buildPipeline = async ( metricsAtAllLevels=${vis.isHierarchical()} partialRows=${vis.type.requiresPartialRows || vis.params.showPartialRows || false} `; if (indexPattern) { - pipeline += `${prepareString('index', indexPattern.id)}`; + pipeline += `${prepareString('index', indexPattern.id)} `; + if (vis.data.aggs) { + pipeline += `${prepareJson('aggConfigs', vis.data.aggs!.aggs)}`; + } } } diff --git a/x-pack/plugins/ingest_manager/common/services/agent_status.ts b/x-pack/plugins/ingest_manager/common/services/agent_status.ts index 6489c30308771d2..536003b0f743d98 100644 --- a/x-pack/plugins/ingest_manager/common/services/agent_status.ts +++ b/x-pack/plugins/ingest_manager/common/services/agent_status.ts @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - AGENT_POLLING_THRESHOLD_MS, - AGENT_TYPE_PERMANENT, - AGENT_SAVED_OBJECT_TYPE, -} from '../constants'; +import { AGENT_POLLING_THRESHOLD_MS, AGENT_SAVED_OBJECT_TYPE } from '../constants'; import { Agent, AgentStatus } from '../types'; export function getAgentStatus(agent: Agent, now: number = Date.now()): AgentStatus { @@ -41,8 +37,16 @@ export function getAgentStatus(agent: Agent, now: number = Date.now()): AgentSta return 'online'; } +export function buildKueryForEnrollingAgents() { + return `not ${AGENT_SAVED_OBJECT_TYPE}.last_checkin:*`; +} + +export function buildKueryForUnenrollingAgents() { + return `${AGENT_SAVED_OBJECT_TYPE}.unenrollment_started_at:*`; +} + export function buildKueryForOnlineAgents() { - return `not (${buildKueryForOfflineAgents()}) AND not (${buildKueryForErrorAgents()})`; + return `not (${buildKueryForOfflineAgents()}) AND not (${buildKueryForErrorAgents()}) AND not (${buildKueryForEnrollingAgents()}) AND not (${buildKueryForUnenrollingAgents()})`; } export function buildKueryForErrorAgents() { @@ -50,7 +54,7 @@ export function buildKueryForErrorAgents() { } export function buildKueryForOfflineAgents() { - return `((${AGENT_SAVED_OBJECT_TYPE}.type:${AGENT_TYPE_PERMANENT} AND ${AGENT_SAVED_OBJECT_TYPE}.last_checkin < now-${ + return `${AGENT_SAVED_OBJECT_TYPE}.last_checkin < now-${ (4 * AGENT_POLLING_THRESHOLD_MS) / 1000 - }s) AND not ( ${buildKueryForErrorAgents()} ))`; + }s AND not (${buildKueryForErrorAgents()})`; } diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts index ed7d73ab0b71973..7ec5a8d68311f7d 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent.ts @@ -173,5 +173,6 @@ export interface GetAgentStatusResponse { online: number; error: number; offline: number; + other: number; }; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/donut_chart.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/donut_chart.tsx index bfa9c80f12851d1..99a4f27b428febe 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/donut_chart.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/donut_chart.tsx @@ -31,7 +31,7 @@ export const DonutChart = ({ height, width, data }: DonutChartProps) => { .ordinal() // @ts-ignore .domain(data) - .range(['#017D73', '#98A2B3', '#BD271E']); + .range(['#017D73', '#98A2B3', '#BD271E', '#F5A700']); const pieGenerator = d3.layout .pie() .value(({ value }: any) => value) diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/list_layout.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/list_layout.tsx index 46190033d4d6bb9..16acda9dc4afd2d 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/list_layout.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/list_layout.tsx @@ -66,6 +66,7 @@ export const ListLayout: React.FunctionComponent<{}> = ({ children }) => { online: agentStatus?.online || 0, offline: agentStatus?.offline || 0, error: agentStatus?.error || 0, + other: agentStatus?.other || 0, }} /> diff --git a/x-pack/plugins/ingest_manager/server/services/agents/status.ts b/x-pack/plugins/ingest_manager/server/services/agents/status.ts index 016a2344cf532f5..86336714a511eed 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/status.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/status.ts @@ -25,9 +25,10 @@ export async function getAgentStatusForConfig( soClient: SavedObjectsClientContract, configId?: string ) { - const [all, error, offline] = await Promise.all( + const [all, online, error, offline] = await Promise.all( [ undefined, + AgentStatusKueryHelper.buildKueryForOnlineAgents(), AgentStatusKueryHelper.buildKueryForErrorAgents(), AgentStatusKueryHelper.buildKueryForOfflineAgents(), ].map((kuery) => @@ -47,9 +48,10 @@ export async function getAgentStatusForConfig( return { events: await getEventsCount(soClient, configId), total: all.total, - online: all.total - error.total - offline.total, + online: online.total, error: error.total, offline: offline.total, + other: all.total - online.total - error.total - offline.total, }; } diff --git a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts index e73126ef10d3d1f..cd7d2d5d0f461f3 100644 --- a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts @@ -64,6 +64,22 @@ export type DataMeta = Partial & Partial & Partial; +type NumericalStyleFieldData = { + avg: number; + max: number; + min: number; + std_deviation: number; +}; + +type CategoricalStyleFieldData = { + buckets: Array<{ key: string; doc_count: number }>; +}; + +export type StyleMetaData = { + // key is field name for field requiring style meta + [key: string]: NumericalStyleFieldData | CategoricalStyleFieldData; +}; + export type DataRequestDescriptor = { dataId: string; dataMetaAtStart?: DataMeta | null; diff --git a/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts b/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts index 6da72fbbd8227c6..452a3fdc3a19f17 100644 --- a/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts +++ b/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts @@ -15,4 +15,6 @@ export class InnerJoin implements IJoin { getRightJoinSource(): IESTermSource; toDescriptor(): JoinDescriptor; + + getSourceMetaDataRequestId(): string; } diff --git a/x-pack/plugins/maps/public/classes/joins/join.ts b/x-pack/plugins/maps/public/classes/joins/join.ts index 6554f7581d99cfa..4a551c5bcc4857a 100644 --- a/x-pack/plugins/maps/public/classes/joins/join.ts +++ b/x-pack/plugins/maps/public/classes/joins/join.ts @@ -11,4 +11,6 @@ export interface IJoin { getRightJoinSource(): IESTermSource; toDescriptor(): JoinDescriptor; + + getSourceMetaDataRequestId(): string; } diff --git a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts index 5388a82e5924d1b..aefa2beede7d174 100644 --- a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts @@ -33,6 +33,7 @@ import { VectorStyleDescriptor, SizeDynamicOptions, DynamicStylePropertyOptions, + StylePropertyOptions, VectorLayerDescriptor, } from '../../../../common/descriptor_types'; import { IStyle } from '../../styles/style'; @@ -44,7 +45,7 @@ interface CountData { isSyncClustered: boolean; } -function getAggType(dynamicProperty: IDynamicStyleProperty): AGG_TYPE { +function getAggType(dynamicProperty: IDynamicStyleProperty): AGG_TYPE { return dynamicProperty.isOrdinal() ? AGG_TYPE.AVG : AGG_TYPE.TERMS; } @@ -100,52 +101,57 @@ function getClusterStyleDescriptor( }, }, }; - documentStyle.getAllStyleProperties().forEach((styleProperty: IStyleProperty) => { - const styleName = styleProperty.getStyleName(); - if ( - [VECTOR_STYLES.LABEL_TEXT, VECTOR_STYLES.ICON_SIZE].includes(styleName) && - (!styleProperty.isDynamic() || !styleProperty.isComplete()) - ) { - // Do not migrate static label and icon size properties to provide unique cluster styling out of the box - return; - } + documentStyle + .getAllStyleProperties() + .forEach((styleProperty: IStyleProperty) => { + const styleName = styleProperty.getStyleName(); + if ( + [VECTOR_STYLES.LABEL_TEXT, VECTOR_STYLES.ICON_SIZE].includes(styleName) && + (!styleProperty.isDynamic() || !styleProperty.isComplete()) + ) { + // Do not migrate static label and icon size properties to provide unique cluster styling out of the box + return; + } - if (styleName === VECTOR_STYLES.SYMBOLIZE_AS || styleName === VECTOR_STYLES.LABEL_BORDER_SIZE) { - // copy none static/dynamic styles to cluster style - clusterStyleDescriptor.properties[styleName] = { + if ( + styleName === VECTOR_STYLES.SYMBOLIZE_AS || + styleName === VECTOR_STYLES.LABEL_BORDER_SIZE + ) { + // copy none static/dynamic styles to cluster style + clusterStyleDescriptor.properties[styleName] = { + // @ts-expect-error + options: { ...styleProperty.getOptions() }, + }; + } else if (styleProperty.isDynamic()) { + // copy dynamic styles to cluster style + const options = styleProperty.getOptions() as DynamicStylePropertyOptions; + const field = + options && options.field && options.field.name + ? { + ...options.field, + name: clusterSource.getAggKey( + getAggType(styleProperty as IDynamicStyleProperty), + options.field.name + ), + } + : undefined; // @ts-expect-error - options: { ...styleProperty.getOptions() }, - }; - } else if (styleProperty.isDynamic()) { - // copy dynamic styles to cluster style - const options = styleProperty.getOptions() as DynamicStylePropertyOptions; - const field = - options && options.field && options.field.name - ? { - ...options.field, - name: clusterSource.getAggKey( - getAggType(styleProperty as IDynamicStyleProperty), - options.field.name - ), - } - : undefined; - // @ts-expect-error - clusterStyleDescriptor.properties[styleName] = { - type: STYLE_TYPE.DYNAMIC, - options: { - ...options, - field, - }, - }; - } else { - // copy static styles to cluster style - // @ts-expect-error - clusterStyleDescriptor.properties[styleName] = { - type: STYLE_TYPE.STATIC, - options: { ...styleProperty.getOptions() }, - }; - } - }); + clusterStyleDescriptor.properties[styleName] = { + type: STYLE_TYPE.DYNAMIC, + options: { + ...options, + field, + }, + }; + } else { + // copy static styles to cluster style + // @ts-expect-error + clusterStyleDescriptor.properties[styleName] = { + type: STYLE_TYPE.STATIC, + options: { ...styleProperty.getOptions() }, + }; + } + }); return clusterStyleDescriptor; } diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.d.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.d.ts index a93f9121d1e62fc..91ed0ab52b08e7f 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.d.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.d.ts @@ -14,6 +14,7 @@ export interface IESAggSource extends IESSource { getAggKey(aggType: AGG_TYPE, fieldName: string): string; getAggLabel(aggType: AGG_TYPE, fieldName: string): string; getMetricFields(): IESAggField[]; + hasMatchingMetricField(fieldName: string): boolean; } export class AbstractESAggSource extends AbstractESSource implements IESAggSource { @@ -22,4 +23,5 @@ export class AbstractESAggSource extends AbstractESSource implements IESAggSourc getAggKey(aggType: AGG_TYPE, fieldName: string): string; getAggLabel(aggType: AGG_TYPE, fieldName: string): string; getMetricFields(): IESAggField[]; + hasMatchingMetricField(fieldName: string): boolean; } diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts index d95ec5a64e6c332..1f2985ffcc27cde 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts @@ -7,7 +7,10 @@ import { AbstractVectorSource } from '../vector_source'; import { IVectorSource } from '../vector_source'; import { IndexPattern, ISearchSource } from '../../../../../../../src/plugins/data/public'; -import { VectorSourceRequestMeta } from '../../../../common/descriptor_types'; +import { + DynamicStylePropertyOptions, + VectorSourceRequestMeta, +} from '../../../../common/descriptor_types'; import { VectorStyle } from '../../styles/vector/vector_style'; import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; @@ -25,7 +28,7 @@ export interface IESSource extends IVectorSource { loadStylePropsMeta( layerName: string, style: VectorStyle, - dynamicStyleProps: IDynamicStyleProperty[], + dynamicStyleProps: Array>, registerCancelCallback: (requestToken: symbol, callback: () => void) => void, searchFilters: VectorSourceRequestMeta ): Promise; @@ -45,7 +48,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource loadStylePropsMeta( layerName: string, style: VectorStyle, - dynamicStyleProps: IDynamicStyleProperty[], + dynamicStyleProps: Array>, registerCancelCallback: (requestToken: symbol, callback: () => void) => void, searchFilters: VectorSourceRequestMeta ): Promise; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/field_meta/categorical_field_meta_popover.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/field_meta/categorical_field_meta_popover.tsx index fac002d0a877de1..e49c15c68b8db9d 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/field_meta/categorical_field_meta_popover.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/field_meta/categorical_field_meta_popover.tsx @@ -9,18 +9,17 @@ import React from 'react'; import { EuiFormRow, EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FieldMetaPopover } from './field_meta_popover'; -import { IDynamicStyleProperty } from '../../properties/dynamic_style_property'; import { FieldMetaOptions } from '../../../../../../common/descriptor_types'; type Props = { - styleProperty: IDynamicStyleProperty; + fieldMetaOptions: FieldMetaOptions; onChange: (fieldMetaOptions: FieldMetaOptions) => void; }; export function CategoricalFieldMetaPopover(props: Props) { const onIsEnabledChange = (event: EuiSwitchEvent) => { props.onChange({ - ...props.styleProperty.getFieldMetaOptions(), + ...props.fieldMetaOptions, isEnabled: event.target.checked, }); }; @@ -32,7 +31,7 @@ export function CategoricalFieldMetaPopover(props: Props) { label={i18n.translate('xpack.maps.styles.fieldMetaOptions.isEnabled.categoricalLabel', { defaultMessage: 'Get categories from indices', })} - checked={props.styleProperty.getFieldMetaOptions().isEnabled} + checked={props.fieldMetaOptions.isEnabled} onChange={onIsEnabledChange} compressed /> diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/field_meta/ordinal_field_meta_popover.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/field_meta/ordinal_field_meta_popover.tsx index 32a54a45ed4d7ca..9086c4df3159673 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/field_meta/ordinal_field_meta_popover.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/field_meta/ordinal_field_meta_popover.tsx @@ -11,7 +11,6 @@ import { EuiFormRow, EuiRange, EuiSwitch, EuiSwitchEvent } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { DEFAULT_SIGMA } from '../../vector_style_defaults'; import { FieldMetaPopover } from './field_meta_popover'; -import { IDynamicStyleProperty } from '../../properties/dynamic_style_property'; import { FieldMetaOptions } from '../../../../../../common/descriptor_types'; import { VECTOR_STYLES } from '../../../../../../common/constants'; @@ -38,21 +37,22 @@ function getIsEnableToggleLabel(styleName: string) { } type Props = { - styleProperty: IDynamicStyleProperty; + fieldMetaOptions: FieldMetaOptions; + styleName: VECTOR_STYLES; onChange: (fieldMetaOptions: FieldMetaOptions) => void; }; export function OrdinalFieldMetaPopover(props: Props) { const onIsEnabledChange = (event: EuiSwitchEvent) => { props.onChange({ - ...props.styleProperty.getFieldMetaOptions(), + ...props.fieldMetaOptions, isEnabled: event.target.checked, }); }; const onSigmaChange = (event: ChangeEvent | MouseEvent) => { props.onChange({ - ...props.styleProperty.getFieldMetaOptions(), + ...props.fieldMetaOptions, sigma: parseInt(event.currentTarget.value, 10), }); }; @@ -62,8 +62,8 @@ export function OrdinalFieldMetaPopover(props: Props) { @@ -79,9 +79,9 @@ export function OrdinalFieldMetaPopover(props: Props) { min={1} max={5} step={0.25} - value={_.get(props.styleProperty.getFieldMetaOptions(), 'sigma', DEFAULT_SIGMA)} + value={_.get(props.fieldMetaOptions, 'sigma', DEFAULT_SIGMA)} onChange={onSigmaChange} - disabled={!props.styleProperty.getFieldMetaOptions().isEnabled} + disabled={!props.fieldMetaOptions.isEnabled} showTicks tickInterval={1} compressed diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.test.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.test.tsx index 4e68baf0bd7b7f7..4d9579e9e4c00dd 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.test.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.test.tsx @@ -29,6 +29,7 @@ import { shallow } from 'enzyme'; import { FIELD_ORIGIN } from '../../../../../../common/constants'; import { AbstractField } from '../../../../fields/field'; import { IDynamicStyleProperty } from '../../properties/dynamic_style_property'; +import { IconDynamicOptions } from '../../../../../../common/descriptor_types'; import { IconMapSelect } from './icon_map_select'; class MockField extends AbstractField {} @@ -46,7 +47,9 @@ class MockDynamicStyleProperty { const defaultProps = { iconPaletteId: 'filledShapes', onChange: () => {}, - styleProperty: (new MockDynamicStyleProperty() as unknown) as IDynamicStyleProperty, + styleProperty: (new MockDynamicStyleProperty() as unknown) as IDynamicStyleProperty< + IconDynamicOptions + >, isCustomOnly: false, }; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.tsx b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.tsx index 1dd55bbb47f78a1..1a908f4c014e492 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/components/symbol/icon_map_select.tsx @@ -12,7 +12,7 @@ import { i18n } from '@kbn/i18n'; import { IconStops } from './icon_stops'; // @ts-expect-error import { getIconPaletteOptions, PREFERRED_ICONS } from '../../symbol_utils'; -import { IconStop } from '../../../../../../common/descriptor_types'; +import { IconDynamicOptions, IconStop } from '../../../../../../common/descriptor_types'; import { IDynamicStyleProperty } from '../../properties/dynamic_style_property'; const CUSTOM_MAP_ID = 'CUSTOM_MAP_ID'; @@ -32,7 +32,7 @@ interface Props { customIconStops?: IconStop[]; iconPaletteId: string | null; onChange: ({ customIconStops, iconPaletteId, useCustomIconMap }: StyleOptionChanges) => void; - styleProperty: IDynamicStyleProperty; + styleProperty: IDynamicStyleProperty; useCustomIconMap?: boolean; isCustomOnly: boolean; } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.js b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.js index 7992ee5b3aeaf85..2183a298a28417d 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.js @@ -187,7 +187,7 @@ test('Should pluck the categorical style-meta from fieldmeta', async () => { colorCategory: 'palette_0', }); - const meta = colorStyle.pluckCategoricalStyleMetaFromFieldMetaData({ + const meta = colorStyle._pluckCategoricalStyleMetaFromFieldMetaData({ foobar: { buckets: [ { diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.d.ts b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.d.ts deleted file mode 100644 index e153b6e4850f799..000000000000000 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.d.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; - * you may not use this file except in compliance with the Elastic License. - */ -/* eslint-disable @typescript-eslint/consistent-type-definitions */ - -import { IStyleProperty } from './style_property'; -import { FIELD_ORIGIN } from '../../../../../common/constants'; -import { - CategoryFieldMeta, - DynamicStylePropertyOptions, - FieldMetaOptions, - RangeFieldMeta, -} from '../../../../../common/descriptor_types'; -import { IField } from '../../../fields/field'; - -export interface IDynamicStyleProperty extends IStyleProperty { - getOptions(): DynamicStylePropertyOptions; - getFieldMetaOptions(): FieldMetaOptions; - getField(): IField | undefined; - getFieldName(): string; - getFieldOrigin(): FIELD_ORIGIN | undefined; - getRangeFieldMeta(): RangeFieldMeta; - getCategoryFieldMeta(): CategoryFieldMeta; - getNumberOfCategories(): number; - isFieldMetaEnabled(): boolean; - isOrdinal(): boolean; - supportsFieldMeta(): boolean; - getFieldMetaRequest(): Promise; - supportsMbFeatureState(): boolean; - pluckOrdinalStyleMetaFromFeatures(features: unknown[]): RangeFieldMeta; - pluckCategoricalStyleMetaFromFeatures(features: unknown[]): CategoryFieldMeta; - pluckOrdinalStyleMetaFromFieldMetaData(fieldMetaData: unknown): RangeFieldMeta; - pluckCategoricalStyleMetaFromFieldMetaData(fieldMetaData: unknown): CategoryFieldMeta; - getValueSuggestions(query: string): string[]; -} diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.js b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx similarity index 54% rename from x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.js rename to x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx index 15d0b3c4bf91375..216fde595af3257 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx @@ -3,42 +3,87 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ import _ from 'lodash'; -import { AbstractStyleProperty } from './style_property'; +import React from 'react'; +import { Feature } from 'geojson'; +import { AbstractStyleProperty, IStyleProperty } from './style_property'; import { DEFAULT_SIGMA } from '../vector_style_defaults'; import { STYLE_TYPE, SOURCE_META_DATA_REQUEST_ID, FIELD_ORIGIN, + VECTOR_STYLES, } from '../../../../../common/constants'; -import React from 'react'; import { OrdinalFieldMetaPopover } from '../components/field_meta/ordinal_field_meta_popover'; import { CategoricalFieldMetaPopover } from '../components/field_meta/categorical_field_meta_popover'; +import { + CategoryFieldMeta, + FieldMetaOptions, + StyleMetaData, + RangeFieldMeta, +} from '../../../../../common/descriptor_types'; +import { IField } from '../../../fields/field'; +import { IVectorLayer } from '../../../layers/vector_layer/vector_layer'; +import { IJoin } from '../../../joins/join'; + +export interface IDynamicStyleProperty extends IStyleProperty { + getFieldMetaOptions(): FieldMetaOptions; + getField(): IField | null; + getFieldName(): string; + getFieldOrigin(): FIELD_ORIGIN | null; + getRangeFieldMeta(): RangeFieldMeta | null; + getCategoryFieldMeta(): CategoryFieldMeta | null; + getNumberOfCategories(): number; + isFieldMetaEnabled(): boolean; + isOrdinal(): boolean; + supportsFieldMeta(): boolean; + getFieldMetaRequest(): Promise; + supportsMbFeatureState(): boolean; + pluckOrdinalStyleMetaFromFeatures(features: Feature[]): RangeFieldMeta | null; + pluckCategoricalStyleMetaFromFeatures(features: Feature[]): CategoryFieldMeta | null; + getValueSuggestions(query: string): Promise; +} + +type fieldFormatter = (value: string | undefined) => string; -export class DynamicStyleProperty extends AbstractStyleProperty { +export class DynamicStyleProperty extends AbstractStyleProperty + implements IDynamicStyleProperty { static type = STYLE_TYPE.DYNAMIC; - constructor(options, styleName, field, vectorLayer, getFieldFormatter) { + protected readonly _field: IField | null; + protected readonly _layer: IVectorLayer; + protected readonly _getFieldFormatter: (fieldName: string) => null | fieldFormatter; + + constructor( + options: T, + styleName: VECTOR_STYLES, + field: IField | null, + vectorLayer: IVectorLayer, + getFieldFormatter: (fieldName: string) => null | fieldFormatter + ) { super(options, styleName); this._field = field; this._layer = vectorLayer; this._getFieldFormatter = getFieldFormatter; } - getValueSuggestions = (query) => { - const field = this.getField(); - const fieldSource = this._getFieldSource(); - return fieldSource && field ? fieldSource.getValueSuggestions(field, query) : []; + // ignore TS error about "Type '(query: string) => Promise | never[]' is not assignable to type '(query: string) => Promise'." + // @ts-expect-error + getValueSuggestions = (query: string) => { + return this._field === null + ? [] + : this._field.getSource().getValueSuggestions(this._field, query); }; - _getStyleMetaDataRequestId(fieldName) { + _getStyleMetaDataRequestId(fieldName: string) { if (this.getFieldOrigin() === FIELD_ORIGIN.SOURCE) { return SOURCE_META_DATA_REQUEST_ID; } - const join = this._layer.getValidJoins().find((join) => { - return join.getRightJoinSource().hasMatchingMetricField(fieldName); + const join = this._layer.getValidJoins().find((validJoin: IJoin) => { + return validJoin.getRightJoinSource().hasMatchingMetricField(fieldName); }); return join ? join.getSourceMetaDataRequestId() : null; } @@ -63,8 +108,8 @@ export class DynamicStyleProperty extends AbstractStyleProperty { return rangeFieldMetaFromLocalFeatures; } - const data = styleMetaDataRequest.getData(); - const rangeFieldMeta = this.pluckOrdinalStyleMetaFromFieldMetaData(data); + const data = styleMetaDataRequest.getData() as StyleMetaData; + const rangeFieldMeta = this._pluckOrdinalStyleMetaFromFieldMetaData(data); return rangeFieldMeta ? rangeFieldMeta : rangeFieldMetaFromLocalFeatures; } @@ -88,8 +133,8 @@ export class DynamicStyleProperty extends AbstractStyleProperty { return categoryFieldMetaFromLocalFeatures; } - const data = styleMetaDataRequest.getData(); - const rangeFieldMeta = this.pluckCategoricalStyleMetaFromFieldMetaData(data); + const data = styleMetaDataRequest.getData() as StyleMetaData; + const rangeFieldMeta = this._pluckCategoricalStyleMetaFromFieldMetaData(data); return rangeFieldMeta ? rangeFieldMeta : categoryFieldMetaFromLocalFeatures; } @@ -97,10 +142,6 @@ export class DynamicStyleProperty extends AbstractStyleProperty { return this._field; } - _getFieldSource() { - return this._field ? this._field.getSource() : null; - } - getFieldName() { return this._field ? this._field.getName() : ''; } @@ -126,7 +167,7 @@ export class DynamicStyleProperty extends AbstractStyleProperty { } getFieldOrigin() { - return this._field.getOrigin(); + return this._field ? this._field.getOrigin() : null; } isFieldMetaEnabled() { @@ -135,10 +176,14 @@ export class DynamicStyleProperty extends AbstractStyleProperty { } supportsFieldMeta() { - return this.isComplete() && this._field.supportsFieldMeta(); + return this.isComplete() && !!this._field && this._field.supportsFieldMeta(); } async getFieldMetaRequest() { + if (!this._field) { + return null; + } + if (this.isOrdinal()) { return this._field.getOrdinalFieldMetaRequest(); } else if (this.isCategorical()) { @@ -154,20 +199,20 @@ export class DynamicStyleProperty extends AbstractStyleProperty { } getFieldMetaOptions() { - return _.get(this.getOptions(), 'fieldMetaOptions', {}); + return _.get(this.getOptions(), 'fieldMetaOptions', { isEnabled: true }); } - pluckOrdinalStyleMetaFromFeatures(features) { + pluckOrdinalStyleMetaFromFeatures(features: Feature[]) { if (!this.isOrdinal()) { return null; } - const name = this.getField().getName(); + const name = this.getFieldName(); let min = Infinity; let max = -Infinity; for (let i = 0; i < features.length; i++) { const feature = features[i]; - const newValue = parseFloat(feature.properties[name]); + const newValue = parseFloat(feature.properties ? feature.properties[name] : null); if (!isNaN(newValue)) { min = Math.min(min, newValue); max = Math.max(max, newValue); @@ -176,25 +221,24 @@ export class DynamicStyleProperty extends AbstractStyleProperty { return min === Infinity || max === -Infinity ? null - : { - min: min, - max: max, + : ({ + min, + max, delta: max - min, - }; + } as RangeFieldMeta); } - pluckCategoricalStyleMetaFromFeatures(features) { + pluckCategoricalStyleMetaFromFeatures(features: Feature[]) { const size = this.getNumberOfCategories(); if (!this.isCategorical() || size <= 0) { return null; } - const fieldName = this.getField().getName(); const counts = new Map(); for (let i = 0; i < features.length; i++) { const feature = features[i]; - const term = feature.properties[fieldName]; - //properties object may be sparse, so need to check if the field is effectively present + const term = feature.properties ? feature.properties[this.getFieldName()] : undefined; + // properties object may be sparse, so need to check if the field is effectively present if (typeof term !== undefined) { if (counts.has(term)) { counts.set(term, counts.get(term) + 1); @@ -215,16 +259,16 @@ export class DynamicStyleProperty extends AbstractStyleProperty { const truncated = ordered.slice(0, size); return { categories: truncated, - }; + } as CategoryFieldMeta; } - pluckOrdinalStyleMetaFromFieldMetaData(fieldMetaData) { - if (!this.isOrdinal()) { + _pluckOrdinalStyleMetaFromFieldMetaData(styleMetaData: StyleMetaData) { + if (!this.isOrdinal() || !this._field) { return null; } - const stats = fieldMetaData[this._field.getRootName()]; - if (!stats) { + const stats = styleMetaData[this._field.getRootName()]; + if (!stats || !('avg' in stats)) { return null; } @@ -242,55 +286,56 @@ export class DynamicStyleProperty extends AbstractStyleProperty { }; } - pluckCategoricalStyleMetaFromFieldMetaData(fieldMetaData) { - if (!this.isCategorical()) { + _pluckCategoricalStyleMetaFromFieldMetaData(styleMetaData: StyleMetaData) { + if (!this.isCategorical() || !this._field) { return null; } - const rootFieldName = this._field.getRootName(); - if (!fieldMetaData[rootFieldName] || !fieldMetaData[rootFieldName].buckets) { + const fieldMeta = styleMetaData[this._field.getRootName()]; + if (!fieldMeta || !('buckets' in fieldMeta)) { return null; } - const ordered = fieldMetaData[rootFieldName].buckets.map((bucket) => { - return { - key: bucket.key, - count: bucket.doc_count, - }; - }); return { - categories: ordered, + categories: fieldMeta.buckets.map((bucket) => { + return { + key: bucket.key, + count: bucket.doc_count, + }; + }), }; } - formatField(value) { + formatField(value: string | undefined): string { if (this.getField()) { - const fieldName = this.getField().getName(); + const fieldName = this.getFieldName(); const fieldFormatter = this._getFieldFormatter(fieldName); - return fieldFormatter ? fieldFormatter(value) : value; + return fieldFormatter ? fieldFormatter(value) : super.formatField(value); } else { - return value; + return super.formatField(value); } } - getNumericalMbFeatureStateValue(value) { - const valueAsFloat = parseFloat(value); - return isNaN(valueAsFloat) ? null : valueAsFloat; - } - renderLegendDetailRow() { return null; } - renderFieldMetaPopover(onFieldMetaOptionsChange) { + renderFieldMetaPopover(onFieldMetaOptionsChange: (fieldMetaOptions: FieldMetaOptions) => void) { if (!this.supportsFieldMeta()) { return null; } return this.isCategorical() ? ( - + ) : ( - + ); } } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/style_property.ts b/x-pack/plugins/maps/public/classes/styles/vector/properties/style_property.ts index b704e4bd5697035..7a0ed4fb3e9625c 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/style_property.ts +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/style_property.ts @@ -8,7 +8,7 @@ import { ReactElement } from 'react'; // @ts-ignore import { getVectorStyleLabel } from '../components/get_vector_style_label'; -import { FieldMetaOptions, StylePropertyOptions } from '../../../../../common/descriptor_types'; +import { FieldMetaOptions } from '../../../../../common/descriptor_types'; import { VECTOR_STYLES } from '../../../../../common/constants'; type LegendProps = { @@ -17,12 +17,12 @@ type LegendProps = { symbolId?: string; }; -export interface IStyleProperty { +export interface IStyleProperty { isDynamic(): boolean; isComplete(): boolean; formatField(value: string | undefined): string; getStyleName(): VECTOR_STYLES; - getOptions(): StylePropertyOptions; + getOptions(): T; renderLegendDetailRow(legendProps: LegendProps): ReactElement | null; renderFieldMetaPopover( onFieldMetaOptionsChange: (fieldMetaOptions: FieldMetaOptions) => void @@ -30,11 +30,11 @@ export interface IStyleProperty { getDisplayStyleName(): string; } -export class AbstractStyleProperty implements IStyleProperty { - private readonly _options: StylePropertyOptions; - private readonly _styleName: VECTOR_STYLES; +export class AbstractStyleProperty implements IStyleProperty { + protected readonly _options: T; + protected readonly _styleName: VECTOR_STYLES; - constructor(options: StylePropertyOptions, styleName: VECTOR_STYLES) { + constructor(options: T, styleName: VECTOR_STYLES) { this._options = options; this._styleName = styleName; } @@ -62,15 +62,17 @@ export class AbstractStyleProperty implements IStyleProperty { return this._styleName; } - getOptions(): StylePropertyOptions { - return this._options || {}; + getOptions(): T { + return this._options; } renderLegendDetailRow() { return null; } - renderFieldMetaPopover() { + renderFieldMetaPopover( + onFieldMetaOptionsChange: (fieldMetaOptions: FieldMetaOptions) => void + ): ReactElement | null { return null; } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.d.ts b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.d.ts index ea0736c4837d881..d48d075288a28ef 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.d.ts +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.d.ts @@ -9,14 +9,16 @@ import { IVectorLayer } from '../../layers/vector_layer/vector_layer'; import { IVectorSource } from '../../sources/vector_source'; import { AbstractStyle, IStyle } from '../style'; import { + DynamicStylePropertyOptions, + StylePropertyOptions, VectorStyleDescriptor, VectorStylePropertiesDescriptor, } from '../../../../common/descriptor_types'; import { StyleMeta } from './style_meta'; export interface IVectorStyle extends IStyle { - getAllStyleProperties(): IStyleProperty[]; - getDynamicPropertiesArray(): IDynamicStyleProperty[]; + getAllStyleProperties(): Array>; + getDynamicPropertiesArray(): Array>; getSourceFieldNames(): string[]; getStyleMeta(): StyleMeta; } @@ -26,7 +28,7 @@ export class VectorStyle extends AbstractStyle implements IVectorStyle { static createDefaultStyleProperties(mapColors: string[]): VectorStylePropertiesDescriptor; constructor(descriptor: VectorStyleDescriptor, source: IVectorSource, layer: IVectorLayer); getSourceFieldNames(): string[]; - getAllStyleProperties(): IStyleProperty[]; - getDynamicPropertiesArray(): IDynamicStyleProperty[]; + getAllStyleProperties(): Array>; + getDynamicPropertiesArray(): Array>; getStyleMeta(): StyleMeta; } diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.js b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.js index 3cff48e4d682ed3..907e16a6a842725 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.js @@ -48,6 +48,11 @@ const POINTS = [GEO_JSON_TYPE.POINT, GEO_JSON_TYPE.MULTI_POINT]; const LINES = [GEO_JSON_TYPE.LINE_STRING, GEO_JSON_TYPE.MULTI_LINE_STRING]; const POLYGONS = [GEO_JSON_TYPE.POLYGON, GEO_JSON_TYPE.MULTI_POLYGON]; +function getNumericalMbFeatureStateValue(value) { + const valueAsFloat = parseFloat(value); + return isNaN(valueAsFloat) ? null : valueAsFloat; +} + export class VectorStyle extends AbstractStyle { static type = LAYER_STYLE_TYPE.VECTOR; @@ -518,14 +523,14 @@ export class VectorStyle extends AbstractStyle { const computedName = getComputedFieldName(dynamicStyleProp.getStyleName(), name); const rawValue = feature.properties[name]; if (dynamicStyleProp.supportsMbFeatureState()) { - tmpFeatureState[name] = dynamicStyleProp.getNumericalMbFeatureStateValue(rawValue); //the same value will be potentially overridden multiple times, if the name remains identical + tmpFeatureState[name] = getNumericalMbFeatureStateValue(rawValue); //the same value will be potentially overridden multiple times, if the name remains identical } else { //in practice, a new system property will only be created for: // - label text: this requires the value to be formatted first. // - icon orientation: this is a lay-out property which do not support feature-state (but we're still coercing to a number) const formattedValue = dynamicStyleProp.isOrdinal() - ? dynamicStyleProp.getNumericalMbFeatureStateValue(rawValue) + ? getNumericalMbFeatureStateValue(rawValue) : dynamicStyleProp.formatField(rawValue); feature.properties[computedName] = formattedValue; diff --git a/x-pack/plugins/maps/public/routing/routes/maps_app/load_map_and_render.js b/x-pack/plugins/maps/public/routing/routes/maps_app/load_map_and_render.js index c87f6eb330531d6..3eea5b00d324e35 100644 --- a/x-pack/plugins/maps/public/routing/routes/maps_app/load_map_and_render.js +++ b/x-pack/plugins/maps/public/routing/routes/maps_app/load_map_and_render.js @@ -7,7 +7,7 @@ import React from 'react'; import { MapsAppView } from '.'; import { getMapsSavedObjectLoader } from '../../bootstrap/services/gis_map_saved_object_loader'; -import { getToasts } from '../../../kibana_services'; +import { getCoreChrome, getToasts } from '../../../kibana_services'; import { i18n } from '@kbn/i18n'; import { Redirect } from 'react-router-dom'; @@ -30,6 +30,10 @@ export const LoadMapAndRender = class extends React.Component { try { const savedMap = await getMapsSavedObjectLoader().get(this.props.savedMapId); if (this._isMounted) { + getCoreChrome().docTitle.change(savedMap.title); + if (this.props.savedMapId) { + getCoreChrome().recentlyAccessed.add(savedMap.getFullPath(), savedMap.title, savedMap.id); + } this.setState({ savedMap }); } } catch (err) { diff --git a/x-pack/plugins/maps/public/routing/routes/maps_app/maps_app_view.js b/x-pack/plugins/maps/public/routing/routes/maps_app/maps_app_view.js index 29fbb5f46e29bbd..aa7f24155ab4304 100644 --- a/x-pack/plugins/maps/public/routing/routes/maps_app/maps_app_view.js +++ b/x-pack/plugins/maps/public/routing/routes/maps_app/maps_app_view.js @@ -56,11 +56,6 @@ export class MapsAppView extends React.Component { } componentDidMount() { - const { savedMap } = this.props; - - getCoreChrome().docTitle.change(savedMap.title); - getCoreChrome().recentlyAccessed.add(savedMap.getFullPath(), savedMap.title, savedMap.id); - // Init sync utils // eslint-disable-next-line react-hooks/rules-of-hooks this._globalSyncUnsubscribe = useGlobalStateSyncing(); diff --git a/x-pack/plugins/ml/common/types/anomaly_detection_jobs/summary_job.ts b/x-pack/plugins/ml/common/types/anomaly_detection_jobs/summary_job.ts index 6cf109dc553ae57..2102673060273f9 100644 --- a/x-pack/plugins/ml/common/types/anomaly_detection_jobs/summary_job.ts +++ b/x-pack/plugins/ml/common/types/anomaly_detection_jobs/summary_job.ts @@ -30,6 +30,7 @@ export interface MlSummaryJob { isSingleMetricViewerJob: boolean; deleting?: boolean; latestTimestampSortValue?: number; + earliestStartTimestampMs?: number; } export interface AuditMessage { diff --git a/x-pack/plugins/ml/common/util/job_utils.test.ts b/x-pack/plugins/ml/common/util/job_utils.test.ts index 233e2c2cd19ac64..a56ccd5208babb5 100644 --- a/x-pack/plugins/ml/common/util/job_utils.test.ts +++ b/x-pack/plugins/ml/common/util/job_utils.test.ts @@ -18,8 +18,10 @@ import { prefixDatafeedId, getSafeAggregationName, getLatestDataOrBucketTimestamp, + getEarliestDatafeedStartTime, } from './job_utils'; import { CombinedJob, Job } from '../types/anomaly_detection_jobs'; +import moment from 'moment'; describe('ML - job utils', () => { describe('calculateDatafeedFrequencyDefaultSeconds', () => { @@ -581,4 +583,22 @@ describe('ML - job utils', () => { expect(getLatestDataOrBucketTimestamp(undefined, undefined)).toBe(undefined); }); }); + + describe('getEarliestDatafeedStartTime', () => { + test('returns expected value when no gap in data at end of bucket processing', () => { + expect(getEarliestDatafeedStartTime(1549929594000, 1549928700000)).toBe(1549929594000); + }); + test('returns expected value when there is a gap in data at end of bucket processing', () => { + expect(getEarliestDatafeedStartTime(1549929594000, 1562256600000)).toBe(1562256600000); + }); + test('returns expected value when bucket span is provided', () => { + expect( + getEarliestDatafeedStartTime(1549929594000, 1562256600000, moment.duration(1, 'h')) + ).toBe(1562260200000); + }); + + test('returns expected value when job has not run', () => { + expect(getLatestDataOrBucketTimestamp(undefined, undefined)).toBe(undefined); + }); + }); }); diff --git a/x-pack/plugins/ml/common/util/job_utils.ts b/x-pack/plugins/ml/common/util/job_utils.ts index 7ea4ceccf578d50..bb0e351ebfec832 100644 --- a/x-pack/plugins/ml/common/util/job_utils.ts +++ b/x-pack/plugins/ml/common/util/job_utils.ts @@ -6,7 +6,7 @@ import _ from 'lodash'; import semver from 'semver'; -import { Duration } from 'moment'; +import moment, { Duration } from 'moment'; // @ts-ignore import numeral from '@elastic/numeral'; @@ -621,6 +621,23 @@ function isValidTimeInterval(value: string | undefined): boolean { return parseTimeIntervalForJob(value) !== null; } +// The earliest start time for the datafeed should be the max(latest_record_timestamp, latest_bucket.timestamp + bucket_span). +export function getEarliestDatafeedStartTime( + latestRecordTimestamp: number | undefined, + latestBucketTimestamp: number | undefined, + bucketSpan?: Duration | null | undefined +): number | undefined { + if (latestRecordTimestamp !== undefined && latestBucketTimestamp !== undefined) { + // if bucket span is available (e.g. 15m) add it to the latest bucket timestamp in ms + const adjustedBucketStartTime = bucketSpan + ? moment(latestBucketTimestamp).add(bucketSpan).valueOf() + : latestBucketTimestamp; + return Math.max(latestRecordTimestamp, adjustedBucketStartTime); + } else { + return latestRecordTimestamp !== undefined ? latestRecordTimestamp : latestBucketTimestamp; + } +} + // Returns the latest of the last source data and last processed bucket timestamp, // as used for example in setting the end time of results views for cases where // anomalies might have been raised after the point at which data ingest has stopped. diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js index 9ce15fb881bd873..d0d3dc56ababfab 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js @@ -222,6 +222,6 @@ StartDatafeedModal.propTypes = { }; function getLowestLatestTime(jobs) { - const times = jobs.map((j) => j.latestTimestampSortValue); + const times = jobs.map((j) => j.earliestStartTimestampMs || 0); return moment(Math.min(...times)); } diff --git a/x-pack/plugins/ml/server/models/job_service/jobs.ts b/x-pack/plugins/ml/server/models/job_service/jobs.ts index aca0c5d72a9f5a5..e9ed2d0941d96f3 100644 --- a/x-pack/plugins/ml/server/models/job_service/jobs.ts +++ b/x-pack/plugins/ml/server/models/job_service/jobs.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import { uniq } from 'lodash'; import Boom from 'boom'; import { ILegacyScopedClusterClient } from 'kibana/server'; +import { parseTimeIntervalForJob } from '../../../common/util/job_utils'; import { JOB_STATE, DATAFEED_STATE } from '../../../common/constants/states'; import { MlSummaryJob, @@ -24,11 +25,11 @@ import { resultsServiceProvider } from '../results_service'; import { CalendarManager, Calendar } from '../calendar'; import { fillResultsWithTimeouts, isRequestTimeout } from './error_utils'; import { + getEarliestDatafeedStartTime, getLatestDataOrBucketTimestamp, isTimeSeriesViewJob, } from '../../../common/util/job_utils'; import { groupsProvider } from './groups'; - export interface MlJobsResponse { jobs: Job[]; count: number; @@ -171,6 +172,11 @@ export function jobsProvider(mlClusterClient: ILegacyScopedClusterClient) { description: job.description || '', groups: Array.isArray(job.groups) ? job.groups.sort() : [], processed_record_count: job.data_counts?.processed_record_count, + earliestStartTimestampMs: getEarliestDatafeedStartTime( + dataCounts?.latest_record_timestamp, + dataCounts?.latest_bucket_timestamp, + parseTimeIntervalForJob(job.analysis_config?.bucket_span) + ), memory_status: job.model_size_stats ? job.model_size_stats.memory_status : '', jobState: job.deleting === true ? deletingStr : job.state, hasDatafeed, @@ -182,8 +188,8 @@ export function jobsProvider(mlClusterClient: ILegacyScopedClusterClient) { latestTimestampMs: dataCounts?.latest_record_timestamp, earliestTimestampMs: dataCounts?.earliest_record_timestamp, latestResultsTimestampMs: getLatestDataOrBucketTimestamp( - dataCounts?.latest_record_timestamp as number, - dataCounts?.latest_bucket_timestamp as number + dataCounts?.latest_record_timestamp, + dataCounts?.latest_bucket_timestamp ), isSingleMetricViewerJob: isTimeSeriesViewJob(job), nodeName: job.node ? job.node.name : undefined, diff --git a/x-pack/plugins/monitoring/public/alerts/callout.tsx b/x-pack/plugins/monitoring/public/alerts/callout.tsx index 748ec257ea7650a..d000f470da33422 100644 --- a/x-pack/plugins/monitoring/public/alerts/callout.tsx +++ b/x-pack/plugins/monitoring/public/alerts/callout.tsx @@ -24,7 +24,7 @@ const TYPES = [ severity: AlertSeverity.Danger, color: 'danger', label: i18n.translate('xpack.monitoring.alerts.callout.dangerLabel', { - defaultMessage: 'DAnger alert(s)', + defaultMessage: 'Danger alert(s)', }), }, ]; diff --git a/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_duration.tsx b/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_duration.tsx index 23a9ea1facbc9cb..862f32efd7361c7 100644 --- a/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_duration.tsx +++ b/x-pack/plugins/monitoring/public/alerts/flyout_expressions/alert_param_duration.tsx @@ -49,7 +49,7 @@ interface Props { setAlertParams: (property: string, value: any) => void; } -const parseRegex = /(\d+)(\smhd)/; +const parseRegex = /(\d+)([smhd]{1})/; export const AlertParamDuration: React.FC = (props: Props) => { const { name, label, setAlertParams, errors } = props; const parsed = parseRegex.exec(props.duration); diff --git a/x-pack/plugins/monitoring/public/alerts/lib/should_show_alert_badge.ts b/x-pack/plugins/monitoring/public/alerts/lib/should_show_alert_badge.ts index c6773e9ca0156a8..6de371b53ef9b3d 100644 --- a/x-pack/plugins/monitoring/public/alerts/lib/should_show_alert_badge.ts +++ b/x-pack/plugins/monitoring/public/alerts/lib/should_show_alert_badge.ts @@ -10,6 +10,9 @@ export function shouldShowAlertBadge( alerts: { [alertTypeId: string]: CommonAlertStatus }, alertTypeIds: string[] ) { + if (!alerts) { + return false; + } const inSetupMode = isInSetupMode(); return inSetupMode || alertTypeIds.find((name) => alerts[name] && alerts[name].states.length); } diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.test.ts index 8fd31db421a3099..c256cce362ff825 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.test.ts @@ -63,7 +63,7 @@ describe('BaseAlert', () => { interval: '1m', }, tags: [], - throttle: '1m', + throttle: '1d', }, }); }); diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.ts index 622ee7dc51af14a..cac57f599633d73 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.ts @@ -40,7 +40,7 @@ import { MonitoringLicenseService } from '../types'; export class BaseAlert { public type!: string; public label!: string; - public defaultThrottle: string = '1m'; + public defaultThrottle: string = '1d'; public defaultInterval: string = '1m'; public rawAlert: Alert | undefined; public isLegacy: boolean = false; diff --git a/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.test.ts index 10b75c43ac87981..f25179fa63c2fab 100644 --- a/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/cluster_health_alert.test.ts @@ -22,7 +22,7 @@ describe('ClusterHealthAlert', () => { const alert = new ClusterHealthAlert(); expect(alert.type).toBe(ALERT_CLUSTER_HEALTH); expect(alert.label).toBe('Cluster health'); - expect(alert.defaultThrottle).toBe('1m'); + expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ { diff --git a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.test.ts index f0d11abab149241..1a66560ae124a70 100644 --- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.test.ts @@ -22,9 +22,9 @@ describe('CpuUsageAlert', () => { const alert = new CpuUsageAlert(); expect(alert.type).toBe(ALERT_CPU_USAGE); expect(alert.label).toBe('CPU Usage'); - expect(alert.defaultThrottle).toBe('1m'); + expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore - expect(alert.defaultParams).toStrictEqual({ threshold: 90, duration: '5m' }); + expect(alert.defaultParams).toStrictEqual({ threshold: 85, duration: '5m' }); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ { diff --git a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts index 9171745fba74707..b543a4c976377fb 100644 --- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts @@ -39,7 +39,7 @@ const FIRING = i18n.translate('xpack.monitoring.alerts.cpuUsage.firing', { defaultMessage: 'firing', }); -const DEFAULT_THRESHOLD = 90; +const DEFAULT_THRESHOLD = 85; const DEFAULT_DURATION = '5m'; interface CpuUsageParams { @@ -393,7 +393,16 @@ export class CpuUsageAlert extends BaseAlert { continue; } - const instance = services.alertInstanceFactory(`${this.type}:${cluster.clusterUuid}`); + const firingNodeUuids = nodes.reduce((list: string[], node) => { + const stat = node.meta as AlertCpuUsageNodeStats; + if (node.shouldFire) { + list.push(stat.nodeId); + } + return list; + }, [] as string[]); + firingNodeUuids.sort(); // It doesn't matter how we sort, but keep the order consistent + const instanceId = `${this.type}:${cluster.clusterUuid}:${firingNodeUuids.join(',')}`; + const instance = services.alertInstanceFactory(instanceId); const state = (instance.getState() as unknown) as AlertInstanceState; const alertInstanceState: AlertInstanceState = { alertStates: state?.alertStates || [] }; let shouldExecuteActions = false; diff --git a/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.test.ts index 44684939ca261ad..50bf40825c515f4 100644 --- a/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/elasticsearch_version_mismatch_alert.test.ts @@ -22,7 +22,7 @@ describe('ElasticsearchVersionMismatchAlert', () => { const alert = new ElasticsearchVersionMismatchAlert(); expect(alert.type).toBe(ALERT_ELASTICSEARCH_VERSION_MISMATCH); expect(alert.label).toBe('Elasticsearch version mismatch'); - expect(alert.defaultThrottle).toBe('1m'); + expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ { diff --git a/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.test.ts index 6c56c7aa08d7120..1a76fae9fc42073 100644 --- a/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/kibana_version_mismatch_alert.test.ts @@ -22,7 +22,7 @@ describe('KibanaVersionMismatchAlert', () => { const alert = new KibanaVersionMismatchAlert(); expect(alert.type).toBe(ALERT_KIBANA_VERSION_MISMATCH); expect(alert.label).toBe('Kibana version mismatch'); - expect(alert.defaultThrottle).toBe('1m'); + expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ { diff --git a/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.test.ts index 09173df1d88b1e2..0f677dcc9c12056 100644 --- a/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/license_expiration_alert.test.ts @@ -29,7 +29,7 @@ describe('LicenseExpirationAlert', () => { const alert = new LicenseExpirationAlert(); expect(alert.type).toBe(ALERT_LICENSE_EXPIRATION); expect(alert.label).toBe('License expiration'); - expect(alert.defaultThrottle).toBe('1m'); + expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ { diff --git a/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.test.ts index 3f6d38809a9492f..f29c199b3f1e173 100644 --- a/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/logstash_version_mismatch_alert.test.ts @@ -22,7 +22,7 @@ describe('LogstashVersionMismatchAlert', () => { const alert = new LogstashVersionMismatchAlert(); expect(alert.type).toBe(ALERT_LOGSTASH_VERSION_MISMATCH); expect(alert.label).toBe('Logstash version mismatch'); - expect(alert.defaultThrottle).toBe('1m'); + expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ { diff --git a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.test.ts index 13c3dbbbe6e8aea..d45d404b38304d0 100644 --- a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.test.ts @@ -29,7 +29,7 @@ describe('NodesChangedAlert', () => { const alert = new NodesChangedAlert(); expect(alert.type).toBe(ALERT_NODES_CHANGED); expect(alert.label).toBe('Nodes changed'); - expect(alert.defaultThrottle).toBe('1m'); + expect(alert.defaultThrottle).toBe('1d'); // @ts-ignore expect(alert.actionVariables).toStrictEqual([ { diff --git a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts index 5b38503c7ece458..73f3ee055c92850 100644 --- a/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/nodes_changed_alert.ts @@ -155,6 +155,21 @@ export class NodesChangedAlert extends BaseAlert { }; } + if ( + Object.values(states.added).length === 0 && + Object.values(states.removed).length === 0 && + Object.values(states.restarted).length === 0 + ) { + return { + text: i18n.translate( + 'xpack.monitoring.alerts.nodesChanged.ui.nothingDetectedFiringMessage', + { + defaultMessage: `Elasticsearch nodes have changed`, + } + ), + }; + } + const addedText = Object.values(states.added).length > 0 ? i18n.translate('xpack.monitoring.alerts.nodesChanged.ui.addedFiringMessage', { diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts index 1d83644fce756e4..b7cc088d2716c86 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/enable.ts @@ -18,7 +18,6 @@ export function enableAlertsRoute(server: any, npRoute: RouteDependencies) { npRoute.router.post( { path: '/api/monitoring/v1/alerts/enable', - options: { tags: ['access:monitoring'] }, validate: false, }, async (context, request, response) => { diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts index eef99bbc4ac6864..78daa5e47c49f19 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/status.ts @@ -15,7 +15,6 @@ export function alertStatusRoute(server: any, npRoute: RouteDependencies) { npRoute.router.post( { path: '/api/monitoring/v1/alert/{clusterUuid}/status', - options: { tags: ['access:monitoring'] }, validate: { params: schema.object({ clusterUuid: schema.string(), diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts index b3b74c2ca9dae5a..e7aa2c8893f8e51 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/reducer.ts @@ -21,6 +21,7 @@ export const initialPolicyDetailsState: () => Immutable = () offline: 0, online: 0, total: 0, + other: 0, }, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts index 0a24c9eea71ebb7..8203aae244f246a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/index.test.ts @@ -152,6 +152,7 @@ describe('policy list store concerns', () => { offline: 0, online: 0, total: 0, + other: 0, }, }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/reducer.ts index 52bed8d850ef428..53954449ab9c3a1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_list/reducer.ts @@ -31,6 +31,7 @@ export const initialPolicyListState: () => Immutable = () => ({ offline: 0, online: 0, total: 0, + other: 0, }, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx index 3b7865e59b9e6b9..3a2afff03c58ff0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.test.tsx @@ -36,6 +36,7 @@ describe('WebhookActionConnectorFields renders', () => { /> ); expect(wrapper.find('[data-test-subj="webhookViewHeadersSwitch"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="webhookHeaderText"]').length > 0).toBeTruthy(); wrapper.find('[data-test-subj="webhookViewHeadersSwitch"]').first().simulate('click'); expect(wrapper.find('[data-test-subj="webhookMethodSelect"]').length > 0).toBeTruthy(); expect(wrapper.find('[data-test-subj="webhookUrlText"]').length > 0).toBeTruthy(); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx index 57c88607c0884e0..2321d5b4b54793b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook_connectors.tsx @@ -31,14 +31,16 @@ const HTTP_VERBS = ['post', 'put']; const WebhookActionConnectorFields: React.FunctionComponent> = ({ action, editActionConfig, editActionSecrets, errors }) => { + const { user, password } = action.secrets; + const { method, url, headers } = action.config; + const [httpHeaderKey, setHttpHeaderKey] = useState(''); const [httpHeaderValue, setHttpHeaderValue] = useState(''); const [hasHeaders, setHasHeaders] = useState(false); - const { user, password } = action.secrets; - const { method, url, headers } = action.config; - - editActionConfig('method', 'post'); // set method to POST by default + if (!method) { + editActionConfig('method', 'post'); // set method to POST by default + } const headerErrors = { keyHeader: new Array(), @@ -80,7 +82,7 @@ const WebhookActionConnectorFields: React.FunctionComponent
- {hasHeaders && Object.keys(headers || {}).length > 0 ? ( - + {Object.keys(headers || {}).length > 0 ? ( + <>
@@ -351,10 +353,10 @@ const WebhookActionConnectorFields: React.FunctionComponent {headersList} - + ) : null} - {headerControl} + {hasHeaders && headerControl}
diff --git a/x-pack/test/functional/apps/discover/async_scripted_fields.js b/x-pack/test/functional/apps/discover/async_scripted_fields.js index 46848b1db0ef468..33a64e4f9cdd3e0 100644 --- a/x-pack/test/functional/apps/discover/async_scripted_fields.js +++ b/x-pack/test/functional/apps/discover/async_scripted_fields.js @@ -14,9 +14,9 @@ export default function ({ getService, getPageObjects }) { const esArchiver = getService('esArchiver'); const log = getService('log'); const testSubjects = getService('testSubjects'); - const PageObjects = getPageObjects(['common', 'settings', 'discover', 'timePicker']); const queryBar = getService('queryBar'); + const security = getService('security'); describe('async search with scripted fields', function () { this.tags(['skipFirefox']); @@ -24,6 +24,7 @@ export default function ({ getService, getPageObjects }) { before(async function () { await esArchiver.load('kibana_scripted_fields_on_logstash'); await esArchiver.loadIfNeeded('logstash_functional'); + await security.testUser.setRoles(['test_logstash_reader', 'global_discover_read']); // changing the timepicker default here saves us from having to set it in Discover (~8s) await kibanaServer.uiSettings.update({ 'timepicker:timeDefaults': @@ -36,6 +37,7 @@ export default function ({ getService, getPageObjects }) { await kibanaServer.uiSettings.update({}); await esArchiver.unload('logstash_functional'); await esArchiver.load('empty_kibana'); + await security.testUser.restoreDefaults(); }); it('query should show failed shards pop up', async function () {