From a9968cb18d3de6a6179cd8179557404f705ba5ef Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 19 Mar 2024 14:26:04 +0100 Subject: [PATCH] [ML] Decouple open-in-anomaly-explorer action (#178729) ## Summary Decouples open-in-anomaly-explorer UI action from embeddable framework. - Modifies and exports helper utils from the embeddable plugin to convert embeddable inputs and outputs to APIs - Updates anomaly swim lane and anomaly charts embeddables to expose required API for the "Open in Anomaly Explorer" action Part of https://github.com/elastic/kibana/issues/178375 --- src/plugins/embeddable/public/index.ts | 5 + .../embeddable_compatibility_utils.ts | 25 +-- .../anomaly_charts_embeddable.tsx | 18 ++- .../anomaly_swimlane_embeddable.tsx | 45 ++++-- .../common/anomaly_detection_embeddable.ts | 5 + x-pack/plugins/ml/public/embeddables/types.ts | 47 ++---- x-pack/plugins/ml/public/ui_actions/index.ts | 1 - .../open_in_anomaly_explorer_action.tsx | 143 +++++++++++++----- 8 files changed, 197 insertions(+), 92 deletions(-) diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts index 3214e18aeff26..52e408e8ebcd6 100644 --- a/src/plugins/embeddable/public/index.ts +++ b/src/plugins/embeddable/public/index.ts @@ -112,3 +112,8 @@ export { registerSavedObjectToPanelMethod } from './registry/saved_object_to_pan export function plugin(initializerContext: PluginInitializerContext) { return new EmbeddablePublicPlugin(initializerContext); } + +export { + embeddableInputToSubject, + embeddableOutputToSubject, +} from './lib/embeddables/compatibility/embeddable_compatibility_utils'; diff --git a/src/plugins/embeddable/public/lib/embeddables/compatibility/embeddable_compatibility_utils.ts b/src/plugins/embeddable/public/lib/embeddables/compatibility/embeddable_compatibility_utils.ts index fb4ce7e7bfa03..611654173b09f 100644 --- a/src/plugins/embeddable/public/lib/embeddables/compatibility/embeddable_compatibility_utils.ts +++ b/src/plugins/embeddable/public/lib/embeddables/compatibility/embeddable_compatibility_utils.ts @@ -15,6 +15,7 @@ import { map, Subscription, } from 'rxjs'; +import { IEmbeddable } from '../..'; import { Container } from '../../containers'; import { ViewMode as LegacyViewMode } from '../../types'; import { @@ -23,10 +24,13 @@ import { CommonLegacyOutput, } from './legacy_embeddable_to_api'; -export const embeddableInputToSubject = ( +export const embeddableInputToSubject = < + T extends unknown = unknown, + LegacyInput extends CommonLegacyInput = CommonLegacyInput +>( subscription: Subscription, - embeddable: CommonLegacyEmbeddable, - key: keyof CommonLegacyInput, + embeddable: IEmbeddable, + key: keyof LegacyInput, useExplicitInput = false ) => { const subject = new BehaviorSubject(embeddable.getExplicitInput()?.[key] as T); @@ -36,12 +40,10 @@ export const embeddableInputToSubject = ( .getInput$() .pipe( distinctUntilChanged((prev, current) => { - const previousValue = (prev.panels[embeddable.id]?.explicitInput as CommonLegacyInput)[ + const previousValue = (prev.panels[embeddable.id]?.explicitInput as LegacyInput)[key]; + const currentValue = (current.panels[embeddable.id]?.explicitInput as LegacyInput)?.[ key ]; - const currentValue = ( - current.panels[embeddable.id]?.explicitInput as CommonLegacyInput - )?.[key]; return deepEqual(previousValue, currentValue); }) ) @@ -58,10 +60,13 @@ export const embeddableInputToSubject = ( return subject; }; -export const embeddableOutputToSubject = ( +export const embeddableOutputToSubject = < + T extends unknown = unknown, + LegacyOutput extends CommonLegacyOutput = CommonLegacyOutput +>( subscription: Subscription, - embeddable: CommonLegacyEmbeddable, - key: keyof CommonLegacyOutput + embeddable: IEmbeddable, + key: keyof LegacyOutput ) => { const subject = new BehaviorSubject(embeddable.getOutput()[key] as T); subscription.add( diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx index e2524ee015200..1b8e77514e108 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/anomaly_charts_embeddable.tsx @@ -9,9 +9,11 @@ import React, { Suspense } from 'react'; import ReactDOM from 'react-dom'; import type { CoreStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; -import { Subject } from 'rxjs'; +import { Subject, Subscription, type BehaviorSubject } from 'rxjs'; import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import type { IContainer } from '@kbn/embeddable-plugin/public'; +import { embeddableOutputToSubject } from '@kbn/embeddable-plugin/public'; +import type { MlEntityField } from '@kbn/ml-anomaly-utils'; import { EmbeddableAnomalyChartsContainer } from './embeddable_anomaly_charts_container_lazy'; import type { JobId } from '../../../common/types/anomaly_detection_jobs'; import type { MlDependencies } from '../../application/app'; @@ -40,12 +42,23 @@ export class AnomalyChartsEmbeddable extends AnomalyDetectionEmbeddable< private reload$ = new Subject(); public readonly type: string = ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE; + // API + public entityFields: BehaviorSubject; + + private apiSubscriptions = new Subscription(); + constructor( initialInput: AnomalyChartsEmbeddableInput, public services: [CoreStart, MlDependencies, AnomalyChartsServices], parent?: IContainer ) { super(initialInput, services[2].anomalyDetectorService, services[1].data.dataViews, parent); + + this.entityFields = embeddableOutputToSubject( + this.apiSubscriptions, + this, + 'entityFields' + ); } public onLoading() { @@ -108,6 +121,9 @@ export class AnomalyChartsEmbeddable extends AnomalyDetectionEmbeddable< public destroy() { super.destroy(); + + this.apiSubscriptions.unsubscribe(); + if (this.node) { ReactDOM.unmountComponentAtNode(this.node); } diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx index 21892e8c5bae2..968d505dc46ee 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx @@ -5,25 +5,26 @@ * 2.0. */ -import React, { Suspense } from 'react'; -import ReactDOM from 'react-dom'; import type { CoreStart } from '@kbn/core/public'; +import type { IContainer } from '@kbn/embeddable-plugin/public'; +import { embeddableInputToSubject, embeddableOutputToSubject } from '@kbn/embeddable-plugin/public'; import { i18n } from '@kbn/i18n'; -import { Subject } from 'rxjs'; import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import type { IContainer } from '@kbn/embeddable-plugin/public'; -import { EmbeddableSwimLaneContainer } from './embeddable_swim_lane_container_lazy'; -import type { JobId } from '../../../common/types/anomaly_detection_jobs'; -import type { MlDependencies } from '../../application/app'; -import { SWIM_LANE_SELECTION_TRIGGER } from '../../ui_actions'; +import React, { Suspense } from 'react'; +import ReactDOM from 'react-dom'; +import { Subject, Subscription, type BehaviorSubject } from 'rxjs'; import type { AnomalySwimlaneEmbeddableInput, AnomalySwimlaneEmbeddableOutput, AnomalySwimlaneServices, } from '..'; import { ANOMALY_SWIMLANE_EMBEDDABLE_TYPE } from '..'; -import { EmbeddableLoading } from '../common/components/embeddable_loading_fallback'; +import type { JobId } from '../../../common/types/anomaly_detection_jobs'; +import type { MlDependencies } from '../../application/app'; +import { SWIM_LANE_SELECTION_TRIGGER } from '../../ui_actions'; import { AnomalyDetectionEmbeddable } from '../common/anomaly_detection_embeddable'; +import { EmbeddableLoading } from '../common/components/embeddable_loading_fallback'; +import { EmbeddableSwimLaneContainer } from './embeddable_swim_lane_container_lazy'; export const getDefaultSwimlanePanelTitle = (jobIds: JobId[]) => i18n.translate('xpack.ml.swimlaneEmbeddable.title', { @@ -41,12 +42,37 @@ export class AnomalySwimlaneEmbeddable extends AnomalyDetectionEmbeddable< private reload$ = new Subject(); public readonly type: string = ANOMALY_SWIMLANE_EMBEDDABLE_TYPE; + // API + public viewBy: BehaviorSubject; + public perPage: BehaviorSubject; + public fromPage: BehaviorSubject; + + private apiSubscriptions = new Subscription(); + constructor( initialInput: AnomalySwimlaneEmbeddableInput, public services: [CoreStart, MlDependencies, AnomalySwimlaneServices], parent?: IContainer ) { super(initialInput, services[2].anomalyDetectorService, services[1].data.dataViews, parent); + + this.viewBy = embeddableInputToSubject( + this.apiSubscriptions, + this, + 'viewBy' + ); + + this.perPage = embeddableOutputToSubject( + this.apiSubscriptions, + this, + 'perPage' + ); + + this.fromPage = embeddableOutputToSubject( + this.apiSubscriptions, + this, + 'fromPage' + ); } public reportsEmbeddableLoad() { @@ -104,6 +130,7 @@ export class AnomalySwimlaneEmbeddable extends AnomalyDetectionEmbeddable< } public destroy() { + this.apiSubscriptions.unsubscribe(); super.destroy(); if (this.node) { ReactDOM.unmountComponentAtNode(this.node); diff --git a/x-pack/plugins/ml/public/embeddables/common/anomaly_detection_embeddable.ts b/x-pack/plugins/ml/public/embeddables/common/anomaly_detection_embeddable.ts index 0b24a5b47b18c..08c00273c44a0 100644 --- a/x-pack/plugins/ml/public/embeddables/common/anomaly_detection_embeddable.ts +++ b/x-pack/plugins/ml/public/embeddables/common/anomaly_detection_embeddable.ts @@ -15,6 +15,7 @@ import { type DataView } from '@kbn/data-views-plugin/common'; import { type DataViewsContract } from '@kbn/data-views-plugin/public'; import { firstValueFrom } from 'rxjs'; import { type AnomalyDetectorService } from '../../application/services/anomaly_detector_service'; +import type { JobId } from '../../shared'; export type CommonInput = { jobIds: string[] } & EmbeddableInput; @@ -27,6 +28,8 @@ export abstract class AnomalyDetectionEmbeddable< // Need to defer embeddable load in order to resolve data views deferEmbeddableLoad = true; + public jobIds: JobId[] = []; + protected constructor( initialInput: Input, private anomalyDetectorService: AnomalyDetectorService, @@ -43,6 +46,8 @@ export abstract class AnomalyDetectionEmbeddable< protected async initializeOutput(initialInput: CommonInput) { const { jobIds } = initialInput; + this.jobIds = jobIds; + try { const jobs = await firstValueFrom(this.anomalyDetectorService.getJobs$(jobIds)); diff --git a/x-pack/plugins/ml/public/embeddables/types.ts b/x-pack/plugins/ml/public/embeddables/types.ts index 961fe0b78799a..a0bd799352d4f 100644 --- a/x-pack/plugins/ml/public/embeddables/types.ts +++ b/x-pack/plugins/ml/public/embeddables/types.ts @@ -6,34 +6,29 @@ */ import type { CoreStart } from '@kbn/core/public'; -import type { Filter, Query, TimeRange } from '@kbn/es-query'; import type { RefreshInterval } from '@kbn/data-plugin/common'; -import type { EmbeddableInput, EmbeddableOutput, IEmbeddable } from '@kbn/embeddable-plugin/public'; import type { DataView } from '@kbn/data-views-plugin/common'; -import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { EmbeddableInput, EmbeddableOutput, IEmbeddable } from '@kbn/embeddable-plugin/public'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; import type { MlEntityField } from '@kbn/ml-anomaly-utils'; import type { JobId } from '../../common/types/anomaly_detection_jobs'; -import type { SwimlaneType } from '../application/explorer/explorer_constants'; -import type { AnomalyDetectorService } from '../application/services/anomaly_detector_service'; -import type { AnomalyTimelineService } from '../application/services/anomaly_timeline_service'; import type { MlDependencies } from '../application/app'; +import type { MlCapabilitiesService } from '../application/capabilities/check_capabilities'; +import type { SwimlaneType } from '../application/explorer/explorer_constants'; import type { AppStateSelectedCells } from '../application/explorer/explorer_utils'; +import type { AnomalyDetectorService } from '../application/services/anomaly_detector_service'; import type { AnomalyExplorerChartsService } from '../application/services/anomaly_explorer_charts_service'; +import type { AnomalyTimelineService } from '../application/services/anomaly_timeline_service'; +import type { MlFieldFormatService } from '../application/services/field_format_service'; import type { MlJobService } from '../application/services/job_service'; +import type { MlApiServices } from '../application/services/ml_api_service'; +import type { MlResultsService } from '../application/services/results_service'; +import type { MlTimeSeriesSearchService } from '../application/timeseriesexplorer/timeseriesexplorer_utils/time_series_search_service'; import type { AnomalyExplorerChartsEmbeddableType, AnomalySwimLaneEmbeddableType, MlEmbeddableTypes, } from './constants'; -import { - ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, - ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, -} from './constants'; -import type { MlResultsService } from '../application/services/results_service'; -import type { MlApiServices } from '../application/services/ml_api_service'; -import type { MlFieldFormatService } from '../application/services/field_format_service'; -import type { MlTimeSeriesSearchService } from '../application/timeseriesexplorer/timeseriesexplorer_utils/time_series_search_service'; -import type { MlCapabilitiesService } from '../application/capabilities/check_capabilities'; export interface AnomalySwimlaneEmbeddableCustomInput { jobIds: JobId[]; @@ -65,7 +60,7 @@ export interface AnomalySwimlaneEmbeddableCustomOutput { perPage?: number; fromPage?: number; interval?: number; - indexPatterns?: DataView[]; + indexPatterns: DataView[]; } export type AnomalySwimlaneEmbeddableOutput = EmbeddableOutput & @@ -82,14 +77,6 @@ export interface SwimLaneDrilldownContext extends EditSwimlanePanelContext { data?: AppStateSelectedCells; } -export function isSwimLaneEmbeddable(arg: unknown): arg is SwimLaneDrilldownContext { - return ( - isPopulatedObject(arg, ['embeddable']) && - isPopulatedObject(arg.embeddable, ['type']) && - arg.embeddable.type === ANOMALY_SWIMLANE_EMBEDDABLE_TYPE - ); -} - /** * Anomaly Explorer */ @@ -152,27 +139,19 @@ export type SingleMetricViewerEmbeddableServices = [ export interface AnomalyChartsCustomOutput { entityFields?: MlEntityField[]; severity?: number; - indexPatterns?: DataView[]; + indexPatterns: DataView[]; } export type AnomalyChartsEmbeddableOutput = EmbeddableOutput & AnomalyChartsCustomOutput; export interface EditAnomalyChartsPanelContext { embeddable: IEmbeddable; } + export interface AnomalyChartsFieldSelectionContext extends EditAnomalyChartsPanelContext { /** * Optional fields selected using anomaly charts */ data?: MlEntityField[]; } -export function isAnomalyExplorerEmbeddable( - arg: unknown -): arg is AnomalyChartsFieldSelectionContext { - return ( - isPopulatedObject(arg, ['embeddable']) && - isPopulatedObject(arg.embeddable, ['type']) && - arg.embeddable.type === ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE - ); -} export type MappedEmbeddableTypeOf = TEmbeddableType extends AnomalySwimLaneEmbeddableType diff --git a/x-pack/plugins/ml/public/ui_actions/index.ts b/x-pack/plugins/ml/public/ui_actions/index.ts index 46f07ed516e10..bdbd5f3f3e858 100644 --- a/x-pack/plugins/ml/public/ui_actions/index.ts +++ b/x-pack/plugins/ml/public/ui_actions/index.ts @@ -54,7 +54,6 @@ export function registerMlUiActions( // Register actions uiActions.registerAction(editSwimlanePanelAction); - uiActions.registerAction(openInExplorerAction); uiActions.registerAction(applyInfluencerFiltersAction); uiActions.registerAction(applyEntityFieldFilterAction); uiActions.registerAction(applyTimeRangeSelectionAction); diff --git a/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx b/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx index e29f1ce5cf06c..e955245adec86 100644 --- a/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx +++ b/x-pack/plugins/ml/public/ui_actions/open_in_anomaly_explorer_action.tsx @@ -5,30 +5,106 @@ * 2.0. */ +import type { TimeRange } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; -import type { SerializableRecord } from '@kbn/utility-types'; -import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public'; +import type { MlEntityField } from '@kbn/ml-anomaly-utils'; import { ML_ENTITY_FIELD_OPERATIONS } from '@kbn/ml-anomaly-utils'; -import type { MlCoreSetup } from '../plugin'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { + EmbeddableApiContext, + HasParentApi, + HasType, + PublishesUnifiedSearch, + PublishingSubject, +} from '@kbn/presentation-publishing'; +import { apiHasType, apiIsOfType } from '@kbn/presentation-publishing'; +import { createAction } from '@kbn/ui-actions-plugin/public'; +import type { SerializableRecord } from '@kbn/utility-types'; import { ML_APP_LOCATOR } from '../../common/constants/locator'; -import type { AnomalyChartsFieldSelectionContext, SwimLaneDrilldownContext } from '../embeddables'; +import type { ExplorerAppState } from '../../common/types/locator'; +import type { AppStateSelectedCells } from '../application/explorer/explorer_utils'; +import type { + AnomalyExplorerChartsEmbeddableType, + AnomalySwimLaneEmbeddableType, +} from '../embeddables'; import { ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE, ANOMALY_SWIMLANE_EMBEDDABLE_TYPE, - isAnomalyExplorerEmbeddable, - isSwimLaneEmbeddable, } from '../embeddables'; -import type { ExplorerAppState } from '../../common/types/locator'; +import type { MlCoreSetup } from '../plugin'; +import type { JobId } from '../shared'; + +export interface AnomalyChartsFieldSelectionApi { + entityFields: PublishingSubject; +} + +export interface SwimLaneDrilldownApi { + viewBy: PublishingSubject; + perPage: PublishingSubject; + fromPage: PublishingSubject; +} + +export interface OpenInAnomalyExplorerSwimLaneActionContext extends EmbeddableApiContext { + embeddable: OpenInAnomalyExplorerFromSwimLaneActionApi; + /** + * Optional data provided by swim lane selection + */ + data?: AppStateSelectedCells; +} + +export interface OpenInAnomalyExplorerAnomalyChartsActionContext extends EmbeddableApiContext { + embeddable: OpenInAnomalyExplorerFromAnomalyChartActionApi; + /** + * Optional fields selected using anomaly charts + */ + data?: MlEntityField[]; +} + +export type OpenInAnomalyExplorerBaseActionApi = Partial< + HasParentApi & PublishesUnifiedSearch & { jobIds: JobId[] } +>; + +export type OpenInAnomalyExplorerFromSwimLaneActionApi = HasType & + OpenInAnomalyExplorerBaseActionApi & + SwimLaneDrilldownApi; + +export type OpenInAnomalyExplorerFromAnomalyChartActionApi = + HasType & + OpenInAnomalyExplorerBaseActionApi & + AnomalyChartsFieldSelectionApi; export const OPEN_IN_ANOMALY_EXPLORER_ACTION = 'openInAnomalyExplorerAction'; -export function createOpenInExplorerAction( - getStartServices: MlCoreSetup['getStartServices'] -): UiActionsActionDefinition { - return { +export function isSwimLaneEmbeddableContext( + arg: unknown +): arg is OpenInAnomalyExplorerSwimLaneActionContext { + return ( + isPopulatedObject(arg, ['embeddable']) && + apiIsOfType(arg.embeddable, ANOMALY_SWIMLANE_EMBEDDABLE_TYPE) + ); +} + +export function isAnomalyChartsEmbeddableContext( + arg: unknown +): arg is OpenInAnomalyExplorerAnomalyChartsActionContext { + return ( + isPopulatedObject(arg, ['embeddable']) && + apiIsOfType(arg.embeddable, ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE) + ); +} + +export const isApiCompatible = (api: unknown | null): api is OpenInAnomalyExplorerBaseActionApi => + Boolean(apiHasType(api)); + +const getTimeRange = (embeddable: OpenInAnomalyExplorerBaseActionApi): TimeRange | undefined => { + return embeddable.timeRange$?.getValue() ?? embeddable.parentApi?.timeRange$?.getValue(); +}; + +export function createOpenInExplorerAction(getStartServices: MlCoreSetup['getStartServices']) { + return createAction({ id: 'open-in-anomaly-explorer', type: OPEN_IN_ANOMALY_EXPLORER_ACTION, - getIconType(context): string { + getIconType(): string { return 'visTable'; }, getDisplayName() { @@ -40,21 +116,20 @@ export function createOpenInExplorerAction( const [, pluginsStart] = await getStartServices(); const locator = pluginsStart.share.url.locators.get(ML_APP_LOCATOR)!; - if (isSwimLaneEmbeddable(context)) { - const { embeddable, data } = context; + if (isSwimLaneEmbeddableContext(context)) { + const { data, embeddable } = context; - const { jobIds, timeRange, viewBy } = embeddable.getInput(); - const { perPage, fromPage } = embeddable.getOutput(); + const { viewBy, jobIds, perPage, fromPage } = embeddable; return locator.getUrl({ page: 'explorer', pageState: { jobIds, - timeRange, + timeRange: getTimeRange(embeddable), mlExplorerSwimlane: { - viewByFromPage: fromPage, - viewByPerPage: perPage, - viewByFieldName: viewBy, + viewByFromPage: fromPage.getValue(), + viewByPerPage: perPage.getValue(), + viewByFieldName: viewBy.getValue(), ...(data ? { selectedType: data.type, @@ -65,19 +140,18 @@ export function createOpenInExplorerAction( }, }, }); - } else if (isAnomalyExplorerEmbeddable(context)) { + } else if (isAnomalyChartsEmbeddableContext(context)) { const { embeddable } = context; - - const { jobIds, timeRange } = embeddable.getInput(); - const { entityFields } = embeddable.getOutput(); + const { jobIds, entityFields } = embeddable; let mlExplorerFilter: ExplorerAppState['mlExplorerFilter'] | undefined; + const entityFieldsValue = entityFields.getValue(); if ( - Array.isArray(entityFields) && - entityFields.length === 1 && - entityFields[0].operation === ML_ENTITY_FIELD_OPERATIONS.ADD + Array.isArray(entityFieldsValue) && + entityFieldsValue.length === 1 && + entityFieldsValue[0].operation === ML_ENTITY_FIELD_OPERATIONS.ADD ) { - const { fieldName, fieldValue } = entityFields[0]; + const { fieldName, fieldValue } = entityFieldsValue[0]; if (fieldName !== undefined && fieldValue !== undefined) { const influencersFilterQuery = { bool: { @@ -104,7 +178,7 @@ export function createOpenInExplorerAction( page: 'explorer', pageState: { jobIds, - timeRange, + timeRange: getTimeRange(embeddable), // @ts-ignore QueryDslQueryContainer is not compatible with SerializableRecord ...(mlExplorerFilter ? ({ mlExplorerFilter } as SerializableRecord) : {}), query: {}, @@ -122,13 +196,8 @@ export function createOpenInExplorerAction( await application.navigateToUrl(anomalyExplorerUrl!); } }, - async isCompatible({ - embeddable, - }: SwimLaneDrilldownContext | AnomalyChartsFieldSelectionContext) { - return ( - embeddable.type === ANOMALY_SWIMLANE_EMBEDDABLE_TYPE || - embeddable.type === ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE - ); + async isCompatible(context: EmbeddableApiContext) { + return isSwimLaneEmbeddableContext(context) || isAnomalyChartsEmbeddableContext(context); }, - }; + }); }