mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[ML] AIOps: UI action for Change Point Detection embeddable to open in the ML app (#176694)
## Summary
Closes #161248
Added a UI action to open change point detection in the AIOps labs.
<img width="1137" alt="image"
src="077c0e34
-0ac0-4790-8cbe-c6048ee90d22">
### Checklist
- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
This commit is contained in:
parent
28d46a8c02
commit
550c10dc5e
7 changed files with 191 additions and 3 deletions
|
@ -17,7 +17,8 @@
|
|||
"presentationUtil",
|
||||
"dashboard",
|
||||
"fieldFormats",
|
||||
"unifiedSearch"
|
||||
"unifiedSearch",
|
||||
"share"
|
||||
],
|
||||
"optionalPlugins": [
|
||||
"cases",
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
} from '@kbn/ml-ui-actions/src/aiops/ui_actions';
|
||||
|
||||
import type { CoreStart } from '@kbn/core/public';
|
||||
import { createOpenChangePointInMlAppAction } from './open_change_point_ml';
|
||||
import type { AiopsPluginStartDeps } from '../types';
|
||||
import { createEditChangePointChartsPanelAction } from './edit_change_point_charts_panel';
|
||||
import { createCategorizeFieldAction } from '../components/log_categorization';
|
||||
|
@ -27,6 +28,8 @@ export function registerAiopsUiActions(
|
|||
coreStart,
|
||||
pluginStart
|
||||
);
|
||||
const openChangePointInMlAppAction = createOpenChangePointInMlAppAction(coreStart, pluginStart);
|
||||
|
||||
// // Register actions and triggers
|
||||
uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, editChangePointChartPanelAction);
|
||||
|
||||
|
@ -36,4 +39,6 @@ export function registerAiopsUiActions(
|
|||
CATEGORIZE_FIELD_TRIGGER,
|
||||
createCategorizeFieldAction(coreStart, pluginStart)
|
||||
);
|
||||
|
||||
uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, openChangePointInMlAppAction);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { UiActionsActionDefinition } from '@kbn/ui-actions-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { CoreStart } from '@kbn/core/public';
|
||||
import type { AiopsPluginStartDeps } from '../types';
|
||||
import type { EditChangePointChartsPanelContext } from '../embeddable/types';
|
||||
import { EMBEDDABLE_CHANGE_POINT_CHART_TYPE } from '../../common/constants';
|
||||
|
||||
export const OPEN_CHANGE_POINT_IN_ML_APP_ACTION = 'openChangePointInMlAppAction';
|
||||
|
||||
export function createOpenChangePointInMlAppAction(
|
||||
coreStart: CoreStart,
|
||||
pluginStart: AiopsPluginStartDeps
|
||||
): UiActionsActionDefinition<EditChangePointChartsPanelContext> {
|
||||
return {
|
||||
id: 'open-change-point-in-ml-app',
|
||||
type: OPEN_CHANGE_POINT_IN_ML_APP_ACTION,
|
||||
getIconType(context): string {
|
||||
return 'link';
|
||||
},
|
||||
getDisplayName: () =>
|
||||
i18n.translate('xpack.aiops.actions.openChangePointInMlAppName', {
|
||||
defaultMessage: 'Open in AIOps Labs',
|
||||
}),
|
||||
async getHref(context): Promise<string | undefined> {
|
||||
const locator = pluginStart.share.url.locators.get('ML_APP_LOCATOR')!;
|
||||
|
||||
const { timeRange, metricField, fn, splitField, dataViewId } = context.embeddable.getInput();
|
||||
|
||||
return locator.getUrl({
|
||||
page: 'aiops/change_point_detection',
|
||||
pageState: {
|
||||
index: dataViewId,
|
||||
timeRange,
|
||||
fieldConfigs: [{ fn, metricField, ...(splitField ? { splitField } : {}) }],
|
||||
},
|
||||
});
|
||||
},
|
||||
async execute(context) {
|
||||
if (!context.embeddable) {
|
||||
throw new Error('Not possible to execute an action without the embeddable context');
|
||||
}
|
||||
const aiopsChangePointUrl = await this.getHref!(context);
|
||||
if (aiopsChangePointUrl) {
|
||||
await coreStart.application.navigateToUrl(aiopsChangePointUrl!);
|
||||
}
|
||||
},
|
||||
async isCompatible({ embeddable }) {
|
||||
return embeddable.type === EMBEDDABLE_CHANGE_POINT_CHART_TYPE;
|
||||
},
|
||||
};
|
||||
}
|
|
@ -124,6 +124,7 @@ export interface ExplorerAppState {
|
|||
query?: any;
|
||||
mlShowCharts?: boolean;
|
||||
}
|
||||
|
||||
export interface ExplorerGlobalState {
|
||||
ml: { jobIds: JobId[] };
|
||||
time?: TimeRange;
|
||||
|
@ -279,7 +280,8 @@ export type MlLocatorState =
|
|||
| MlGenericUrlState
|
||||
| NotificationsUrlState
|
||||
| TrainedModelsUrlState
|
||||
| MemoryUsageUrlState;
|
||||
| MemoryUsageUrlState
|
||||
| ChangePointDetectionUrlState;
|
||||
|
||||
export type MlLocatorParams = MlLocatorState & SerializableRecord;
|
||||
|
||||
|
@ -303,3 +305,18 @@ export type NotificationsUrlState = MLPageState<
|
|||
typeof ML_PAGES.NOTIFICATIONS,
|
||||
NotificationsQueryState | undefined
|
||||
>;
|
||||
|
||||
export interface ChangePointDetectionQueryState {
|
||||
index: string;
|
||||
timeRange?: TimeRange;
|
||||
fieldConfigs: Array<{
|
||||
fn: string;
|
||||
splitField?: string;
|
||||
metricField: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export type ChangePointDetectionUrlState = MLPageState<
|
||||
typeof ML_PAGES.AIOPS_CHANGE_POINT_DETECTION,
|
||||
ChangePointDetectionQueryState
|
||||
>;
|
||||
|
|
49
x-pack/plugins/ml/public/locator/formatters/aiops.ts
Normal file
49
x-pack/plugins/ml/public/locator/formatters/aiops.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public';
|
||||
import { ML_PAGES } from '../../../common/constants/locator';
|
||||
import type { ChangePointDetectionUrlState } from '../../../common/types/locator';
|
||||
|
||||
/**
|
||||
* Creates URL to the Change Point Detection page
|
||||
*/
|
||||
export function formatChangePointDetectionUrl(
|
||||
appBasePath: string,
|
||||
params: ChangePointDetectionUrlState['pageState']
|
||||
): string {
|
||||
let url = `${appBasePath}/${ML_PAGES.AIOPS_CHANGE_POINT_DETECTION}`;
|
||||
|
||||
if (!params?.fieldConfigs) {
|
||||
throw new Error('Field configs are required to create a change point detection URL');
|
||||
}
|
||||
|
||||
if (!params.index) {
|
||||
throw new Error('Data view is required to create a change point detection URL');
|
||||
}
|
||||
|
||||
url = `${url}?index=${params.index}`;
|
||||
|
||||
const { timeRange, fieldConfigs } = params;
|
||||
|
||||
const appState = {
|
||||
fieldConfigs,
|
||||
};
|
||||
const queryState = {
|
||||
time: timeRange,
|
||||
};
|
||||
|
||||
url = setStateToKbnUrl('_g', queryState, { useHash: false, storeInHashQuery: false }, url);
|
||||
url = setStateToKbnUrl(
|
||||
'_a',
|
||||
{ changePoint: appState },
|
||||
{ useHash: false, storeInHashQuery: false },
|
||||
url
|
||||
);
|
||||
|
||||
return url;
|
||||
}
|
|
@ -340,5 +340,56 @@ describe('ML locator', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('AIOps labs', () => {
|
||||
it('should throw an error for invalid Change point detection page state', async () => {
|
||||
await expect(
|
||||
definition.getLocation({
|
||||
page: ML_PAGES.AIOPS_CHANGE_POINT_DETECTION,
|
||||
pageState: {
|
||||
index: '123123',
|
||||
},
|
||||
})
|
||||
).rejects.toThrow('Field configs are required to create a change point detection URL');
|
||||
|
||||
await expect(
|
||||
definition.getLocation({
|
||||
page: ML_PAGES.AIOPS_CHANGE_POINT_DETECTION,
|
||||
pageState: {
|
||||
fieldConfigs: [
|
||||
{
|
||||
fn: 'max',
|
||||
metricField: 'CPUUtilization',
|
||||
splitField: 'instance',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
).rejects.toThrow('Data view is required to create a change point detection URL');
|
||||
});
|
||||
|
||||
it('should generate valid URL for the Change point detection page', async () => {
|
||||
const location = await definition.getLocation({
|
||||
page: ML_PAGES.AIOPS_CHANGE_POINT_DETECTION,
|
||||
pageState: {
|
||||
index: 'test-index',
|
||||
timeRange: { from: '2019-10-28T00:00:00.000Z', to: '2019-11-11T13:31:00.000Z' },
|
||||
fieldConfigs: [
|
||||
{
|
||||
fn: 'max',
|
||||
metricField: 'CPUUtilization',
|
||||
splitField: 'instance',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(location).toMatchObject({
|
||||
app: 'ml',
|
||||
path: "/aiops/change_point_detection?index=test-index&_g=(time:(from:'2019-10-28T00:00:00.000Z',to:'2019-11-11T13:31:00.000Z'))&_a=(changePoint:(fieldConfigs:!((fn:max,metricField:CPUUtilization,splitField:instance))))",
|
||||
state: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,11 +6,13 @@
|
|||
*/
|
||||
|
||||
import type { LocatorDefinition, KibanaLocation } from '@kbn/share-plugin/public';
|
||||
import { formatChangePointDetectionUrl } from './formatters/aiops';
|
||||
import { formatNotificationsUrl } from './formatters/notifications';
|
||||
import {
|
||||
DataFrameAnalyticsExplorationUrlState,
|
||||
MlLocatorParams,
|
||||
MlLocator,
|
||||
ChangePointDetectionQueryState,
|
||||
} from '../../common/types/locator';
|
||||
import { ML_APP_LOCATOR, ML_PAGES } from '../../common/constants/locator';
|
||||
import {
|
||||
|
@ -77,6 +79,12 @@ export class MlLocatorDefinition implements LocatorDefinition<MlLocatorParams> {
|
|||
case ML_PAGES.MEMORY_USAGE:
|
||||
path = formatMemoryUsageUrl('', params.pageState);
|
||||
break;
|
||||
case ML_PAGES.AIOPS_CHANGE_POINT_DETECTION:
|
||||
path = formatChangePointDetectionUrl(
|
||||
'',
|
||||
params.pageState as ChangePointDetectionQueryState
|
||||
);
|
||||
break;
|
||||
case ML_PAGES.DATA_DRIFT_INDEX_SELECT:
|
||||
case ML_PAGES.DATA_DRIFT_CUSTOM:
|
||||
case ML_PAGES.DATA_DRIFT:
|
||||
|
@ -96,7 +104,6 @@ export class MlLocatorDefinition implements LocatorDefinition<MlLocatorParams> {
|
|||
case ML_PAGES.AIOPS_LOG_RATE_ANALYSIS_INDEX_SELECT:
|
||||
case ML_PAGES.AIOPS_LOG_CATEGORIZATION:
|
||||
case ML_PAGES.AIOPS_LOG_CATEGORIZATION_INDEX_SELECT:
|
||||
case ML_PAGES.AIOPS_CHANGE_POINT_DETECTION:
|
||||
case ML_PAGES.AIOPS_CHANGE_POINT_DETECTION_INDEX_SELECT:
|
||||
case ML_PAGES.OVERVIEW:
|
||||
case ML_PAGES.SETTINGS:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue