diff --git a/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx index e7b65ec174c7e..b040069a235f4 100644 --- a/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx +++ b/x-pack/plugins/aiops/public/embeddable/embeddable_change_point_chart.tsx @@ -58,12 +58,31 @@ export class EmbeddableChangePointChart extends AbstractEmbeddable< private node?: HTMLElement; + // Need to defer embeddable load in order to resolve data views + deferEmbeddableLoad = true; + constructor( private readonly deps: EmbeddableChangePointChartDeps, initialInput: EmbeddableChangePointChartInput, parent?: IContainer ) { super(initialInput, { defaultTitle: initialInput.title }, parent); + + this.initOutput().finally(() => this.setInitializationFinished()); + } + + private async initOutput() { + const { + data: { dataViews: dataViewsService }, + } = this.deps; + + const { dataViewId } = this.getInput(); + + const dataView = await dataViewsService.get(dataViewId); + + this.updateOutput({ + indexPatterns: [dataView], + }); } public reportsEmbeddableLoad() { diff --git a/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx b/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx index 3cc8a6d6e47a0..54ed702eadf63 100644 --- a/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx +++ b/x-pack/plugins/aiops/public/embeddable/embeddable_chart_component_wrapper.tsx @@ -6,7 +6,7 @@ */ import { type Observable } from 'rxjs'; -import React, { FC, useCallback, useEffect, useMemo } from 'react'; +import React, { FC, useEffect, useMemo } from 'react'; import { useTimefilter } from '@kbn/ml-date-picker'; import { css } from '@emotion/react'; import useObservable from 'react-use/lib/useObservable'; @@ -21,11 +21,7 @@ import type { } from './embeddable_change_point_chart'; import { EmbeddableChangePointChartProps } from './embeddable_change_point_chart_component'; import { FilterQueryContextProvider, useFilerQueryUpdates } from '../hooks/use_filters_query'; -import { - DataSourceContextProvider, - type DataSourceContextProviderProps, - useDataSource, -} from '../hooks/use_data_source'; +import { DataSourceContextProvider, useDataSource } from '../hooks/use_data_source'; import { useAiopsAppContext } from '../hooks/use_aiops_app_context'; import { useTimeBuckets } from '../hooks/use_time_buckets'; import { createMergedEsQuery } from '../application/utils/search_utils'; @@ -59,16 +55,9 @@ export const EmbeddableInputTracker: FC = ({ }) => { const input = useObservable(input$, initialInput); - const onChange = useCallback>( - ({ dataViews }) => { - onOutputChange({ indexPatterns: dataViews }); - }, - [onOutputChange] - ); - return ( - + i18n.translate('xpack.ml.anomalyChartsEmbeddable.title', { defaultMessage: 'ML anomaly charts for {jobIds}', @@ -31,7 +32,7 @@ export const getDefaultExplorerChartsPanelTitle = (jobIds: JobId[]) => export type IAnomalyChartsEmbeddable = typeof AnomalyChartsEmbeddable; -export class AnomalyChartsEmbeddable extends Embeddable< +export class AnomalyChartsEmbeddable extends AnomalyDetectionEmbeddable< AnomalyChartsEmbeddableInput, AnomalyChartsEmbeddableOutput > { @@ -44,52 +45,7 @@ export class AnomalyChartsEmbeddable extends Embeddable< public services: [CoreStart, MlDependencies, AnomalyChartsServices], parent?: IContainer ) { - super( - initialInput, - { - defaultTitle: initialInput.title, - defaultDescription: initialInput.description, - }, - parent - ); - this.initializeOutput(initialInput); - } - - private async initializeOutput(initialInput: AnomalyChartsEmbeddableInput) { - const { anomalyExplorerService } = this.services[2]; - const { jobIds } = initialInput; - - try { - const jobs = await anomalyExplorerService.getCombinedJobs(jobIds); - const dataViewsService = this.services[1].data.dataViews; - - // First get list of unique indices from the selected jobs - const indices = new Set(jobs.map((j) => j.datafeed_config.indices).flat()); - // Then find the data view assuming the data view title matches the index name - const indexPatterns: Record = {}; - for (const indexName of indices) { - const response = await dataViewsService.find(`"${indexName}"`); - - const indexPattern = response.find( - (obj) => obj.title.toLowerCase() === indexName.toLowerCase() - ); - if (indexPattern !== undefined) { - indexPatterns[indexPattern.id!] = indexPattern; - } - } - - this.updateOutput({ - ...this.getOutput(), - indexPatterns: Object.values(indexPatterns), - }); - } catch (e) { - // Unable to find and load data view but we can ignore the error - // as we only load it to support the filter & query bar - // the visualizations should still work correctly - - // eslint-disable-next-line no-console - console.error(`Unable to load data views for ${jobIds}`, e); - } + super(initialInput, services[2].anomalyDetectorService, services[1].data.dataViews, parent); } public onLoading() { 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 4d28dbd4f0e6f..75a6e0650181a 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 @@ -11,7 +11,7 @@ import { CoreStart } from '@kbn/core/public'; import { i18n } from '@kbn/i18n'; import { Subject } from 'rxjs'; import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; +import { 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'; @@ -23,6 +23,7 @@ import { AnomalySwimlaneServices, } from '..'; import { EmbeddableLoading } from '../common/components/embeddable_loading_fallback'; +import { AnomalyDetectionEmbeddable } from '../common/anomaly_detection_embeddable'; export const getDefaultSwimlanePanelTitle = (jobIds: JobId[]) => i18n.translate('xpack.ml.swimlaneEmbeddable.title', { @@ -32,7 +33,7 @@ export const getDefaultSwimlanePanelTitle = (jobIds: JobId[]) => export type IAnomalySwimlaneEmbeddable = typeof AnomalySwimlaneEmbeddable; -export class AnomalySwimlaneEmbeddable extends Embeddable< +export class AnomalySwimlaneEmbeddable extends AnomalyDetectionEmbeddable< AnomalySwimlaneEmbeddableInput, AnomalySwimlaneEmbeddableOutput > { @@ -45,14 +46,7 @@ export class AnomalySwimlaneEmbeddable extends Embeddable< public services: [CoreStart, MlDependencies, AnomalySwimlaneServices], parent?: IContainer ) { - super( - initialInput, - { - defaultTitle: initialInput.title, - defaultDescription: initialInput.description, - }, - parent - ); + super(initialInput, services[2].anomalyDetectorService, services[1].data.dataViews, parent); } public reportsEmbeddableLoad() { 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 new file mode 100644 index 0000000000000..7722238cdbb38 --- /dev/null +++ b/x-pack/plugins/ml/public/embeddables/common/anomaly_detection_embeddable.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + Embeddable, + type EmbeddableInput, + type EmbeddableOutput, + type IContainer, +} from '@kbn/embeddable-plugin/public'; +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'; + +export type CommonInput = { jobIds: string[] } & EmbeddableInput; + +export type CommonOutput = { indexPatterns?: DataView[] } & EmbeddableOutput; + +export abstract class AnomalyDetectionEmbeddable< + Input extends CommonInput, + Output extends CommonOutput +> extends Embeddable { + // Need to defer embeddable load in order to resolve data views + deferEmbeddableLoad = true; + + protected constructor( + initialInput: Input, + private anomalyDetectorService: AnomalyDetectorService, + private dataViewsService: DataViewsContract, + parent?: IContainer + ) { + super( + initialInput, + { + defaultTitle: initialInput.title, + defaultDescription: initialInput.description, + } as Output, + parent + ); + + this.initializeOutput(initialInput).finally(() => { + this.setInitializationFinished(); + }); + } + + protected async initializeOutput(initialInput: CommonInput) { + const { jobIds } = initialInput; + + try { + const jobs = await firstValueFrom(this.anomalyDetectorService.getJobs$(jobIds)); + + // First get list of unique indices from the selected jobs + const indices = new Set(jobs.map((j) => j.datafeed_config!.indices).flat()); + // Then find the data view assuming the data view title matches the index name + const indexPatterns: Record = {}; + for (const indexName of indices) { + const response = await this.dataViewsService.find(`"${indexName}"`); + const indexPattern = response.find((obj) => + obj.getIndexPattern().toLowerCase().includes(indexName.toLowerCase()) + ); + + if (indexPattern !== undefined) { + indexPatterns[indexPattern.id!] = indexPattern; + } + } + + this.updateOutput({ + ...this.getOutput(), + indexPatterns: Object.values(indexPatterns), + }); + } catch (e) { + // Unable to find and load data view but we can ignore the error + // as we only load it to support the filter & query bar + // the visualizations should still work correctly + + // eslint-disable-next-line no-console + console.error(`Unable to load data views for ${jobIds}`, e); + } + } +} diff --git a/x-pack/plugins/ml/public/embeddables/types.ts b/x-pack/plugins/ml/public/embeddables/types.ts index 74a2e042a9898..48a7b5d43a5ac 100644 --- a/x-pack/plugins/ml/public/embeddables/types.ts +++ b/x-pack/plugins/ml/public/embeddables/types.ts @@ -58,6 +58,7 @@ export interface AnomalySwimlaneEmbeddableCustomOutput { perPage?: number; fromPage?: number; interval?: number; + indexPatterns?: DataView[]; } export type AnomalySwimlaneEmbeddableOutput = EmbeddableOutput &