Skip to content

Commit

Permalink
Merge branch '7766-frontend' into 13470-pt2-edit-query
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacob Shandling committed Oct 6, 2023
2 parents 762c49a + 11fc7ed commit f8260e4
Show file tree
Hide file tree
Showing 14 changed files with 137 additions and 62 deletions.
22 changes: 16 additions & 6 deletions frontend/components/LiveQuery/SelectTargets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
ISelectLabel,
ISelectTeam,
ISelectTargetsEntity,
ISelectedTargets,
ISelectedTargetsForApi,
} from "interfaces/target";
import { ITeam } from "interfaces/team";

Expand Down Expand Up @@ -48,8 +48,8 @@ interface ISelectTargetsProps {
targetedTeams: ITeam[];
goToQueryEditor: () => void;
goToRunQuery: () => void;
setSelectedTargets:
| React.Dispatch<React.SetStateAction<ITarget[]>> // Used for policies page level useState hook
setSelectedTargets: // TODO: Refactor policy targets to streamline selectedTargets/selectedTargetsByType
| React.Dispatch<React.SetStateAction<ITarget[]>> // Used for policies page level useState hook
| ((value: ITarget[]) => void); // Used for queries app level QueryContext
setTargetedHosts: React.Dispatch<React.SetStateAction<IHost[]>>;
setTargetedLabels: React.Dispatch<React.SetStateAction<ILabel[]>>;
Expand All @@ -67,7 +67,7 @@ interface ITargetsQueryKey {
scope: string;
query_id?: number | null;
query?: string | null;
selected?: ISelectedTargets | null;
selected?: ISelectedTargetsForApi | null;
}

const DEBOUNCE_DELAY = 500;
Expand Down Expand Up @@ -381,12 +381,22 @@ const SelectTargets = ({
}

const { targets_count: total, targets_online: online } = counts;
const onlinePercentage = total > 0 ? Math.round((online / total) * 100) : 0;
const onlinePercentage = () => {
if (total === 0) {
return 0;
}
// If at least 1 host is online, displays <1% instead of 0%
const roundPercentage =
Math.round((online / total) * 100) === 0
? "<1"
: Math.round((online / total) * 100) === 0;
return roundPercentage;
};

return (
<>
<span>{total}</span>&nbsp;host{total > 1 ? `s` : ``} targeted&nbsp; (
{onlinePercentage}
{onlinePercentage()}
%&nbsp;
<TooltipWrapper
tipContent={`Hosts are online if they<br /> have recently checked <br />into Fleet.`}
Expand Down
31 changes: 29 additions & 2 deletions frontend/context/query.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { DEFAULT_QUERY } from "utilities/constants";
import { DEFAULT_OSQUERY_TABLE, IOsQueryTable } from "interfaces/osquery_table";
import { SelectedPlatformString } from "interfaces/platform";
import { QueryLoggingOption } from "interfaces/schedulable_query";
import { DEFAULT_TARGETS, ITarget } from "interfaces/target";
import {
DEFAULT_TARGETS,
DEFAULT_TARGETS_BY_TYPE,
ISelectedTargetsByType,
ITarget,
} from "interfaces/target";

type Props = {
children: ReactNode;
Expand All @@ -24,7 +29,8 @@ type InitialStateType = {
lastEditedQueryMinOsqueryVersion: string;
lastEditedQueryLoggingType: QueryLoggingOption;
lastEditedQueryDiscardData: boolean;
selectedQueryTargets: ITarget[];
selectedQueryTargets: ITarget[]; // Mimicks old selectedQueryTargets still used for policies for SelectTargets.tsx and running a live query
selectedQueryTargetsByType: ISelectedTargetsByType; // New format by type for cleaner app wide state
setLastEditedQueryId: (value: number | null) => void;
setLastEditedQueryName: (value: string) => void;
setLastEditedQueryDescription: (value: string) => void;
Expand All @@ -37,6 +43,7 @@ type InitialStateType = {
setLastEditedQueryDiscardData: (value: boolean) => void;
setSelectedOsqueryTable: (tableName: string) => void;
setSelectedQueryTargets: (value: ITarget[]) => void;
setSelectedQueryTargetsByType: (value: ISelectedTargetsByType) => void;
};

export type IQueryContext = InitialStateType;
Expand All @@ -55,6 +62,7 @@ const initialState = {
lastEditedQueryLoggingType: DEFAULT_QUERY.logging,
lastEditedQueryDiscardData: DEFAULT_QUERY.discard_data,
selectedQueryTargets: DEFAULT_TARGETS,
selectedQueryTargetsByType: DEFAULT_TARGETS_BY_TYPE,
setLastEditedQueryId: () => null,
setLastEditedQueryName: () => null,
setLastEditedQueryDescription: () => null,
Expand All @@ -67,12 +75,14 @@ const initialState = {
setLastEditedQueryDiscardData: () => null,
setSelectedOsqueryTable: () => null,
setSelectedQueryTargets: () => null,
setSelectedQueryTargetsByType: () => null,
};

const actions = {
SET_SELECTED_OSQUERY_TABLE: "SET_SELECTED_OSQUERY_TABLE",
SET_LAST_EDITED_QUERY_INFO: "SET_LAST_EDITED_QUERY_INFO",
SET_SELECTED_QUERY_TARGETS: "SET_SELECTED_QUERY_TARGETS",
SET_SELECTED_QUERY_TARGETS_BY_TYPE: "SET_SELECTED_QUERY_TARGETS_BY_TYPE",
} as const;

const reducer = (state: InitialStateType, action: any) => {
Expand Down Expand Up @@ -136,6 +146,14 @@ const reducer = (state: InitialStateType, action: any) => {
? state.selectedQueryTargets
: action.selectedQueryTargets,
};
case actions.SET_SELECTED_QUERY_TARGETS_BY_TYPE:
return {
...state,
selectedQueryTargetsByType:
typeof action.selectedQueryTargetsByType === "undefined"
? state.selectedQueryTargetsByType
: action.selectedQueryTargetsByType,
};
default:
return state;
}
Expand All @@ -159,6 +177,7 @@ const QueryProvider = ({ children }: Props) => {
lastEditedQueryLoggingType: state.lastEditedQueryLoggingType,
lastEditedQueryDiscardData: state.lastEditedQueryDiscardData,
selectedQueryTargets: state.selectedQueryTargets,
selectedQueryTargetsByType: state.selectedQueryTargetsByType,
setLastEditedQueryId: (lastEditedQueryId: number | null) => {
dispatch({
type: actions.SET_LAST_EDITED_QUERY_INFO,
Expand Down Expand Up @@ -229,6 +248,14 @@ const QueryProvider = ({ children }: Props) => {
selectedQueryTargets,
});
},
setSelectedQueryTargetsByType: (
selectedQueryTargetsByType: ISelectedTargetsByType
) => {
dispatch({
type: actions.SET_SELECTED_QUERY_TARGETS_BY_TYPE,
selectedQueryTargetsByType,
});
},
setSelectedOsqueryTable: (tableName: string) => {
dispatch({ type: actions.SET_SELECTED_OSQUERY_TABLE, tableName });
},
Expand Down
4 changes: 2 additions & 2 deletions frontend/hooks/useQueryTargets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { filter, uniqueId } from "lodash";
import { IHost } from "interfaces/host";
import { ILabel } from "interfaces/label";
import { ITeam } from "interfaces/team";
import { ISelectedTargets } from "interfaces/target";
import { ISelectedTargetsForApi } from "interfaces/target";
import targetsAPI from "services/entities/targets";

export interface ITargetsLabels {
Expand All @@ -25,7 +25,7 @@ export interface ITargetsQueryKey {
scope: string;
query: string;
queryId: number | null;
selected: ISelectedTargets;
selected: ISelectedTargetsForApi;
includeLabels: boolean;
}

Expand Down
14 changes: 13 additions & 1 deletion frontend/interfaces/target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,18 @@ export interface ISelectTeam extends ITeam {

export type ISelectTargetsEntity = ISelectHost | ISelectLabel | ISelectTeam;

export interface ISelectedTargets {
export interface ISelectedTargetsForApi {
hosts: number[];
labels: number[];
teams: number[];
}

export interface ISelectedTargetsByType {
hosts: IHost[];
labels: ILabel[];
teams: ITeam[];
}

export interface IPackTargets {
host_ids: (number | string)[];
label_ids: (number | string)[];
Expand All @@ -52,3 +58,9 @@ export interface IPackTargets {

// TODO: Also use for testing
export const DEFAULT_TARGETS: ITarget[] = [];

export const DEFAULT_TARGETS_BY_TYPE: ISelectedTargetsByType = {
hosts: [],
labels: [],
teams: [],
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import queryAPI from "services/entities/queries";
import teamAPI, { ILoadTeamsResponse } from "services/entities/teams";
import { AppContext } from "context/app";
import { PolicyContext } from "context/policy";
import { QueryContext } from "context/query";
import { NotificationContext } from "context/notification";
import {
IHost,
Expand All @@ -26,6 +27,7 @@ import { ILabel } from "interfaces/label";
import { IHostPolicy } from "interfaces/policy";
import { IQueryStats } from "interfaces/query_stats";
import { ISoftware } from "interfaces/software";
import { DEFAULT_TARGETS_BY_TYPE } from "interfaces/target";
import { ITeam } from "interfaces/team";
import {
IListQueriesResponse,
Expand All @@ -45,6 +47,7 @@ import {
TAGGED_TEMPLATES,
} from "utilities/helpers";
import permissions from "utilities/permissions";
import { DEFAULT_QUERY } from "utilities/constants";

import HostSummaryCard from "../cards/HostSummary";
import AboutCard from "../cards/About";
Expand Down Expand Up @@ -133,6 +136,7 @@ const HostDetailsPage = ({
setLastEditedQueryCritical,
setPolicyTeamId,
} = useContext(PolicyContext);
const { setSelectedQueryTargetsByType } = useContext(QueryContext);
const { renderFlash } = useContext(NotificationContext);

const handlePageError = useErrorHandler();
Expand Down Expand Up @@ -519,12 +523,15 @@ const HostDetailsPage = ({
};

const onQueryHostCustom = () => {
setLastEditedQueryBody(DEFAULT_QUERY.query);
setSelectedQueryTargetsByType(DEFAULT_TARGETS_BY_TYPE);
router.push(
PATHS.NEW_QUERY() + TAGGED_TEMPLATES.queryByHostRoute(host?.id)
);
};

const onQueryHostSaved = (selectedQuery: ISchedulableQuery) => {
setSelectedQueryTargetsByType(DEFAULT_TARGETS_BY_TYPE);
router.push(
PATHS.EDIT_QUERY(selectedQuery.id) +
TAGGED_TEMPLATES.queryByHostRoute(host?.id)
Expand Down
16 changes: 15 additions & 1 deletion frontend/pages/queries/ManageQueriesPage/ManageQueriesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useQuery } from "react-query";
import { pick } from "lodash";

import { AppContext } from "context/app";
import { QueryContext } from "context/query";
import { TableContext } from "context/table";
import { NotificationContext } from "context/notification";
import { performanceIndicator } from "utilities/helpers";
Expand All @@ -20,8 +21,10 @@ import {
IQueryKeyQueriesLoadAll,
ISchedulableQuery,
} from "interfaces/schedulable_query";
import { DEFAULT_TARGETS_BY_TYPE } from "interfaces/target";
import queriesAPI from "services/entities/queries";
import PATHS from "router/paths";
import { DEFAULT_QUERY } from "utilities/constants";
import { checkPlatformCompatibility } from "utilities/sql_tools";
import Button from "components/buttons/Button";
import Spinner from "components/Spinner";
Expand Down Expand Up @@ -87,6 +90,9 @@ const ManageQueriesPage = ({
isSandboxMode,
config,
} = useContext(AppContext);
const { setLastEditedQueryBody, setSelectedQueryTargetsByType } = useContext(
QueryContext
);

const { setResetSelectedRows } = useContext(TableContext);
const { renderFlash } = useContext(NotificationContext);
Expand Down Expand Up @@ -178,7 +184,15 @@ const ManageQueriesPage = ({
}
}, [location, filteredQueriesPath, setFilteredQueriesPath]);

const onCreateQueryClick = () => router.push(PATHS.NEW_QUERY(currentTeamId));
// Reset selected targets when returned to this page
useEffect(() => {
setSelectedQueryTargetsByType(DEFAULT_TARGETS_BY_TYPE);
}, []);

const onCreateQueryClick = () => {
setLastEditedQueryBody(DEFAULT_QUERY.query);
router.push(PATHS.NEW_QUERY(currentTeamId));
};

const toggleDeleteQueryModal = useCallback(() => {
setShowDeleteQueryModal(!showDeleteQueryModal);
Expand Down
12 changes: 5 additions & 7 deletions frontend/pages/queries/edit/EditQueryPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ const EditQueryPage = ({
lastEditedQueryPlatforms,
lastEditedQueryLoggingType,
lastEditedQueryMinOsqueryVersion,
selectedQueryTargets,
setLastEditedQueryId,
setLastEditedQueryName,
setLastEditedQueryDescription,
Expand All @@ -89,14 +88,10 @@ const EditQueryPage = ({
setLastEditedQueryLoggingType,
setLastEditedQueryMinOsqueryVersion,
setLastEditedQueryPlatforms,
// setSelectedQueryTargets,
} = useContext(QueryContext);
const { setConfig } = useContext(AppContext);
const { renderFlash } = useContext(NotificationContext);

// const [queryParamHostsAdded, setQueryParamHostsAdded] = useState(false);
// const [targetedHosts, setTargetedHosts] = useState<IHost[]>([]);

const [isLiveQueryRunnable, setIsLiveQueryRunnable] = useState(true);
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
const [showOpenSchemaActionText, setShowOpenSchemaActionText] = useState(
Expand Down Expand Up @@ -158,7 +153,7 @@ const EditQueryPage = ({
setLastEditedQueryId(DEFAULT_QUERY.id);
setLastEditedQueryName(DEFAULT_QUERY.name);
setLastEditedQueryDescription(DEFAULT_QUERY.description);
setLastEditedQueryBody(DEFAULT_QUERY.query);
// Persist lastEditedQueryBody through live query flow instead of resetting to DEFAULT_QUERY.query
setLastEditedQueryObserverCanRun(DEFAULT_QUERY.observer_can_run);
setLastEditedQueryFrequency(DEFAULT_QUERY.interval);
setLastEditedQueryLoggingType(DEFAULT_QUERY.logging);
Expand Down Expand Up @@ -304,7 +299,10 @@ const EditQueryPage = ({
<div className={`${baseClass}_wrapper`}>
<div className={`${baseClass}__form`}>
<div className={`${baseClass}__header-links`}>
<BackLink text="Back to report" path={backToQueriesPath()} />
<BackLink
text={queryId ? "Back to report" : "Back to queries"}
path={backToQueriesPath()}
/>
</div>
<EditQueryForm
router={router}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -598,11 +598,10 @@ const EditQueryForm = ({
className={`${baseClass}__run`}
variant="blue-green"
onClick={() => {
queryIdForEdit &&
router.push(
PATHS.LIVE_QUERY(queryIdForEdit) +
TAGGED_TEMPLATES.queryByHostRoute(hostId)
);
router.push(
PATHS.LIVE_QUERY(queryIdForEdit) +
TAGGED_TEMPLATES.queryByHostRoute(hostId)
);
}}
>
Live query
Expand Down Expand Up @@ -819,11 +818,10 @@ const EditQueryForm = ({
className={`${baseClass}__run`}
variant="blue-green"
onClick={() => {
queryIdForEdit &&
router.push(
PATHS.LIVE_QUERY(queryIdForEdit) +
TAGGED_TEMPLATES.queryByHostRoute(hostId)
);
router.push(
PATHS.LIVE_QUERY(queryIdForEdit) +
TAGGED_TEMPLATES.queryByHostRoute(hostId)
);
}}
>
Live query
Expand Down
Loading

0 comments on commit f8260e4

Please sign in to comment.