Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] Fix links to dashboards in Lens created anomaly detection jobs #160156

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,20 @@ import { i18n } from '@kbn/i18n';
import { mergeWith, uniqWith, isEqual } from 'lodash';
import type { IUiSettingsClient } from '@kbn/core/public';
import type { TimefilterContract } from '@kbn/data-plugin/public';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import { firstValueFrom } from 'rxjs';
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { DashboardAppLocatorParams } from '@kbn/dashboard-plugin/public';
import type { DashboardAppLocatorParams, DashboardStart } from '@kbn/dashboard-plugin/public';
import type { Filter, Query, DataViewBase } from '@kbn/es-query';
import { FilterStateStore } from '@kbn/es-query';
import type { Embeddable } from '@kbn/lens-plugin/public';
import type { MapEmbeddable } from '@kbn/maps-plugin/public';
import type { ErrorType } from '@kbn/ml-error-utils';
import type { MlApiServices } from '../../../services/ml_api_service';
import type { Job, Datafeed } from '../../../../../common/types/anomaly_detection_jobs';
import { getFiltersForDSLQuery } from '../../../../../common/util/job_utils';
import { CREATED_BY_LABEL } from '../../../../../common/constants/new_job';
import { createQueries } from '../utils/new_job_utils';
import { createDatafeedId } from '../../../../../common/util/job_utils';
import { Job, Datafeed } from '../../../../../common/types/anomaly_detection_jobs';

