Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
640874b
add preconfigured jobs page in anomaly detection
alvarezmelissa87 Aug 8, 2024
e3b05a8
ensure link to job wizard works
alvarezmelissa87 Aug 28, 2024
9290b99
add links to kibana dashboards if they exist
alvarezmelissa87 Aug 28, 2024
1e7f95f
rename to supplied configurations
alvarezmelissa87 Aug 28, 2024
9838277
update copy
alvarezmelissa87 Aug 30, 2024
f9a9bbc
add suppliedConfigs to serverless security project
alvarezmelissa87 Sep 3, 2024
67df24b
Merge branch 'main' into ml-preconfigured-jobs-page
alvarezmelissa87 Sep 3, 2024
1d2d0f4
Merge branch 'main' into ml-preconfigured-jobs-page
alvarezmelissa87 Sep 4, 2024
1574beb
fix types
alvarezmelissa87 Sep 4, 2024
cdb1656
update permissions for viewing supplied configs and add permission to…
alvarezmelissa87 Sep 4, 2024
483c2bb
update endpoint to run query against index pattern
alvarezmelissa87 Sep 5, 2024
5666926
add basic functional test
alvarezmelissa87 Sep 5, 2024
b21695c
remove app reference when in serverless
alvarezmelissa87 Sep 5, 2024
c95eeaa
update manifest and endpoint
alvarezmelissa87 Sep 6, 2024
f06624c
Merge branch 'main' into ml-preconfigured-jobs-page
alvarezmelissa87 Sep 6, 2024
e472817
fix api integration test
alvarezmelissa87 Sep 6, 2024
17e2732
Merge branch 'main' into ml-preconfigured-jobs-page
elasticmachine Sep 9, 2024
c81e7f6
ensure promise all does not fail with one rejection
alvarezmelissa87 Sep 11, 2024
7cba584
ensure mounted check in right place
alvarezmelissa87 Sep 11, 2024
9a0214e
Merge branch 'main' into ml-preconfigured-jobs-page
elasticmachine Sep 11, 2024
1022636
add check for existing logo in moduleConfig
alvarezmelissa87 Sep 11, 2024
7d8cbbd
ensure job ids bold in job tab
alvarezmelissa87 Sep 11, 2024
c05ad0c
remove unnecessary error callout
alvarezmelissa87 Sep 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/deeplinks/ml/deep_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export type LinkId =
| 'calendarSettings'
| 'calendarSettings'
| 'filterListsSettings'
| 'notifications';
| 'notifications'
| 'suppliedConfigurations';

