[ML] AIOps: Additional props for Change Point embeddable (#167606)

This commit is contained in:
Dima Arnautov 2023-09-29 18:24:10 +02:00 committed by GitHub
parent ee1f448d64
commit 772739ab40
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 20 deletions

View file

@ -18,6 +18,7 @@ import { EuiLoadingChart } from '@elastic/eui';
import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '../../common/constants'; import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '../../common/constants';
import type { AiopsPluginStartDeps } from '../types'; import type { AiopsPluginStartDeps } from '../types';
import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart'; import type { EmbeddableChangePointChartInput } from './embeddable_change_point_chart';
import type { ChangePointAnnotation } from '../components/change_point_detection/change_point_detection_context';
export interface EmbeddableChangePointChartProps { export interface EmbeddableChangePointChartProps {
dataViewId: string; dataViewId: string;
@ -27,6 +28,18 @@ export interface EmbeddableChangePointChartProps {
splitField?: string; splitField?: string;
partitions?: string[]; partitions?: string[];
maxSeriesToPlot?: number; maxSeriesToPlot?: number;
/**
* Component to render if there are no change points found
*/
emptyState?: React.ReactElement;
/**
* Outputs the most recent change point data
*/
onChange?: (changePointData: ChangePointAnnotation[]) => void;
/**
* Last reload request time, can be used for manual reload
*/
lastReloadRequestTime?: number;
} }
export function getEmbeddableChangePointChart(core: CoreStart, plugins: AiopsPluginStartDeps) { export function getEmbeddableChangePointChart(core: CoreStart, plugins: AiopsPluginStartDeps) {

View file

@ -5,8 +5,9 @@
* 2.0. * 2.0.
*/ */
import { type Observable } from 'rxjs'; import { BehaviorSubject, type Observable, combineLatest } from 'rxjs';
import React, { FC, useEffect, useMemo } from 'react'; import { map, distinctUntilChanged } from 'rxjs/operators';
import React, { FC, useEffect, useMemo, useState } from 'react';
import { useTimefilter } from '@kbn/ml-date-picker'; import { useTimefilter } from '@kbn/ml-date-picker';
import { css } from '@emotion/react'; import { css } from '@emotion/react';
import useObservable from 'react-use/lib/useObservable'; import useObservable from 'react-use/lib/useObservable';
@ -55,8 +56,31 @@ export const EmbeddableInputTracker: FC<EmbeddableInputTrackerProps> = ({
}) => { }) => {
const input = useObservable(input$, initialInput); const input = useObservable(input$, initialInput);
const [manualReload$] = useState<BehaviorSubject<number>>(
new BehaviorSubject<number>(initialInput.lastReloadRequestTime ?? Date.now())
);
useEffect(
function updateManualReloadSubject() {
if (
input.lastReloadRequestTime === initialInput.lastReloadRequestTime ||
!input.lastReloadRequestTime
)
return;
manualReload$.next(input.lastReloadRequestTime);
},
[input.lastReloadRequestTime, initialInput.lastReloadRequestTime, manualReload$]
);
const resultObservable$ = useMemo<Observable<number>>(() => {
return combineLatest([reload$, manualReload$]).pipe(
map(([reload, manualReload]) => Math.max(reload, manualReload)),
distinctUntilChanged()
);
}, [manualReload$, reload$]);
return ( return (
<ReloadContextProvider reload$={reload$}> <ReloadContextProvider reload$={resultObservable$}>
<DataSourceContextProvider dataViewId={input.dataViewId}> <DataSourceContextProvider dataViewId={input.dataViewId}>
<FilterQueryContextProvider timeRange={input.timeRange}> <FilterQueryContextProvider timeRange={input.timeRange}>
<ChartGridEmbeddableWrapper <ChartGridEmbeddableWrapper
@ -70,6 +94,8 @@ export const EmbeddableInputTracker: FC<EmbeddableInputTrackerProps> = ({
onLoading={onLoading} onLoading={onLoading}
onRenderComplete={onRenderComplete} onRenderComplete={onRenderComplete}
onError={onError} onError={onError}
onChange={input.onChange}
emptyState={input.emptyState}
/> />
</FilterQueryContextProvider> </FilterQueryContextProvider>
</DataSourceContextProvider> </DataSourceContextProvider>
@ -103,6 +129,8 @@ export const ChartGridEmbeddableWrapper: FC<
onError, onError,
onLoading, onLoading,
onRenderComplete, onRenderComplete,
onChange,
emptyState,
}) => { }) => {
const { filters, query, timeRange } = useFilerQueryUpdates(); const { filters, query, timeRange } = useFilerQueryUpdates();
@ -189,8 +217,12 @@ export const ChartGridEmbeddableWrapper: FC<
resultChangePoints = resultChangePoints.slice(0, maxSeriesToPlot); resultChangePoints = resultChangePoints.slice(0, maxSeriesToPlot);
} }
if (onChange) {
onChange(resultChangePoints);
}
return resultChangePoints; return resultChangePoints;
}, [results, maxSeriesToPlot]); }, [results, maxSeriesToPlot, onChange]);
return ( return (
<div <div
@ -205,6 +237,8 @@ export const ChartGridEmbeddableWrapper: FC<
interval={requestParams.interval} interval={requestParams.interval}
onRenderComplete={onRenderComplete} onRenderComplete={onRenderComplete}
/> />
) : emptyState ? (
emptyState
) : ( ) : (
<NoChangePointsWarning /> <NoChangePointsWarning />
)} )}

