Sync current team to url params on manage hosts page (#4010)

This commit is contained in:
gillespi314 2022-02-06 10:07:43 -06:00 committed by GitHub
parent 6e5aee3cdf
commit 90fe417ce0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 148 additions and 106 deletions

View file

@ -46,7 +46,7 @@ export default (
regex: new RegExp(`^${URL_PREFIX}/hosts/`),
pathname: PATHS.MANAGE_HOSTS,
},
// withContext: true,
withContext: true,
},
{
icon: "software",

View file

@ -4,7 +4,7 @@ import moment from "moment";
import yaml from "js-yaml";
import { ILabel } from "interfaces/label";
import { ITeam } from "interfaces/team";
import { ITeam, ITeamSummary } from "interfaces/team";
import { IUser } from "interfaces/user";
import { IPackQueryFormData } from "interfaces/scheduled_query";
@ -688,12 +688,12 @@ export const getSortedTeamOptions = memoize((teams: ITeam[]) =>
);
export const getValidatedTeamId = (
teams: ITeam[],
teams: ITeam[] | ITeamSummary[],
teamId: number,
currentUser: IUser | null,
isOnGlobalTeam: boolean
): number => {
let currentUserTeams: ITeam[] = [];
let currentUserTeams: ITeamSummary[] = [];
if (isOnGlobalTeam) {
currentUserTeams = teams;
} else if (currentUser && currentUser.teams) {

View file

@ -1,4 +1,4 @@
import React, { useState, useContext, useEffect } from "react";
import React, { useState, useContext, useEffect, useCallback } from "react";
import { useDispatch } from "react-redux";
import { useQuery } from "react-query";
import { InjectedRouter, Params } from "react-router/lib/Router";
@ -9,6 +9,7 @@ import ReactTooltip from "react-tooltip";
import enrollSecretsAPI from "services/entities/enroll_secret";
import labelsAPI from "services/entities/labels";
import teamsAPI from "services/entities/teams";
import usersAPI, { IGetMeResponse } from "services/entities/users";
import globalPoliciesAPI from "services/entities/global_policies";
import teamPoliciesAPI from "services/entities/team_policies";
import hostsAPI, {
@ -132,8 +133,10 @@ const ManageHostsPage = ({
const dispatch = useDispatch();
const queryParams = location.query;
const {
currentUser,
availableTeams,
config,
currentTeam,
currentUser,
isGlobalAdmin,
isGlobalMaintainer,
isTeamMaintainer,
@ -142,9 +145,32 @@ const ManageHostsPage = ({
isOnlyObserver,
isPremiumTier,
isFreeTier,
currentTeam,
setAvailableTeams,
setCurrentTeam,
setCurrentUser,
} = useContext(AppContext);
useQuery(["me"], () => usersAPI.me(), {
onSuccess: ({ user, available_teams }: IGetMeResponse) => {
setCurrentUser(user);
setAvailableTeams(available_teams);
if (queryParams.team_id) {
const teamIdParam = parseInt(queryParams.team_id, 10);
if (
isNaN(teamIdParam) ||
(teamIdParam &&
available_teams &&
!available_teams.find((t) => t.id === teamIdParam))
) {
router.replace({
pathname: location.pathname,
query: omit(queryParams, "team_id"),
});
}
}
},
});
const { selectedOsqueryTable, setSelectedOsqueryTable } = useContext(
QueryContext
);
@ -398,7 +424,7 @@ const ManageHostsPage = ({
options = {
...options,
teamId: getValidatedTeamId(
teams || [],
availableTeams || [],
options.teamId as number,
currentUser,
isOnGlobalTeam as boolean
@ -429,7 +455,7 @@ const ManageHostsPage = ({
options = {
...options,
teamId: getValidatedTeamId(
teams || [],
availableTeams || [],
options.teamId as number,
currentUser,
isOnGlobalTeam as boolean
@ -459,9 +485,21 @@ const ManageHostsPage = ({
retrieveHostCount(options);
};
let teamSync = false;
if (currentUser && availableTeams) {
const teamIdParam = queryParams.team_id
? parseInt(queryParams.team_id, 10) // we don't want to parse undefined so we can differntiate non-numeric strings as NaN
: undefined;
if (currentTeam?.id && !teamIdParam) {
teamSync = true;
} else if (teamIdParam === currentTeam?.id) {
teamSync = true;
}
}
useEffect(() => {
const teamId = parseInt(queryParams?.team_id, 10) || 0;
const selectedTeam = find(teams, ["id", teamId]);
const selectedTeam = find(availableTeams, ["id", teamId]);
if (selectedTeam) {
setCurrentTeam(selectedTeam);
}
@ -490,11 +528,12 @@ const ManageHostsPage = ({
if (isEqual(options, currentQueryOptions)) {
return;
}
retrieveHosts(options);
retrieveHostCount(options);
setCurrentQueryOptions(options);
}, [location, labels]);
if (teamSync) {
retrieveHosts(options);
retrieveHostCount(options);
setCurrentQueryOptions(options);
}
}, [availableTeams, currentTeam, location, labels]);
const handleLabelChange = ({ slug }: ILabel): boolean => {
if (!slug) {
@ -575,24 +614,15 @@ const ManageHostsPage = ({
};
const handleClearSoftwareFilter = () => {
// TODO: In current UX, clearing the software filter resets all URL params.
// The code below can be reimplemented if other URL params are to be preserved.
// router.replace(
// getNextLocationPath({
// pathPrefix: PATHS.MANAGE_HOSTS,
// routeTemplate,
// routeParams,
// queryParams: omit(queryParams, ["software_id"]),
// })
// );
router.replace(PATHS.MANAGE_HOSTS);
setCurrentTeam(undefined);
setSoftwareDetails(null);
};
const handleTeamSelect = (teamId: number) => {
const { MANAGE_HOSTS } = PATHS;
const teamIdParam = getValidatedTeamId(
teams || [],
availableTeams || [],
teamId,
currentUser,
isOnGlobalTeam as boolean
@ -615,7 +645,7 @@ const ManageHostsPage = ({
queryParams: newQueryParams,
});
router.replace(nextLocation);
const selectedTeam = find(teams, ["id", teamId]);
const selectedTeam = find(availableTeams, ["id", teamId]);
setCurrentTeam(selectedTeam);
};
@ -663,82 +693,87 @@ const ManageHostsPage = ({
};
// NOTE: this is called once on initial render and every time the query changes
const onTableQueryChange = async (newTableQuery: ITableQueryProps) => {
if (isEqual(newTableQuery, tableQueryData)) {
return;
}
const onTableQueryChange = useCallback(
async (newTableQuery: ITableQueryProps) => {
if (isEqual(newTableQuery, tableQueryData)) {
return;
}
setTableQueryData({ ...newTableQuery });
setTableQueryData({ ...newTableQuery });
const {
searchQuery: searchText,
sortHeader,
sortDirection,
} = newTableQuery;
const {
searchQuery: searchText,
sortHeader,
sortDirection,
} = newTableQuery;
const teamId = getValidatedTeamId(
teams || [],
currentTeam?.id as number,
let sort = sortBy;
if (sortHeader) {
sort = [
{
key: sortHeader,
direction: sortDirection || DEFAULT_SORT_DIRECTION,
},
];
} else if (!sortBy.length) {
sort = [
{ key: DEFAULT_SORT_HEADER, direction: DEFAULT_SORT_DIRECTION },
];
}
if (!isEqual(sort, sortBy)) {
setSortBy([...sort]);
}
if (!isEqual(searchText, searchQuery)) {
setSearchQuery(searchText);
}
// Rebuild queryParams to dispatch new browser location to react-router
const newQueryParams: { [key: string]: any } = {};
if (!isEmpty(searchText)) {
newQueryParams.query = searchText;
}
newQueryParams.order_key = sort[0].key || DEFAULT_SORT_HEADER;
newQueryParams.order_direction =
sort[0].direction || DEFAULT_SORT_DIRECTION;
if (currentTeam?.id) {
newQueryParams.team_id = currentTeam.id;
}
if (policyId) {
newQueryParams.policy_id = policyId;
}
if (policyResponse) {
newQueryParams.policy_response = policyResponse;
}
if (softwareId && !policyId) {
newQueryParams.software_id = softwareId;
}
router.replace(
getNextLocationPath({
pathPrefix: PATHS.MANAGE_HOSTS,
routeTemplate,
routeParams,
queryParams: newQueryParams,
})
);
},
[
availableTeams,
currentTeam,
currentUser,
isOnGlobalTeam as boolean
);
let sort = sortBy;
if (sortHeader) {
sort = [
{ key: sortHeader, direction: sortDirection || DEFAULT_SORT_DIRECTION },
];
} else if (!sortBy.length) {
sort = [{ key: DEFAULT_SORT_HEADER, direction: DEFAULT_SORT_DIRECTION }];
}
if (!isEqual(sort, sortBy)) {
setSortBy([...sort]);
}
if (!isEqual(searchText, searchQuery)) {
setSearchQuery(searchText);
}
// Rebuild queryParams to dispatch new browser location to react-router
const newQueryParams: { [key: string]: any } = {};
if (!isEmpty(searchText)) {
newQueryParams.query = searchText;
}
newQueryParams.order_key = sort[0].key || DEFAULT_SORT_HEADER;
newQueryParams.order_direction =
sort[0].direction || DEFAULT_SORT_DIRECTION;
if (teamId) {
newQueryParams.team_id = teamId;
}
if (queryParams.team_id) {
newQueryParams.team_id = queryParams.team_id;
}
if (policyId) {
newQueryParams.policy_id = policyId;
}
if (policyResponse) {
newQueryParams.policy_response = policyResponse;
}
if (softwareId && !policyId) {
newQueryParams.software_id = softwareId;
}
router.replace(
getNextLocationPath({
pathPrefix: PATHS.MANAGE_HOSTS,
routeTemplate,
routeParams,
queryParams: newQueryParams,
})
);
};
policyId,
queryParams,
softwareId,
sortBy,
]
);
const onSaveSecret = async (enrollSecretString: string) => {
const { MANAGE_HOSTS } = PATHS;
@ -1045,7 +1080,7 @@ const ManageHostsPage = ({
const renderTeamsFilterDropdown = () => (
<TeamsDropdown
currentUserTeams={teams || []}
currentUserTeams={availableTeams || []}
selectedTeamId={
(policyId && policy?.team_id) || (currentTeam?.id as number)
}
@ -1309,13 +1344,13 @@ const ManageHostsPage = ({
<div className={`${baseClass}__title`}>
{isFreeTier && <h1>Hosts</h1>}
{isPremiumTier &&
teams &&
(teams.length > 1 || isOnGlobalTeam) &&
availableTeams &&
(availableTeams.length > 1 || isOnGlobalTeam) &&
renderTeamsFilterDropdown()}
{isPremiumTier &&
!isOnGlobalTeam &&
teams &&
teams.length === 1 && <h1>{teams[0].name}</h1>}
availableTeams &&
availableTeams.length === 1 && <h1>{availableTeams[0].name}</h1>}
</div>
</div>
</div>
@ -1430,7 +1465,8 @@ const ManageHostsPage = ({
!currentUser ||
!hosts ||
selectedFilters.length === 0 ||
selectedLabel === undefined
selectedLabel === undefined ||
!teamSync
) {
return <Spinner />;
}
@ -1442,9 +1478,11 @@ const ManageHostsPage = ({
// There are no hosts for this instance yet
if (
getStatusSelected() === ALL_HOSTS_LABEL &&
!isHostCountLoading &&
filteredHostCount === 0 &&
searchQuery === "" &&
!isHostsLoading
!isHostsLoading &&
teamSync
) {
const { software_id, policy_id } = queryParams || {};
const includesSoftwareOrPolicyFilter = !!(software_id || policy_id);
@ -1549,6 +1587,10 @@ const ManageHostsPage = ({
);
};
if (!teamSync) {
return <Spinner />;
}
return (
<div className="has-sidebar">
{renderForm()}