export function isLensEmbeddable(arg: any): arg is Embeddable {
return arg.hasOwnProperty('type') && arg.type === 'lens';
Expand Down Expand Up @@ -61,7 +60,7 @@ export class QuickJobCreatorBase {
constructor(
protected readonly kibanaConfig: IUiSettingsClient,
protected readonly timeFilter: TimefilterContract,
protected readonly share: SharePluginStart,
protected readonly dashboardService: DashboardStart,
protected readonly mlApiServices: MlApiServices
) {}

Expand Down Expand Up @@ -232,13 +231,22 @@ export class QuickJobCreatorBase {
}

protected async createDashboardLink(dashboard: Dashboard, datafeedConfig: estypes.MlDatafeed) {
if (dashboard === undefined) {
const dashboardTitle = dashboard?.getTitle();
if (dashboardTitle === undefined || dashboardTitle === '') {
// embeddable may have not been in a dashboard
// and my not have been given a title as it is unsaved.
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo - may instead of my

return null;
}

const findDashboardsService = await this.dashboardService.findDashboardsService();
// find the dashboard from the dashboard service as the dashboard passed in may not have the correct id
const foundDashboard = await findDashboardsService.findByTitle(dashboardTitle);
if (foundDashboard === undefined) {
return null;
}

const params: DashboardAppLocatorParams = {
dashboardId: dashboard.id,
dashboardId: foundDashboard.id,
timeRange: {
from: '$earliest$',
to: '$latest$',
Expand All @@ -251,28 +259,23 @@ export class QuickJobCreatorBase {
FilterStateStore.GLOBAL_STATE
),
};
const dashboardLocator = this.share.url.locators.get('DASHBOARD_APP_LOCATOR');
const encodedUrl = dashboardLocator ? await dashboardLocator.getUrl(params) : '';
const url = decodeURIComponent(encodedUrl).replace(/^.+dashboards/, 'dashboards');

const dashboardName = dashboard.getOutput().title;
const location = await this.dashboardService.locator?.getLocation(params);
if (location === undefined) {
return null;
}

const urlName =
dashboardName === undefined
? i18n.translate('xpack.ml.newJob.fromLens.createJob.defaultUrlDashboard', {
defaultMessage: 'Original dashboard',
})
: i18n.translate('xpack.ml.newJob.fromLens.createJob.namedUrlDashboard', {
defaultMessage: 'Open {dashboardName}',
values: { dashboardName },
});
const url = `${location.app}${location.path}`;
const urlName = i18n.translate('xpack.ml.newJob.fromLens.createJob.namedUrlDashboard', {
defaultMessage: 'Open {dashboardTitle}',
values: { dashboardTitle },
});

return { url_name: urlName, url_value: url, time_range: 'auto' };
}

protected async getCustomUrls(dashboard: Dashboard, datafeedConfig: estypes.MlDatafeed) {
return dashboard !== undefined
? { custom_urls: [await this.createDashboardLink(dashboard, datafeedConfig)] }
: {};
const customUrls = await this.createDashboardLink(dashboard, datafeedConfig);
return dashboard !== undefined && customUrls !== null ? { custom_urls: [customUrls] } : {};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import type {
} from '@kbn/lens-plugin/public';
import type { IUiSettingsClient } from '@kbn/core/public';
import type { TimefilterContract } from '@kbn/data-plugin/public';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { Filter, Query } from '@kbn/es-query';
import type { DashboardStart } from '@kbn/dashboard-plugin/public';

import type { JobCreatorType } from '../common/job_creator';
import { createEmptyJob, createEmptyDatafeed } from '../common/job_creator/util/default_configs';
Expand All @@ -33,17 +33,17 @@ import {
getChartInfoFromVisualization,
} from './utils';
import { VisualizationExtractor } from './visualization_extractor';
import { QuickJobCreatorBase, CreateState } from '../job_from_dashboard';
import { QuickJobCreatorBase, type CreateState } from '../job_from_dashboard';

export class QuickLensJobCreator extends QuickJobCreatorBase {
constructor(
private readonly lens: LensPublicStart,
public readonly kibanaConfig: IUiSettingsClient,
public readonly timeFilter: TimefilterContract,
public readonly share: SharePluginStart,
public readonly mlApiServices: MlApiServices
kibanaConfig: IUiSettingsClient,
timeFilter: TimefilterContract,
dashboardService: DashboardStart,
mlApiServices: MlApiServices
) {
super(kibanaConfig, timeFilter, share, mlApiServices);
super(kibanaConfig, timeFilter, dashboardService, mlApiServices);
}

public async createAndSaveJob(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { Filter } from '@kbn/es-query';
import type { LensPublicStart, LensSavedObjectAttributes } from '@kbn/lens-plugin/public';
import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
import type { TimefilterContract } from '@kbn/data-plugin/public';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { DashboardStart } from '@kbn/dashboard-plugin/public';
import { QuickLensJobCreator } from './quick_create_job';
import type { MlApiServices } from '../../../services/ml_api_service';

Expand All @@ -21,7 +21,7 @@ interface Dependencies {
lens: LensPublicStart;
kibanaConfig: IUiSettingsClient;
timeFilter: TimefilterContract;
share: SharePluginStart;
dashboardService: DashboardStart;
mlApiServices: MlApiServices;
}
export async function resolver(
Expand All @@ -33,7 +33,7 @@ export async function resolver(
filtersRisonString: string,
layerIndexRisonString: string
) {
const { lens, mlApiServices, timeFilter, kibanaConfig, share } = deps;
const { lens, mlApiServices, timeFilter, kibanaConfig, dashboardService } = deps;
if (lensSavedObjectRisonString === undefined) {
throw new Error('Cannot create visualization');
}
Expand Down Expand Up @@ -75,6 +75,12 @@ export async function resolver(
layerIndex = undefined;
}

const jobCreator = new QuickLensJobCreator(lens, kibanaConfig, timeFilter, share, mlApiServices);
const jobCreator = new QuickLensJobCreator(
lens,
kibanaConfig,
timeFilter,
dashboardService,
mlApiServices
);
await jobCreator.createAndStashADJob(vis, from, to, query, filters, layerIndex);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { i18n } from '@kbn/i18n';
import type { MapEmbeddable } from '@kbn/maps-plugin/public';
import type { IUiSettingsClient } from '@kbn/core/public';
import type { TimefilterContract } from '@kbn/data-plugin/public';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { Filter, Query } from '@kbn/es-query';
import type { DataView } from '@kbn/data-views-plugin/public';
import type { DashboardStart } from '@kbn/dashboard-plugin/public';

import type { MlApiServices } from '../../../services/ml_api_service';
import { getDataViews } from '../../../util/dependency_cache';
Expand Down Expand Up @@ -40,12 +40,12 @@ interface VisDescriptor {

export class QuickGeoJobCreator extends QuickJobCreatorBase {
constructor(
public readonly kibanaConfig: IUiSettingsClient,
public readonly timeFilter: TimefilterContract,
public readonly share: SharePluginStart,
public readonly mlApiServices: MlApiServices
kibanaConfig: IUiSettingsClient,
timeFilter: TimefilterContract,
dashboardService: DashboardStart,
mlApiServices: MlApiServices
) {
super(kibanaConfig, timeFilter, share, mlApiServices);
super(kibanaConfig, timeFilter, dashboardService, mlApiServices);
}

public async createAndSaveGeoJob({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { Query } from '@kbn/es-query';
import type { Filter } from '@kbn/es-query';
import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
import type { TimefilterContract } from '@kbn/data-plugin/public';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { DashboardStart } from '@kbn/dashboard-plugin/public';
import type { MlApiServices } from '../../../services/ml_api_service';
import { QuickGeoJobCreator } from './quick_create_job';

Expand All @@ -19,7 +19,7 @@ import { getDefaultQuery } from '../utils/new_job_utils';
interface Dependencies {
kibanaConfig: IUiSettingsClient;
timeFilter: TimefilterContract;
share: SharePluginStart;
dashboardService: DashboardStart;
mlApiServices: MlApiServices;
}
export async function resolver(
Expand All @@ -33,7 +33,7 @@ export async function resolver(
toRisonString: string,
layer?: string
) {
const { kibanaConfig, timeFilter, share, mlApiServices } = deps;
const { kibanaConfig, timeFilter, dashboardService, mlApiServices } = deps;
let decodedDashboard;
let decodedEmbeddable;
let decodedLayer;
Expand Down Expand Up @@ -85,7 +85,12 @@ export async function resolver(
to = '';
}

const jobCreator = new QuickGeoJobCreator(kibanaConfig, timeFilter, share, mlApiServices);
const jobCreator = new QuickGeoJobCreator(
kibanaConfig,
timeFilter,
dashboardService,
mlApiServices
);

await jobCreator.createAndStashGeoJob(
dvId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const PageWrapper: FC<PageProps> = ({ location }) => {
timefilter: { timefilter: timeFilter },
},
},
share,
dashboard: dashboardService,
uiSettings: kibanaConfig,
mlServices: { mlApiServices },
lens,
Expand All @@ -44,7 +44,7 @@ const PageWrapper: FC<PageProps> = ({ location }) => {
const { context } = useRouteResolver('full', ['canCreateJob'], {
redirect: () =>
resolver(
{ lens, mlApiServices, timeFilter, kibanaConfig, share },
{ lens, mlApiServices, timeFilter, kibanaConfig, dashboardService },
vis,
from,
to,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const PageWrapper: FC<PageProps> = ({ location }) => {
timefilter: { timefilter: timeFilter },
},
},
share,
dashboard: dashboardService,
uiSettings: kibanaConfig,
mlServices: { mlApiServices },
},
Expand All @@ -50,7 +50,7 @@ const PageWrapper: FC<PageProps> = ({ location }) => {
const { context } = useRouteResolver('full', ['canCreateJob'], {
redirect: () =>
resolver(
{ mlApiServices, timeFilter, kibanaConfig, share },
{ mlApiServices, timeFilter, kibanaConfig, dashboardService },
dashboard,
dataViewId,
embeddable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type { CoreStart } from '@kbn/core/public';
import type { LensPublicStart } from '@kbn/lens-plugin/public';
import type { MapEmbeddable } from '@kbn/maps-plugin/public';
import type { Embeddable } from '@kbn/lens-plugin/public';
import type { DashboardStart } from '@kbn/dashboard-plugin/public';

import { getMlGlobalServices } from '../../../application/app';

Expand All @@ -28,6 +29,7 @@ export function createFlyout(
coreStart: CoreStart,
share: SharePluginStart,
data: DataPublicPluginStart,
dashboardService: DashboardStart,
lens?: LensPublicStart
): Promise<void> {
const {
Expand All @@ -53,6 +55,7 @@ export function createFlyout(
share,
data,
lens,
dashboardService,
mlServices: getMlGlobalServices(http),
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import { useKibana } from '@kbn/kibana-react-plugin/public';

import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { LensPublicStart } from '@kbn/lens-plugin/public';
import type { DashboardStart } from '@kbn/dashboard-plugin/public';
import type { MlServicesContext } from '../../../application/app';

interface StartPlugins {
data: DataPublicPluginStart;
share: SharePluginStart;
lens: LensPublicStart;
dashboardService: DashboardStart;
}
export type StartServices = CoreStart & StartPlugins & MlServicesContext;
export const useMlFromLensKibanaContext = () => useKibana<StartServices>();
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const CompatibleLayer: FC<Props> = ({ layer, layerIndex, embeddable }) =>
uiSettings,
mlServices: { mlApiServices },
lens,
dashboardService,
},
} = useMlFromLensKibanaContext();

Expand All @@ -43,7 +44,7 @@ export const CompatibleLayer: FC<Props> = ({ layer, layerIndex, embeddable }) =>
lens,
uiSettings,
data.query.timefilter.timefilter,
share,
dashboardService,
mlApiServices
),
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { CoreStart } from '@kbn/core/public';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
import type { LensPublicStart } from '@kbn/lens-plugin/public';
import type { DashboardStart } from '@kbn/dashboard-plugin/public';
import { createFlyout } from '../common/create_flyout';
import { LensLayerSelectionFlyout } from './lens_vis_layer_selection_flyout';

Expand All @@ -18,7 +19,16 @@ export async function showLensVisToADJobFlyout(
coreStart: CoreStart,
share: SharePluginStart,
data: DataPublicPluginStart,
lens: LensPublicStart
lens: LensPublicStart,
dashboardService: DashboardStart
): Promise<void> {
return createFlyout(LensLayerSelectionFlyout, embeddable, coreStart, share, data, lens);
return createFlyout(
LensLayerSelectionFlyout,
embeddable,
coreStart,
share,
data,
dashboardService,
lens
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,19 @@ export const CompatibleLayer: FC<Props> = ({ embeddable, layer, layerIndex }) =>
data,
share,
uiSettings,
dashboardService,
mlServices: { mlApiServices },
},
} = useMlFromLensKibanaContext();

const quickJobCreator = useMemo(
() =>
new QuickGeoJobCreator(uiSettings, data.query.timefilter.timefilter, share, mlApiServices),
new QuickGeoJobCreator(
uiSettings,
data.query.timefilter.timefilter,
dashboardService,
mlApiServices
),
// eslint-disable-next-line react-hooks/exhaustive-deps
[data, uiSettings]
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import type { LayerResult } from '../../../../../application/jobs/new_job/job_from_map';
import { CompatibleLayer } from './compatible_layer';
import { IncompatibleLayer } from './incompatible_layer';

interface Props {
layer: LayerResult;
layerIndex: number;
Expand Down
Loading