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][Fleet] Link to ML assets from Integration > Assets tab #189767

Merged
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}))`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Might not be possible, but adding the description of the transform, if set, would be nice here, as happens for dashboard assets:

Screenshot 2024-08-02 at 16 15 59

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently for ES assets that isn't possible but I can create a follow up issue to allow it for ES assets.

case 'ml_model':
// TODO: Confirm link for ml models
return '';
return `/app/ml/trained_models?_a=(trained_models:(queryText:'model_id:(${id})'))`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same for models - would be nice to add the description, if it exists.

Copy link
Contributor

Choose a reason for hiding this comment

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

❓ is it possible to utilize our ML locator to retrieve these URLs? It'd make it easier to maintain

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was directed to keep it consistent in that file, for now. If maintainability becomes an issue then we can bring it up.

default:
return '';
}
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,6 +18,7 @@ import {
EuiInMemoryTable,
EuiPageTemplate,
EuiPopover,
EuiSearchBar,
EuiTitle,
} from '@elastic/eui';
import {
Expand Down Expand Up @@ -64,6 +65,7 @@ import { useTableSettings } from './use_table_settings';
import { useAlertRuleFlyout } from '../../../../../alerting/transform_alerting_flyout';
import type { TransformHealthAlertRule } from '../../../../../../common/types/alerting';
import { StopActionModal } from '../action_stop/stop_action_modal';
import type { ListingPageUrlState } from '../../transform_management_section';

type ItemIdToExpandedRowMap = Record<string, JSX.Element>;

Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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 '../../transform_management_section';

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
Expand All @@ -30,15 +30,6 @@ export interface CriteriaWithPagination<T extends object> extends Criteria<T> {
};
}

interface AnalyticsBasicTableSettings<T extends object> {
pageIndex: number;
pageSize: number;
totalItemCount: number;
showPerPageOptions: boolean;
sortField: keyof T;
sortDirection: Direction;
}

interface UseTableSettingsReturnValue<T extends object> {
onTableChange: EuiBasicTableProps<T>['onChange'];
pagination: Pagination;
Expand All @@ -47,34 +38,29 @@ interface UseTableSettingsReturnValue<T extends object> {

export function useTableSettings<TypeOfItem extends object>(
sortByField: keyof TypeOfItem,
items: TypeOfItem[]
items: TypeOfItem[],
pageState: ListingPageUrlState,
updatePageState: (update: Partial<ListingPageUrlState>) => void
): UseTableSettingsReturnValue<TypeOfItem> {
const [tableSettings, setTableSettings] = useState<AnalyticsBasicTableSettings<TypeOfItem>>({
pageIndex: 0,
pageSize: PAGE_SIZE,
totalItemCount: 0,
showPerPageOptions: true,
sortField: sortByField,
sortDirection: 'asc',
});
const { pageIndex, pageSize, sortField, sortDirection } = pageState;

const onTableChange: EuiBasicTableProps<TypeOfItem>['onChange'] = ({
page = { index: 0, size: PAGE_SIZE },
sort = { field: sortByField, direction: 'asc' },
}: CriteriaWithPagination<TypeOfItem>) => {
const { index, size } = page;
const { field, direction } = sort;
const onTableChange: EuiBasicTableProps<TypeOfItem>['onChange'] = useCallback(
({ page, sort }: CriteriaWithPagination<TypeOfItem>) => {
let resultSortField = sort?.field;
if (typeof resultSortField !== 'string') {
Copy link
Contributor

Choose a reason for hiding this comment

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

what other types can it be?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

🤔 I think this is holdover defensive code from the example code I was using from https://github.com/elastic/kibana/pull/140613/files in use_table_settings.ts
I think it depends on the dataset - it's possible it could not be a string type always.

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,
Expand All @@ -85,8 +71,8 @@ export function useTableSettings<TypeOfItem extends object>(

const sorting = {
sort: {
field: sortField as string,
direction: sortDirection,
field: sortField,
direction: sortDirection as Direction,
},
};
return { onTableChange, pagination, sorting };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import type { IHttpFetchError } from '@kbn/core-http-browser';
import { usePageUrlState } from '@kbn/ml-url-state';
import { UrlStateProvider } from '@kbn/ml-url-state';
darnautov marked this conversation as resolved.
Show resolved Hide resolved

import { useAppDependencies } from '../../app_dependencies';
import type { TransformListRow } from '../../common';
Expand All @@ -28,6 +30,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,
Expand All @@ -50,6 +53,28 @@ import {
TransformAlertFlyoutWrapper,
} from '../../../alerting/transform_alerting_flyout';

export interface ListingPageUrlState {
pageSize: number;
pageIndex: number;
sortField: string;
sortDirection: string;
queryText?: string;
showPerPageOptions?: boolean;
}
darnautov marked this conversation as resolved.
Show resolved Hide resolved

interface PageUrlState {
darnautov marked this conversation as resolved.
Show resolved Hide resolved
pageKey: string;
pageUrlState: ListingPageUrlState;
}

const getDefaultTransformListState = (): ListingPageUrlState => ({
pageIndex: 0,
pageSize: 10,
sortField: TRANSFORM_LIST_COLUMN.ID,
sortDirection: 'asc',
showPerPageOptions: true,
});

const ErrorMessageCallout: FC<{
text: JSX.Element;
errorMessage: IHttpFetchError<unknown> | null;
Expand Down Expand Up @@ -78,6 +103,10 @@ export const TransformManagement: FC = () => {
const { esTransform } = useDocumentationLinks();
const { showNodeInfo } = useEnabledFeatures();
const { dataViewEditor } = useAppDependencies();
const [transformPageState, setTransformPageState] = usePageUrlState<PageUrlState>(
'transform',
getDefaultTransformListState()
);

const deleteTransforms = useDeleteTransforms();

Expand Down Expand Up @@ -346,6 +375,8 @@ export const TransformManagement: FC = () => {
transforms={transforms}
transformsLoading={transformsWithoutStatsLoading}
transformsStatsLoading={transformsStatsLoading}
pageState={transformPageState}
updatePageState={setTransformPageState}
/>
)}
<TransformAlertFlyoutWrapper />
Expand Down Expand Up @@ -380,7 +411,9 @@ export const TransformManagementSection: FC = () => {

return (
<CapabilitiesWrapper requiredCapabilities={'canGetTransform'}>
<TransformManagement />
<UrlStateProvider>
<TransformManagement />
</UrlStateProvider>
</CapabilitiesWrapper>
);
};

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.