Skip to content

Commit

Permalink
Refactor Asset Inventory page (#212436)
Browse files Browse the repository at this point in the history
## Summary

Refactors code in Asset Inventory page for simplicity and consistency.

### Changes

- Centralized constants for consistency
- Simplified `<AllAssets>` page, removed unused props, renamed
variables, etc...
- Encapsulated technical preview stuff into `<TechnicalPreviewBadge>`
- Removed deprecations in EUI components and styling

Also, this PR **deletes the mocked data** that was used before
integrating the UI with the backend.

### Questions

- Do we see value in centralizing all strings in a new file such as
`localized_strings.ts`?

### Out of scope

Hooks in `hooks/use_asset_inventory_data_table` and field selector
components were all duplicated from the CSP plugin. I haven't put effort
in refactoring those since we'll need to remove the duplication and make
them reusable

### Checklist

- [x] 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/src/platform/packages/shared/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
- [ ] 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)
- [x] This was checked for breaking HTTP API changes, and any breaking
changes have been approved by the breaking-change committee. The
`release_note:breaking` label should be applied in these situations.
- [ ] [Flaky Test
Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was
used on any tests changed
- [x] The PR description includes the appropriate Release Notes section,
and the correct `release_note:*` label is applied per the
[guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)

### Risks

No risk since code is still hidden behind the *Enable Asset Inventory*
advanced setting and the beta *Cloud Asset* integration must be
installed.
  • Loading branch information
albertoblaz authored Mar 4, 2025
1 parent e24c1c3 commit 2473d59
Show file tree
Hide file tree
Showing 16 changed files with 182 additions and 311 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,9 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { css } from '@emotion/react';
import illustration from '../../common/images/illustration_product_no_results_magnifying_glass.svg';
import { DOCS_URL, TEST_SUBJ_EMPTY_STATE } from '../constants';

const ASSET_INVENTORY_DOCS_URL = 'https://ela.st/asset-inventory';
const EMPTY_STATE_TEST_SUBJ = 'assetInventory:empty-state';

export const EmptyState = ({
onResetFilters,
docsUrl = ASSET_INVENTORY_DOCS_URL,
}: {
onResetFilters: () => void;
docsUrl?: string;
}) => {
export const EmptyState = ({ onResetFilters }: { onResetFilters: () => void }) => {
const { euiTheme } = useEuiTheme();

return (
Expand All @@ -35,7 +27,7 @@ export const EmptyState = ({
margin-top: ${euiTheme.size.xxxl}};
}
`}
data-test-subj={EMPTY_STATE_TEST_SUBJ}
data-test-subj={TEST_SUBJ_EMPTY_STATE}
icon={
<EuiImage
url={illustration}
Expand Down Expand Up @@ -74,7 +66,7 @@ export const EmptyState = ({
defaultMessage="Reset filters"
/>
</EuiButton>,
<EuiLink href={docsUrl} target="_blank">
<EuiLink href={DOCS_URL} target="_blank">
<FormattedMessage
id="xpack.securitySolution.assetInventory.emptyState.readDocsLink"
defaultMessage="Read the docs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import type { FilterControlConfig } from '@kbn/alerts-ui-shared';
import type { Filter } from '@kbn/es-query';
import { Storage } from '@kbn/kibana-utils-plugin/public';
import { ControlGroupRenderer } from '@kbn/controls-plugin/public';
import { useDataViewContext } from '../../hooks/data_view_context';
import { useSpaceId } from '../../../common/hooks/use_space_id';
import { useDataViewContext } from '../../hooks/data_view_context';
import type { AssetsURLQuery } from '../../hooks/use_asset_inventory_data_table';
import { ASSET_INVENTORY_INDEX_PATTERN } from '../../constants';
import { FilterGroupLoading } from './filters_loading';
import { ASSET_INVENTORY_RULE_TYPE_IDS } from './rule_type_ids';
Expand Down Expand Up @@ -46,10 +47,10 @@ const DEFAULT_ASSET_INVENTORY_FILTERS: FilterControlConfig[] = [
];

export interface FiltersProps {
onFiltersChange: (newFilters: Filter[]) => void;
setQuery: (v: Partial<AssetsURLQuery>) => void;
}

export const Filters = ({ onFiltersChange }: FiltersProps) => {
export const Filters = ({ setQuery }: FiltersProps) => {
const { dataView, dataViewIsLoading } = useDataViewContext();
const spaceId = useSpaceId();

Expand Down Expand Up @@ -85,7 +86,9 @@ export const Filters = ({ onFiltersChange }: FiltersProps) => {
<EuiSpacer size="l" />
<FilterGroup
dataViewId={dataViewSpec?.id || null}
onFiltersChange={onFiltersChange}
onFiltersChange={(filters: Filter[]) => {
setQuery({ filters });
}}
ruleTypeIds={ASSET_INVENTORY_RULE_TYPE_IDS}
Storage={Storage}
defaultControls={DEFAULT_ASSET_INVENTORY_FILTERS}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@ import type { Filter } from '@kbn/es-query';
import { useKibana } from '../../common/lib/kibana';
import { FiltersGlobal } from '../../common/components/filters_global/filters_global';
import { useDataViewContext } from '../hooks/data_view_context';
import type { AssetsBaseURLQuery } from '../hooks/use_asset_inventory_data_table';

type SearchBarQueryProps = Pick<AssetsBaseURLQuery, 'query' | 'filters'>;
import type { AssetsURLQuery } from '../hooks/use_asset_inventory_data_table';

interface AssetInventorySearchBarProps {
setQuery(v: Partial<SearchBarQueryProps>): void;
setQuery(v: Partial<AssetsURLQuery>): void;
loading: boolean;
placeholder?: string;
query: SearchBarQueryProps;
query: AssetsURLQuery;
}

export const AssetInventorySearchBar = ({
Expand Down Expand Up @@ -54,7 +52,7 @@ export const AssetInventorySearchBar = ({
isLoading={loading}
indexPatterns={[dataView]}
onQuerySubmit={setQuery}
onFiltersUpdated={(value: Filter[]) => setQuery({ filters: value })}
onFiltersUpdated={(filters: Filter[]) => setQuery({ filters })}
placeholder={placeholder}
query={{
query: query?.query?.query || '',
Expand All @@ -69,6 +67,6 @@ export const AssetInventorySearchBar = ({

const getContainerStyle = (theme: EuiThemeComputed) => css`
border-bottom: ${theme.border.thin};
background-color: ${theme.colors.body};
background-color: ${theme.colors.backgroundBaseSubdued};
padding: ${theme.size.base};
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 React from 'react';
import { css } from '@emotion/react';
import { EuiBetaBadge, useEuiTheme } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

export const TechnicalPreviewBadge = () => {
const { euiTheme } = useEuiTheme();

return (
<EuiBetaBadge
css={css`
margin-left: ${euiTheme.size.s};
`}
label={i18n.translate('xpack.securitySolution.assetInventory.technicalPreviewLabel', {
defaultMessage: 'Technical Preview',
})}
size="s"
color="subdued"
tooltipContent={i18n.translate(
'xpack.securitySolution.assetInventory.technicalPreviewTooltip',
{
defaultMessage:
'This functionality is experimental and not supported. It may change or be removed at any time.',
}
)}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@
* 2.0.
*/

export const MAX_ASSETS_TO_LOAD = 500; // equivalent to MAX_FINDINGS_TO_LOAD in @kbn/cloud-security-posture-common
export const MAX_ASSETS_TO_LOAD = 500;
export const DEFAULT_VISIBLE_ROWS_PER_PAGE = 25;
export const ASSET_INVENTORY_INDEX_PATTERN = 'logs-cloud_asset_inventory.asset_inventory-*';

export const QUERY_KEY_GRID_DATA = 'asset_inventory_grid_data';
export const QUERY_KEY_CHART_DATA = 'asset_inventory_chart_data';

export const ASSET_INVENTORY_TABLE_ID = 'asset-inventory-table';

const LOCAL_STORAGE_PREFIX = 'assetInventory';
export const LOCAL_STORAGE_COLUMNS_KEY = `${LOCAL_STORAGE_PREFIX}:columns`;
export const LOCAL_STORAGE_COLUMNS_SETTINGS_KEY = `${LOCAL_STORAGE_COLUMNS_KEY}:settings`;
export const LOCAL_STORAGE_DATA_TABLE_PAGE_SIZE_KEY = `${LOCAL_STORAGE_PREFIX}:dataTable:pageSize`;
export const LOCAL_STORAGE_DATA_TABLE_COLUMNS_KEY = `${LOCAL_STORAGE_PREFIX}:dataTable:columns`;

export const TEST_SUBJ_DATA_GRID = 'asset-inventory-test-subj-grid-wrapper';
export const TEST_SUBJ_PAGE_TITLE = 'asset-inventory-test-subj-page-title';
export const TEST_SUBJ_EMPTY_STATE = 'asset-inventory-empty-state';

export const DOCS_URL = 'https://ela.st/asset-inventory';
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,23 @@ import { type Dispatch, type SetStateAction, useCallback } from 'react';
import type { BoolQuery, Filter, Query } from '@kbn/es-query';
import type { CriteriaWithPagination } from '@elastic/eui';
import type { DataTableRecord } from '@kbn/discover-utils/types';
import { LOCAL_STORAGE_DATA_TABLE_COLUMNS_KEY } from '../../constants';
import { useUrlQuery } from './use_url_query';
import { usePageSize } from './use_page_size';
import { getDefaultQuery } from './utils';
import { useBaseEsQuery } from './use_base_es_query';
import { usePersistedQuery } from './use_persisted_query';

const LOCAL_STORAGE_DATA_TABLE_COLUMNS_KEY = 'assetInventory:dataTable:columns';

export interface AssetsBaseURLQuery {
query: Query;
filters: Filter[];
/**
* Filters that are part of the query but not persisted in the URL or in the Filter Manager
*/
nonPersistedFilters?: Filter[];
/**
* Grouping component selection
*/
groupBy?: string[];
}

export type AssetsURLQuery = Pick<AssetsBaseURLQuery, 'query' | 'filters'>;

export type URLQuery = AssetsBaseURLQuery & Record<string, unknown>;

type SortOrder = [string, string];
Expand All @@ -53,19 +49,24 @@ export interface AssetInventoryDataTableResult {
getRowsFromPages: (data: Array<{ page: DataTableRecord[] }> | undefined) => DataTableRecord[];
}

const getDefaultQuery = ({ query, filters }: AssetsBaseURLQuery) => ({
query,
filters,
sort: { field: '@timestamp', direction: 'desc' },
pageIndex: 0,
});

/*
Hook for managing common table state and methods for the Asset Inventory DataTable
*/
export const useAssetInventoryDataTable = ({
defaultQuery = getDefaultQuery,
paginationLocalStorageKey,
columnsLocalStorageKey,
nonPersistedFilters,
}: {
defaultQuery?: (params: AssetsBaseURLQuery) => URLQuery;
paginationLocalStorageKey: string;
columnsLocalStorageKey?: string;
nonPersistedFilters?: Filter[];
}): AssetInventoryDataTableResult => {
const getPersistedDefaultQuery = usePersistedQuery<URLQuery>(defaultQuery);
const { urlQuery, setUrlQuery } = useUrlQuery<URLQuery>(getPersistedDefaultQuery);
Expand Down Expand Up @@ -128,7 +129,6 @@ export const useAssetInventoryDataTable = ({
const baseEsQuery = useBaseEsQuery({
filters: urlQuery.filters,
query: urlQuery.query,
...(nonPersistedFilters ? { nonPersistedFilters } : {}),
});

const handleUpdateQuery = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import type { DataView } from '@kbn/data-views-plugin/common';
import { buildEsQuery, type EsQueryConfig } from '@kbn/es-query';
import { i18n } from '@kbn/i18n';
import { useEffect, useMemo } from 'react';
import { useDataViewContext } from '../data_view_context';
import { useKibana } from '../../../common/lib/kibana';
import { useDataViewContext } from '../data_view_context';
import type { AssetsBaseURLQuery } from './use_asset_inventory_data_table';

interface AssetsBaseESQueryConfig {
Expand Down Expand Up @@ -38,11 +38,7 @@ const getBaseQuery = ({
}
};

export const useBaseEsQuery = ({
filters = [],
query,
nonPersistedFilters,
}: AssetsBaseURLQuery) => {
export const useBaseEsQuery = ({ filters = [], query }: AssetsBaseURLQuery) => {
const {
notifications: { toasts },
data: {
Expand All @@ -57,11 +53,11 @@ export const useBaseEsQuery = ({
() =>
getBaseQuery({
dataView,
filters: filters.concat(nonPersistedFilters ?? []).flat(),
filters,
query,
config,
}),
[dataView, filters, nonPersistedFilters, query, config]
[dataView, filters, query, config]
);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
* 2.0.
*/
import useLocalStorage from 'react-use/lib/useLocalStorage';

const DEFAULT_VISIBLE_ROWS_PER_PAGE = 10; // generic default # of table rows to show (currently we only have a list of policies)
import { DEFAULT_VISIBLE_ROWS_PER_PAGE } from '../../constants';

/**
* @description handles persisting the users table row size selection
Expand All @@ -17,11 +16,8 @@ export const usePageSize = (localStorageKey: string) => {
DEFAULT_VISIBLE_ROWS_PER_PAGE
);

let pageSize = DEFAULT_VISIBLE_ROWS_PER_PAGE;

if (persistedPageSize) {
pageSize = persistedPageSize;
}

return { pageSize, setPageSize: setPersistedPageSize };
return {
pageSize: persistedPageSize || DEFAULT_VISIBLE_ROWS_PER_PAGE,
setPageSize: setPersistedPageSize,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import { useCallback } from 'react';
import type { Query } from '@kbn/es-query';
import type { AssetsBaseURLQuery } from './use_asset_inventory_data_table';
import { useKibana } from '../../../common/lib/kibana';
import type { AssetsBaseURLQuery } from './use_asset_inventory_data_table';

export const usePersistedQuery = <T>(getter: ({ filters, query }: AssetsBaseURLQuery) => T) => {
const {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import { renderHook } from '@testing-library/react';
import { useAssetInventoryRoutes } from './use_asset_inventory_routes';
import { useKibana } from '../../common/lib/kibana';
import { API_VERSIONS } from '../../../common/constants';
import {
ASSET_INVENTORY_ENABLE_API_PATH,
ASSET_INVENTORY_STATUS_API_PATH,
} from '../../../common/api/asset_inventory/constants';

jest.mock('../../common/lib/kibana');

Expand All @@ -31,7 +35,7 @@ describe('useAssetInventoryRoutes', () => {
await result.current.postEnableAssetInventory();

expect(mockFetch).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledWith('/api/asset_inventory/enable', {
expect(mockFetch).toHaveBeenCalledWith(ASSET_INVENTORY_ENABLE_API_PATH, {
method: 'POST',
version: API_VERSIONS.public.v1,
body: JSON.stringify({}),
Expand All @@ -45,7 +49,7 @@ describe('useAssetInventoryRoutes', () => {
const response = await result.current.getAssetInventoryStatus();

expect(mockFetch).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledWith('/api/asset_inventory/status', {
expect(mockFetch).toHaveBeenCalledWith(ASSET_INVENTORY_STATUS_API_PATH, {
method: 'GET',
version: API_VERSIONS.public.v1,
query: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { showErrorToast } from '@kbn/cloud-security-posture';
import type { IKibanaSearchResponse, IKibanaSearchRequest } from '@kbn/search-types';
import type { BaseEsQuery } from '@kbn/cloud-security-posture';
import { useKibana } from '../../common/lib/kibana';
import { ASSET_INVENTORY_INDEX_PATTERN } from '../constants';
import { ASSET_INVENTORY_INDEX_PATTERN, QUERY_KEY_CHART_DATA } from '../constants';
import { getMultiFieldsSort } from './fetch_utils';

interface UseTopAssetsOptions extends BaseEsQuery {
Expand Down Expand Up @@ -159,7 +159,7 @@ export function useFetchChartData(options: UseTopAssetsOptions) {
notifications: { toasts },
} = useKibana().services;
return useQuery(
['asset_inventory_top_assets_chart', { params: options }],
[QUERY_KEY_CHART_DATA, { params: options }],
async () => {
const {
rawResponse: { aggregations },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import { showErrorToast } from '@kbn/cloud-security-posture';
import type { IKibanaSearchResponse, IKibanaSearchRequest } from '@kbn/search-types';
import type { BaseEsQuery } from '@kbn/cloud-security-posture';
import { useKibana } from '../../common/lib/kibana';
import { MAX_ASSETS_TO_LOAD, ASSET_INVENTORY_INDEX_PATTERN } from '../constants';
import {
MAX_ASSETS_TO_LOAD,
ASSET_INVENTORY_INDEX_PATTERN,
QUERY_KEY_GRID_DATA,
} from '../constants';
import { getRuntimeMappingsFromSort, getMultiFieldsSort } from './fetch_utils';

interface UseAssetsOptions extends BaseEsQuery {
Expand Down Expand Up @@ -59,13 +63,13 @@ interface Asset {
type LatestAssetsRequest = IKibanaSearchRequest<estypes.SearchRequest>;
type LatestAssetsResponse = IKibanaSearchResponse<estypes.SearchResponse<Asset, never>>;

export function useFetchData(options: UseAssetsOptions) {
export function useFetchGridData(options: UseAssetsOptions) {
const {
data,
notifications: { toasts },
} = useKibana().services;
return useInfiniteQuery(
['asset_inventory', { params: options }],
[QUERY_KEY_GRID_DATA, { params: options }],
async ({ pageParam }) => {
const {
rawResponse: { hits },
Expand Down
Loading

0 comments on commit 2473d59

Please sign in to comment.