View file

@ -17,8 +17,8 @@ import type { UiActionsStart, UiActionsSetup } from '@kbn/ui-actions-plugin/publ
import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public'; import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public';
import type { CasesUiSetup } from '@kbn/cases-plugin/public'; import type { CasesUiSetup } from '@kbn/cases-plugin/public';
import { LicensingPluginSetup } from '@kbn/licensing-plugin/public'; import type { LicensingPluginSetup } from '@kbn/licensing-plugin/public';
import type { EmbeddableChangePointChartProps } from './embeddable'; import type { EmbeddableChangePointChartInput } from './embeddable/embeddable_change_point_chart';
export interface AiopsPluginSetupDeps { export interface AiopsPluginSetupDeps {
embeddable: EmbeddableSetup; embeddable: EmbeddableSetup;
@ -44,5 +44,5 @@ export interface AiopsPluginStartDeps {
export type AiopsPluginSetup = void; export type AiopsPluginSetup = void;
export interface AiopsPluginStart { export interface AiopsPluginStart {
EmbeddableChangePointChart: React.ComponentType<EmbeddableChangePointChartProps>; EmbeddableChangePointChart: React.ComponentType<EmbeddableChangePointChartInput>;
} }

View file

@ -213,19 +213,19 @@ export default function AlertDetailsAppSection({
<EuiSpacer size="l" /> <EuiSpacer size="l" />
<EuiFlexGroup direction="column" data-test-subj="thresholdAlertRelatedEventsSection"> <EuiFlexGroup direction="column" data-test-subj="thresholdAlertRelatedEventsSection">
{ruleParams.criteria.map((criterion, criterionIndex) => {ruleParams.criteria.map((criterion, criterionIndex) =>
criterion.metrics?.map( criterion.metrics?.map((metric, metricIndex) => {
(metric, metricIndex) => const id = `embeddableChart-criterion${criterionIndex}-metric${metricIndex}`;
dataView && return dataView?.id ? (
dataView.id && ( <EmbeddableChangePointChart
<EmbeddableChangePointChart id={id}
key={`embeddableChart-criterion${criterionIndex}-metric${metricIndex}`} key={id}
dataViewId={dataView.id} dataViewId={dataView.id}
timeRange={relatedEventsTimeRange(criterion)} timeRange={relatedEventsTimeRange(criterion)}
fn={metric.aggType ?? ''} fn={metric.aggType ?? ''}
metricField={metric.field ?? ''} metricField={metric.field ?? ''}
/> />
) ) : null;
) })
)} )}
</EuiFlexGroup> </EuiFlexGroup>
</> </>