From 5e898734d5c5fcd4d1d9cffaf4a02d78b94e6bd7 Mon Sep 17 00:00:00 2001 From: Bryan Clement Date: Thu, 24 Jun 2021 09:31:57 -0700 Subject: [PATCH] [Asset management] Osquery app bug squashing (#102406) * only display healthy agents to query * updated toasts to clear on update * null checking aggBuckets * properly display expired actions * clear the error toasts on success * review comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../action_results/action_results_summary.tsx | 58 ++++++++++++------- .../action_results/use_action_results.ts | 14 ++--- .../public/actions/use_action_details.ts | 10 ++-- .../osquery/public/actions/use_all_actions.ts | 10 ++-- .../agent_policies/use_agent_policies.ts | 10 ++-- .../public/agent_policies/use_agent_policy.ts | 10 ++-- .../osquery/public/agents/use_agent_groups.ts | 10 ++-- .../public/agents/use_agent_policies.ts | 10 ++-- .../osquery/public/agents/use_agent_status.ts | 10 ++-- .../osquery/public/agents/use_all_agents.ts | 25 ++++---- .../public/agents/use_osquery_policies.ts | 10 ++-- .../public/common/hooks/use_error_toast.tsx | 26 +++++++++ .../common/hooks/use_osquery_integration.tsx | 9 ++- .../public/live_queries/form/index.tsx | 28 ++++++--- .../osquery/public/queries/edit/tabs.tsx | 11 +++- .../osquery/public/results/use_all_results.ts | 10 ++-- .../routes/live_queries/details/index.tsx | 23 ++++++-- .../active_state_switch.tsx | 5 +- .../scheduled_query_groups/form/index.tsx | 5 +- .../osquery/server/lib/parse_agent_groups.ts | 4 +- 20 files changed, 186 insertions(+), 112 deletions(-) create mode 100644 x-pack/plugins/osquery/public/common/hooks/use_error_toast.tsx diff --git a/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx b/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx index 23eaaeac1439d4..257c89047aab0c 100644 --- a/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx +++ b/x-pack/plugins/osquery/public/action_results/action_results_summary.tsx @@ -36,6 +36,7 @@ const StyledEuiCard = styled(EuiCard)` interface ActionResultsSummaryProps { actionId: string; + expirationDate: Date; agentIds?: string[]; isLive?: boolean; } @@ -48,6 +49,7 @@ const renderErrorMessage = (error: string) => ( const ActionResultsSummaryComponent: React.FC = ({ actionId, + expirationDate, agentIds, isLive, }) => { @@ -56,6 +58,7 @@ const ActionResultsSummaryComponent: React.FC = ({ const [pageIndex, setPageIndex] = useState(0); // @ts-expect-error update types const [pageSize, setPageSize] = useState(50); + const expired = useMemo(() => expirationDate < new Date(), [expirationDate]); const { // @ts-expect-error update types data: { aggregations, edges }, @@ -66,7 +69,7 @@ const ActionResultsSummaryComponent: React.FC = ({ limit: pageSize, direction: Direction.asc, sortField: '@timestamp', - isLive, + isLive: !expired && isLive, }); const { data: logsResults } = useAllResults({ @@ -79,7 +82,7 @@ const ActionResultsSummaryComponent: React.FC = ({ direction: Direction.asc, }, ], - isLive, + isLive: !expired && isLive, }); const notRespondedCount = useMemo(() => { @@ -108,9 +111,13 @@ const ActionResultsSummaryComponent: React.FC = ({ description: aggregations.successful, }, { - title: i18n.translate('xpack.osquery.liveQueryActionResults.summary.pendingLabelText', { - defaultMessage: 'Not yet responded', - }), + title: expired + ? i18n.translate('xpack.osquery.liveQueryActionResults.summary.expiredLabelText', { + defaultMessage: 'Expired', + }) + : i18n.translate('xpack.osquery.liveQueryActionResults.summary.pendingLabelText', { + defaultMessage: 'Not yet responded', + }), description: notRespondedCount, }, { @@ -124,7 +131,7 @@ const ActionResultsSummaryComponent: React.FC = ({ ), }, ], - [agentIds, aggregations.failed, aggregations.successful, notRespondedCount] + [agentIds, aggregations.failed, aggregations.successful, notRespondedCount, expired] ); const renderAgentIdColumn = useCallback( @@ -158,23 +165,30 @@ const ActionResultsSummaryComponent: React.FC = ({ [logsResults] ); - const renderStatusColumn = useCallback((_, item) => { - if (!item.fields.completed_at) { - return i18n.translate('xpack.osquery.liveQueryActionResults.table.pendingStatusText', { - defaultMessage: 'pending', - }); - } + const renderStatusColumn = useCallback( + (_, item) => { + if (!item.fields.completed_at) { + return expired + ? i18n.translate('xpack.osquery.liveQueryActionResults.table.expiredStatusText', { + defaultMessage: 'expired', + }) + : i18n.translate('xpack.osquery.liveQueryActionResults.table.pendingStatusText', { + defaultMessage: 'pending', + }); + } - if (item.fields['error.keyword']) { - return i18n.translate('xpack.osquery.liveQueryActionResults.table.errorStatusText', { - defaultMessage: 'error', - }); - } + if (item.fields['error.keyword']) { + return i18n.translate('xpack.osquery.liveQueryActionResults.table.errorStatusText', { + defaultMessage: 'error', + }); + } - return i18n.translate('xpack.osquery.liveQueryActionResults.table.successStatusText', { - defaultMessage: 'success', - }); - }, []); + return i18n.translate('xpack.osquery.liveQueryActionResults.table.successStatusText', { + defaultMessage: 'success', + }); + }, + [expired] + ); const columns = useMemo( () => [ @@ -227,7 +241,7 @@ const ActionResultsSummaryComponent: React.FC = ({ - {notRespondedCount ? : null} + {!expired && notRespondedCount ? : null} { - const { - data, - notifications: { toasts }, - } = useKibana().services; + const { data } = useKibana().services; + const setErrorToast = useErrorToast(); return useQuery( ['actionResults', { actionId }], @@ -103,9 +102,9 @@ export const useActionResults = ({ aggregations: { totalResponded, // @ts-expect-error update types - successful: aggsBuckets.find((bucket) => bucket.key === 'success')?.doc_count ?? 0, + successful: aggsBuckets?.find((bucket) => bucket.key === 'success')?.doc_count ?? 0, // @ts-expect-error update types - failed: aggsBuckets.find((bucket) => bucket.key === 'error')?.doc_count ?? 0, + failed: aggsBuckets?.find((bucket) => bucket.key === 'error')?.doc_count ?? 0, }, inspect: getInspectResponse(responseData, {} as InspectResponse), }; @@ -124,8 +123,9 @@ export const useActionResults = ({ refetchInterval: isLive ? 1000 : false, keepPreviousData: true, enabled: !skip && !!agentIds?.length, + onSuccess: () => setErrorToast(), onError: (error: Error) => - toasts.addError(error, { + setErrorToast(error, { title: i18n.translate('xpack.osquery.action_results.fetchError', { defaultMessage: 'Error while fetching action results', }), diff --git a/x-pack/plugins/osquery/public/actions/use_action_details.ts b/x-pack/plugins/osquery/public/actions/use_action_details.ts index bb260cd78ca766..445912b27bc93c 100644 --- a/x-pack/plugins/osquery/public/actions/use_action_details.ts +++ b/x-pack/plugins/osquery/public/actions/use_action_details.ts @@ -18,6 +18,7 @@ import { import { ESTermQuery } from '../../common/typed_json'; import { getInspectResponse, InspectResponse } from './helpers'; +import { useErrorToast } from '../common/hooks/use_error_toast'; export interface ActionDetailsArgs { actionDetails: Record; @@ -33,10 +34,8 @@ interface UseActionDetails { } export const useActionDetails = ({ actionId, filterQuery, skip = false }: UseActionDetails) => { - const { - data, - notifications: { toasts }, - } = useKibana().services; + const { data } = useKibana().services; + const setErrorToast = useErrorToast(); return useQuery( ['actionDetails', { actionId, filterQuery }], @@ -61,8 +60,9 @@ export const useActionDetails = ({ actionId, filterQuery, skip = false }: UseAct }, { enabled: !skip, + onSuccess: () => setErrorToast(), onError: (error: Error) => - toasts.addError(error, { + setErrorToast(error, { title: i18n.translate('xpack.osquery.action_details.fetchError', { defaultMessage: 'Error while fetching action details', }), diff --git a/x-pack/plugins/osquery/public/actions/use_all_actions.ts b/x-pack/plugins/osquery/public/actions/use_all_actions.ts index 375d108c4dd8b1..ae872d3c1ed523 100644 --- a/x-pack/plugins/osquery/public/actions/use_all_actions.ts +++ b/x-pack/plugins/osquery/public/actions/use_all_actions.ts @@ -21,6 +21,7 @@ import { import { ESTermQuery } from '../../common/typed_json'; import { generateTablePaginationOptions, getInspectResponse, InspectResponse } from './helpers'; +import { useErrorToast } from '../common/hooks/use_error_toast'; export interface ActionsArgs { actions: ActionEdges; @@ -48,10 +49,8 @@ export const useAllActions = ({ filterQuery, skip = false, }: UseAllActions) => { - const { - data, - notifications: { toasts }, - } = useKibana().services; + const { data } = useKibana().services; + const setErrorToast = useErrorToast(); return useQuery( ['actions', { activePage, direction, limit, sortField }], @@ -82,8 +81,9 @@ export const useAllActions = ({ { keepPreviousData: true, enabled: !skip, + onSuccess: () => setErrorToast(), onError: (error: Error) => - toasts.addError(error, { + setErrorToast(error, { title: i18n.translate('xpack.osquery.all_actions.fetchError', { defaultMessage: 'Error while fetching actions', }), diff --git a/x-pack/plugins/osquery/public/agent_policies/use_agent_policies.ts b/x-pack/plugins/osquery/public/agent_policies/use_agent_policies.ts index d4bd0a1f4277fc..6f876106671987 100644 --- a/x-pack/plugins/osquery/public/agent_policies/use_agent_policies.ts +++ b/x-pack/plugins/osquery/public/agent_policies/use_agent_policies.ts @@ -14,12 +14,11 @@ import { GetAgentPoliciesResponse, GetAgentPoliciesResponseItem, } from '../../../fleet/common'; +import { useErrorToast } from '../common/hooks/use_error_toast'; export const useAgentPolicies = () => { - const { - http, - notifications: { toasts }, - } = useKibana().services; + const { http } = useKibana().services; + const setErrorToast = useErrorToast(); return useQuery( ['agentPolicies'], @@ -34,8 +33,9 @@ export const useAgentPolicies = () => { placeholderData: [], keepPreviousData: true, select: (response) => response.items, + onSuccess: () => setErrorToast(), onError: (error) => - toasts.addError(error as Error, { + setErrorToast(error as Error, { title: i18n.translate('xpack.osquery.agent_policies.fetchError', { defaultMessage: 'Error while fetching agent policies', }), diff --git a/x-pack/plugins/osquery/public/agent_policies/use_agent_policy.ts b/x-pack/plugins/osquery/public/agent_policies/use_agent_policy.ts index e87d8d1c9f28ed..dcebf136b6773f 100644 --- a/x-pack/plugins/osquery/public/agent_policies/use_agent_policy.ts +++ b/x-pack/plugins/osquery/public/agent_policies/use_agent_policy.ts @@ -10,6 +10,7 @@ import { useQuery } from 'react-query'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; import { agentPolicyRouteService } from '../../../fleet/common'; +import { useErrorToast } from '../common/hooks/use_error_toast'; interface UseAgentPolicy { policyId: string; @@ -17,10 +18,8 @@ interface UseAgentPolicy { } export const useAgentPolicy = ({ policyId, skip }: UseAgentPolicy) => { - const { - http, - notifications: { toasts }, - } = useKibana().services; + const { http } = useKibana().services; + const setErrorToast = useErrorToast(); return useQuery( ['agentPolicy', { policyId }], @@ -29,8 +28,9 @@ export const useAgentPolicy = ({ policyId, skip }: UseAgentPolicy) => { enabled: !skip, keepPreviousData: true, select: (response) => response.item, + onSuccess: () => setErrorToast(), onError: (error: Error) => - toasts.addError(error, { + setErrorToast(error, { title: i18n.translate('xpack.osquery.agent_policy_details.fetchError', { defaultMessage: 'Error while fetching agent policy details', }), diff --git a/x-pack/plugins/osquery/public/agents/use_agent_groups.ts b/x-pack/plugins/osquery/public/agents/use_agent_groups.ts index 44737af9d34775..bfa224a23135bf 100644 --- a/x-pack/plugins/osquery/public/agents/use_agent_groups.ts +++ b/x-pack/plugins/osquery/public/agents/use_agent_groups.ts @@ -18,6 +18,7 @@ import { import { generateTablePaginationOptions, processAggregations } from './helpers'; import { Overlap, Group } from './types'; +import { useErrorToast } from '../common/hooks/use_error_toast'; interface UseAgentGroups { osqueryPolicies: string[]; @@ -25,10 +26,8 @@ interface UseAgentGroups { } export const useAgentGroups = ({ osqueryPolicies, osqueryPoliciesLoading }: UseAgentGroups) => { - const { - data, - notifications: { toasts }, - } = useKibana().services; + const { data } = useKibana().services; + const setErrorToast = useErrorToast(); const { agentPoliciesLoading, agentPolicyById } = useAgentPolicies(osqueryPolicies); const [platforms, setPlatforms] = useState([]); @@ -100,8 +99,9 @@ export const useAgentGroups = ({ osqueryPolicies, osqueryPoliciesLoading }: UseA }, { enabled: !osqueryPoliciesLoading && !agentPoliciesLoading, + onSuccess: () => setErrorToast(), onError: (error) => - toasts.addError(error as Error, { + setErrorToast(error as Error, { title: i18n.translate('xpack.osquery.agent_groups.fetchError', { defaultMessage: 'Error while fetching agent groups', }), diff --git a/x-pack/plugins/osquery/public/agents/use_agent_policies.ts b/x-pack/plugins/osquery/public/agents/use_agent_policies.ts index ecb95fff8838ee..115b5af9d3a1ba 100644 --- a/x-pack/plugins/osquery/public/agents/use_agent_policies.ts +++ b/x-pack/plugins/osquery/public/agents/use_agent_policies.ts @@ -10,20 +10,20 @@ import { useQueries, UseQueryResult } from 'react-query'; import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; import { agentPolicyRouteService, GetOneAgentPolicyResponse } from '../../../fleet/common'; +import { useErrorToast } from '../common/hooks/use_error_toast'; export const useAgentPolicies = (policyIds: string[] = []) => { - const { - http, - notifications: { toasts }, - } = useKibana().services; + const { http } = useKibana().services; + const setErrorToast = useErrorToast(); const agentResponse = useQueries( policyIds.map((policyId) => ({ queryKey: ['agentPolicy', policyId], queryFn: () => http.get(agentPolicyRouteService.getInfoPath(policyId)), enabled: policyIds.length > 0, + onSuccess: () => setErrorToast(), onError: (error) => - toasts.addError(error as Error, { + setErrorToast(error as Error, { title: i18n.translate('xpack.osquery.action_policy_details.fetchError', { defaultMessage: 'Error while fetching policy details', }), diff --git a/x-pack/plugins/osquery/public/agents/use_agent_status.ts b/x-pack/plugins/osquery/public/agents/use_agent_status.ts index 4954eb0dc80c45..c8bc8d2fe5c0ef 100644 --- a/x-pack/plugins/osquery/public/agents/use_agent_status.ts +++ b/x-pack/plugins/osquery/public/agents/use_agent_status.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { useQuery } from 'react-query'; import { GetAgentStatusResponse, agentRouteService } from '../../../fleet/common'; +import { useErrorToast } from '../common/hooks/use_error_toast'; import { useKibana } from '../common/lib/kibana'; interface UseAgentStatus { @@ -17,10 +18,8 @@ interface UseAgentStatus { } export const useAgentStatus = ({ policyId, skip }: UseAgentStatus) => { - const { - http, - notifications: { toasts }, - } = useKibana().services; + const { http } = useKibana().services; + const setErrorToast = useErrorToast(); return useQuery( ['agentStatus', policyId], @@ -38,8 +37,9 @@ export const useAgentStatus = ({ policyId, skip }: UseAgentStatus) => { { enabled: !skip, select: (response) => response.results, + onSuccess: () => setErrorToast(), onError: (error) => - toasts.addError(error as Error, { + setErrorToast(error as Error, { title: i18n.translate('xpack.osquery.agent_status.fetchError', { defaultMessage: 'Error while fetching agent status', }), diff --git a/x-pack/plugins/osquery/public/agents/use_all_agents.ts b/x-pack/plugins/osquery/public/agents/use_all_agents.ts index 674deb3b339bd7..30ba4d2f579079 100644 --- a/x-pack/plugins/osquery/public/agents/use_all_agents.ts +++ b/x-pack/plugins/osquery/public/agents/use_all_agents.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { useQuery } from 'react-query'; import { GetAgentsResponse, agentRouteService } from '../../../fleet/common'; +import { useErrorToast } from '../common/hooks/use_error_toast'; import { useKibana } from '../common/lib/kibana'; interface UseAllAgents { @@ -28,36 +29,30 @@ export const useAllAgents = ( opts: RequestOptions = { perPage: 9000 } ) => { const { perPage } = opts; - const { - http, - notifications: { toasts }, - } = useKibana().services; + const { http } = useKibana().services; + const setErrorToast = useErrorToast(); const { isLoading: agentsLoading, data: agentData } = useQuery( ['agents', osqueryPolicies, searchValue, perPage], () => { - const kueryFragments: string[] = []; - if (osqueryPolicies.length) { - kueryFragments.push(`${osqueryPolicies.map((p) => `policy_id:${p}`).join(' or ')}`); - } + const policyFragment = osqueryPolicies.map((p) => `policy_id:${p}`).join(' or '); + let kuery = `last_checkin_status: online and (${policyFragment})`; if (searchValue) { - kueryFragments.push( - `local_metadata.host.hostname:*${searchValue}* or local_metadata.elastic.agent.id:*${searchValue}*` - ); + kuery += `and (local_metadata.host.hostname:*${searchValue}* or local_metadata.elastic.agent.id:*${searchValue}*)`; } return http.get(agentRouteService.getListPath(), { query: { - kuery: kueryFragments.map((frag) => `(${frag})`).join(' and '), + kuery, perPage, - showInactive: true, }, }); }, { - enabled: !osqueryPoliciesLoading, + enabled: !osqueryPoliciesLoading && osqueryPolicies.length > 0, + onSuccess: () => setErrorToast(), onError: (error) => - toasts.addError(error as Error, { + setErrorToast(error as Error, { title: i18n.translate('xpack.osquery.agents.fetchError', { defaultMessage: 'Error while fetching agents', }), diff --git a/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts b/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts index 0eb94af73e3a8d..9064dac1ae5d00 100644 --- a/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts +++ b/x-pack/plugins/osquery/public/agents/use_osquery_policies.ts @@ -12,12 +12,11 @@ import { i18n } from '@kbn/i18n'; import { useKibana } from '../common/lib/kibana'; import { packagePolicyRouteService, PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../fleet/common'; import { OSQUERY_INTEGRATION_NAME } from '../../common'; +import { useErrorToast } from '../common/hooks/use_error_toast'; export const useOsqueryPolicies = () => { - const { - http, - notifications: { toasts }, - } = useKibana().services; + const { http } = useKibana().services; + const setErrorToast = useErrorToast(); const { isLoading: osqueryPoliciesLoading, data: osqueryPolicies = [] } = useQuery( ['osqueryPolicies'], @@ -30,8 +29,9 @@ export const useOsqueryPolicies = () => { { select: (response) => uniq(response.items.map((p: { policy_id: string }) => p.policy_id)), + onSuccess: () => setErrorToast(), onError: (error: Error) => - toasts.addError(error, { + setErrorToast(error, { title: i18n.translate('xpack.osquery.osquery_policies.fetchError', { defaultMessage: 'Error while fetching osquery policies', }), diff --git a/x-pack/plugins/osquery/public/common/hooks/use_error_toast.tsx b/x-pack/plugins/osquery/public/common/hooks/use_error_toast.tsx new file mode 100644 index 00000000000000..fb17803a9d57b1 --- /dev/null +++ b/x-pack/plugins/osquery/public/common/hooks/use_error_toast.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ErrorToastOptions, Toast } from 'kibana/public'; +import { useState } from 'react'; +import { useKibana } from '../../common/lib/kibana'; + +export const useErrorToast = () => { + const [errorToast, setErrorToast] = useState(); + const { + notifications: { toasts }, + } = useKibana().services; + return (error?: unknown, opts?: ErrorToastOptions) => { + if (errorToast) { + toasts.remove(errorToast); + } + if (error) { + // @ts-expect-error update types + setErrorToast(toasts.addError(error, opts)); + } + }; +}; diff --git a/x-pack/plugins/osquery/public/common/hooks/use_osquery_integration.tsx b/x-pack/plugins/osquery/public/common/hooks/use_osquery_integration.tsx index ccfb407eab58b8..236fdb1af18151 100644 --- a/x-pack/plugins/osquery/public/common/hooks/use_osquery_integration.tsx +++ b/x-pack/plugins/osquery/public/common/hooks/use_osquery_integration.tsx @@ -12,12 +12,11 @@ import { useQuery } from 'react-query'; import { GetPackagesResponse, epmRouteService } from '../../../../fleet/common'; import { OSQUERY_INTEGRATION_NAME } from '../../../common'; import { useKibana } from '../lib/kibana'; +import { useErrorToast } from './use_error_toast'; export const useOsqueryIntegration = () => { - const { - http, - notifications: { toasts }, - } = useKibana().services; + const { http } = useKibana().services; + const setErrorToast = useErrorToast(); return useQuery( 'integrations', @@ -31,7 +30,7 @@ export const useOsqueryIntegration = () => { select: ({ response }: GetPackagesResponse) => find(['name', OSQUERY_INTEGRATION_NAME], response), onError: (error: Error) => - toasts.addError(error, { + setErrorToast(error, { title: i18n.translate('xpack.osquery.osquery_integration.fetchError', { defaultMessage: 'Error while fetching osquery integration', }), diff --git a/x-pack/plugins/osquery/public/live_queries/form/index.tsx b/x-pack/plugins/osquery/public/live_queries/form/index.tsx index 4cf2d4aa4fe913..6f2d1afec6fe93 100644 --- a/x-pack/plugins/osquery/public/live_queries/form/index.tsx +++ b/x-pack/plugins/osquery/public/live_queries/form/index.tsx @@ -19,6 +19,7 @@ import { useKibana } from '../../common/lib/kibana'; import { ResultTabs } from '../../queries/edit/tabs'; import { queryFieldValidation } from '../../common/validations'; import { fieldValidators } from '../../shared_imports'; +import { useErrorToast } from '../../common/hooks/use_error_toast'; const FORM_ID = 'liveQueryForm'; @@ -35,10 +36,9 @@ const LiveQueryFormComponent: React.FC = ({ // onSubmit, onSuccess, }) => { - const { - http, - notifications: { toasts }, - } = useKibana().services; + const { http } = useKibana().services; + + const setErrorToast = useErrorToast(); const { data, @@ -53,14 +53,20 @@ const LiveQueryFormComponent: React.FC = ({ body: JSON.stringify(payload), }), { - onSuccess, + onSuccess: () => { + setErrorToast(); + if (onSuccess) { + onSuccess(); + } + }, onError: (error) => { - // @ts-expect-error update types - toasts.addError(error, { title: error.body.error, toastMessage: error.body.message }); + setErrorToast(error); }, } ); + const expirationDate = useMemo(() => new Date(data?.actions[0].expiration), [data?.actions]); + const formSchema = { query: { type: FIELD_TYPES.TEXT, @@ -173,7 +179,12 @@ const LiveQueryFormComponent: React.FC = ({ defaultMessage: 'Check results', }), children: actionId ? ( - + ) : null, status: resultsStatus, }, @@ -185,6 +196,7 @@ const LiveQueryFormComponent: React.FC = ({ queryComponentProps, queryStatus, queryValueProvided, + expirationDate, resultsStatus, submit, ] diff --git a/x-pack/plugins/osquery/public/queries/edit/tabs.tsx b/x-pack/plugins/osquery/public/queries/edit/tabs.tsx index 978c3f938f1d68..2c9421606ea309 100644 --- a/x-pack/plugins/osquery/public/queries/edit/tabs.tsx +++ b/x-pack/plugins/osquery/public/queries/edit/tabs.tsx @@ -14,6 +14,7 @@ import { ActionResultsSummary } from '../../action_results/action_results_summar interface ResultTabsProps { actionId: string; agentIds?: string[]; + expirationDate: Date; isLive?: boolean; startDate?: string; endDate?: string; @@ -22,6 +23,7 @@ interface ResultTabsProps { const ResultTabsComponent: React.FC = ({ actionId, agentIds, + expirationDate, endDate, isLive, startDate, @@ -34,7 +36,12 @@ const ResultTabsComponent: React.FC = ({ content: ( <> - + ), }, @@ -55,7 +62,7 @@ const ResultTabsComponent: React.FC = ({ ), }, ], - [actionId, agentIds, endDate, isLive, startDate] + [actionId, agentIds, endDate, isLive, startDate, expirationDate] ); return ( diff --git a/x-pack/plugins/osquery/public/results/use_all_results.ts b/x-pack/plugins/osquery/public/results/use_all_results.ts index d5e2bbc886940f..11218984102786 100644 --- a/x-pack/plugins/osquery/public/results/use_all_results.ts +++ b/x-pack/plugins/osquery/public/results/use_all_results.ts @@ -21,6 +21,7 @@ import { import { ESTermQuery } from '../../common/typed_json'; import { generateTablePaginationOptions, getInspectResponse, InspectResponse } from './helpers'; +import { useErrorToast } from '../common/hooks/use_error_toast'; export interface ResultsArgs { results: ResultEdges; @@ -50,10 +51,8 @@ export const useAllResults = ({ skip = false, isLive = false, }: UseAllResults) => { - const { - data, - notifications: { toasts }, - } = useKibana().services; + const { data } = useKibana().services; + const setErrorToast = useErrorToast(); return useQuery( ['allActionResults', { actionId, activePage, limit, sort }], @@ -81,8 +80,9 @@ export const useAllResults = ({ { refetchInterval: isLive ? 1000 : false, enabled: !skip, + onSuccess: () => setErrorToast(), onError: (error: Error) => - toasts.addError(error, { + setErrorToast(error, { title: i18n.translate('xpack.osquery.results.fetchError', { defaultMessage: 'Error while fetching results', }), diff --git a/x-pack/plugins/osquery/public/routes/live_queries/details/index.tsx b/x-pack/plugins/osquery/public/routes/live_queries/details/index.tsx index 5a80e12d0fef34..64a1fb0791e835 100644 --- a/x-pack/plugins/osquery/public/routes/live_queries/details/index.tsx +++ b/x-pack/plugins/osquery/public/routes/live_queries/details/index.tsx @@ -43,6 +43,10 @@ const LiveQueryDetailsPageComponent = () => { const liveQueryListProps = useRouterNavigate('live_queries'); const { data } = useActionDetails({ actionId }); + const expirationDate = useMemo(() => new Date(data?.actionDetails._source.expiration), [ + data?.actionDetails, + ]); + const expired = useMemo(() => expirationDate < new Date(), [expirationDate]); const { data: actionResultsData } = useActionResults({ actionId, activePage: 0, @@ -78,6 +82,18 @@ const LiveQueryDetailsPageComponent = () => { [liveQueryListProps] ); + const failed = useMemo(() => { + let result = actionResultsData?.aggregations.failed; + if (expired) { + result = '-'; + if (data?.actionDetails?.fields?.agents && actionResultsData?.aggregations) { + result = + data.actionDetails.fields.agents.length - actionResultsData.aggregations.successful; + } + } + return result; + }, [expired, actionResultsData?.aggregations, data?.actionDetails?.fields?.agents]); + const RightColumn = useMemo( () => ( @@ -114,15 +130,13 @@ const LiveQueryDetailsPageComponent = () => { /> - - {actionResultsData?.aggregations.failed} - + {failed} ), - [actionResultsData?.aggregations.failed, data?.actionDetails?.fields?.agents?.length] + [data?.actionDetails?.fields?.agents?.length, failed] ); return ( @@ -133,6 +147,7 @@ const LiveQueryDetailsPageComponent = () => { theme.eui.paddingSizes.s}; @@ -36,6 +37,7 @@ const ActiveStateSwitchComponent: React.FC = ({ item }) http, notifications: { toasts }, } = useKibana().services; + const setErrorToast = useErrorToast(); const [confirmationModal, setConfirmationModal] = useState(false); const hideConfirmationModal = useCallback(() => setConfirmationModal(false), []); @@ -51,6 +53,7 @@ const ActiveStateSwitchComponent: React.FC = ({ item }) { onSuccess: (response) => { queryClient.invalidateQueries('scheduledQueries'); + setErrorToast(); toasts.addSuccess( response.item.enabled ? i18n.translate( @@ -75,7 +78,7 @@ const ActiveStateSwitchComponent: React.FC = ({ item }) }, onError: (error) => { // @ts-expect-error update types - toasts.addError(error, { title: error.body.error, toastMessage: error.body.message }); + setErrorToast(error, { title: error.body.error, toastMessage: error.body.message }); }, } ); diff --git a/x-pack/plugins/osquery/public/scheduled_query_groups/form/index.tsx b/x-pack/plugins/osquery/public/scheduled_query_groups/form/index.tsx index 64efdf61fc7359..c940b1f8527b50 100644 --- a/x-pack/plugins/osquery/public/scheduled_query_groups/form/index.tsx +++ b/x-pack/plugins/osquery/public/scheduled_query_groups/form/index.tsx @@ -45,6 +45,7 @@ import { PolicyIdComboBoxField } from './policy_id_combobox_field'; import { QueriesField } from './queries_field'; import { ConfirmDeployAgentPolicyModal } from './confirmation_modal'; import { useAgentPolicies } from '../../agent_policies'; +import { useErrorToast } from '../../common/hooks/use_error_toast'; const GhostFormField = () => <>; @@ -68,6 +69,7 @@ const ScheduledQueryGroupFormComponent: React.FC = http, notifications: { toasts }, } = useKibana().services; + const setErrorToast = useErrorToast(); const [showConfirmationModal, setShowConfirmationModal] = useState(false); const handleHideConfirmationModal = useCallback(() => setShowConfirmationModal(false), []); @@ -110,6 +112,7 @@ const ScheduledQueryGroupFormComponent: React.FC = return; } + setErrorToast(); navigateToApp(PLUGIN_ID, { path: `scheduled_query_groups/${data.item.id}` }); toasts.addSuccess( i18n.translate('xpack.osquery.scheduledQueryGroup.form.updateSuccessToastMessageText', { @@ -122,7 +125,7 @@ const ScheduledQueryGroupFormComponent: React.FC = }, onError: (error) => { // @ts-expect-error update types - toasts.addError(error, { title: error.body.error, toastMessage: error.body.message }); + setErrorToast(error, { title: error.body.error, toastMessage: error.body.message }); }, } ); diff --git a/x-pack/plugins/osquery/server/lib/parse_agent_groups.ts b/x-pack/plugins/osquery/server/lib/parse_agent_groups.ts index a120d7deddf50e..8fe60f59f01d76 100644 --- a/x-pack/plugins/osquery/server/lib/parse_agent_groups.ts +++ b/x-pack/plugins/osquery/server/lib/parse_agent_groups.ts @@ -63,7 +63,7 @@ export const parseAgentSelection = async ( perPage, page, kuery, - showInactive: true, + showInactive: false, }); return { results: res.agents.map((agent) => agent.id), total: res.total }; }); @@ -84,7 +84,7 @@ export const parseAgentSelection = async ( perPage, page, kuery, - showInactive: true, + showInactive: false, }); return { results: res.agents.map((agent) => agent.id), total: res.total }; });