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

Refactor Asset Inventory page #212436

Merged
merged 2 commits into from
Mar 4, 2025
Merged

Conversation

albertoblaz
Copy link
Contributor

@albertoblaz albertoblaz commented Feb 25, 2025

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

  • Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support
  • Documentation was added for features that require explanation or tutorials
  • Unit or functional tests 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
  • 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 was used on any tests changed
  • The PR description includes the appropriate Release Notes section, and the correct release_note:* label is applied per the guidelines

Risks

No risk since code is still hidden behind the Enable Asset Inventory advanced setting and the beta Cloud Asset integration must be installed.

@albertoblaz albertoblaz added release_note:skip Skip the PR/issue when compiling release notes backport:skip This commit does not require backporting Team:Cloud Security Cloud Security team related labels Feb 25, 2025
@albertoblaz albertoblaz self-assigned this Feb 25, 2025
@albertoblaz albertoblaz force-pushed the asset-inv-refactor branch 3 times, most recently from eeea887 to c50144e Compare March 3, 2025 15:37
Copy link
Contributor Author

@albertoblaz albertoblaz left a comment

Choose a reason for hiding this comment

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

Notes for reviewers down below

@@ -498,14 +442,12 @@ export const AllAssets = ({
showTimeCol={false}
settings={settings}
onFetchMoreRecords={loadMore}
externalControlColumns={externalControlColumns}
rowAdditionalLeadingControls={externalControlColumns}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

externalControlColumns was deprecated and rowAdditionalLeadingControls was encouraged instead

onResetColumns={onResetColumns}
/>
);

const externalControlColumns: EuiDataGridControlColumn[] = [
const externalControlColumns: RowControlColumn[] = [
Copy link
Contributor Author

Choose a reason for hiding this comment

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

rowAdditionalLeadingControls's expected interface is RowControlColumn instead

)}
/>
<FormattedMessage id={`${I18N_PREFIX}.allAssets.title`} defaultMessage="All Assets" />
<TechnicalPreviewBadge />
Copy link
Contributor Author

Choose a reason for hiding this comment

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

<EuiBetaBadge> stuff was encapsulated in <TechnicalPreviewBadge>

Comment on lines -507 to -508
showFullScreenButton
// showKeyboardShortcuts
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed unnecessary props that default to true

};

return {
wrapperHeight: getWrapperHeight(),
mode: isVirtualizationEnabled ? 'virtualized' : 'standard',
};
}, [pageSize, height, filters?.length, hasDistributionBar]);
}, [pageSize]);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

wrapperHeight function was simplified since <AllAssets> does not receive props that alter its height unlike in Findings

Comment on lines -157 to -163
export const AllAssets = ({
nonPersistedFilters,
height,
hasDistributionBar = true,
createFn,
...rest
}: AllAssetsProps) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

<AllAssets> does not need any prop so they're all gone now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This file was only imported by use_asset_inventory_data_table so getDefaultQuery now lives there

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We forgot to delete the mocked data after the backend integration work

