From 8be5ad8fc9498d381e5b79ec66a4a5bcae31c276 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 13 Aug 2024 08:55:55 -0600 Subject: [PATCH] [ML][Fleet] Link to ML assets from Integration > Assets tab (#189767) ## Summary Related issue: https://github.com/elastic/kibana/issues/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. image - 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 image - Select the installed package you want to view and then go to the `Assets` tab image - 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 --- x-pack/packages/ml/url_state/index.ts | 2 + .../packages/ml/url_state/src/url_state.tsx | 12 +++- .../types/index_data_visualizer_state.ts | 12 +--- .../services/epm/packages/get_bulk_assets.ts | 6 +- x-pack/plugins/ml/common/types/common.ts | 9 --- x-pack/plugins/ml/common/types/locator.ts | 2 +- .../analytics_list/analytics_list.tsx | 2 +- .../analytics_list/use_table_settings.ts | 2 +- .../pages/analytics_management/page.tsx | 3 +- .../application/jobs/jobs_list/jobs.tsx | 2 +- .../nodes_overview/nodes_list.tsx | 2 +- .../model_management/models_list.tsx | 2 +- .../components/notifications_list.tsx | 2 +- .../locator/formatters/anomaly_detection.ts | 3 +- .../formatters/data_frame_analytics.ts | 3 +- .../locator/formatters/trained_models.ts | 3 +- .../transform_list/transform_list.test.tsx | 2 + .../transform_list/transform_list.tsx | 43 ++++++++++--- .../transform_search_bar_filters.tsx | 6 +- .../transform_list/use_table_settings.ts | 62 +++++++------------ .../transform_management_section.tsx | 25 +++++++- .../epm/__snapshots__/bulk_get_assets.snap | 2 +- 22 files changed, 121 insertions(+), 86 deletions(-) diff --git a/x-pack/packages/ml/url_state/index.ts b/x-pack/packages/ml/url_state/index.ts index c2b3f80c62a15..13443af51bc4d 100644 --- a/x-pack/packages/ml/url_state/index.ts +++ b/x-pack/packages/ml/url_state/index.ts @@ -15,5 +15,7 @@ export { UrlStateProvider, type Accessor, type Dictionary, + type ListingPageUrlState, + type PageUrlState, type SetUrlState, } from './src/url_state'; diff --git a/x-pack/packages/ml/url_state/src/url_state.tsx b/x-pack/packages/ml/url_state/src/url_state.tsx index 1aa796187f9d4..585761b93cce3 100644 --- a/x-pack/packages/ml/url_state/src/url_state.tsx +++ b/x-pack/packages/ml/url_state/src/url_state.tsx @@ -31,6 +31,16 @@ export interface Dictionary { [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, @@ -241,7 +251,7 @@ export class PageUrlStateService { } } -interface PageUrlState { +export interface PageUrlState { pageKey: string; pageUrlState: object; } diff --git a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/types/index_data_visualizer_state.ts b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/types/index_data_visualizer_state.ts index 767179c3c7c37..60ddfed8b8812 100644 --- a/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/types/index_data_visualizer_state.ts +++ b/x-pack/plugins/data_visualizer/public/application/index_data_visualizer/types/index_data_visualizer_state.ts @@ -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'; @@ -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 { +export interface DataVisualizerIndexBasedAppState + extends Omit { searchString?: Query['query']; searchQuery?: Query['query']; searchQueryLanguage?: SearchQueryLanguage; diff --git a/x-pack/plugins/fleet/server/services/epm/packages/get_bulk_assets.ts b/x-pack/plugins/fleet/server/services/epm/packages/get_bulk_assets.ts index 8171ee33f22f7..082691fb0812b 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/get_bulk_assets.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/get_bulk_assets.ts @@ -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 ''; } diff --git a/x-pack/plugins/ml/common/types/common.ts b/x-pack/plugins/ml/common/types/common.ts index 3e0213b8ab959..ec75a2b8d525f 100644 --- a/x-pack/plugins/ml/common/types/common.ts +++ b/x-pack/plugins/ml/common/types/common.ts @@ -35,15 +35,6 @@ type DeepReadonlyObject = { readonly [P in keyof T]: DeepReadonly; }; -export interface ListingPageUrlState { - pageSize: number; - pageIndex: number; - sortField: string; - sortDirection: string; - queryText?: string; - showAll?: boolean; -} - export type AppPageState = { [key in MlPages]?: Partial; }; diff --git a/x-pack/plugins/ml/common/types/locator.ts b/x-pack/plugins/ml/common/types/locator.ts index bad72454356d4..dd9997f7b59f2 100644 --- a/x-pack/plugins/ml/common/types/locator.ts +++ b/x-pack/plugins/ml/common/types/locator.ts @@ -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; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx index 73f50f51befc2..e0a12f4187ab3 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/analytics_list.tsx @@ -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'; @@ -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'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts index 027175881dcbe..670efb1627ef7 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings.ts @@ -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]; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/page.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/page.tsx index d28312b4a9098..583bf7449e463 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/page.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/page.tsx @@ -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'; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/jobs.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/jobs.tsx index 340a75c5b6a29..0516fa1b7986d 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/jobs.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/jobs.tsx @@ -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'; diff --git a/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/nodes_list.tsx b/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/nodes_list.tsx index c6632adcefccf..94a17f85aad2c 100644 --- a/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/nodes_list.tsx +++ b/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/nodes_list.tsx @@ -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'; @@ -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'; diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 1080488a78895..bc5c2450ebacf 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -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'; @@ -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'; diff --git a/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx b/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx index 0cfb482fda7f7..1ebba13d1f520 100644 --- a/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx +++ b/x-pack/plugins/ml/public/application/notifications/components/notifications_list.tsx @@ -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'; @@ -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, diff --git a/x-pack/plugins/ml/public/locator/formatters/anomaly_detection.ts b/x-pack/plugins/ml/public/locator/formatters/anomaly_detection.ts index 1f9501d07fd43..82a7ea26f6890 100644 --- a/x-pack/plugins/ml/public/locator/formatters/anomaly_detection.ts +++ b/x-pack/plugins/ml/public/locator/formatters/anomaly_detection.ts @@ -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, @@ -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 diff --git a/x-pack/plugins/ml/public/locator/formatters/data_frame_analytics.ts b/x-pack/plugins/ml/public/locator/formatters/data_frame_analytics.ts index 56ce886536b87..e1d3317364edf 100644 --- a/x-pack/plugins/ml/public/locator/formatters/data_frame_analytics.ts +++ b/x-pack/plugins/ml/public/locator/formatters/data_frame_analytics.ts @@ -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, @@ -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, diff --git a/x-pack/plugins/ml/public/locator/formatters/trained_models.ts b/x-pack/plugins/ml/public/locator/formatters/trained_models.ts index ddf4782005b42..d3048405be91d 100644 --- a/x-pack/plugins/ml/public/locator/formatters/trained_models.ts +++ b/x-pack/plugins/ml/public/locator/formatters/trained_models.ts @@ -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, diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx index c47c0f0add99f..ede0470d917f2 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx @@ -40,6 +40,8 @@ describe('Transform: Transform List ', () => { transforms={[]} transformsLoading={false} transformsStatsLoading={false} + pageState={{ pageSize: 10, pageIndex: 0, sortField: 'id', sortDirection: 'asc' }} + updatePageState={jest.fn()} /> diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx index b70de170df611..b8aa9a749c765 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx @@ -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'; @@ -18,8 +18,10 @@ import { EuiInMemoryTable, EuiPageTemplate, EuiPopover, + EuiSearchBar, EuiTitle, } from '@elastic/eui'; +import type { ListingPageUrlState } from '@kbn/ml-url-state'; import { isReauthorizeActionDisabled, ReauthorizeActionModal, @@ -85,25 +87,36 @@ function getItemIdToExpandedRowMap( interface TransformListProps { isLoading: boolean; onCreateTransform: MouseEventHandler; + pageState: ListingPageUrlState; transformNodes: number; transforms: TransformListRow[]; transformsLoading: boolean; transformsStatsLoading: boolean; + updatePageState: (update: Partial) => void; } export const TransformList: FC = ({ isLoading, onCreateTransform, + pageState, transformNodes, transforms, transformsLoading, transformsStatsLoading, + updatePageState, }) => { const refreshTransformList = useRefreshTransformList(); const { setEditAlertRule } = useAlertRuleFlyout(); - const [query, setQuery] = useState>[0]>(); + const searchQueryText = pageState.queryText ?? ''; + const setSearchQueryText = useCallback( + (value) => { + updatePageState({ queryText: value }); + }, + [updatePageState] + ); + const [searchError, setSearchError] = useState(); const [expandedRowItemIds, setExpandedRowItemIds] = useState([]); const [transformSelection, setTransformSelection] = useState([]); const [isActionsMenuOpen, setIsActionsMenuOpen] = useState(false); @@ -122,7 +135,9 @@ export const TransformList: FC = ({ const { sorting, pagination, onTableChange } = useTableSettings( TRANSFORM_LIST_COLUMN.ID, - transforms + transforms, + pageState, + updatePageState ); const { columns, modals: singleActionModals } = useColumns( @@ -133,10 +148,11 @@ export const TransformList: FC = ({ 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 ( @@ -317,14 +333,25 @@ export const TransformList: FC = ({ ); + 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 = { diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_search_bar_filters.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_search_bar_filters.tsx index c60c302d86e06..67ba6cffa997a 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_search_bar_filters.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_search_bar_filters.tsx @@ -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) => ({ @@ -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) => ({ @@ -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') { diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_table_settings.ts b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_table_settings.ts index 9a38986c4c85b..843737bd0a8dc 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_table_settings.ts +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_table_settings.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { useState } from 'react'; +import { useCallback } from 'react'; import type { Direction, EuiBasicTableProps, Pagination, PropertySort } from '@elastic/eui'; +import type { ListingPageUrlState } from '@kbn/ml-url-state'; -const PAGE_SIZE = 10; const PAGE_SIZE_OPTIONS = [10, 25, 50]; // Copying from EUI EuiBasicTable types as type is not correctly picked up for table's onChange @@ -30,15 +30,6 @@ export interface CriteriaWithPagination extends Criteria { }; } -interface AnalyticsBasicTableSettings { - pageIndex: number; - pageSize: number; - totalItemCount: number; - showPerPageOptions: boolean; - sortField: keyof T; - sortDirection: Direction; -} - interface UseTableSettingsReturnValue { onTableChange: EuiBasicTableProps['onChange']; pagination: Pagination; @@ -47,34 +38,29 @@ interface UseTableSettingsReturnValue { export function useTableSettings( sortByField: keyof TypeOfItem, - items: TypeOfItem[] + items: TypeOfItem[], + pageState: ListingPageUrlState, + updatePageState: (update: Partial) => void ): UseTableSettingsReturnValue { - const [tableSettings, setTableSettings] = useState>({ - pageIndex: 0, - pageSize: PAGE_SIZE, - totalItemCount: 0, - showPerPageOptions: true, - sortField: sortByField, - sortDirection: 'asc', - }); + const { pageIndex, pageSize, sortField, sortDirection } = pageState; - const onTableChange: EuiBasicTableProps['onChange'] = ({ - page = { index: 0, size: PAGE_SIZE }, - sort = { field: sortByField, direction: 'asc' }, - }: CriteriaWithPagination) => { - const { index, size } = page; - const { field, direction } = sort; + const onTableChange: EuiBasicTableProps['onChange'] = useCallback( + ({ page, sort }: CriteriaWithPagination) => { + let resultSortField = sort?.field; + if (typeof resultSortField !== 'string') { + resultSortField = pageState.sortField as keyof TypeOfItem; + } - setTableSettings({ - ...tableSettings, - pageIndex: index, - pageSize: size, - sortField: field, - sortDirection: direction, - }); - }; - - const { pageIndex, pageSize, sortField, sortDirection } = tableSettings; + const result = { + pageIndex: page?.index ?? pageState.pageIndex, + pageSize: page?.size ?? pageState.pageSize, + sortField: resultSortField as string, + sortDirection: sort?.direction ?? pageState.sortDirection, + }; + updatePageState(result); + }, + [pageState, updatePageState] + ); const pagination = { pageIndex, @@ -85,8 +71,8 @@ export function useTableSettings( const sorting = { sort: { - field: sortField as string, - direction: sortDirection, + field: sortField, + direction: sortDirection as Direction, }, }; return { onTableChange, pagination, sorting }; diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx index 514ed89a9414e..5f703a57dff2c 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx @@ -20,6 +20,12 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import type { IHttpFetchError } from '@kbn/core-http-browser'; +import { + usePageUrlState, + UrlStateProvider, + type ListingPageUrlState, + type PageUrlState, +} from '@kbn/ml-url-state'; import { useAppDependencies } from '../../app_dependencies'; import type { TransformListRow } from '../../common'; @@ -28,6 +34,7 @@ import { useGetTransformsStats } from '../../hooks/use_get_transform_stats'; import { useEnabledFeatures } from '../../serverless_context'; import { needsReauthorization } from '../../common/reauthorization_utils'; import { TRANSFORM_STATE } from '../../../../common/constants'; +import { TRANSFORM_LIST_COLUMN } from '../../common'; import { useDocumentationLinks, @@ -50,6 +57,14 @@ import { TransformAlertFlyoutWrapper, } from '../../../alerting/transform_alerting_flyout'; +const getDefaultTransformListState = (): ListingPageUrlState => ({ + pageIndex: 0, + pageSize: 10, + sortField: TRANSFORM_LIST_COLUMN.ID, + sortDirection: 'asc', + showPerPageOptions: true, +}); + const ErrorMessageCallout: FC<{ text: JSX.Element; errorMessage: IHttpFetchError | null; @@ -78,6 +93,10 @@ export const TransformManagement: FC = () => { const { esTransform } = useDocumentationLinks(); const { showNodeInfo } = useEnabledFeatures(); const { dataViewEditor } = useAppDependencies(); + const [transformPageState, setTransformPageState] = usePageUrlState( + 'transform', + getDefaultTransformListState() + ); const deleteTransforms = useDeleteTransforms(); @@ -346,6 +365,8 @@ export const TransformManagement: FC = () => { transforms={transforms} transformsLoading={transformsWithoutStatsLoading} transformsStatsLoading={transformsStatsLoading} + pageState={transformPageState as ListingPageUrlState} + updatePageState={setTransformPageState} /> )} @@ -380,7 +401,9 @@ export const TransformManagementSection: FC = () => { return ( - + + + ); }; diff --git a/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/bulk_get_assets.snap b/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/bulk_get_assets.snap index de3f68ba26115..761ecfa66af7b 100644 --- a/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/bulk_get_assets.snap +++ b/x-pack/test/fleet_api_integration/apis/epm/__snapshots__/bulk_get_assets.snap @@ -21,7 +21,7 @@ Array [ "type": "data_stream_ilm_policy", }, Object { - "appLink": "", + "appLink": "/app/ml/trained_models?_a=(trained_models:(queryText:'model_id:(default)'))", "attributes": Object {}, "id": "default", "type": "ml_model",