fleet/frontend/interfaces/schedulable_query.ts
Nico 2e70ad2955
Surface queries in host details (#37646)
<!-- 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"
/>
2026-01-02 10:06:12 -03:00

176 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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";