[ML] Trained models list: disable 'View training data' action if data frame analytics job no longer exists (#171061)

## Summary

Fixes https://github.com/elastic/kibana/issues/167667, disabling the
'View training data' action for models in the Trained Models list if the
data frame analytics job which created the model no longer exists

Adds `origin_job_exists` property to trained models list model items.
This is set during the models fetch for models with associated data
frame analytics jobs.

### Checklist

Delete any items that are not applicable to this PR.

- [ ] 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)
- [ ]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [ ] [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
- [ ] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] 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))
- [ ] If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)
- [ ] 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))
- [ ] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Melissa Alvarez 2023-11-21 08:50:38 -07:00 committed by GitHub
parent 5256108075
commit 39af788067
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 40 additions and 11 deletions

View file

@ -98,6 +98,7 @@ export type TrainedModelConfigResponse = estypes.MlTrainedModelConfig & {
* Associated pipelines. Extends response from the ES endpoint.
*/
pipelines?: Record<string, PipelineDefinition> | null;
origin_job_exists?: boolean;
metadata?: {
analytics_config: DataFrameAnalyticsConfig;

View file

@ -130,18 +130,19 @@ export function useModelActions({
return useMemo(
() => [
{
name: i18n.translate('xpack.ml.trainedModels.modelsList.viewTrainingDataActionLabel', {
name: i18n.translate('xpack.ml.trainedModels.modelsList.viewTrainingDataNameActionLabel', {
defaultMessage: 'View training data',
}),
description: i18n.translate(
'xpack.ml.trainedModels.modelsList.viewTrainingDataActionLabel',
{
defaultMessage: 'View training data',
defaultMessage: 'Training data can be viewed when data frame analytics job exists.',
}
),
icon: 'visTable',
type: 'icon',
available: (item) => !!item.metadata?.analytics_config?.id,
enabled: (item) => item.origin_job_exists === true,
onClick: async (item) => {
if (item.metadata?.analytics_config === undefined) return;
@ -164,7 +165,6 @@ export function useModelActions({
await navigateToUrl(url);
},
isPrimary: true,
},
{
name: i18n.translate('xpack.ml.inference.modelsList.analyticsMapActionLabel', {

View file

@ -79,6 +79,7 @@ export type ModelItem = TrainedModelConfigResponse & {
type?: string[];
stats?: Stats & { deployment_stats: TrainedModelDeploymentStatsResponse[] };
pipelines?: ModelPipelines['pipelines'] | null;
origin_job_exists?: boolean;
deployment_ids: string[];
putModelConfig?: object;
state: ModelState;

View file

@ -29,9 +29,9 @@ import {
createIngestPipelineSchema,
modelDownloadsQuery,
} from './schemas/inference_schema';
import type {
import {
PipelineDefinition,
TrainedModelConfigResponse,
type TrainedModelConfigResponse,
} from '../../common/types/trained_models';
import { mlLog } from '../lib/log';
import { forceQuerySchema } from './schemas/anomaly_detectors_schema';
@ -39,10 +39,9 @@ import { modelsProvider } from '../models/model_management';
export const DEFAULT_TRAINED_MODELS_PAGE_SIZE = 10000;
export function filterForEnabledFeatureModels(
models: TrainedModelConfigResponse[] | estypes.MlTrainedModelConfig[],
enabledFeatures: MlFeatures
) {
export function filterForEnabledFeatureModels<
T extends TrainedModelConfigResponse | estypes.MlTrainedModelConfig
>(models: T[], enabledFeatures: MlFeatures) {
let filteredModels = models;
if (enabledFeatures.nlp === false) {
filteredModels = filteredModels.filter((m) => m.model_type === 'tree_ensemble');
@ -191,10 +190,38 @@ export function trainedModelsRoutes(
mlLog.debug(e);
}
const body = filterForEnabledFeatureModels(result, getEnabledFeatures());
const filteredModels = filterForEnabledFeatureModels(result, getEnabledFeatures());
try {
const jobIds = filteredModels
.map((model) => {
const id = model.metadata?.analytics_config?.id;
if (id) {
return `${id}*`;
}
})
.filter((id) => id !== undefined);
if (jobIds.length) {
const { data_frame_analytics: jobs } = await mlClient.getDataFrameAnalytics({
id: jobIds.join(','),
allow_no_match: true,
});
filteredModels.forEach((model) => {
const dfaId = model?.metadata?.analytics_config?.id;
if (dfaId !== undefined) {
// if this is a dfa model, set origin_job_exists
model.origin_job_exists = jobs.find((job) => job.id === dfaId) !== undefined;
}
});
}
} catch (e) {
// Swallow error to prevent blocking trained models result
}
return response.ok({
body,
body: filteredModels,
});
} catch (e) {
return response.customError(wrapError(e));