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

decouple anomaly job creation action from Embeddable framework #176869

Merged
merged 26 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
55f14d7
decoupld anomaly job creation action from Embeddable framework
nreese Feb 13, 2024
13a1aaf
finish conversion and typing
nreese Feb 15, 2024
a032c54
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Feb 15, 2024
c0e2286
merge with main
nreese Feb 15, 2024
80a079b
move MapsApi into maps plugin
nreese Feb 15, 2024
c91a509
move LensApi to has_lens_config
nreese Feb 15, 2024
dc059a6
clean-up
nreese Feb 15, 2024
a4c8686
Merge branch 'main' into ml_job_creation_action
kibanamachine Feb 20, 2024
f11b045
merge with main
nreese Feb 21, 2024
e0e271c
Merge branch 'main' into ml_job_creation_action
kibanamachine Feb 22, 2024
b729f06
use getPanelTitle utility method to read panel titles
nreese Feb 22, 2024
e1d233d
merge with main
nreese Mar 5, 2024
835e3ae
add title and search api guards to isLensApi type guard
nreese Mar 5, 2024
c8c6a4c
add type guard to map API
nreese Mar 5, 2024
1b3f3e3
review feedback
nreese Mar 5, 2024
b82062c
eslint
nreese Mar 5, 2024
514010c
clean up
nreese Mar 5, 2024
3df9ef8
Merge branch 'main' into ml_job_creation_action
kibanamachine Mar 5, 2024
1d61828
clean up
nreese Mar 5, 2024
dfbb86b
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Mar 5, 2024
0057af8
merge with main
nreese Mar 8, 2024
f5ad2f5
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Mar 8, 2024
6e6fa3e
Merge branch 'main' into ml_job_creation_action
kibanamachine Mar 11, 2024
3bfa986
move DashboardApi interface into existing DashboardAPI interface
nreese Mar 11, 2024
a5ef271
update DashboardAPI
nreese Mar 11, 2024
ce78326
eslint
nreese Mar 11, 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
2 changes: 1 addition & 1 deletion x-pack/plugins/lens/public/embeddable/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@

export * from './embeddable';

export { type HasLensConfig, apiHasLensConfig } from './interfaces/has_lens_config';
export { type HasLensConfig, type LensApi, apiHasLensConfig } from './interfaces/has_lens_config';
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,25 @@
* 2.0.
*/

import { type HasType, apiIsOfType } from '@kbn/presentation-publishing';
import type {
HasParentApi,
HasType,
PublishesLocalUnifiedSearch,
PublishesPanelTitle,
} from '@kbn/presentation-publishing';
import { apiIsOfType } from '@kbn/presentation-publishing';
import { LensSavedObjectAttributes } from '../embeddable';

export type HasLensConfig = HasType<'lens'> & {
getSavedVis: () => Readonly<LensSavedObjectAttributes | undefined>;
};

