Show all teams for global users and save team selection between pages (#3204)

This commit is contained in:
Luke Heath 2021-12-06 20:04:40 -06:00 committed by GitHub
parent b3a69680e7
commit 78d5e13a97
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 134 additions and 94 deletions

View file

@ -0,0 +1 @@
* Ensure show all teams is displayed to global users and save team selection between pages

View file

@ -102,6 +102,9 @@ describe("Teams flow", () => {
cy.visit("/schedule/manage");
cy.wait(2000); // eslint-disable-line cypress/no-unnecessary-waiting
cy.get(".component__team-dropdown").click();
cy.findByText(/valor/i).should("exist");
cy.findByText(/query all window crashes/i).should("exist");

View file

@ -8,7 +8,6 @@ import hostSummaryAPI from "services/entities/host_summary";
import teamsAPI from "services/entities/teams";
import { IHostSummary, IHostSummaryPlatforms } from "interfaces/host_summary";
import { ITeam } from "interfaces/team";
import { getSortedTeamOptions } from "fleet/helpers";
import sortUtils from "utilities/sort";
import TeamsDropdown from "components/TeamsDropdown";
@ -38,6 +37,7 @@ const Homepage = (): JSX.Element => {
config,
currentTeam,
isPremiumTier,
isFreeTier,
isPreviewMode,
isOnGlobalTeam,
setCurrentTeam,
@ -53,24 +53,20 @@ const Homepage = (): JSX.Element => {
const [offlineCount, setOfflineCount] = useState<string | undefined>();
const [newCount, setNewCount] = useState<string | undefined>();
const { data: teams, isLoading: isLoadingTeams } = useQuery<
ITeamsResponse,
Error,
ITeam[]
>(["teams"], () => teamsAPI.loadAll(), {
enabled: !!isPremiumTier,
select: (data: ITeamsResponse) =>
data.teams.sort((a, b) => sortUtils.caseInsensitiveAsc(a.name, b.name)),
onSuccess: (responseTeams) => {
if (!isOnGlobalTeam) {
const sortedTeams = getSortedTeamOptions(responseTeams);
const firstTeamOption = responseTeams.find(
(responseTeam) => responseTeam.id === sortedTeams[0].value
);
setCurrentTeam(firstTeamOption);
}
},
});
const { data: teams } = useQuery<ITeamsResponse, Error, ITeam[]>(
["teams"],
() => teamsAPI.loadAll(),
{
enabled: !!isPremiumTier,
select: (data: ITeamsResponse) =>
data.teams.sort((a, b) => sortUtils.caseInsensitiveAsc(a.name, b.name)),
onSuccess: (responseTeams) => {
if (!currentTeam && !isOnGlobalTeam && responseTeams.length) {
setCurrentTeam(responseTeams[0]);
}
},
}
);
const handleTeamSelect = (teamId: number) => {
const selectedTeam = find(teams, ["id", teamId]);
@ -108,19 +104,22 @@ const Homepage = (): JSX.Element => {
<div className={`${baseClass}__header`}>
<div className={`${baseClass}__text`}>
<div className={`${baseClass}__title`}>
{isPremiumTier && teams && teams.length > 1 && (
<TeamsDropdown
selectedTeamId={currentTeam?.id || 0}
currentUserTeams={teams || []}
onChange={(newSelectedValue: number) =>
handleTeamSelect(newSelectedValue)
}
/>
)}
{isPremiumTier && teams && teams.length === 1 && (
<h1>{teams[0].name}</h1>
)}
{!isPremiumTier && <h1>{config?.org_name}</h1>}
{isFreeTier && <h1>{config?.org_name}</h1>}
{isPremiumTier &&
teams &&
(teams.length > 1 || isOnGlobalTeam) && (
<TeamsDropdown
selectedTeamId={currentTeam?.id || 0}
currentUserTeams={teams || []}
onChange={(newSelectedValue: number) =>
handleTeamSelect(newSelectedValue)
}
/>
)}
{isPremiumTier &&
!isOnGlobalTeam &&
teams &&
teams.length === 1 && <h1>{teams[0].name}</h1>}
</div>
</div>
</div>

View file

@ -122,7 +122,7 @@ const ManageHostsPage = ({
router,
params: routeParams,
location,
}: IManageHostsProps) => {
}: IManageHostsProps): JSX.Element => {
const dispatch = useDispatch();
const queryParams = location.query;
const {
@ -137,6 +137,7 @@ const ManageHostsPage = ({
isOnGlobalTeam,
isOnlyObserver,
isPremiumTier,
isFreeTier,
currentTeam,
setCurrentTeam,
} = useContext(AppContext);
@ -316,7 +317,7 @@ const ManageHostsPage = ({
select: (data: ITeamsResponse) =>
data.teams.sort((a, b) => sortUtils.caseInsensitiveAsc(a.name, b.name)),
onSuccess: (responseTeams: ITeam[]) => {
if (responseTeams.length === 1) {
if (!currentTeam && !isOnGlobalTeam && responseTeams.length) {
setCurrentTeam(responseTeams[0]);
}
},
@ -460,7 +461,9 @@ const ManageHostsPage = ({
// set the team object in context
const teamId = parseInt(queryParams?.team_id, 10) || 0;
const selectedTeam = find(teams, ["id", teamId]);
setCurrentTeam(selectedTeam);
if (selectedTeam) {
setCurrentTeam(selectedTeam);
}
setShowNoEnrollSecretBanner(true);
// set selected label
@ -495,7 +498,9 @@ const ManageHostsPage = ({
// set the team object in context
const teamId = parseInt(queryParams?.team_id, 10) || 0;
const selectedTeam = find(teams, ["id", teamId]);
setCurrentTeam(selectedTeam);
if (selectedTeam) {
setCurrentTeam(selectedTeam);
}
setShowNoEnrollSecretBanner(true);
// set selected label
@ -620,12 +625,11 @@ const ManageHostsPage = ({
setSoftwareDetails(null);
};
// The handleChange method below is for the filter-by-team dropdown rather than the dropdown used in modals
const handleChangeSelectedTeamFilter = (selectedTeam: number) => {
const handleTeamSelect = (teamId: number) => {
const { MANAGE_HOSTS } = PATHS;
const teamIdParam = getValidatedTeamId(
teams || [],
selectedTeam,
teamId,
currentUser,
isOnGlobalTeam as boolean
);
@ -647,6 +651,8 @@ const ManageHostsPage = ({
queryParams: newQueryParams,
});
router.replace(nextLocation);
const selectedTeam = find(teams, ["id", teamId]);
setCurrentTeam(selectedTeam);
};
const handleStatusDropdownChange = (statusName: string) => {
@ -1078,7 +1084,7 @@ const ManageHostsPage = ({
(policyId && policy?.team_id) || (currentTeam?.id as number)
}
onChange={(newSelectedValue: number) =>
handleChangeSelectedTeamFilter(newSelectedValue)
handleTeamSelect(newSelectedValue)
}
/>
);
@ -1340,14 +1346,15 @@ const ManageHostsPage = ({
<div className={`${baseClass}__header`}>
<div className={`${baseClass}__text`}>
<div className={`${baseClass}__title`}>
{!isPremiumTier && <h1>Hosts</h1>}
{isFreeTier && <h1>Hosts</h1>}
{isPremiumTier &&
teams &&
teams.length > 1 &&
(teams.length > 1 || isOnGlobalTeam) &&
renderTeamsFilterDropdown()}
{isPremiumTier && teams && teams.length === 1 && (
<h1>{teams[0].name}</h1>
)}
{isPremiumTier &&
!isOnGlobalTeam &&
teams &&
teams.length === 1 && <h1>{teams[0].name}</h1>}
</div>
</div>
</div>

View file

@ -1,7 +1,7 @@
import React, { useCallback, useContext, useEffect, useState } from "react";
import { useQuery } from "react-query";
import { useDispatch } from "react-redux";
import { noop } from "lodash";
import { find, noop } from "lodash";
// @ts-ignore
import { renderFlash } from "redux/nodes/notifications/actions";
@ -67,6 +67,8 @@ const ManagePolicyPage = (managePoliciesPageProps: {
isOnGlobalTeam,
isFreeTier,
isPremiumTier,
currentTeam,
setCurrentTeam,
} = useContext(AppContext);
const {
@ -181,6 +183,8 @@ const ManagePolicyPage = (managePoliciesPageProps: {
setShowInheritedPolicies(false);
setSelectedPolicyIds([]);
setPolicyTeamId(id);
const selectedTeam = find(teams, ["id", id]);
setCurrentTeam(selectedTeam);
};
const toggleAddPolicyModal = () => setShowAddPolicyModal(!showAddPolicyModal);
@ -266,7 +270,7 @@ const ManagePolicyPage = (managePoliciesPageProps: {
if (userTeams && !userTeams.find((t) => t.id === teamId)) {
if (isOnGlobalTeam) {
// For global users, default to zero (i.e. all teams).
if (teamId === undefined) {
if (teamId === undefined && !currentTeam) {
handleTeamSelect(0);
return;
}
@ -275,7 +279,7 @@ const ManagePolicyPage = (managePoliciesPageProps: {
// If there is no default team, set teamId to null so that getPolicies
// API request will not be triggered.
teamId = userTeams[0]?.id || null;
if (teamId) {
if (!currentTeam && teamId) {
handleTeamSelect(teamId);
return;
}
@ -284,7 +288,11 @@ const ManagePolicyPage = (managePoliciesPageProps: {
// Null case must be distinguished from 0 (which is used as the id for the "All teams" option)
// so a falsiness check cannot be used here. Null case here allows us to skip API call
// that would be triggered on a change to selectedTeamId.
teamId !== null && setSelectedTeamId(teamId);
if (currentTeam) {
setSelectedTeamId(currentTeam.id);
} else {
teamId !== null && setSelectedTeamId(teamId);
}
}, [isOnGlobalTeam, location, userTeams]);
// Watch for selected team changes and call getPolicies to make new policies API request.
@ -348,8 +356,7 @@ const ManagePolicyPage = (managePoliciesPageProps: {
{isFreeTier && <h1>Policies</h1>}
{isPremiumTier &&
userTeams &&
userTeams.length > 1 &&
selectedTeamId >= 0 && (
(userTeams.length > 1 || isOnGlobalTeam) && (
<TeamsDropdown
selectedTeamId={selectedTeamId}
currentUserTeams={userTeams || []}
@ -358,9 +365,10 @@ const ManagePolicyPage = (managePoliciesPageProps: {
}
/>
)}
{isPremiumTier && userTeams && userTeams.length === 1 && (
<h1>{userTeams[0].name}</h1>
)}
{isPremiumTier &&
!isOnGlobalTeam &&
userTeams &&
userTeams.length === 1 && <h1>{userTeams[0].name}</h1>}
</div>
</div>
</div>

View file

@ -5,6 +5,7 @@ import { useQuery } from "react-query";
import { useDispatch, useSelector } from "react-redux";
import { AppContext } from "context/app";
import { push } from "react-router-redux";
import { find } from "lodash";
// @ts-ignore
import deepDifference from "utilities/deep_difference";
@ -123,9 +124,14 @@ const ManageSchedulePage = ({
const { MANAGE_PACKS, MANAGE_SCHEDULE, MANAGE_TEAM_SCHEDULE } = paths;
const handleAdvanced = () => dispatch(push(MANAGE_PACKS));
const { currentUser, isOnGlobalTeam, isPremiumTier, isFreeTier } = useContext(
AppContext
);
const {
currentUser,
isOnGlobalTeam,
isPremiumTier,
isFreeTier,
currentTeam,
setCurrentTeam,
} = useContext(AppContext);
const filterAndSortTeamOptions = (allTeams: ITeam[], userTeams: ITeam[]) => {
const filteredSortedTeams = allTeams
@ -167,31 +173,39 @@ const ManageSchedulePage = ({
}
);
const teamId = team_id ? parseInt(team_id, 10) : 0;
let selectedTeamId: number;
const handleTeamSelect = (selectedTeamId: number) => {
if (selectedTeamId) {
dispatch(push(MANAGE_TEAM_SCHEDULE(selectedTeamId)));
if (currentTeam) {
selectedTeamId = currentTeam.id;
} else {
selectedTeamId = team_id ? parseInt(team_id, 10) : 0;
}
const handleTeamSelect = (teamId: number) => {
if (teamId) {
dispatch(push(MANAGE_TEAM_SCHEDULE(teamId)));
} else {
dispatch(push(MANAGE_SCHEDULE));
}
const selectedTeam = find(teams, ["id", teamId]);
setCurrentTeam(selectedTeam);
};
if (!isOnGlobalTeam && !teamId && teams) {
if (!isOnGlobalTeam && !selectedTeamId && teams) {
handleTeamSelect(teams[0].id);
}
// TODO: move team scheduled queries and global scheduled queries into services entities, remove redux
useEffect(() => {
dispatch(
teamId
? teamScheduledQueryActions.loadAll(teamId)
selectedTeamId
? teamScheduledQueryActions.loadAll(selectedTeamId)
: globalScheduledQueryActions.loadAll()
);
}, [dispatch, teamId]);
}, [dispatch, selectedTeamId]);
const allScheduledQueries = useSelector((state: IRootState) => {
if (teamId) {
if (selectedTeamId) {
return state.entities.team_scheduled_queries;
}
return state.entities.global_scheduled_queries;
@ -212,7 +226,7 @@ const ManageSchedulePage = ({
const inheritedQueryOrQueries =
allTeamsScheduledQueriesList.length === 1 ? "query" : "queries";
const selectedTeam = !teamId ? "global" : teamId;
const selectedTeam = !selectedTeamId ? "global" : selectedTeamId;
const selectedTeamData =
teams?.find((team: ITeam) => selectedTeam === team.id) || undefined;
@ -267,8 +281,8 @@ const ManageSchedulePage = ({
const onRemoveScheduledQuerySubmit = useCallback(() => {
const promises = selectedQueryIds.map((id: number) => {
return dispatch(
teamId
? teamScheduledQueryActions.destroy(teamId, id)
selectedTeamId
? teamScheduledQueryActions.destroy(selectedTeamId, id)
: globalScheduledQueryActions.destroy({ id })
);
});
@ -283,8 +297,8 @@ const ManageSchedulePage = ({
);
toggleRemoveScheduledQueryModal();
dispatch(
teamId
? teamScheduledQueryActions.loadAll(teamId)
selectedTeamId
? teamScheduledQueryActions.loadAll(selectedTeamId)
: globalScheduledQueryActions.loadAll()
);
})
@ -297,7 +311,12 @@ const ManageSchedulePage = ({
);
toggleRemoveScheduledQueryModal();
});
}, [dispatch, teamId, selectedQueryIds, toggleRemoveScheduledQueryModal]);
}, [
dispatch,
selectedTeamId,
selectedQueryIds,
toggleRemoveScheduledQueryModal,
]);
const onAddScheduledQuerySubmit = useCallback(
(
@ -307,7 +326,7 @@ const ManageSchedulePage = ({
if (editQuery) {
const updatedAttributes = deepDifference(formData, editQuery);
dispatch(
teamId
selectedTeamId
? teamScheduledQueryActions.update(editQuery, updatedAttributes)
: globalScheduledQueryActions.update(editQuery, updatedAttributes)
)
@ -319,8 +338,8 @@ const ManageSchedulePage = ({
)
);
dispatch(
teamId
? teamScheduledQueryActions.loadAll(teamId)
selectedTeamId
? teamScheduledQueryActions.loadAll(selectedTeamId)
: globalScheduledQueryActions.loadAll()
);
})
@ -334,7 +353,7 @@ const ManageSchedulePage = ({
});
} else {
dispatch(
teamId
selectedTeamId
? teamScheduledQueryActions.create({ ...formData })
: globalScheduledQueryActions.create({ ...formData })
)
@ -346,8 +365,8 @@ const ManageSchedulePage = ({
)
);
dispatch(
teamId
? teamScheduledQueryActions.loadAll(teamId)
selectedTeamId
? teamScheduledQueryActions.loadAll(selectedTeamId)
: globalScheduledQueryActions.loadAll()
);
})
@ -362,7 +381,7 @@ const ManageSchedulePage = ({
}
toggleScheduleEditorModal();
},
[dispatch, teamId, toggleScheduleEditorModal]
[dispatch, selectedTeamId, toggleScheduleEditorModal]
);
return (
@ -373,18 +392,21 @@ const ManageSchedulePage = ({
<div className={`${baseClass}__text`}>
<div className={`${baseClass}__title`}>
{isFreeTier && <h1>Schedule</h1>}
{isPremiumTier && teams && teams.length > 1 && (
<TeamsDropdown
selectedTeamId={teamId}
currentUserTeams={teams || []}
onChange={(newSelectedValue: number) =>
handleTeamSelect(newSelectedValue)
}
/>
)}
{isPremiumTier && teams && teams.length === 1 && (
<h1>{teams[0].name}</h1>
)}
{isPremiumTier &&
teams &&
(teams.length > 1 || isOnGlobalTeam) && (
<TeamsDropdown
selectedTeamId={selectedTeamId}
currentUserTeams={teams || []}
onChange={(newSelectedValue: number) =>
handleTeamSelect(newSelectedValue)
}
/>
)}
{isPremiumTier &&
!isOnGlobalTeam &&
teams &&
teams.length === 1 && <h1>{teams[0].name}</h1>}
</div>
</div>
</div>
@ -413,7 +435,7 @@ const ManageSchedulePage = ({
<div className={`${baseClass}__description`}>
{!isLoadingTeams && (
<div>
{!teamId ? (
{!selectedTeamId ? (
<p>
Schedule queries to run at regular intervals across{" "}
<strong>all of your hosts.</strong>
@ -440,7 +462,7 @@ const ManageSchedulePage = ({
)}
</div>
{/* must use ternary for NaN */}
{teamId && allTeamsScheduledQueriesList.length > 0 ? (
{selectedTeamId && allTeamsScheduledQueriesList.length > 0 ? (
<>
<span>
<Button
@ -479,7 +501,7 @@ const ManageSchedulePage = ({
onScheduleSubmit={onAddScheduledQuerySubmit}
allQueries={fleetQueries}
editQuery={selectedScheduledQuery}
teamId={teamId}
teamId={selectedTeamId}
togglePreviewDataModal={togglePreviewDataModal}
showPreviewDataModal={showPreviewDataModal}
/>