import { IHostScript, IScript, ScriptBatchHostStatus, ScriptBatchStatus, } from "interfaces/script"; import sendRequest from "services"; import { createMockBatchScriptSummary, createMockScriptBatchHostResults, } from "__mocks__/scriptMock"; import endpoints from "utilities/endpoints"; import { buildQueryStringFromParams } from "utilities/url"; import { ListEntitiesResponseCommon, OrderDirection, PaginationParams, } from "./common"; /** Single script response from GET /script/:id */ export type IScriptResponse = IScript; /** All scripts response from GET /scripts */ export interface IScriptsResponse { scripts: IScript[] | null; meta: { has_next_results: boolean; has_previous_results: boolean; }; } export interface IListScriptsApiParams { page?: number; per_page?: number; team_id?: number; } export interface IListScriptsQueryKey extends IListScriptsApiParams { scope: "scripts"; } /** * Script Result response from GET /scripts/results/:id */ export interface IScriptResultResponse { hostname: string; host_id: number; execution_id: string; script_contents: string; script_id: number | null; // null for ad-hoc script run via API exit_code: number | null; output: string; message: string; runtime: number; host_timeout: boolean; created_at: string; } /** * Request params for for GET /hosts/:id/scripts */ export interface IHostScriptsRequestParams { host_id: number; page?: number; per_page?: number; } export interface IHostScriptsQueryKey extends IHostScriptsRequestParams { scope: "host_scripts"; } /** * Script response from GET /hosts/:id/scripts */ export interface IHostScriptsResponse { scripts: IHostScript[]; meta: { has_next_results: boolean; has_previous_results: boolean; }; } /** * Request body for POST /scripts/run * * https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/api-for-contributors.md#run-script-asynchronously */ export interface IScriptRunRequest { host_id: number; script_id: number; // script_id is not required by the API currently, but we require it here to ensure it is always provided // script_contents: string; // script_contents is only supported for the CLI currently } /** * Response body for POST /scripts/run * * https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/api-for-contributors.md#run-script-asynchronously */ export interface IScriptRunResponse { host_id: number; execution_id: string; } export interface IScriptBatchSupportedFilters { // a search string, not a Fleet.Query query?: string; label_id?: number; team_id?: number; status: any; // TODO - improve upstream typing } interface IRunScriptBatchRequestBase { script_id: number; not_before?: string; // ISO 8601 date-time string } interface IByFilters extends IRunScriptBatchRequestBase { host_ids?: never; filters: IScriptBatchSupportedFilters; } interface IByHostIds extends IRunScriptBatchRequestBase { host_ids: number[]; filters?: never; } /** Request body for POST /scripts/run/batch */ export type IRunScriptBatchRequest = IByFilters | IByHostIds; /** 202 successful response body for POST /scripts/run/batch */ export interface IRunScriptBatchResponse { batch_execution_id: string; } export interface IScriptBatchSummaryParams { batch_execution_id: string; } export interface IScriptBatchSummaryQueryKey extends IScriptBatchSummaryParams { scope: "script_batch_summary"; } export interface IScriptBatchHostCountsV1 { ran: number; pending: number; errored: number; canceled: number; } export type ScriptBatchHostCountV1 = keyof IScriptBatchHostCountsV1; // 200 successful response export interface IScriptBatchSummaryV1 extends IScriptBatchHostCountsV1 { team_id: number; script_name: string; created_at: string; // below fields not yet used by the UI targeted: number; script_id: number; } export interface IScriptBatchHostCountsV2 { targeted_host_count: number; ran_host_count: number; errored_host_count: number; pending_host_count: number; incompatible_host_count: number; canceled_host_count: number; } export interface IScriptBatchSummaryV2 extends IScriptBatchHostCountsV2 { batch_execution_id: string; /** ISO 8601 date-time string. When the script batch run was created (NOT when it is/was scheduled * to run by, which is represented by `not_before`. */ created_at: string; script_id: number; script_name: string; team_id: number; status: ScriptBatchStatus; canceled: boolean; /** ISO 8601 date-time string. Always present as of Fleet 4.73.0 - `null`able for backwards compatibility with older batch runs. */ not_before: string | null; // /** ISO 8601 date-time string. If present, this script batch run has started. */ started_at: string | null; /** ISO 8601 date-time string. If present, this script has completed running. */ finished_at: string | null; } export interface IScriptBatchSummariesParams { team_id: number; status: ScriptBatchStatus; page: number; per_page: number; } export interface IScriptBatchSummariesQueryKey extends IScriptBatchSummariesParams { scope: "script_batch_summaries"; } export interface IScriptBatchSummariesResponse extends ListEntitiesResponseCommon { batch_executions: IScriptBatchSummaryV2[] | null; // should not return `null`, but API currently does sometimes. Remove this option when it's fixed. } export type ScriptBatchHostsOrderKey = "display_name" | "script_executed_at"; export interface IScriptBatchHostResultsParams extends PaginationParams { batch_execution_id: string; status: ScriptBatchHostStatus; order_key: ScriptBatchHostsOrderKey; order_direction: OrderDirection; } export interface IScriptBatchHostResultsQueryKey extends IScriptBatchHostResultsParams { scope: "script_batch_host_results"; } export interface IScriptBatchHostResult { id: number; display_name: string; script_status: ScriptBatchHostStatus; script_execution_id: string | null; // if status === pending, this may be `null` or contain a value dependending on whether the script exectution is this hosts next scheduled activity on the server // /** ISO 8601 date-time string. `null` if pending, cancelled, or incompatible. */ script_executed_at: string | null; /** `null` if pending, cancelled, or incompatible. */ script_output_preview: string | null; } export interface IScriptBatchHostResultsResponse extends ListEntitiesResponseCommon { hosts: IScriptBatchHostResult[]; } export default { getHostScripts({ host_id, page, per_page }: IHostScriptsRequestParams) { const { HOST_SCRIPTS } = endpoints; const path = `${HOST_SCRIPTS(host_id)}?${buildQueryStringFromParams({ page, per_page, })}`; return sendRequest("GET", path); }, getScripts(params: IListScriptsApiParams): Promise { const { SCRIPTS } = endpoints; const path = `${SCRIPTS}?${buildQueryStringFromParams({ ...params })}`; return sendRequest("GET", path); }, getScript(id: number) { const { SCRIPT } = endpoints; return sendRequest("GET", SCRIPT(id)); }, uploadScript(file: File, teamId?: number) { const { SCRIPTS } = endpoints; const formData = new FormData(); formData.append("script", file); if (teamId) { formData.append("team_id", teamId.toString()); } return sendRequest("POST", SCRIPTS, formData); }, downloadScript(id: number) { const { SCRIPT } = endpoints; const path = `${SCRIPT(id)}?${buildQueryStringFromParams({ alt: "media", })}`; return sendRequest("GET", path); }, updateScript(id: number, contents: string, name: string) { const { SCRIPT } = endpoints; const path = `${SCRIPT(id)}`; const file = new File([contents], name); const formData = new FormData(); formData.append("script", file); return sendRequest("PATCH", path, formData); }, deleteScript(id: number) { const { SCRIPT } = endpoints; return sendRequest("DELETE", SCRIPT(id)); }, getScriptResult(executionId: string) { const { SCRIPT_RESULT } = endpoints; return sendRequest("GET", SCRIPT_RESULT(executionId)); }, runScript(request: IScriptRunRequest): Promise { const { SCRIPT_RUN } = endpoints; return sendRequest("POST", SCRIPT_RUN, request); }, runScriptBatch( request: IRunScriptBatchRequest ): Promise { const { SCRIPT_RUN_BATCH } = endpoints; return sendRequest("POST", SCRIPT_RUN_BATCH, request); }, cancelScriptBatch(batchExecutionId: string) { const { SCRIPT_CANCEL_BATCH } = endpoints; return sendRequest("POST", SCRIPT_CANCEL_BATCH(batchExecutionId)); }, /** calls the deprecated endpoint */ getRunScriptBatchSummaryV1({ batch_execution_id, }: IScriptBatchSummaryParams): Promise { return sendRequest( "GET", `${endpoints.SCRIPT_RUN_BATCH_SUMMARY_V1(batch_execution_id)}` ); }, getRunScriptBatchSummaryV2({ batch_execution_id, }: IScriptBatchSummaryParams): Promise { return sendRequest( "GET", `${endpoints.SCRIPT_RUN_BATCH_SUMMARY_V2(batch_execution_id)}` ); }, getRunScriptBatchSummaries( params: IScriptBatchSummariesParams ): Promise { const path = `${ endpoints.SCRIPT_RUN_BATCH_SUMMARIES }?${buildQueryStringFromParams({ ...params })}`; return sendRequest("GET", path); }, getScriptBatchHostResults( params: IScriptBatchHostResultsParams ): Promise { const { batch_execution_id, status, page, per_page, order_key, order_direction, } = params; const path = `${endpoints.SCRIPT_BATCH_HOST_RESULTS( batch_execution_id )}?${buildQueryStringFromParams({ status, page, per_page, order_key: order_key === "script_executed_at" ? "updated_at" : order_key, // map to server field name order_direction, })}`; return sendRequest("GET", path); }, };