fleet/frontend/pages/SoftwarePage/SoftwareTitles/SoftwareTable/SoftwareTable.tsx

392 lines
12 KiB
TypeScript
Raw Normal View History

/**
software/titles Software tab > Table
software/versions Software tab > Table (version toggle on)
*/
import React, { useCallback, useMemo } from "react";
import { InjectedRouter } from "react-router";
import { Row } from "react-table";
import PATHS from "router/paths";
import { getNextLocationPath } from "utilities/helpers";
import { GITHUB_NEW_ISSUE_LINK } from "utilities/constants";
import {
Update UI for software self-service features (#19244) Issues https://github.com/fleetdm/fleet/issues/17587, https://github.com/fleetdm/fleet/issues/18836, https://github.com/fleetdm/fleet/issues/18837, https://github.com/fleetdm/fleet/pull/18339, and https://github.com/fleetdm/fleet/pull/18340 # TODOS - Integrate backend - Unit/integration tests - Various todos noted in comments - Cleanup styles and organization of components (de-duplicating and consolidating where possible) - Activity feed updates (if any) # Checklist for submitter If some of the following don't apply, delete the relevant line. <!-- Note that API documentation changes are now addressed by the product design team. --> - [ ] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features. - [ ] Added/updated tests - [ ] If database migrations are included, checked table schema to confirm autoupdate - For database migrations: - [ ] Checked schema for all modified table for columns that will auto-update timestamps during migration. - [ ] Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects. - [ ] Ensured the correct collation is explicitly set for character columns (`COLLATE utf8mb4_unicode_ci`). - [ ] Manual QA for all new/changed functionality - For Orbit and Fleet Desktop changes: - [ ] Manual QA must be performed in the three main OSs, macOS, Windows and Linux. - [ ] Auto-update manual QA, from released version of component to new version (see [tools/tuf/test](../tools/tuf/test/README.md)). --------- Co-authored-by: Gabriel Hernandez <ghernandez345@gmail.com>
2024-05-31 10:09:53 +00:00
buildQueryStringFromParams,
convertParamsToSnakeCase,
} from "utilities/url";
import {
ISoftwareApiParams,
ISoftwareTitlesResponse,
ISoftwareVersionsResponse,
} from "services/entities/software";
import { ISoftwareTitle, ISoftwareVersion } from "interfaces/software";
// @ts-ignore
import Dropdown from "components/forms/fields/Dropdown";
import TableContainer from "components/TableContainer";
import Slider from "components/forms/fields/Slider";
import CustomLink from "components/CustomLink";
import LastUpdatedText from "components/LastUpdatedText";
import { ITableQueryData } from "components/TableContainer/TableContainer";
import TableCount from "components/TableContainer/TableCount";
import Button from "components/buttons/Button";
import Icon from "components/Icon";
import TooltipWrapper from "components/TooltipWrapper";
import EmptySoftwareTable from "pages/SoftwarePage/components/EmptySoftwareTable";
import generateTitlesTableConfig from "./SoftwareTitlesTableConfig";
import generateVersionsTableConfig from "./SoftwareVersionsTableConfig";
import {
ISoftwareDropdownFilterVal,
ISoftwareVulnFiltersParams,
SOFTWARE_TITLES_DROPDOWN_OPTIONS,
buildSoftwareFilterQueryParams,
buildSoftwareVulnFiltersQueryParams,
getVulnFilterRenderDetails,
} from "./helpers";
interface IRowProps extends Row {
original: {
id?: number;
};
}
type ITableConfigGenerator = (router: InjectedRouter, teamId?: number) => void;
const isSoftwareTitles = (
data?: ISoftwareTitlesResponse | ISoftwareVersionsResponse
): data is ISoftwareTitlesResponse => {
if (!data) return false;
return (data as ISoftwareTitlesResponse).software_titles !== undefined;
};
interface ISoftwareTableProps {
router: InjectedRouter;
data?: ISoftwareTitlesResponse | ISoftwareVersionsResponse;
showVersions: boolean;
installableSoftwareExists: boolean;
isSoftwareEnabled: boolean;
query: string;
perPage: number;
orderDirection: "asc" | "desc";
orderKey: string;
softwareFilter: ISoftwareDropdownFilterVal;
vulnFilters: ISoftwareVulnFiltersParams;
currentPage: number;
teamId?: number;
isLoading: boolean;
resetPageIndex: boolean;
onAddFiltersClick: () => void;
}
const baseClass = "software-table";
const SoftwareTable = ({
router,
data,
showVersions,
installableSoftwareExists,
isSoftwareEnabled,
query,
perPage,
orderDirection,
orderKey,
softwareFilter,
vulnFilters,
currentPage,
teamId,
isLoading,
resetPageIndex,
onAddFiltersClick,
}: ISoftwareTableProps) => {
const currentPath = showVersions
? PATHS.SOFTWARE_VERSIONS
: PATHS.SOFTWARE_TITLES;
const determineQueryParamChange = useCallback(
(newTableQuery: ITableQueryData) => {
const changedEntry = Object.entries(newTableQuery).find(([key, val]) => {
switch (key) {
case "searchQuery":
return val !== query;
case "sortDirection":
return val !== orderDirection;
case "sortHeader":
return val !== orderKey;
case "pageIndex":
return val !== currentPage;
default:
return false;
}
});
return changedEntry?.[0] ?? "";
},
[currentPage, orderDirection, orderKey, query]
);
const generateNewQueryParams = useCallback(
(newTableQuery: ITableQueryData, changedParam: string) => {
const newQueryParam: Record<string, string | number | undefined> = {
query: newTableQuery.searchQuery,
team_id: teamId,
order_direction: newTableQuery.sortDirection,
order_key: newTableQuery.sortHeader,
page: changedParam === "pageIndex" ? newTableQuery.pageIndex : 0,
...buildSoftwareVulnFiltersQueryParams(vulnFilters),
};
if (softwareFilter === "installableSoftware") {
newQueryParam.available_for_install = true.toString();
}
if (softwareFilter === "selfServiceSoftware") {
newQueryParam.self_service = true.toString();
}
return newQueryParam;
},
[softwareFilter, teamId, vulnFilters]
);
// NOTE: this is called once on initial render and every time the query changes
const onQueryChange = useCallback(
(newTableQuery: ITableQueryData) => {
// we want to determine which query param has changed in order to
// reset the page index to 0 if any other param has changed.
const changedParam = determineQueryParamChange(newTableQuery);
// if nothing has changed, don't update the route. this can happen when
// this handler is called on the inital render. Can also happen when
// the filter dropdown is changed. That is handled on the onChange handler
// for the dropdown.
if (changedParam === "") return;
const newRoute = getNextLocationPath({
pathPrefix: currentPath,
routeTemplate: "",
queryParams: generateNewQueryParams(newTableQuery, changedParam),
});
router.replace(newRoute);
},
[determineQueryParamChange, generateNewQueryParams, router, currentPath]
);
let tableData: ISoftwareTitle[] | ISoftwareVersion[] | undefined;
let generateTableConfig: ITableConfigGenerator;
if (data === undefined) {
tableData;
generateTableConfig = () => [];
} else if (isSoftwareTitles(data)) {
tableData = data.software_titles;
generateTableConfig = generateTitlesTableConfig;
} else {
tableData = data.software;
generateTableConfig = generateVersionsTableConfig;
}
const softwareTableHeaders = useMemo(() => {
if (!data) return [];
return generateTableConfig(router, teamId);
}, [generateTableConfig, data, router, teamId]);
// Determines if a user should be able to filter or search in the table
const hasData = tableData && tableData.length > 0;
const hasQuery = query !== "";
const hasSoftwareFilter = softwareFilter !== "allSoftware";
const hasVersionFilter = showVersions;
const vulnFilterDetails = getVulnFilterRenderDetails(vulnFilters);
const hasVulnFilters = vulnFilterDetails.filterCount > 0;
const showFilterHeaders =
isSoftwareEnabled &&
(hasData ||
hasQuery ||
hasSoftwareFilter ||
hasVersionFilter ||
hasVulnFilters);
const handleShowVersionsToggle = () => {
const queryParams: Record<string, string | number | boolean | undefined> = {
query,
team_id: teamId,
order_direction: orderDirection,
order_key: orderKey,
page: 0, // resets page index
...buildSoftwareFilterQueryParams("allSoftware"), // Reset to all software
...buildSoftwareVulnFiltersQueryParams(vulnFilters),
};
router.replace(
getNextLocationPath({
pathPrefix: showVersions
? PATHS.SOFTWARE_TITLES
: PATHS.SOFTWARE_VERSIONS,
routeTemplate: "",
queryParams,
})
);
};
Update UI for software self-service features (#19244) Issues https://github.com/fleetdm/fleet/issues/17587, https://github.com/fleetdm/fleet/issues/18836, https://github.com/fleetdm/fleet/issues/18837, https://github.com/fleetdm/fleet/pull/18339, and https://github.com/fleetdm/fleet/pull/18340 # TODOS - Integrate backend - Unit/integration tests - Various todos noted in comments - Cleanup styles and organization of components (de-duplicating and consolidating where possible) - Activity feed updates (if any) # Checklist for submitter If some of the following don't apply, delete the relevant line. <!-- Note that API documentation changes are now addressed by the product design team. --> - [ ] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features. - [ ] Added/updated tests - [ ] If database migrations are included, checked table schema to confirm autoupdate - For database migrations: - [ ] Checked schema for all modified table for columns that will auto-update timestamps during migration. - [ ] Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects. - [ ] Ensured the correct collation is explicitly set for character columns (`COLLATE utf8mb4_unicode_ci`). - [ ] Manual QA for all new/changed functionality - For Orbit and Fleet Desktop changes: - [ ] Manual QA must be performed in the three main OSs, macOS, Windows and Linux. - [ ] Auto-update manual QA, from released version of component to new version (see [tools/tuf/test](../tools/tuf/test/README.md)). --------- Co-authored-by: Gabriel Hernandez <ghernandez345@gmail.com>
2024-05-31 10:09:53 +00:00
const handleCustomFilterDropdownChange = (
value: ISoftwareDropdownFilterVal
) => {
Update UI for software self-service features (#19244) Issues https://github.com/fleetdm/fleet/issues/17587, https://github.com/fleetdm/fleet/issues/18836, https://github.com/fleetdm/fleet/issues/18837, https://github.com/fleetdm/fleet/pull/18339, and https://github.com/fleetdm/fleet/pull/18340 # TODOS - Integrate backend - Unit/integration tests - Various todos noted in comments - Cleanup styles and organization of components (de-duplicating and consolidating where possible) - Activity feed updates (if any) # Checklist for submitter If some of the following don't apply, delete the relevant line. <!-- Note that API documentation changes are now addressed by the product design team. --> - [ ] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features. - [ ] Added/updated tests - [ ] If database migrations are included, checked table schema to confirm autoupdate - For database migrations: - [ ] Checked schema for all modified table for columns that will auto-update timestamps during migration. - [ ] Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects. - [ ] Ensured the correct collation is explicitly set for character columns (`COLLATE utf8mb4_unicode_ci`). - [ ] Manual QA for all new/changed functionality - For Orbit and Fleet Desktop changes: - [ ] Manual QA must be performed in the three main OSs, macOS, Windows and Linux. - [ ] Auto-update manual QA, from released version of component to new version (see [tools/tuf/test](../tools/tuf/test/README.md)). --------- Co-authored-by: Gabriel Hernandez <ghernandez345@gmail.com>
2024-05-31 10:09:53 +00:00
const queryParams: ISoftwareApiParams = {
query,
Update UI for software self-service features (#19244) Issues https://github.com/fleetdm/fleet/issues/17587, https://github.com/fleetdm/fleet/issues/18836, https://github.com/fleetdm/fleet/issues/18837, https://github.com/fleetdm/fleet/pull/18339, and https://github.com/fleetdm/fleet/pull/18340 # TODOS - Integrate backend - Unit/integration tests - Various todos noted in comments - Cleanup styles and organization of components (de-duplicating and consolidating where possible) - Activity feed updates (if any) # Checklist for submitter If some of the following don't apply, delete the relevant line. <!-- Note that API documentation changes are now addressed by the product design team. --> - [ ] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features. - [ ] Added/updated tests - [ ] If database migrations are included, checked table schema to confirm autoupdate - For database migrations: - [ ] Checked schema for all modified table for columns that will auto-update timestamps during migration. - [ ] Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects. - [ ] Ensured the correct collation is explicitly set for character columns (`COLLATE utf8mb4_unicode_ci`). - [ ] Manual QA for all new/changed functionality - For Orbit and Fleet Desktop changes: - [ ] Manual QA must be performed in the three main OSs, macOS, Windows and Linux. - [ ] Auto-update manual QA, from released version of component to new version (see [tools/tuf/test](../tools/tuf/test/README.md)). --------- Co-authored-by: Gabriel Hernandez <ghernandez345@gmail.com>
2024-05-31 10:09:53 +00:00
teamId,
orderDirection,
orderKey,
page: 0, // resets page index
...buildSoftwareVulnFiltersQueryParams(vulnFilters),
...buildSoftwareFilterQueryParams(value),
};
router.replace(
getNextLocationPath({
pathPrefix: currentPath,
routeTemplate: "",
Update UI for software self-service features (#19244) Issues https://github.com/fleetdm/fleet/issues/17587, https://github.com/fleetdm/fleet/issues/18836, https://github.com/fleetdm/fleet/issues/18837, https://github.com/fleetdm/fleet/pull/18339, and https://github.com/fleetdm/fleet/pull/18340 # TODOS - Integrate backend - Unit/integration tests - Various todos noted in comments - Cleanup styles and organization of components (de-duplicating and consolidating where possible) - Activity feed updates (if any) # Checklist for submitter If some of the following don't apply, delete the relevant line. <!-- Note that API documentation changes are now addressed by the product design team. --> - [ ] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://fleetdm.com/docs/contributing/committing-changes#changes-files) for more information. - [ ] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements) - [ ] Added support on fleet's osquery simulator `cmd/osquery-perf` for new osquery data ingestion features. - [ ] Added/updated tests - [ ] If database migrations are included, checked table schema to confirm autoupdate - For database migrations: - [ ] Checked schema for all modified table for columns that will auto-update timestamps during migration. - [ ] Confirmed that updating the timestamps is acceptable, and will not cause unwanted side effects. - [ ] Ensured the correct collation is explicitly set for character columns (`COLLATE utf8mb4_unicode_ci`). - [ ] Manual QA for all new/changed functionality - For Orbit and Fleet Desktop changes: - [ ] Manual QA must be performed in the three main OSs, macOS, Windows and Linux. - [ ] Auto-update manual QA, from released version of component to new version (see [tools/tuf/test](../tools/tuf/test/README.md)). --------- Co-authored-by: Gabriel Hernandez <ghernandez345@gmail.com>
2024-05-31 10:09:53 +00:00
queryParams: convertParamsToSnakeCase(queryParams),
})
);
};
const handleRowSelect = (row: IRowProps) => {
const queryParams = showVersions
? buildQueryStringFromParams({
software_version_id: row.original.id,
team_id: teamId,
})
: buildQueryStringFromParams({
software_title_id: row.original.id,
team_id: teamId,
});
const path = `${PATHS.MANAGE_HOSTS}?${queryParams}`;
router.push(path);
};
const renderSoftwareCount = () => {
if (!tableData || !data) return null;
return (
<>
<TableCount name="items" count={data?.count} />
{tableData && data?.counts_updated_at && (
<LastUpdatedText
lastUpdatedAt={data.counts_updated_at}
customTooltipText={
<>
The last time software data was <br />
updated, including vulnerabilities <br />
and host counts.
</>
}
/>
)}
</>
);
};
const renderCustomControls = () => {
return (
<div className={`${baseClass}__filter-controls`}>
{!showVersions && ( // Hidden when viewing versions table
<Dropdown
value={softwareFilter}
className={`${baseClass}__vuln_dropdown`}
options={SOFTWARE_TITLES_DROPDOWN_OPTIONS}
searchable={false}
onChange={handleCustomFilterDropdownChange}
iconName="filter"
/>
)}
<Slider
value={showVersions}
onChange={handleShowVersionsToggle}
inactiveText="Show versions"
activeText="Show versions"
/>
</div>
);
};
const renderCustomFiltersButton = () => {
return (
<TooltipWrapper
className={`${baseClass}__filters`}
position="left"
underline={false}
showArrow
tipOffset={12}
tipContent={vulnFilterDetails.tooltipText}
disableTooltip={!hasVulnFilters}
>
<Button variant="text-link" onClick={onAddFiltersClick}>
<Icon name="filter" color="core-fleet-blue" />
<span>{vulnFilterDetails.buttonText}</span>
</Button>
</TooltipWrapper>
);
};
const renderTableHelpText = () => (
<div>
Seeing unexpected software or vulnerabilities?{" "}
<CustomLink
url={GITHUB_NEW_ISSUE_LINK}
text="File an issue on GitHub"
newTab
/>
</div>
);
return (
<div className={baseClass}>
<TableContainer
columnConfigs={softwareTableHeaders}
data={tableData ?? []}
isLoading={isLoading}
resultsTitle="items"
emptyComponent={() => (
<EmptySoftwareTable
softwareFilter={softwareFilter}
vulnFilters={vulnFilters}
isSoftwareDisabled={!isSoftwareEnabled}
noSearchQuery={query === ""}
installableSoftwareExists={installableSoftwareExists}
/>
)}
defaultSortHeader={orderKey}
defaultSortDirection={orderDirection}
defaultPageIndex={currentPage}
defaultSearchQuery={query}
manualSortBy
pageSize={perPage}
showMarkAllPages={false}
isAllPagesSelected={false}
disableNextPage={!data?.meta.has_next_results}
searchable={showFilterHeaders}
inputPlaceHolder="Search by name or vulnerability (CVE)"
onQueryChange={onQueryChange}
// additionalQueries serves as a trigger for the useDeepEffect hook
// to fire onQueryChange for events happening outside of
// the TableContainer.
// additionalQueries={softwareFilter}
customControl={showFilterHeaders ? renderCustomControls : undefined}
customFiltersButton={
showFilterHeaders ? renderCustomFiltersButton : undefined
}
stackControls
renderCount={renderSoftwareCount}
renderTableHelpText={renderTableHelpText}
disableMultiRowSelect
onSelectSingleRow={handleRowSelect}
resetPageIndex={resetPageIndex}
/>
</div>
);
};
export default SoftwareTable;