export const apiHasLensConfig = (api: unknown): api is HasLensConfig => {
export type LensApi = HasLensConfig &
PublishesPanelTitle &
PublishesLocalUnifiedSearch &
Partial<HasParentApi<unknown>>;

export const apiHasLensConfig = (api: unknown): api is LensApi => {
nreese marked this conversation as resolved.
Show resolved Hide resolved
return Boolean(
api && apiIsOfType(api, 'lens') && typeof (api as HasLensConfig).getSavedVis === 'function'
);
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/lens/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export type { InlineEditLensEmbeddableContext } from './trigger_actions/open_len

export type {
HasLensConfig,
LensApi,
LensEmbeddableInput,
LensSavedObjectAttributes,
Embeddable,
Expand Down
32 changes: 32 additions & 0 deletions x-pack/plugins/maps/public/embeddable/has_map_config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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 {
HasParentApi,
HasType,
PublishesDataViews,
PublishesPanelTitle,
PublishesLocalUnifiedSearch,
} from '@kbn/presentation-publishing';
import { apiIsOfType } from '@kbn/presentation-publishing';
import type { ILayer } from '../classes/layers/layer';

export type HasMapConfig = HasType<'map'> & {
getLayerList: () => ILayer[];
};

export type MapApi = HasMapConfig &
PublishesDataViews &
PublishesPanelTitle &
PublishesLocalUnifiedSearch &
Partial<HasParentApi<unknown>>;

export const apiHasMapConfig = (api: unknown): api is MapApi => {
nreese marked this conversation as resolved.
Show resolved Hide resolved
return Boolean(
api && apiIsOfType(api, 'map') && typeof (api as HasMapConfig).getLayerList === 'function'
);
};
1 change: 1 addition & 0 deletions x-pack/plugins/maps/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type {
export type { MapsSetupApi, MapsStartApi } from './api';

export type { MapEmbeddable, MapEmbeddableInput, MapEmbeddableOutput } from './embeddable';
export { type HasMapConfig, type MapApi, apiHasMapConfig } from './embeddable/has_map_config';

export type { EMSTermJoinConfig, SampleValuesConfig } from './ems_autosuggest';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
* 2.0.
*/

export { QuickJobCreatorBase, isLensEmbeddable, isMapEmbeddable } from './quick_create_job_base';
export { QuickJobCreatorBase } from './quick_create_job_base';
export type { CreateState } from './quick_create_job_base';
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,15 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import type { DashboardLocatorParams, 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 { DashboardApi } from '../../../../ui_actions/types';
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';

export function isLensEmbeddable(arg: any): arg is Embeddable {
return arg.hasOwnProperty('type') && arg.type === 'lens';
}

export function isMapEmbeddable(arg: any): arg is MapEmbeddable {
return arg.hasOwnProperty('type') && arg.type === 'map';
}

export type Dashboard = Embeddable['parent'];

interface CreationState {
success: boolean;
error?: ErrorType;
Expand Down Expand Up @@ -83,7 +72,7 @@ export class QuickJobCreatorBase {
end: number | undefined;
startJob: boolean;
runInRealTime: boolean;
dashboard?: Dashboard;
dashboard?: DashboardApi;
}) {
const datafeedId = createDatafeedId(jobId);
const datafeed = { ...datafeedConfig, job_id: jobId, datafeed_id: datafeedId };
Expand Down Expand Up @@ -230,23 +219,14 @@ export class QuickJobCreatorBase {
return mergedQueries;
}

private async createDashboardLink(dashboard: Dashboard, datafeedConfig: estypes.MlDatafeed) {
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.
return null;
}

const findDashboardsService = await this.dashboardService.findDashboardsService();
nreese marked this conversation as resolved.
Show resolved Hide resolved
// 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) {
private async createDashboardLink(dashboard: DashboardApi, datafeedConfig: estypes.MlDatafeed) {
const savedObjectId = dashboard.savedObjectId?.value;
if (!savedObjectId) {
return null;
}

const params: DashboardLocatorParams = {
dashboardId: foundDashboard.id,
dashboardId: savedObjectId,
timeRange: {
from: '$earliest$',
to: '$latest$',
Expand All @@ -268,13 +248,13 @@ export class QuickJobCreatorBase {
const url = `${location.app}${location.path}`;
const urlName = i18n.translate('xpack.ml.newJob.fromLens.createJob.namedUrlDashboard', {
defaultMessage: 'Open {dashboardTitle}',
values: { dashboardTitle },
values: { dashboardTitle: dashboard.panelTitle?.value ?? 'dashboard' },
});

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

private async getCustomUrls(dashboard: Dashboard, datafeedConfig: estypes.MlDatafeed) {
private async getCustomUrls(dashboard: DashboardApi, datafeedConfig: estypes.MlDatafeed) {
const customUrls = await this.createDashboardLink(dashboard, datafeedConfig);
return dashboard !== undefined && customUrls !== null ? { custom_urls: [customUrls] } : {};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ export {
getJobsItemsFromEmbeddable,
isCompatibleVisualizationType,
redirectToADJobWizards,
getChartInfoFromVisualization,
} from './utils';
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@
import { i18n } from '@kbn/i18n';
import type {
ChartInfo,
Embeddable,
LensPublicStart,
LensSavedObjectAttributes,
} from '@kbn/lens-plugin/public';
import type { IUiSettingsClient } from '@kbn/core/public';
import type { TimefilterContract } from '@kbn/data-plugin/public';
import type { Filter, Query } from '@kbn/es-query';
import type { DashboardStart } from '@kbn/dashboard-plugin/public';

import type { LensApi } from '@kbn/lens-plugin/public';
import type { JobCreatorType } from '../common/job_creator';
import { createEmptyJob, createEmptyDatafeed } from '../common/job_creator/util/default_configs';
import { stashJobForCloning } from '../common/job_creator/util/general';
Expand Down Expand Up @@ -49,7 +48,7 @@ export class QuickLensJobCreator extends QuickJobCreatorBase {
public async createAndSaveJob(
jobId: string,
bucketSpan: string,
embeddable: Embeddable,
embeddable: LensApi,
startJob: boolean,
runInRealTime: boolean,
layerIndex: number
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@

import { i18n } from '@kbn/i18n';
import type {
Embeddable,
LensPublicStart,
DataType,
ChartInfo,
LensSavedObjectAttributes,
} from '@kbn/lens-plugin/public';
import type { Query } from '@kbn/es-query';
import { apiIsOfType } from '@kbn/presentation-publishing';
import type { SerializableRecord } from '@kbn/utility-types';
import type { SharePluginStart } from '@kbn/share-plugin/public';
import { layerTypes } from '@kbn/lens-plugin/public';
import { KBN_FIELD_TYPES } from '@kbn/field-types';
import { ML_JOB_AGGREGATION } from '@kbn/ml-anomaly-utils';

import type { LensApi } from '@kbn/lens-plugin/public';
import type { DashboardApi } from '../../../../ui_actions/types';
import { ML_PAGES, ML_APP_LOCATOR } from '../../../../../common/constants/locator';

export const COMPATIBLE_SERIES_TYPES = [
Expand All @@ -43,7 +45,7 @@ export const COMPATIBLE_SPLIT_FIELD_TYPES: DataType[] = [
];

export async function redirectToADJobWizards(
embeddable: Embeddable,
embeddable: LensApi,
layerIndex: number,
share: SharePluginStart,
lens: LensPublicStart
Expand All @@ -66,7 +68,7 @@ export async function redirectToADJobWizards(
window.open(url, '_blank');
}

export async function getJobsItemsFromEmbeddable(embeddable: Embeddable, lens?: LensPublicStart) {
export async function getJobsItemsFromEmbeddable(embeddable: LensApi, lens?: LensPublicStart) {
if (!lens) {
throw Error(
i18n.translate('xpack.ml.newJob.fromLens.createJob.error.lensNotFound', {
Expand All @@ -75,18 +77,18 @@ export async function getJobsItemsFromEmbeddable(embeddable: Embeddable, lens?:
);
}

const { filters, timeRange, ...input } = embeddable.getInput();
const query = input.query === undefined ? { query: '', language: 'kuery' } : input.query;
const dashboardApi = apiIsOfType(embeddable.parentApi, 'dashboard')
? (embeddable.parentApi as DashboardApi)
: undefined;

const timeRange = embeddable.localTimeRange?.value ?? dashboardApi?.localTimeRange?.value;
if (timeRange === undefined) {
throw Error(
i18n.translate('xpack.ml.newJob.fromLens.createJob.error.noTimeRange', {
defaultMessage: 'Time range not specified.',
})
);
}
const { to, from } = timeRange;

const vis = embeddable.getSavedVis();

if (vis === undefined) {
Expand All @@ -97,17 +99,14 @@ export async function getJobsItemsFromEmbeddable(embeddable: Embeddable, lens?:
);
}

const chartInfo = await getChartInfoFromVisualization(lens, vis);
const dashboard = embeddable.parent?.type === 'dashboard' ? embeddable.parent : undefined;

return {
vis,
chartInfo,
from,
to,
query,
filters,
dashboard,
chartInfo: await getChartInfoFromVisualization(lens, vis),
from: timeRange.from,
to: timeRange.to,
query: (dashboardApi?.localQuery?.value as Query) ?? { query: '', language: 'kuery' },
filters: dashboardApi?.localFilters?.value ?? [],
dashboard: dashboardApi,
};
}

Expand Down Expand Up @@ -238,7 +237,7 @@ export function createDetectors(
export async function getChartInfoFromVisualization(
lens: LensPublicStart,
vis: LensSavedObjectAttributes
) {
): Promise<ChartInfo> {
const chartInfo = await (await (await lens.stateHelperApi()).chartInfo).getChartInfo(vis);
if (!chartInfo) {
throw new Error('Cannot create job, chart info is undefined');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
* 2.0.
*/

import type { Embeddable, LensPublicStart, ChartInfo } from '@kbn/lens-plugin/public';
import type { LensPublicStart, ChartInfo } from '@kbn/lens-plugin/public';
import { layerTypes } from '@kbn/lens-plugin/public';

import { i18n } from '@kbn/i18n';

import type { ErrorType } from '@kbn/ml-error-utils';
import type { LensApi } from '@kbn/lens-plugin/public';
import { JOB_TYPE } from '../../../../../common/constants/new_job';
import {
getVisTypeFactory,
Expand Down Expand Up @@ -39,7 +38,7 @@ export class VisualizationExtractor {
constructor() {}

public async getResultLayersFromEmbeddable(
embeddable: Embeddable,
embeddable: LensApi,
lens: LensPublicStart
): Promise<LayerResult[]> {
const { chartInfo } = await getJobsItemsFromEmbeddable(embeddable, lens);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
*/

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 { 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 { MapApi } from '@kbn/maps-plugin/public';
import type { MlApiServices } from '../../../services/ml_api_service';
import { getDataViews } from '../../../util/dependency_cache';
import {
Expand Down Expand Up @@ -62,7 +61,7 @@ export class QuickGeoJobCreator extends QuickJobCreatorBase {
}: {
jobId: string;
bucketSpan: string;
embeddable: MapEmbeddable;
embeddable: MapApi;
startJob: boolean;
runInRealTime: boolean;
dataViewId?: string;
Expand All @@ -80,8 +79,8 @@ export class QuickGeoJobCreator extends QuickJobCreatorBase {
} = await getJobsItemsFromEmbeddable(embeddable);

// Map level stuff
const embeddableQuery = (await embeddable.getQuery()) ?? getDefaultQuery();
const embeddableFilters = (await embeddable.getFilters()) ?? [];
const embeddableQuery = (embeddable.localQuery?.value as Query) ?? getDefaultQuery();
const embeddableFilters = embeddable.localFilters?.value ?? [];

if (dashboardQuery === undefined || dashboardFilters === undefined) {
throw new Error('Cannot create job, query and filters are undefined');
Expand Down
Loading