Skip to content

Commit

Permalink
[ML][Fleet] Link to ML assets from Integration > Assets tab (#189767)
Browse files Browse the repository at this point in the history
## Summary

Related issue: #182199

This PR adds links for ML assets installed via Integrations. 

For `transform` and `ml_model` (as they are ES asset types) the manual
mappings in
[get_bulk_assets.ts](https://github.com/elastic/kibana/blob/ac6f643904c6bb7e8342b68e470e933e33a7c206/x-pack/plugins/fleet/server/services/epm/packages/get_bulk_assets.ts#L21)
has been modified to include a link to the Transform management page and
the Trained models page, respectively. The pages will be filtered to the
asset id.

This PR also adds the ability to save state in the url for the Transform
list to allow the url to link to a filtered list of transforms.

### To test:

- From the side navigation - click the `Add integrations` button at the
bottom to get to the Integrations page.
<img width="318" alt="image"
src="https://github.com/user-attachments/assets/d4632221-7f83-4678-ac0f-cb1e20853a6d">

- To test ml models link, install the `Living off the Land Attack
Detection` integration
- To test transform link, install the `Lateral Movement Detection`
integration

- Once they are installed you can navigate to the `Installed
Integrations` tab on the Integrations page
<img width="660" alt="image"
src="https://github.com/user-attachments/assets/8dc1db76-4b93-4057-b502-a90980a2a484">

- Select the installed package you want to view and then go to the
`Assets` tab
<img width="1256" alt="image"
src="https://github.com/user-attachments/assets/c3382f9d-b1ed-4043-ac3f-73180effefe8">

- You can then expand the desired asset section (Ml models or
transforms) and click the link to ensure it takes you to the correct
place


### 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
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [ ] 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: Elastic Machine <[email protected]>
  • Loading branch information
alvarezmelissa87 and elasticmachine authored Aug 13, 2024
1 parent 0719f5c commit 8be5ad8
Show file tree
Hide file tree
Showing 22 changed files with 121 additions and 86 deletions.
2 changes: 2 additions & 0 deletions x-pack/packages/ml/url_state/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ export {
UrlStateProvider,
type Accessor,
type Dictionary,
type ListingPageUrlState,
type PageUrlState,
type SetUrlState,
} from './src/url_state';
12 changes: 11 additions & 1 deletion x-pack/packages/ml/url_state/src/url_state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ export interface Dictionary<TValue> {
[id: string]: TValue;
}

export interface ListingPageUrlState {
pageSize: number;
pageIndex: number;
sortField: string;
sortDirection: string;
queryText?: string;
showPerPageOptions?: boolean;
showAll?: boolean;
}

export type Accessor = '_a' | '_g';
export type SetUrlState = (
accessor: Accessor,
Expand Down Expand Up @@ -241,7 +251,7 @@ export class PageUrlStateService<T> {
}
}

interface PageUrlState {
export interface PageUrlState {
pageKey: string;
pageUrlState: object;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import type { Filter } from '@kbn/es-query';
import type { Query } from '@kbn/data-plugin/common/query';
import type { SearchQueryLanguage } from '@kbn/ml-query-utils';
import type { ListingPageUrlState } from '@kbn/ml-url-state';
import type { FieldVisConfig } from '../../../../common/types/field_vis_config';
import type { RandomSamplerOption } from '../constants/random_sampler';

Expand All @@ -30,15 +31,8 @@ export interface DataVisualizerPageState {
documentCountStats?: FieldVisConfig;
}

export interface ListingPageUrlState {
pageSize: number;
pageIndex: number;
sortField: string;
sortDirection: string;
queryText?: string;
}

export interface DataVisualizerIndexBasedAppState extends Omit<ListingPageUrlState, 'queryText'> {
export interface DataVisualizerIndexBasedAppState
extends Omit<ListingPageUrlState, 'queryText' | 'showPerPageOptions' | 'showAll'> {
searchString?: Query['query'];
searchQuery?: Query['query'];
searchQueryLanguage?: SearchQueryLanguage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@ const getKibanaLinkForESAsset = (type: ElasticsearchAssetType, id: string): stri
case 'data_stream_ilm_policy':
return `/app/management/data/index_lifecycle_management/policies/edit/${id}`;
case 'transform':
// TODO: Confirm link for transforms
return '';
return `/app/management/data/transform?_a=(transform:(queryText:${id}))`;
case 'ml_model':
// TODO: Confirm link for ml models
return '';
return `/app/ml/trained_models?_a=(trained_models:(queryText:'model_id:(${id})'))`;
default:
return '';
}
Expand Down
9 changes: 0 additions & 9 deletions x-pack/plugins/ml/common/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,6 @@ type DeepReadonlyObject<T> = {
readonly [P in keyof T]: DeepReadonly<T[P]>;
};

export interface ListingPageUrlState {
pageSize: number;
pageIndex: number;
sortField: string;
sortDirection: string;
queryText?: string;
showAll?: boolean;
}

export type AppPageState<T> = {
[key in MlPages]?: Partial<T>;
};
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/ml/common/types/locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import type { RefreshInterval, TimeRange } from '@kbn/data-plugin/common/query';
import type { DataFrameAnalysisConfigType } from '@kbn/ml-data-frame-analytics-utils';
import type { InfluencersFilterQuery } from '@kbn/ml-anomaly-utils';
import type { SearchQueryLanguage } from '@kbn/ml-query-utils';
import type { ListingPageUrlState } from '@kbn/ml-url-state';
import type { JobId } from './anomaly_detection_jobs/job';
import type { ListingPageUrlState } from './common';
import type { ML_PAGES } from '../constants/locator';

type OptionalPageState = object | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
DATA_FRAME_TASK_STATE,
type DataFrameAnalyticsId,
} from '@kbn/ml-data-frame-analytics-utils';
import type { ListingPageUrlState } from '@kbn/ml-url-state';
import { useRefreshAnalyticsList } from '../../../../common';
import { usePermissionCheck } from '../../../../../capabilities/check_capabilities';
import { useNavigateToPath } from '../../../../../contexts/kibana';
Expand All @@ -38,7 +39,6 @@ import { CreateAnalyticsButton } from '../create_analytics_button';
import { filterAnalytics } from '../../../../common/search_bar_filters';
import { AnalyticsEmptyPrompt } from '../empty_prompt';
import { useTableSettings } from './use_table_settings';
import type { ListingPageUrlState } from '../../../../../../../common/types/common';
import { JobsAwaitingNodeWarning } from '../../../../../components/jobs_awaiting_node_warning';
import { useRefresh } from '../../../../../routing/use_refresh';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import type { Direction, EuiBasicTableProps, Pagination } from '@elastic/eui';
import { useCallback, useMemo } from 'react';
import type { ListingPageUrlState } from '../../../../../../../common/types/common';
import type { ListingPageUrlState } from '@kbn/ml-url-state';

const PAGE_SIZE_OPTIONS = [10, 25, 50];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ import React, { useMemo, useState } from 'react';

import { useLocation } from 'react-router-dom';
import { FormattedMessage } from '@kbn/i18n-react';
import { useUrlState, usePageUrlState } from '@kbn/ml-url-state';
import { useUrlState, usePageUrlState, type ListingPageUrlState } from '@kbn/ml-url-state';
import { DataFrameAnalyticsList } from './components/analytics_list';
import { useRefreshInterval } from './components/analytics_list/use_refresh_interval';
import { NodeAvailableWarning } from '../../../components/node_available_warning';
import { SavedObjectsWarning } from '../../../components/saved_objects_warning';
import { UpgradeWarning } from '../../../components/upgrade';
import { JobMap } from '../job_map';
import type { ListingPageUrlState } from '../../../../../common/types/common';
import { DataFrameAnalyticsListColumn } from './components/analytics_list/common';
import { ML_PAGES } from '../../../../../common/constants/locator';
import { HelpMenu } from '../../../components/help_menu';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import type { FC } from 'react';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { usePageUrlState } from '@kbn/ml-url-state';
import type { ListingPageUrlState } from '@kbn/ml-url-state';
import { JobsListView } from './components/jobs_list_view';
import { ML_PAGES } from '../../../../common/constants/locator';
import type { ListingPageUrlState } from '../../../../common/types/common';
import { HelpMenu } from '../../components/help_menu';
import { useMlKibana } from '../../contexts/kibana';
import { MlPageHeader } from '../../components/page_header';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { i18n } from '@kbn/i18n';
import { cloneDeep } from 'lodash';
import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common';
import { usePageUrlState } from '@kbn/ml-url-state';
import type { ListingPageUrlState } from '@kbn/ml-url-state';
import type { ModelsBarStats } from '../../components/stats_bar';
import { StatsBar } from '../../components/stats_bar';
import type { NodeDeploymentStatsResponse } from '../../../../common/types/trained_models';
Expand All @@ -28,7 +29,6 @@ import { useTableSettings } from '../../data_frame_analytics/pages/analytics_man
import { ExpandedRow } from './expanded_row';
import { MemoryPreviewChart } from './memory_preview_chart';
import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter';
import type { ListingPageUrlState } from '../../../../common/types/common';
import { useToastNotificationService } from '../../services/toast_notification_service';
import { useRefresh } from '../../routing/use_refresh';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import { isDefined } from '@kbn/ml-is-defined';
import { useStorage } from '@kbn/ml-local-storage';
import { dynamic } from '@kbn/shared-ux-utility';
import useMountedState from 'react-use/lib/useMountedState';
import type { ListingPageUrlState } from '@kbn/ml-url-state';
import { getModelStateColor, getModelDeploymentState } from './get_model_state';
import { ML_ELSER_CALLOUT_DISMISSED } from '../../../common/types/storage';
import { TechnicalPreviewBadge } from '../components/technical_preview_badge';
Expand All @@ -70,7 +71,6 @@ import type {
} from '../../../common/types/trained_models';
import { DeleteModelsModal } from './delete_models_modal';
import { ML_PAGES } from '../../../common/constants/locator';
import type { ListingPageUrlState } from '../../../common/types/common';
import { useTableSettings } from '../data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings';
import { useToastNotificationService } from '../services/toast_notification_service';
import { useFieldFormatter } from '../contexts/kibana/use_field_formatter';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common';
import useDebounce from 'react-use/lib/useDebounce';
import useMount from 'react-use/lib/useMount';
import { usePageUrlState } from '@kbn/ml-url-state';
import type { ListingPageUrlState } from '@kbn/ml-url-state';
import { useTimefilter, useTimeRangeUpdates } from '@kbn/ml-date-picker';
import { EntityFilter } from './entity_filter';
import { useMlNotifications } from '../../contexts/ml/ml_notifications_context';
Expand All @@ -26,7 +27,6 @@ import { useToastNotificationService } from '../../services/toast_notification_s
import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter';
import { useRefresh } from '../../routing/use_refresh';
import { useTableSettings } from '../../data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings';
import type { ListingPageUrlState } from '../../../../common/types/common';
import { ML_PAGES } from '../../../../common/constants/locator';
import type {
MlNotificationMessageLevel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { isEmpty } from 'lodash';
import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public';
import type { ListingPageUrlState } from '@kbn/ml-url-state';
import type {
AnomalyDetectionUrlState,
ExplorerAppState,
Expand All @@ -21,7 +22,7 @@ import type {
import { ML_PAGES } from '../../../common/constants/locator';
import { formatGenericMlUrl } from './common';
import { getGroupQueryText, getJobQueryText } from '../../../common/util/string_utils';
import type { AppPageState, ListingPageUrlState } from '../../../common/types/common';
import type { AppPageState } from '../../../common/types/common';

/**
* Creates URL to the Anomaly Detection Job management page
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/
import { isEmpty } from 'lodash';
import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public';
import type { ListingPageUrlState } from '@kbn/ml-url-state';
import { formatGenericMlUrl } from './common';
import type {
DataFrameAnalyticsExplorationQueryState,
Expand All @@ -21,7 +22,7 @@ import type {
} from '../../../common/types/locator';
import { ML_PAGES } from '../../../common/constants/locator';
import { getGroupQueryText, getJobQueryText } from '../../../common/util/string_utils';
import type { AppPageState, ListingPageUrlState } from '../../../common/types/common';
import type { AppPageState } from '../../../common/types/common';

export function formatDataFrameAnalyticsJobManagementUrl(
appBasePath: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
*/

import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public';
import type { ListingPageUrlState } from '@kbn/ml-url-state';
import type { MemoryUsageUrlState, TrainedModelsUrlState } from '../../../common/types/locator';
import { ML_PAGES } from '../../../common/constants/locator';
import type { AppPageState, ListingPageUrlState } from '../../../common/types/common';
import type { AppPageState } from '../../../common/types/common';

export function formatTrainedModelsManagementUrl(
appBasePath: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ describe('Transform: Transform List <TransformList />', () => {
transforms={[]}
transformsLoading={false}
transformsStatsLoading={false}
pageState={{ pageSize: 10, pageIndex: 0, sortField: 'id', sortDirection: 'asc' }}
updatePageState={jest.fn()}
/>
</QueryClientProvider>
</IntlProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { type FC, type MouseEventHandler, useState } from 'react';
import React, { type FC, type MouseEventHandler, useCallback, useMemo, useState } from 'react';

import { i18n } from '@kbn/i18n';
import type { EuiSearchBarProps } from '@elastic/eui';
Expand All @@ -18,8 +18,10 @@ import {
EuiInMemoryTable,
EuiPageTemplate,
EuiPopover,
EuiSearchBar,
EuiTitle,
} from '@elastic/eui';
import type { ListingPageUrlState } from '@kbn/ml-url-state';
import {
isReauthorizeActionDisabled,
ReauthorizeActionModal,
Expand Down Expand Up @@ -85,25 +87,36 @@ function getItemIdToExpandedRowMap(
interface TransformListProps {
isLoading: boolean;
onCreateTransform: MouseEventHandler<HTMLButtonElement>;
pageState: ListingPageUrlState;
transformNodes: number;
transforms: TransformListRow[];
transformsLoading: boolean;
transformsStatsLoading: boolean;
updatePageState: (update: Partial<ListingPageUrlState>) => void;
}

export const TransformList: FC<TransformListProps> = ({
isLoading,
onCreateTransform,
pageState,
transformNodes,
transforms,
transformsLoading,
transformsStatsLoading,
updatePageState,
}) => {
const refreshTransformList = useRefreshTransformList();
const { setEditAlertRule } = useAlertRuleFlyout();

const [query, setQuery] = useState<Parameters<NonNullable<EuiSearchBarProps['onChange']>>[0]>();
const searchQueryText = pageState.queryText ?? '';
const setSearchQueryText = useCallback(
(value) => {
updatePageState({ queryText: value });
},
[updatePageState]
);

const [searchError, setSearchError] = useState<string | undefined>();
const [expandedRowItemIds, setExpandedRowItemIds] = useState<TransformId[]>([]);
const [transformSelection, setTransformSelection] = useState<TransformListRow[]>([]);
const [isActionsMenuOpen, setIsActionsMenuOpen] = useState(false);
Expand All @@ -122,7 +135,9 @@ export const TransformList: FC<TransformListProps> = ({

const { sorting, pagination, onTableChange } = useTableSettings<TransformListRow>(
TRANSFORM_LIST_COLUMN.ID,
transforms
transforms,
pageState,
updatePageState
);

const { columns, modals: singleActionModals } = useColumns(
Expand All @@ -133,10 +148,11 @@ export const TransformList: FC<TransformListProps> = ({
transformsStatsLoading
);

const searchError = query?.error ? query?.error.message : undefined;
const clauses = query?.query?.ast?.clauses ?? [];
const filteredTransforms =
clauses.length > 0 ? filterTransforms(transforms, clauses) : transforms;
const filteredTransforms = useMemo(() => {
const query = searchQueryText !== '' ? EuiSearchBar.Query.parse(searchQueryText) : undefined;
const clauses = query?.ast?.clauses ?? [];
return clauses.length > 0 ? filterTransforms(transforms, clauses) : transforms;
}, [searchQueryText, transforms]);

if (transforms.length === 0) {
return (
Expand Down Expand Up @@ -317,14 +333,25 @@ export const TransformList: FC<TransformListProps> = ({
</EuiFlexGroup>
);

const handleSearchOnChange: EuiSearchBarProps['onChange'] = (search) => {
if (search.error !== null) {
setSearchError(search.error.message);
return;
}

setSearchError(undefined);
setSearchQueryText(search.queryText);
};

const search = {
toolsLeft: transformSelection.length > 0 ? renderToolsLeft() : undefined,
toolsRight,
onChange: setQuery,
onChange: handleSearchOnChange,
box: {
incremental: true,
},
filters: transformFilters,
query: searchQueryText,
};

const selection = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { TransformHealthColoredDot } from './transform_health_colored_dot';
export const transformFilters: SearchFilterConfig[] = [
{
type: 'field_value_selection',
field: 'state.state',
field: 'stats.state',
name: i18n.translate('xpack.transform.statusFilter', { defaultMessage: 'Status' }),
multiSelect: 'or',
options: Object.values(TRANSFORM_STATE).map((val) => ({
Expand All @@ -50,7 +50,7 @@ export const transformFilters: SearchFilterConfig[] = [
},
{
type: 'field_value_selection',
field: 'health',
field: 'stats.health.status',
name: i18n.translate('xpack.transform.healthFilter', { defaultMessage: 'Health' }),
multiSelect: false,
options: Object.values(TRANSFORM_HEALTH_STATUS).map((val) => ({
Expand Down Expand Up @@ -113,7 +113,7 @@ export const filterTransforms = (transforms: TransformListRow[], clauses: Clause
} else {
ts = transforms.filter((transform) => {
if (!transform.stats) return false;
if (c.type === 'field' && c.field === 'health') {
if (c.type === 'field' && c.field === 'stats.health.status') {
return transform.stats.health?.status === c.value;
}
if (c.type === 'field' && c.field === 'mode') {
Expand Down
Loading

0 comments on commit 8be5ad8

Please sign in to comment.