mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 21:47:20 +00:00
<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #27322 [Figma](https://www.figma.com/design/v7WjL5zQuFIZerWYaSwy8o/-27322-Surface-custom-host-vitals?node-id=5636-4950&t=LuE3Kp09a5sj24Tt-0) ## Testing - [x] Added/updated automated tests - [ ] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [x] QA'd all new/changed functionality manually (WIP) ## Screenshots ### Host details <img width="1481" height="1000" alt="Screenshot 2025-12-26 at 2 14 48 PM" src="https://github.com/user-attachments/assets/3d9f02f9-f3a7-4a06-b3e4-414bb7b56e25" /> - `Queries` tab removed. - Shows `Queries` card. #### Queries Card - Added client-side pagination. - Added `Add query` button (screenshots below are with `Admin` role). <img width="710" height="395" alt="Screenshot 2025-12-26 at 2 15 07 PM" src="https://github.com/user-attachments/assets/b4e58269-d1b2-4c87-abfa-2cdfe47b533e" /> <img width="723" height="301" alt="Screenshot 2025-12-26 at 2 15 00 PM" src="https://github.com/user-attachments/assets/2615d5bf-5d75-4e83-bc69-bc884232bf32" /> - As an `Observer`, `Add query` is not displayed <img width="2240" height="1077" alt="Screenshot 2025-12-26 at 2 27 25 PM" src="https://github.com/user-attachments/assets/426de709-d2ce-4bef-96f1-919ad5bddb13" /> - As a `Maintainer`, `Add query` is displayed <img width="2236" height="1084" alt="Screenshot 2025-12-26 at 2 31 16 PM" src="https://github.com/user-attachments/assets/218b0d18-2536-4336-88c8-41e7d09a5e9e" /> ### New query page If the user navigates from `Host details`, `host_id` search parameter is added to the URL and the back button displays `Back to host details`. <img width="1097" height="506" alt="Screenshot 2025-12-26 at 2 15 32 PM" src="https://github.com/user-attachments/assets/61777c85-22f5-49dc-a3e6-dcd706119c70" /> ### Host Queries (/hosts/:hostId/queries/:queryId) `Performance impact` added above the table. <img width="2029" height="626" alt="Screenshot 2025-12-26 at 2 16 00 PM" src="https://github.com/user-attachments/assets/05c6b1bc-0587-4b0a-8167-142787592c6d" /> <img width="1555" height="482" alt="Screenshot 2025-12-26 at 2 16 05 PM" src="https://github.com/user-attachments/assets/b9035b63-51c3-46c0-a903-c16d54c22986" />
176 lines
5.1 KiB
TypeScript
176 lines
5.1 KiB
TypeScript
// for legacy legacy query stats interface
|
||
import PropTypes from "prop-types";
|
||
|
||
import { IFormField } from "./form_field";
|
||
import { IPack } from "./pack";
|
||
import {
|
||
CommaSeparatedPlatformString,
|
||
QueryablePlatform,
|
||
SelectedPlatform,
|
||
} from "./platform";
|
||
import { ILabelQuery } from "./label";
|
||
|
||
// Query itself
|
||
export interface ISchedulableQuery {
|
||
created_at: string;
|
||
updated_at: string;
|
||
id: number;
|
||
name: string;
|
||
description: string;
|
||
query: string;
|
||
team_id: number | null;
|
||
interval: number;
|
||
platform: CommaSeparatedPlatformString; // Might more accurately be called `platforms_to_query` or `targeted_platforms` – comma-separated string of platforms to query, default all platforms if omitted
|
||
min_osquery_version: string;
|
||
automations_enabled: boolean;
|
||
logging: QueryLoggingOption;
|
||
saved: boolean;
|
||
author_id: number;
|
||
author_name: string;
|
||
author_email: string;
|
||
observer_can_run: boolean;
|
||
discard_data: boolean;
|
||
packs: IPack[];
|
||
stats: ISchedulableQueryStats;
|
||
editingExistingQuery?: boolean;
|
||
labels_include_any?: ILabelQuery[];
|
||
}
|
||
|
||
export interface IEnhancedQuery extends ISchedulableQuery {
|
||
performance: PerformanceImpactIndicator;
|
||
targetedPlatforms: QueryablePlatform[];
|
||
}
|
||
export interface ISchedulableQueryStats {
|
||
user_time_p50?: number | null;
|
||
user_time_p95?: number | null;
|
||
system_time_p50?: number | null;
|
||
system_time_p95?: number | null;
|
||
total_executions?: number;
|
||
}
|
||
|
||
export const PerformanceImpactIndicatorValue = {
|
||
MINIMAL: "Minimal",
|
||
CONSIDERABLE: "Considerable",
|
||
EXCESSIVE: "Excessive",
|
||
UNDETERMINED: "Undetermined",
|
||
DENYLISTED: "Denylisted",
|
||
} as const;
|
||
|
||
export type PerformanceImpactIndicator = typeof PerformanceImpactIndicatorValue[keyof typeof PerformanceImpactIndicatorValue];
|
||
|
||
export const isPerformanceImpactIndicator = (
|
||
value: unknown
|
||
): value is PerformanceImpactIndicator => {
|
||
return Object.values(PerformanceImpactIndicatorValue).includes(
|
||
value as PerformanceImpactIndicator
|
||
);
|
||
};
|
||
|
||
// legacy
|
||
export default PropTypes.shape({
|
||
user_time_p50: PropTypes.number,
|
||
user_time_p95: PropTypes.number,
|
||
system_time_p50: PropTypes.number,
|
||
system_time_p95: PropTypes.number,
|
||
total_executions: PropTypes.number,
|
||
});
|
||
|
||
// API shapes
|
||
|
||
// Get a query by id
|
||
/** GET /api/v1/fleet/queries/{id}` */
|
||
export interface IGetQueryResponse {
|
||
query: ISchedulableQuery;
|
||
}
|
||
|
||
// List global or team queries
|
||
/** GET /api/v1/fleet/queries?order_key={column_from_queries_table}&order_direction={asc|desc}&team_id={team_id} */
|
||
export interface IListQueriesResponse {
|
||
queries: ISchedulableQuery[];
|
||
}
|
||
|
||
export interface IQueryKeyQueriesLoadAll {
|
||
scope: "queries";
|
||
teamId?: number;
|
||
page?: number;
|
||
perPage?: number;
|
||
query?: string;
|
||
orderDirection?: "asc" | "desc";
|
||
orderKey?: string;
|
||
mergeInherited?: boolean;
|
||
targetedPlatform?: SelectedPlatform;
|
||
}
|
||
// Create a new query
|
||
/** POST /api/v1/fleet/queries */
|
||
export interface ICreateQueryRequestBody {
|
||
name: string;
|
||
query: string;
|
||
description?: string;
|
||
observer_can_run?: boolean;
|
||
discard_data?: boolean;
|
||
team_id?: number; // global query if undefined
|
||
interval?: number; // default 0 means never run
|
||
platform?: CommaSeparatedPlatformString; // Might more accurately be called `platforms_to_query` – comma-separated string of platforms to query, default all platforms if omitted
|
||
min_osquery_version?: string; // default all versions if ommitted
|
||
automations_enabled?: boolean; // whether to send data to the configured log destination according to the query's `interval`. Default false if ommitted.
|
||
logging?: QueryLoggingOption;
|
||
labels_include_any?: string[];
|
||
}
|
||
|
||
// response is ISchedulableQuery
|
||
|
||
// Modify a query by id
|
||
/** PATCH /api/v1/fleet/queries/{id} */
|
||
export interface IModifyQueryRequestBody
|
||
extends Omit<ICreateQueryRequestBody, "name" | "query" | "team_id"> {
|
||
id?: number;
|
||
name?: string;
|
||
query?: string;
|
||
description?: string;
|
||
observer_can_run?: boolean;
|
||
discard_data?: boolean;
|
||
frequency?: number;
|
||
platform?: CommaSeparatedPlatformString;
|
||
min_osquery_version?: string;
|
||
automations_enabled?: boolean;
|
||
}
|
||
|
||
// response is ISchedulableQuery // better way to indicate this?
|
||
|
||
// Delete a query by name
|
||
/** DELETE /api/v1/fleet/queries/{name} */
|
||
export interface IDeleteQueryRequestBody {
|
||
team_id?: number; // searches for a global query if omitted
|
||
}
|
||
|
||
// Delete a query by id
|
||
// DELETE /api/v1/fleet/queries/id/{id}
|
||
// (no body)
|
||
|
||
// Delete queries by id
|
||
/** POST /api/v1/fleet/queries/delete */
|
||
export interface IDeleteQueriesRequestBody {
|
||
ids: number[];
|
||
}
|
||
|
||
export interface IDeleteQueriesResponse {
|
||
deleted: number; // number of queries deleted
|
||
}
|
||
|
||
export interface IEditQueryFormFields {
|
||
name: IFormField<string>;
|
||
description: IFormField<string>;
|
||
query: IFormField<string>;
|
||
observer_can_run: IFormField<boolean>;
|
||
discard_data: IFormField<boolean>;
|
||
frequency: IFormField<number>;
|
||
automations_enabled: IFormField<boolean>;
|
||
platforms: IFormField<CommaSeparatedPlatformString>;
|
||
min_osquery_version: IFormField<string>;
|
||
logging: IFormField<QueryLoggingOption>;
|
||
}
|
||
|
||
export type QueryLoggingOption =
|
||
| "snapshot"
|
||
| "differential"
|
||
| "differential_ignore_removals";
|