@@ -69,6 +65,6 @@ export const AssetInventorySearchBar = ({

const getContainerStyle = (theme: EuiThemeComputed) => css`
border-bottom: ${theme.border.thin};
background-color: ${theme.colors.body};
background-color: ${theme.colors.backgroundBaseSubdued};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

theme.colors.body was deprecated

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Most constants are centralized here to have consistent values i.e.

  • All local storage keys starting with same prefix
  • All test subj with similar naming
  • All query keys living in one place to make sure their values are different
  • etc

@albertoblaz albertoblaz marked this pull request as ready for review March 3, 2025 16:02
@albertoblaz albertoblaz requested a review from a team as a code owner March 3, 2025 16:02
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-cloud-security-posture (Team:Cloud Security)

@albertoblaz albertoblaz marked this pull request as draft March 3, 2025 16:13
@albertoblaz albertoblaz added this to the 9.1 milestone Mar 3, 2025
@albertoblaz albertoblaz marked this pull request as ready for review March 3, 2025 17:56
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Metrics [docs]

Async chunks

Total size of all lazy-loaded chunks that will be downloaded as the user navigates the app

id before after diff
securitySolution 8.9MB 8.9MB -156.0B
Unknown metric groups

References to deprecated APIs

id before after diff
securitySolution 352 351 -1

History

cc @albertoblaz

@albertoblaz albertoblaz requested review from opauloh and JordanSh March 3, 2025 18:24
Copy link
Contributor

@opauloh opauloh left a comment

Choose a reason for hiding this comment

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

Thanks for the refactor 🚀

onResetFilters: () => void;
docsUrl?: string;
}) => {
export const EmptyState = ({ onResetFilters }: { onResetFilters: () => void }) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

EmptyState is generic and i suggest to save this type of naming to generic components. this one is a wrapper of asset inventory around eui empty state components. i suggest to name file and component as asset inventory empty state

Copy link
Contributor

@JordanSh JordanSh left a comment

Choose a reason for hiding this comment

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

Looks good! regarding our naming conventions and what I’m trying to do with the flyout:

Everything I place in the components folder is intended to be as generic as possible, like a package of reusable components that could be used anywhere in Kibana. Typically, these are enhancements or wrappers around EUI’s base components.

For domain-specific wrappers around generic or EUI components, I append the domain name to the component name. For example, I’d use names like AssetInventoryFilters or AssetInventorySearchBar in this case.

Of course, this is a subjective approach, so it's your call if you like or not. We can also take this offline as a group to decide if its something we want to try and stick to.

@albertoblaz
Copy link
Contributor Author

Everything I place in the components folder is intended to be as generic as possible

For domain-specific wrappers around generic or EUI components, I append the domain name to the component name

Interesting. This is something I wanted to bring up because I've seen inconsistencies in my code and in existing modules. I like a lot the idea of separating generic <-> domain-specific and prefixing the domain name. I just think components/ sounds like an umbrella term for both and maybe we could call that group shared/ or reusable/ or something similar.

Regarding hooks, I tried to separate them from components but maybe specific hooks should be co-located (next to the components that use them) while generic hooks or those that are used top-level might be placed at a top-level hooks/ folder. Not sure what you think @JordanSh.

@JordanSh
Copy link
Contributor

JordanSh commented Mar 4, 2025

I just think components/ sounds like an umbrella term for both and maybe we could call that group shared/ or reusable/ or something similar.

Regarding hooks, I tried to separate them from components but maybe specific hooks should be co-located (next to the components that use them) while generic hooks or those that are used top-level might be placed at a top-level hooks/ folder. Not sure what you think @JordanSh.

The hooks example you gave is exactly what i would suggest for the components as well. its much easier to do this when we have good folder structure for the UI itself.
For example in the asset inventory case:

asset_inventory
├── components
│   ├── filters_group.tsx 
│   ├── filters_tree_table.tsx  // Table with an attached filter tree (the one we discussed in the design meetings)
├── pages
│   ├── all_assets
│   │   ├── all_assets.tsx  // Index file for this folder, named same as the folder
│   │   ├── top_assets_graph
│   │   │   ├── top_assets_graph.tsx  // Index file for this folder
│   │   │   ├── assets_filters_group.tsx  // Uses generic filters_group in assets inventory context
│   │   ├── asset_inventory_table   
│   │   │   ├── asset_inventory_table.tsx  // Index file for this folder
│   │   │   ├── asset_inventory_filter_tree_table.tsx  // Uses filters_tree_table component

this is my approach, what makes the most sense to me at least.

Obviously not all components require a generic component in the component folder. the filters_group.tsx, i only added to make an example, but we can also not create a group_filters.tsx generic and just build this directly in assets_filters_group.tsx. it really depends on the implementor and the use case. both are fine. but if we do decide to have a specific separate component, my suggestion is as i explained above.

@albertoblaz
Copy link
Contributor Author

@JordanSh Sounds great to me 💯👍

I'll merge this PR since it's already approved by you two. @opauloh could you give us your input? If you also agree, I'll change the folder structure in a follow-up PR.

@albertoblaz albertoblaz merged commit 2473d59 into elastic:main Mar 4, 2025
17 checks passed
@albertoblaz albertoblaz deleted the asset-inv-refactor branch March 4, 2025 11:28
@opauloh
Copy link
Contributor

opauloh commented Mar 4, 2025

@JordanSh Sounds great to me 💯👍

I'll merge this PR since it's already approved by you two. @opauloh could you give us your input? If you also agree, I'll change the folder structure in a follow-up PR.

Thanks @albertoblaz, I like the proposal of differentiating domain-specific components, it's a part of scalign the project, we started with a simpler structure by grouping only by "File Types" and now we seek to improve the maintainability, I just think we should decide on a consistent and scalable file structure and naming conventions to better differentiate domain-specific components from reusable ones.

For example, we should be consistent about adding the asset_inventory_ prefix to all files related to the asset inventory domain. This will help clarify their context and maintain consistency across the codebase. Generic components (those who doesn't have a weight on asset inventory and can be used across Kibana) would remain separate and unprefixed.

So I guess the separation and naming should take into account if they are reusable outside the asset_inventory domain, I also think we should be consistent on how to include logic related to components, having a components/component/hooks can sound inconsistent if we already have a hook folder and it doesn't follow a pattern, but then there's a conflict of sticking to a domain versus collocation (as on the datatable hooks).

I wrote what could be a proposal for a follow-up PR focused on being consistent in structuring for whether the file is related to a domain or is generic, and I would like to hear more thoughts or suggestions on this approach. I also added some open questions while reviewing it to discuss:

asset_inventory/
├── README.md
├── components/
│   ├── asset_inventory/                  # Grouped asset inventory domain components
│   │   ├── asset_inventory_loading.tsx
│   │   ├── asset_inventory_empty_state.tsx
│   │   ├── asset_inventory_risk_badge.tsx
│   │   ├── asset_inventory_risk_badge.test.tsx
│   │   ├── asset_inventory_search_bar.tsx
│   │   ├── asset_inventory_inventory_title.tsx
│   │   └── asset_inventory_top_assets_bar_chart.tsx
│   │   ├── onboarding/                        # Grouped asset inventory onboarding-related components
│   │   │   ├── asset_inventory_onboarding.tsx
│   │   │   ├── asset_inventory_centered_wrapper.tsx
│   │   │   ├── asset_inventory_get_started.test.tsx
│   │   │   ├── asset_inventory_get_started.tsx
│   │   ├── filters/                           # Group asset inventory filter-related components
│   │   │   ├── asset_inventory_filters.tsx 
│   │   │   ├── asset_inventory_filters_loading.tsx
│   │   │   └── asset_inventory_rule_type_ids.ts
│   ├── fields_selector/                  # Generic field selector components
│   │   ├── fields_selector_modal.tsx
│   │   ├── fields_selector_table.tsx
│   ├── empty_state_illustration_container.tsx.  # Generic component
│   └── hover_for_explanation_tooltip.tsx.      # Generic as is not tied to onboarding
├── contexts/
│   ├── data_view_context.ts           # Generic hook
├── hooks/
│   ├── asset_inventory    # Grouped asset inventory data table hooks
│   │   ├── data_table
│   │   │   ├── use_asset_inventory_data_table.ts
│   │   │   ├── use_asset_inventory_fetch_data_grid.ts # should be renamed to data_table, or rename data_table to data_grid
│   │   │   └── utils.ts
│   │   ├── use_asset_inventory_routes.test.ts
│   │   ├── use_asset_inventory_routes.ts
│   │   ├── use_asset_inventory_status.ts
│   │   ├── use_asset_inventory_fetch_chart_data.ts
│   │   ├── use_asset_inventory_enable.test.ts    
│   │   └── use_asset_inventory_enable.ts       # Not generic, but not tied to onboarding either
│   ├── fields_selector
│   │   ├── use_fields_selector.ts  # rename to align with the component name
│   ├── use_dynamic_entity_flyout.test.ts
│   ├── use_dynamic_entity_flyout.ts     # Is this a generic hook?
│   ├── use_base_es_query.ts     # Generic hook - even though they are just used in data_table
│   ├── use_data_view.ts      # Generic hook - even though they are just used in data_table
│   ├── use_page_size.ts      # Generic hook - even though they are just used in data_table
│   ├── use_persisted_query.ts      # Generic hook - even though they are just used in data_table
│   ├── use_url_query.ts      # Generic hook - even though they are just used in data_table
│   └── use_styles.ts       # I think we should review this hook, either fully adopt it or remove it
│   ├── fetch_utils.ts                 # I think we should review this file, consider creating a lib folder to reuse functions, they aren't necessary under a domain
├── constants.ts
├── index.ts
├── jest.config.js
├── pages/
│   ├── all_assets.tsx
│   └── index.tsx
├── routes/
│   ├── routes.tsx
│   ├── links.ts
└── test/
   └── test_provider.tsx

@JordanSh
Copy link
Contributor

JordanSh commented Mar 5, 2025

@opauloh @albertoblaz

Maybe this will clarify my intention better: https://github.com/alan2207/bulletproof-react/blob/master/docs/project-structure.md

The idea is that we have generic shared folders, which do not contain domain knowledge, including an asset_inventory folder which itself is domain specific.
All the domain specifics are included in the pages folder (in the example I sent its referred to as features)

each folder in pages may include sub folders of the same structure but this time include context in their scope.

Asked some LLM to implement this structure to our files, its pretty good, maybe hallucinated a little, and the files themselves might not be named the best. but this is a general structure that we can aim for and fine tune:

src/
├── app/
│   ├── routes/                        
│   │   ├── allAssets.tsx
│   │   ├── index.tsx
│   ├── app.tsx                        # Main application component
│   ├── provider.tsx                   # Provider for wrapping the app with global providers
│   ├── router.tsx                     # Application routing configuration
├── assets/                            # Static assets (images, fonts, etc.)
│   └── cool_illustration.svg
├── components/                        # Generic components
│   ├── EmptyStateIllustrationContainer.tsx # Probably uses cool_illustration.svg
│   └── HoverForExplanationTooltip.tsx
├── hooks/                    # Generic hooks
│   ├── useDynamicEntityFlyout.ts
│   ├── useBaseEsQuery.ts
│   ├── useDataView.ts
│   ├── usePageSize.ts
│   ├── usePersistedQuery.ts
│   ├── useUrlQuery.ts
│   └── useStyles.ts
├── pages/                          # Page-specific modules
│   ├── all_assets/                # Page folder for Asset Inventory
│   │   ├── api/                       # API calls related to asset inventory (if necessary)
│   │   │   └── assetInventoryApi.ts
│   │   ├── assets/                    # Static assets for asset inventory (if any)
│   │   ├── components/                # Components scoped to the Asset Inventory feature
│   │   │   ├── AssetInventoryLoading.tsx
│   │   │   ├── AssetInventoryEmptyState.tsx
│   │   │   ├── AssetInventoryRiskBadge.tsx
│   │   │   ├── AssetInventorySearchBar.tsx
│   │   │   ├── AssetInventoryInventoryTitle.tsx
│   │   │   ├── AssetInventoryTopAssetsBarChart.tsx
│   │   │   ├── asset_inventory_onboarding/           # Onboarding related components for asset inventory
│   │   │   │   ├── AssetInventoryOnboarding.tsx # Index file matches folder name
│   │   │   │   └── GetStarted.tsx # This file contains asset inventory labels and ids, so it shouldn't be part of the generic components folder
│   │   │   └── filters/              # Filter components for asset inventory
│   │   │       ├── AssetInventoryFilters.tsx
│   │   │       └── AssetInventoryFiltersLoading.tsx
│   │   ├── hooks/                    # Hooks scoped to the Asset Inventory feature
│   │   │   ├── useAssetInventoryDataTable.ts
│   │   │   ├── useAssetInventoryFetchDataGrid.ts
│   │   │   ├── useAssetInventoryRoutes.ts
│   │   │   ├── useAssetInventoryStatus.ts
│   │   │   ├── useAssetInventoryFetchChartData.ts
│   │   │   ├── useAssetInventoryEnable.ts
│   │   ├── types/                    # Types related to asset inventory
│   │   │   └── assetInventoryTypes.ts
│   │   ├── utils/                    # Utility functions for the asset inventory feature
│   │   │   └── assetInventoryUtils.ts
|   |   └── all_assets.tsx # Index file
│   ├── config/                       # Configurations specific to features or global app configuration
│   └── testing/                      # Test utilities and mocks
│       └── TestProvider.tsx

@opauloh
Copy link
Contributor

opauloh commented Mar 5, 2025

@JordanSh thanks for the example, I understand it better now, I think the proposed approach works better as it still prioritizes colocation, 🛳️-it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport:skip This commit does not require backporting refactoring release_note:skip Skip the PR/issue when compiling release notes Team:Cloud Security Cloud Security team related v9.1.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants