Fleet UI: Can run a live query on an edited (but not saved) existing query (#16282)

This commit is contained in:
RachelElysia 2024-01-25 13:12:59 -05:00 committed by GitHub
parent c069a446fd
commit aa60187aa1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 28 additions and 4 deletions

View File

@ -0,0 +1 @@
- Ability to run a live query on an edited existing query before saving

View File

@ -27,6 +27,7 @@ const DEFAULT_QUERY_MOCK: ISchedulableQuery = {
system_time_p95: 1, system_time_p95: 1,
total_executions: 6, total_executions: 6,
}, },
editingExistingQuery: false,
}; };
const createMockQuery = ( const createMockQuery = (

View File

@ -29,6 +29,7 @@ const DEFAULT_SCHEDULABLE_QUERY_MOCK: ISchedulableQuery = {
user_time_p95: 251.4615, user_time_p95: 251.4615,
total_executions: 5746, total_executions: 5746,
}, },
editingExistingQuery: false,
}; };
const createMockSchedulableQuery = ( const createMockSchedulableQuery = (

View File

@ -29,6 +29,7 @@ type InitialStateType = {
lastEditedQueryMinOsqueryVersion: string; lastEditedQueryMinOsqueryVersion: string;
lastEditedQueryLoggingType: QueryLoggingOption; lastEditedQueryLoggingType: QueryLoggingOption;
lastEditedQueryDiscardData: boolean; lastEditedQueryDiscardData: boolean;
editingExistingQuery: boolean;
selectedQueryTargets: ITarget[]; // Mimicks old selectedQueryTargets still used for policies for SelectTargets.tsx and running a live query 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 selectedQueryTargetsByType: ISelectedTargetsByType; // New format by type for cleaner app wide state
setLastEditedQueryId: (value: number | null) => void; setLastEditedQueryId: (value: number | null) => void;
@ -44,6 +45,7 @@ type InitialStateType = {
setSelectedOsqueryTable: (tableName: string) => void; setSelectedOsqueryTable: (tableName: string) => void;
setSelectedQueryTargets: (value: ITarget[]) => void; setSelectedQueryTargets: (value: ITarget[]) => void;
setSelectedQueryTargetsByType: (value: ISelectedTargetsByType) => void; setSelectedQueryTargetsByType: (value: ISelectedTargetsByType) => void;
setEditingExistingQuery: (value: boolean) => void;
}; };
export type IQueryContext = InitialStateType; export type IQueryContext = InitialStateType;
@ -61,6 +63,7 @@ const initialState = {
lastEditedQueryMinOsqueryVersion: DEFAULT_QUERY.min_osquery_version, lastEditedQueryMinOsqueryVersion: DEFAULT_QUERY.min_osquery_version,
lastEditedQueryLoggingType: DEFAULT_QUERY.logging, lastEditedQueryLoggingType: DEFAULT_QUERY.logging,
lastEditedQueryDiscardData: DEFAULT_QUERY.discard_data, lastEditedQueryDiscardData: DEFAULT_QUERY.discard_data,
editingExistingQuery: DEFAULT_QUERY.editingExistingQuery,
selectedQueryTargets: DEFAULT_TARGETS, selectedQueryTargets: DEFAULT_TARGETS,
selectedQueryTargetsByType: DEFAULT_TARGETS_BY_TYPE, selectedQueryTargetsByType: DEFAULT_TARGETS_BY_TYPE,
setLastEditedQueryId: () => null, setLastEditedQueryId: () => null,
@ -76,6 +79,7 @@ const initialState = {
setSelectedOsqueryTable: () => null, setSelectedOsqueryTable: () => null,
setSelectedQueryTargets: () => null, setSelectedQueryTargets: () => null,
setSelectedQueryTargetsByType: () => null, setSelectedQueryTargetsByType: () => null,
setEditingExistingQuery: () => null,
}; };
const actions = { const actions = {
@ -137,6 +141,10 @@ const reducer = (state: InitialStateType, action: any) => {
typeof action.lastEditedQueryDiscardData === "undefined" typeof action.lastEditedQueryDiscardData === "undefined"
? state.lastEditedQueryDiscardData ? state.lastEditedQueryDiscardData
: action.lastEditedQueryDiscardData, : action.lastEditedQueryDiscardData,
editingExistingQuery:
typeof action.editingExistingQuery === "undefined"
? state.editingExistingQuery
: action.editingExistingQuery,
}; };
case actions.SET_SELECTED_QUERY_TARGETS: case actions.SET_SELECTED_QUERY_TARGETS:
return { return {
@ -176,6 +184,7 @@ const QueryProvider = ({ children }: Props) => {
lastEditedQueryMinOsqueryVersion: state.lastEditedQueryMinOsqueryVersion, lastEditedQueryMinOsqueryVersion: state.lastEditedQueryMinOsqueryVersion,
lastEditedQueryLoggingType: state.lastEditedQueryLoggingType, lastEditedQueryLoggingType: state.lastEditedQueryLoggingType,
lastEditedQueryDiscardData: state.lastEditedQueryDiscardData, lastEditedQueryDiscardData: state.lastEditedQueryDiscardData,
editingExistingQuery: state.editingExistingQuery,
selectedQueryTargets: state.selectedQueryTargets, selectedQueryTargets: state.selectedQueryTargets,
selectedQueryTargetsByType: state.selectedQueryTargetsByType, selectedQueryTargetsByType: state.selectedQueryTargetsByType,
setLastEditedQueryId: (lastEditedQueryId: number | null) => { setLastEditedQueryId: (lastEditedQueryId: number | null) => {
@ -242,6 +251,12 @@ const QueryProvider = ({ children }: Props) => {
lastEditedQueryDiscardData, lastEditedQueryDiscardData,
}); });
}, },
setEditingExistingQuery: (editingExistingQuery: boolean) => {
dispatch({
type: actions.SET_LAST_EDITED_QUERY_INFO,
editingExistingQuery,
});
},
setSelectedQueryTargets: (selectedQueryTargets: ITarget[]) => { setSelectedQueryTargets: (selectedQueryTargets: ITarget[]) => {
dispatch({ dispatch({
type: actions.SET_SELECTED_QUERY_TARGETS, type: actions.SET_SELECTED_QUERY_TARGETS,

View File

@ -24,6 +24,7 @@ export interface ISchedulableQuery {
discard_data: boolean; discard_data: boolean;
packs: IPack[]; packs: IPack[];
stats: ISchedulableQueryStats; stats: ISchedulableQueryStats;
editingExistingQuery: boolean;
} }
export interface IEnhancedQuery extends ISchedulableQuery { export interface IEnhancedQuery extends ISchedulableQuery {

View File

@ -71,6 +71,7 @@ const EditQueryPage = ({
config, config,
} = useContext(AppContext); } = useContext(AppContext);
const { const {
editingExistingQuery,
selectedOsqueryTable, selectedOsqueryTable,
setSelectedOsqueryTable, setSelectedOsqueryTable,
lastEditedQueryName, lastEditedQueryName,
@ -127,7 +128,7 @@ const EditQueryPage = ({
["query", queryId], ["query", queryId],
() => queryAPI.load(queryId as number), () => queryAPI.load(queryId as number),
{ {
enabled: !!queryId, enabled: !!queryId && !editingExistingQuery,
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
select: (data) => data.query, select: (data) => data.query,
onSuccess: (returnedQuery) => { onSuccess: (returnedQuery) => {

View File

@ -150,6 +150,7 @@ const EditQueryForm = ({
setLastEditedQueryMinOsqueryVersion, setLastEditedQueryMinOsqueryVersion,
setLastEditedQueryLoggingType, setLastEditedQueryLoggingType,
setLastEditedQueryDiscardData, setLastEditedQueryDiscardData,
setEditingExistingQuery,
} = useContext(QueryContext); } = useContext(QueryContext);
const { const {
@ -825,6 +826,7 @@ const EditQueryForm = ({
className={`${baseClass}__run`} className={`${baseClass}__run`}
variant="blue-green" variant="blue-green"
onClick={() => { onClick={() => {
setEditingExistingQuery(true); // Persists edited query data through live query flow
router.push( router.push(
PATHS.LIVE_QUERY(queryIdForEdit) + PATHS.LIVE_QUERY(queryIdForEdit) +
TAGGED_TEMPLATES.queryByHostRoute(hostId) TAGGED_TEMPLATES.queryByHostRoute(hostId)

View File

@ -44,6 +44,7 @@ const RunQueryPage = ({
const handlePageError = useErrorHandler(); const handlePageError = useErrorHandler();
const { config } = useContext(AppContext); const { config } = useContext(AppContext);
const { const {
editingExistingQuery,
selectedQueryTargets, selectedQueryTargets,
setSelectedQueryTargets, setSelectedQueryTargets,
selectedQueryTargetsByType, selectedQueryTargetsByType,
@ -88,7 +89,7 @@ const RunQueryPage = ({
Error, Error,
ISchedulableQuery ISchedulableQuery
>(["query", queryId], () => queryAPI.load(queryId as number), { >(["query", queryId], () => queryAPI.load(queryId as number), {
enabled: !!queryId, enabled: !!queryId && !editingExistingQuery,
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
select: (data) => data.query, select: (data) => data.query,
onSuccess: (returnedQuery) => { onSuccess: (returnedQuery) => {

View File

@ -47,6 +47,8 @@ const RunQuery = ({
DEFAULT_CAMPAIGN_STATE DEFAULT_CAMPAIGN_STATE
); );
const isStoredQueryEdited = storedQuery?.query !== lastEditedQueryBody;
const ws = useRef(null); const ws = useRef(null);
const runQueryInterval = useRef<any>(null); const runQueryInterval = useRef<any>(null);
const globalSocket = useRef<any>(null); const globalSocket = useRef<any>(null);
@ -168,8 +170,6 @@ const RunQuery = ({
destroyCampaign(); destroyCampaign();
try { try {
const isStoredQueryEdited = storedQuery?.query !== lastEditedQueryBody;
const returnedCampaign = await queryAPI.run({ const returnedCampaign = await queryAPI.run({
query: lastEditedQueryBody, query: lastEditedQueryBody,
queryId: isStoredQueryEdited ? null : queryId, // we treat edited SQL as a new query queryId: isStoredQueryEdited ? null : queryId, // we treat edited SQL as a new query

View File

@ -132,6 +132,7 @@ export const DEFAULT_QUERY: ISchedulableQuery = {
team_id: 0, team_id: 0,
author_email: "", author_email: "",
stats: {}, stats: {},
editingExistingQuery: false,
}; };
export const DEFAULT_CAMPAIGN = { export const DEFAULT_CAMPAIGN = {