export type DeepLinkId = AppId | `${AppId}:${LinkId}`;
3 changes: 3 additions & 0 deletions packages/default-nav/ml/default_navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ export const defaultNavigation: MlNodeDefinition = {
{
link: 'ml:singleMetricViewer',
},
{
link: 'ml:suppliedConfigurations',
},
{
link: 'ml:settings',
},
Expand Down
2 changes: 2 additions & 0 deletions packages/kbn-doc-links/src/get_doc_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,8 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
setUpgradeMode: `${ELASTICSEARCH_DOCS}ml-set-upgrade-mode.html`,
trainedModels: `${MACHINE_LEARNING_DOCS}ml-trained-models.html`,
startTrainedModelsDeployment: `${MACHINE_LEARNING_DOCS}ml-nlp-deploy-model.html`,
logsAnomalyDetectionConfigs: `${MACHINE_LEARNING_DOCS}ootb-ml-jobs-logs-ui.html`,
metricsAnomalyDetectionConfigs: `${MACHINE_LEARNING_DOCS}ootb-ml-jobs-metrics-ui.html`,
nlpElser: `${MACHINE_LEARNING_DOCS}ml-nlp-elser.html`,
nlpE5: `${MACHINE_LEARNING_DOCS}ml-nlp-e5.html`,
nlpImportModel: `${MACHINE_LEARNING_DOCS}ml-nlp-import-model.html`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export enum ExternalPageName {
mlAnomalyDetection = 'ml:anomalyDetection',
mlAnomalyExplorer = 'ml:anomalyExplorer',
mlSingleMetricViewer = 'ml:singleMetricViewer',
mlSuppliedConfigurations = 'ml:suppliedConfigurations',
mlSettings = 'ml:settings',
mlDataFrameAnalytics = 'ml:dataFrameAnalytics',
mlResultExplorer = 'ml:resultExplorer',
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/ml/common/constants/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const ML_PAGES = {
MEMORY_USAGE: 'memory_usage',
DATA_FRAME_ANALYTICS_EXPLORATION: 'data_frame_analytics/exploration',
DATA_FRAME_ANALYTICS_MAP: 'data_frame_analytics/map',
SUPPLIED_CONFIGURATIONS: 'supplied_configurations',
/**
* Page: Data Visualizer
*/
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/ml/common/types/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ export type MlGenericUrlState = MLPageState<
| typeof ML_PAGES.AIOPS_LOG_RATE_ANALYSIS
| typeof ML_PAGES.AIOPS_LOG_RATE_ANALYSIS_INDEX_SELECT
| typeof ML_PAGES.AIOPS_CHANGE_POINT_DETECTION_INDEX_SELECT
| typeof ML_PAGES.AIOPS_CHANGE_POINT_DETECTION,
| typeof ML_PAGES.AIOPS_CHANGE_POINT_DETECTION
| typeof ML_PAGES.SUPPLIED_CONFIGURATIONS,
MlGenericUrlPageState | undefined
>;
export interface AnomalyDetectionQueryState {
Expand Down
7 changes: 7 additions & 0 deletions x-pack/plugins/ml/common/types/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ export interface RecognizeResult {
logo: Logo;
}

export interface RecognizeModuleResultDataView {
id: string;
title: string;
name: string | undefined;
}
export type RecognizeModuleResult = RecognizeModuleResultDataView[];

export interface FileBasedModule extends Omit<Module, 'jobs' | 'datafeeds' | 'kibana'> {
jobs: Array<{ file: string; id: string }>;
datafeeds: Array<{ file: string; job_id: string; id: string }>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,18 @@ export function useSideNavItems(activeRoute: MlRoute | undefined) {
testSubj: 'mlMainTab settings',
highlightNestedRoutes: true,
},
{
id: 'supplied_cofigurations',
name: i18n.translate(
'xpack.ml.navMenu.anomalyDetection.suppliedConfigurationsLinkText',
{
defaultMessage: 'Supplied Configurations',
}
),
disabled: disableLinks,
pathId: ML_PAGES.SUPPLIED_CONFIGURATIONS,
testSubj: 'mlMainTab suppliedConfigurations',
},
],
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import React, { createContext, useContext, useMemo } from 'react';
import type { ExperimentalFeatures, MlFeatures } from '../../../../common/constants/app';

export interface EnabledFeatures {
showLogsSuppliedConfigurationsInfo: boolean;
showContextualInsights: boolean;
showNodeInfo: boolean;
showMLNavMenu: boolean;
Expand All @@ -20,6 +21,7 @@ export interface EnabledFeatures {
showRuleFormV2: boolean;
}
export const EnabledFeaturesContext = createContext<EnabledFeatures>({
showLogsSuppliedConfigurationsInfo: true,
showContextualInsights: true,
showNodeInfo: true,
showMLNavMenu: true,
Expand All @@ -45,6 +47,7 @@ export const EnabledFeaturesContextProvider: FC<PropsWithChildren<Props>> = ({
experimentalFeatures,
}) => {
const features: EnabledFeatures = {
showLogsSuppliedConfigurationsInfo: !isServerless,
showContextualInsights: isServerless,
showNodeInfo: !isServerless,
showMLNavMenu,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { isPopulatedObject } from '@kbn/ml-is-populated-object';
import { addExcludeFrozenToQuery } from '@kbn/ml-query-utils';
import { TIME_FORMAT } from '@kbn/ml-date-utils';
import { type RuntimeMappings } from '@kbn/ml-runtime-field-utils';
import type { Module } from '../../../../../common/types/modules';
import { useDataSource } from '../../../contexts/ml';
import { useMlKibana, useMlLocator } from '../../../contexts/kibana';
import type {
Expand Down Expand Up @@ -108,7 +109,7 @@ export const Page: FC<PageProps> = ({ moduleId, existingGroupIds }) => {
*/
const loadModule = useCallback(async () => {
try {
const response = await getDataRecognizerModule({ moduleId });
const response = (await getDataRecognizerModule({ moduleId })) as Module;
setJobs(response.jobs);
setKibanaObjects(response.kibana);

Expand Down
9 changes: 9 additions & 0 deletions x-pack/plugins/ml/public/application/routing/breadcrumbs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ export const TRAINED_MODELS: ChromeBreadcrumb = Object.freeze({
deepLinkId: 'ml:modelManagement',
});

export const SUPPLIED_CONFIGURATIONS: ChromeBreadcrumb = Object.freeze({
text: i18n.translate('xpack.ml.suppliedConfigurationsLabel', {
defaultMessage: 'Supplied configurations',
}),
href: '/supplied_configurations',
deepLinkId: 'ml:suppliedConfigurations',
});

export const DATA_VISUALIZER_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
text: i18n.translate('xpack.ml.datavisualizerBreadcrumbLabel', {
defaultMessage: 'Data Visualizer',
Expand Down Expand Up @@ -153,6 +161,7 @@ const breadcrumbs = {
CREATE_JOB_BREADCRUMB,
CALENDAR_MANAGEMENT_BREADCRUMB,
FILTER_LISTS_BREADCRUMB,
SUPPLIED_CONFIGURATIONS,
};
type Breadcrumb = keyof typeof breadcrumbs;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ export * from './explorer';
export * from './trained_models';
export * from './notifications';
export * from './memory_usage';
export * from './supplied_configurations';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* 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.
*/

export * from './supplied_configurations_view';
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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 { FC } from 'react';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { i18n } from '@kbn/i18n';
import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
import { dynamic } from '@kbn/shared-ux-utility';
import { ML_PAGES } from '../../../../locator';
import type { NavigateToPath } from '../../../contexts/kibana';
import type { MlRoute } from '../../router';
import { createPath, PageLoader } from '../../router';
import { useRouteResolver } from '../../use_resolver';
import { basicResolvers } from '../../resolvers';
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
import { MlPageHeader } from '../../../components/page_header';

const SuppliedConfigurations = dynamic(async () => ({
default: (await import('../../../supplied_configurations/supplied_configurations'))
.SuppliedConfigurations,
}));

export const suppliedConfigurationsRouteFactory = (
navigateToPath: NavigateToPath,
basePath: string
): MlRoute => ({
id: 'supplied_configurations',
path: createPath(ML_PAGES.SUPPLIED_CONFIGURATIONS),
title: i18n.translate('xpack.ml.suppliedConfigurations.suppliedConfigurations.docTitle', {
defaultMessage: 'Supplied configurations',
}),
render: () => <PageWrapper />,
breadcrumbs: [
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
getBreadcrumbWithUrlForApp('ANOMALY_DETECTION_BREADCRUMB', navigateToPath, basePath),
{
text: i18n.translate(
'xpack.ml.suppliedConfigurationsBreadcrumbs.suppliedConfigurationsLabel',
{
defaultMessage: 'Supplied configurations',
}
),
},
],
enableDatePicker: false,
'data-test-subj': 'mlPageSuppliedConfigurations',
});

const PageWrapper: FC = () => {
const { context } = useRouteResolver('full', ['canGetJobs'], basicResolvers());

return (
<PageLoader context={context}>
<MlPageHeader>
<EuiFlexGroup
responsive={false}
wrap={false}
alignItems={'flexStart'}
gutterSize={'m'}
direction="column"
>
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.ml.suppliedConfigurations.preconfigurecJobsHeader"
defaultMessage="Supplied configurations"
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiText size="s">
<FormattedMessage
id="xpack.ml.suppliedConfigurations.preconfigurecJobsHeaderDescription"
defaultMessage="This page lists pre-defined anomaly detection job configurations with related Kibana assets."
/>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</MlPageHeader>
<SuppliedConfigurations />
</PageLoader>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ describe('DashboardService', () => {
});
expect(resp).toEqual([]);
});
test('should generate edit url to the dashboard', () => {
dashboardService.getDashboardEditUrl('test-id');
test('should generate url to the dashboard', () => {
dashboardService.getDashboardUrl('test-id');
expect(dashboard.locator?.getUrl).toHaveBeenCalledWith({
dashboardId: 'test-id',
useHash: false,
Expand Down
13 changes: 11 additions & 2 deletions x-pack/plugins/ml/public/application/services/dashboard_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,18 @@ export function dashboardServiceProvider(dashboardService: DashboardStart) {
return responses.hits;
},
/**
* Generates dashboard url with edit mode
* Fetch dashboards by id
*/
async getDashboardEditUrl(dashboardId: string) {
async fetchDashboardsById(ids: string[]) {
const findDashboardsService = await dashboardService.findDashboardsService();
const responses = await findDashboardsService.findByIds(ids);
Copy link
Copy Markdown
Member

@jgowdyelastic jgowdyelastic Sep 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm seeing 404 errors from this call in the console if the dashboard doesn't exist.
image

I think we should catch these and just return an empty array if they can't be found.

Copy link
Copy Markdown
Contributor Author

@alvarezmelissa87 alvarezmelissa87 Sep 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These errors are actually not thrown from here - findByIds has it's own error handling internally in src/plugins/dashboard/public/services/dashboard_content_management/lib/find_dashboards.ts and returns the caught error in an object with a 'status' and 'error' property. The response is type FindDashboardsByIdResponse - which is defined in that same file I shared. That's why here - we simply filter out the results with error status.

I think if we want to do something to remove the logged 404s, it will need to be in a separate PR and would need to chat with whoever maintains that service.

const existingDashboards = responses.filter(({ status }) => status === 'success');
return existingDashboards;
},
/**
* Generates dashboard url
*/
async getDashboardUrl(dashboardId: string, viewMode: ViewMode = ViewMode.EDIT) {
return await dashboardService.locator?.getUrl({
dashboardId,
viewMode: ViewMode.EDIT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
MlNodeCount,
} from '../../../../common/types/ml_server_info';
import type { MlCapabilitiesResponse } from '../../../../common/types/capabilities';
import type { RecognizeModuleResult } from '../../../../common/types/modules';
import type { Calendar, CalendarId, UpdateCalendar } from '../../../../common/types/calendars';
import type { BucketSpanEstimatorData } from '../../../../common/types/job_service';
import type {
Expand Down Expand Up @@ -439,6 +440,15 @@ export function mlApiProvider(httpService: HttpService) {
});
},

recognizeModule({ moduleId, size }: { moduleId: string; size?: number }) {
return httpService.http<RecognizeModuleResult>({
path: `${ML_INTERNAL_BASE_PATH}/modules/recognize_by_module/${moduleId}`,
method: 'GET',
version: '1',
query: { size },
});
},

listDataRecognizerModules(filter?: string[]) {
return httpService.http<any>({
path: `${ML_INTERNAL_BASE_PATH}/modules/get_module`,
Expand All @@ -448,9 +458,10 @@ export function mlApiProvider(httpService: HttpService) {
});
},

getDataRecognizerModule({ moduleId, filter }: { moduleId: string; filter?: string[] }) {
return httpService.http<Module>({
path: `${ML_INTERNAL_BASE_PATH}/modules/get_module/${moduleId}`,
getDataRecognizerModule(params?: { moduleId: string; filter?: string[] }) {
const { moduleId, filter } = params || {};
return httpService.http<Module | Module[]>({
path: `${ML_INTERNAL_BASE_PATH}/modules/get_module/${moduleId ?? ''}`,
method: 'GET',
version: '1',
query: { filter: filter?.join(',') },
Expand Down
Loading