29762: Fixed bug with run script modal on FreeTier. (#30138)

For #29762 

When running on FreeTier do not apply teamId criteria on end-point used by the Run Script modal.
This commit is contained in:
Juan Fernandez
2025-06-23 13:03:22 -04:00
committed by GitHub
parent 81abc49786
commit e7519eef48
5 changed files with 45 additions and 21 deletions
+1
View File
@@ -0,0 +1 @@
* Fixed bug with the run script modal on the Hosts page when running under FreeTier due to invalid teamId filter.
+6
View File
@@ -11,6 +11,12 @@ export interface IScript {
export const isScriptSupportedPlatform = (hostPlatform: string) =>
["darwin", "windows", ...HOST_LINUX_PLATFORMS].includes(hostPlatform); // excludes chrome, ios, ipados, android see also https://github.com/fleetdm/fleet/blob/5a21e2cfb029053ddad0508869eb9f1f23997bf2/server/fleet/hosts.go#L775
export const addTeamIdCriteria = (
pred: any,
teamId: number,
isFreeTier?: boolean
) => (isFreeTier ? { ...pred } : { ...pred, team_id: teamId });
export type IScriptExecutionStatus = "ran" | "pending" | "error";
export interface ILastExecution {
@@ -2004,18 +2004,19 @@ const ManageHostsPage = ({
totalFilteredHostsCount !== undefined && (
<RunScriptBatchModal
runByFilters={isAllMatchingHostsSelected}
// run script batch supports only these filters, plust team id
// run script batch supports only these filters, plus team id
filters={{
query: searchQuery || undefined,
label_id: isNaN(Number(labelID)) ? undefined : Number(labelID),
status: status || undefined,
}}
// when running by filter, modal needs this count to report number of targeted hosts
// when running by filter, modal needs this count to report the number of targeted hosts
totalFilteredHostsCount={totalFilteredHostsCount}
// when running by selected hosts, modal can use length of this array to report number of targeted
// when running by selected hosts, modal can use the length of this array to report the number of targeted
// hosts
selectedHostIds={selectedHostIds}
teamId={currentTeamId}
isFreeTier={isFreeTier}
onCancel={toggleRunScriptBatchModal}
/>
)}
@@ -5,7 +5,7 @@ import classnames from "classnames";
import { NotificationContext } from "context/notification";
import { IScript } from "interfaces/script";
import { addTeamIdCriteria, IScript } from "interfaces/script";
import { getErrorReason } from "interfaces/errors";
import { DEFAULT_USE_QUERY_OPTIONS } from "utilities/constants";
@@ -33,6 +33,8 @@ interface IRunScriptBatchModal {
// `filters` as needed
filters: Omit<IScriptBatchSupportedFilters, "team_id">;
teamId: number;
// If we are on the free tier, we don't want to apply any kind of team filters (since the feature is Premium only).
isFreeTier?: boolean;
totalFilteredHostsCount: number;
selectedHostIds: number[];
onCancel: () => void;
@@ -44,6 +46,7 @@ const RunScriptBatchModal = ({
totalFilteredHostsCount,
selectedHostIds,
teamId,
isFreeTier,
onCancel,
}: IRunScriptBatchModal) => {
const { renderFlash } = useContext(NotificationContext);
@@ -52,19 +55,14 @@ const RunScriptBatchModal = ({
const [scriptForDetails, setScriptForDetails] = useState<
IPaginatedListScript | undefined
>(undefined);
// just used to get total number of scripts, could be optimized by implementing a dedicated scriptsCount endpoint
// just used to get the total number of scripts, could be optimized by implementing a dedicated scriptsCount endpoint
const { data: scripts } = useQuery<
IScriptsResponse,
Error,
IScript[],
IListScriptsQueryKey[]
>(
[
{
scope: "scripts",
team_id: teamId,
},
],
[addTeamIdCriteria({ scope: "scripts" }, teamId, isFreeTier)],
({ queryKey }) => {
return scriptsAPI.getScripts(queryKey[0]);
},
@@ -80,7 +78,10 @@ const RunScriptBatchModal = ({
setIsUpdating(true);
const body = runByFilters
? // satisfy IScriptBatchSupportedFilters
{ script_id: script.id, filters: { ...filters, team_id: teamId } }
{
script_id: script.id,
filters: addTeamIdCriteria(filters, teamId, isFreeTier),
}
: { script_id: script.id, host_ids: selectedHostIds };
try {
await scriptsAPI.runScriptBatch(body);
@@ -118,7 +119,16 @@ const RunScriptBatchModal = ({
info={
<>
You can add saved scripts{" "}
<a href={`/controls/scripts?team_id=${teamId}`}>here</a>.
<a
href={
isFreeTier
? "/controls/scripts"
: `/controls/scripts?team_id=${teamId}`
}
>
here
</a>
.
</>
}
/>
@@ -140,6 +150,7 @@ const RunScriptBatchModal = ({
onRunScript={onRunScriptBatch}
isUpdating={isUpdating}
teamId={teamId}
isFreeTier={isFreeTier}
scriptCount={scripts.length}
setScriptForDetails={setScriptForDetails}
/>
@@ -3,7 +3,7 @@ import { useQueryClient } from "react-query";
import scriptAPI, { IScriptsResponse } from "services/entities/scripts";
import { IScript } from "interfaces/script";
import { addTeamIdCriteria, IScript } from "interfaces/script";
import PaginatedList from "components/PaginatedList";
import Button from "components/buttons/Button";
@@ -19,6 +19,7 @@ interface IRunScriptBatchPaginatedList {
onRunScript: (script: IPaginatedListScript) => Promise<void>;
isUpdating: boolean;
teamId: number;
isFreeTier?: boolean;
scriptCount: number;
setScriptForDetails: (script: IPaginatedListScript) => void;
}
@@ -28,6 +29,7 @@ export const SCRIPT_BATCH_PAGE_SIZE = 6;
const RunScriptBatchPaginatedList = ({
onRunScript: _onRunScript,
isUpdating,
isFreeTier,
teamId,
scriptCount,
setScriptForDetails,
@@ -40,12 +42,15 @@ const RunScriptBatchPaginatedList = ({
// scripts not supported for All teams
const fetchPromise = queryClient.fetchQuery(
[
{
scope: "scripts",
team_id: teamId,
page: pageNumber,
per_page: SCRIPT_BATCH_PAGE_SIZE,
},
addTeamIdCriteria(
{
scope: "scripts",
page: pageNumber,
per_page: SCRIPT_BATCH_PAGE_SIZE,
},
teamId,
isFreeTier
),
],
({ queryKey }) => {
return scriptAPI.getScripts(queryKey[0]);
@@ -56,7 +61,7 @@ const RunScriptBatchPaginatedList = ({
return scripts || [];
});
},
[queryClient, teamId]
[queryClient, teamId, isFreeTier]
);
const onRunScript = useCallback(