[Osquery] Fix 7.14 live query history view (#105211)
This commit is contained in:
parent
0c9777c602
commit
3e5ed77470
|
@ -8,15 +8,13 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiLink, EuiInMemoryTable, EuiCodeBlock } from '@elastic/eui';
|
||||
import { EuiInMemoryTable, EuiCodeBlock } from '@elastic/eui';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { PLUGIN_ID } from '../../../fleet/common';
|
||||
import { pagePathGetters } from '../../../fleet/public';
|
||||
import { AgentIdToName } from '../agents/agent_id_to_name';
|
||||
import { useActionResults } from './use_action_results';
|
||||
import { useAllResults } from '../results/use_all_results';
|
||||
import { Direction } from '../../common/search_strategy';
|
||||
import { useKibana } from '../common/lib/kibana';
|
||||
|
||||
interface ActionResultsSummaryProps {
|
||||
actionId: string;
|
||||
|
@ -35,7 +33,6 @@ const ActionResultsSummaryComponent: React.FC<ActionResultsSummaryProps> = ({
|
|||
expirationDate,
|
||||
agentIds,
|
||||
}) => {
|
||||
const getUrlForApp = useKibana().services.application.getUrlForApp;
|
||||
// @ts-expect-error update types
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
// @ts-expect-error update types
|
||||
|
@ -70,20 +67,7 @@ const ActionResultsSummaryComponent: React.FC<ActionResultsSummaryProps> = ({
|
|||
isLive,
|
||||
});
|
||||
|
||||
const renderAgentIdColumn = useCallback(
|
||||
(agentId) => (
|
||||
<EuiLink
|
||||
className="eui-textTruncate"
|
||||
href={getUrlForApp(PLUGIN_ID, {
|
||||
path: `#` + pagePathGetters.agent_details({ agentId })[1],
|
||||
})}
|
||||
target="_blank"
|
||||
>
|
||||
{agentId}
|
||||
</EuiLink>
|
||||
),
|
||||
[getUrlForApp]
|
||||
);
|
||||
const renderAgentIdColumn = useCallback((agentId) => <AgentIdToName agentId={agentId} />, []);
|
||||
|
||||
const renderRowsColumn = useCallback(
|
||||
(_, item) => {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { isArray } from 'lodash';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiBasicTable, EuiButtonIcon, EuiCodeBlock, formatDate } from '@elastic/eui';
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { useAllActions } from './use_all_actions';
|
||||
import { Direction } from '../../common/search_strategy';
|
||||
|
@ -27,6 +28,7 @@ const ActionTableResultsButton = React.memo<ActionTableResultsButtonProps>(({ ac
|
|||
ActionTableResultsButton.displayName = 'ActionTableResultsButton';
|
||||
|
||||
const ActionsTableComponent = () => {
|
||||
const { push } = useHistory();
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(20);
|
||||
|
||||
|
@ -67,6 +69,16 @@ const ActionsTableComponent = () => {
|
|||
[]
|
||||
);
|
||||
|
||||
const handlePlayClick = useCallback(
|
||||
(item) =>
|
||||
push('/live_queries/new', {
|
||||
form: {
|
||||
query: item._source?.data?.query,
|
||||
},
|
||||
}),
|
||||
[push]
|
||||
);
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
{
|
||||
|
@ -106,6 +118,11 @@ const ActionsTableComponent = () => {
|
|||
defaultMessage: 'View details',
|
||||
}),
|
||||
actions: [
|
||||
{
|
||||
type: 'icon',
|
||||
icon: 'play',
|
||||
onClick: handlePlayClick,
|
||||
},
|
||||
{
|
||||
render: renderActionsColumn,
|
||||
},
|
||||
|
@ -113,6 +130,7 @@ const ActionsTableComponent = () => {
|
|||
},
|
||||
],
|
||||
[
|
||||
handlePlayClick,
|
||||
renderActionsColumn,
|
||||
renderAgentsColumn,
|
||||
renderCreatedByColumn,
|
||||
|
@ -135,6 +153,7 @@ const ActionsTableComponent = () => {
|
|||
<EuiBasicTable
|
||||
// eslint-disable-next-line react-perf/jsx-no-new-array-as-prop
|
||||
items={actionsData?.actions ?? []}
|
||||
// @ts-expect-error update types
|
||||
columns={columns}
|
||||
pagination={pagination}
|
||||
onChange={onTableChange}
|
||||
|
|
37
x-pack/plugins/osquery/public/agents/agent_id_to_name.tsx
Normal file
37
x-pack/plugins/osquery/public/agents/agent_id_to_name.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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 { EuiLink } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
|
||||
import { useAgentDetails } from './use_agent_details';
|
||||
import { PLUGIN_ID } from '../../../fleet/common';
|
||||
import { pagePathGetters } from '../../../fleet/public';
|
||||
import { useKibana } from '../common/lib/kibana';
|
||||
|
||||
interface AgentIdToNameProps {
|
||||
agentId: string;
|
||||
}
|
||||
|
||||
const AgentIdToNameComponent: React.FC<AgentIdToNameProps> = ({ agentId }) => {
|
||||
const getUrlForApp = useKibana().services.application.getUrlForApp;
|
||||
const { data } = useAgentDetails({ agentId });
|
||||
|
||||
return (
|
||||
<EuiLink
|
||||
className="eui-textTruncate"
|
||||
href={getUrlForApp(PLUGIN_ID, {
|
||||
path: `#` + pagePathGetters.agent_details({ agentId })[1],
|
||||
})}
|
||||
target="_blank"
|
||||
>
|
||||
{data?.item.local_metadata.host.name ?? agentId}
|
||||
</EuiLink>
|
||||
);
|
||||
};
|
||||
|
||||
export const AgentIdToName = React.memo(AgentIdToNameComponent);
|
|
@ -21,7 +21,12 @@ import {
|
|||
generateAgentSelection,
|
||||
} from './helpers';
|
||||
|
||||
import { SELECT_AGENT_LABEL, generateSelectedAgentsMessage } from './translations';
|
||||
import {
|
||||
SELECT_AGENT_LABEL,
|
||||
generateSelectedAgentsMessage,
|
||||
ALL_AGENTS_LABEL,
|
||||
AGENT_POLICY_LABEL,
|
||||
} from './translations';
|
||||
|
||||
import {
|
||||
AGENT_GROUP_KEY,
|
||||
|
@ -72,8 +77,17 @@ const AgentsTableComponent: React.FC<AgentsTableProps> = ({ agentSelection, onCh
|
|||
|
||||
useEffect(() => {
|
||||
if (agentSelection && !defaultValueInitialized.current && options.length) {
|
||||
if (agentSelection.policiesSelected) {
|
||||
const policyOptions = find(['label', 'Policy'], options);
|
||||
if (agentSelection.allAgentsSelected) {
|
||||
const allAgentsOptions = find(['label', ALL_AGENTS_LABEL], options);
|
||||
|
||||
if (allAgentsOptions?.options) {
|
||||
setSelectedOptions(allAgentsOptions.options);
|
||||
defaultValueInitialized.current = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (agentSelection.policiesSelected.length) {
|
||||
const policyOptions = find(['label', AGENT_POLICY_LABEL], options);
|
||||
|
||||
if (policyOptions) {
|
||||
const defaultOptions = policyOptions.options?.filter((option) =>
|
||||
|
@ -82,12 +96,12 @@ const AgentsTableComponent: React.FC<AgentsTableProps> = ({ agentSelection, onCh
|
|||
|
||||
if (defaultOptions?.length) {
|
||||
setSelectedOptions(defaultOptions);
|
||||
defaultValueInitialized.current = true;
|
||||
}
|
||||
defaultValueInitialized.current = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [agentSelection, options]);
|
||||
}, [agentSelection, options, selectedOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
// update the groups when groups or agents have changed
|
||||
|
|
36
x-pack/plugins/osquery/public/agents/use_agent_details.ts
Normal file
36
x-pack/plugins/osquery/public/agents/use_agent_details.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import { GetOneAgentResponse, agentRouteService } from '../../../fleet/common';
|
||||
import { useErrorToast } from '../common/hooks/use_error_toast';
|
||||
import { useKibana } from '../common/lib/kibana';
|
||||
|
||||
interface UseAgentDetails {
|
||||
agentId: string;
|
||||
}
|
||||
|
||||
export const useAgentDetails = ({ agentId }: UseAgentDetails) => {
|
||||
const { http } = useKibana().services;
|
||||
const setErrorToast = useErrorToast();
|
||||
return useQuery<GetOneAgentResponse>(
|
||||
['agentDetails', agentId],
|
||||
() => http.get(agentRouteService.getInfoPath(agentId)),
|
||||
{
|
||||
enabled: agentId.length > 0,
|
||||
onSuccess: () => setErrorToast(),
|
||||
onError: (error) =>
|
||||
setErrorToast(error as Error, {
|
||||
title: i18n.translate('xpack.osquery.agentDetails.fetchError', {
|
||||
defaultMessage: 'Error while fetching agent details',
|
||||
}),
|
||||
}),
|
||||
}
|
||||
);
|
||||
};
|
|
@ -38,7 +38,7 @@ export const useAllAgents = (
|
|||
let kuery = `last_checkin_status: online and (${policyFragment})`;
|
||||
|
||||
if (searchValue) {
|
||||
kuery += `and (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(), {
|
||||
|
|
|
@ -110,7 +110,7 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
|
|||
{
|
||||
agentSelection: {
|
||||
agents: [],
|
||||
allAgentsSelected: false,
|
||||
allAgentsSelected: true,
|
||||
platformsSelected: [],
|
||||
policiesSelected: [],
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import qs from 'query-string';
|
||||
|
||||
import { WithHeaderLayout } from '../../../components/layouts';
|
||||
|
@ -19,12 +19,18 @@ import { BetaBadge, BetaBadgeRowWrapper } from '../../../components/beta_badge';
|
|||
|
||||
const NewLiveQueryPageComponent = () => {
|
||||
useBreadcrumbs('live_query_new');
|
||||
const { replace } = useHistory();
|
||||
const location = useLocation();
|
||||
const liveQueryListProps = useRouterNavigate('live_queries');
|
||||
|
||||
const formDefaultValue = useMemo(() => {
|
||||
const queryParams = qs.parse(location.search);
|
||||
|
||||
if (location.state?.form.query) {
|
||||
replace({ state: null });
|
||||
return { query: location.state?.form.query };
|
||||
}
|
||||
|
||||
if (queryParams?.agentPolicyId) {
|
||||
return {
|
||||
agentSelection: {
|
||||
|
@ -37,7 +43,7 @@ const NewLiveQueryPageComponent = () => {
|
|||
}
|
||||
|
||||
return undefined;
|
||||
}, [location.search]);
|
||||
}, [location.search, location.state, replace]);
|
||||
|
||||
const LeftColumn = useMemo(
|
||||
() => (
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { SavedObject } from 'kibana/public';
|
||||
import { WithHeaderLayout } from '../../../components/layouts';
|
||||
|
@ -51,6 +52,7 @@ const EditButton = React.memo(EditButtonComponent);
|
|||
|
||||
const SavedQueriesPageComponent = () => {
|
||||
useBreadcrumbs('saved_queries');
|
||||
const { push } = useHistory();
|
||||
const newQueryLinkProps = useRouterNavigate('saved_queries/new');
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
|
@ -59,21 +61,15 @@ const SavedQueriesPageComponent = () => {
|
|||
|
||||
const { data } = useSavedQueries({ isLive: true });
|
||||
|
||||
// const handlePlayClick = useCallback(
|
||||
// (item) =>
|
||||
// push({
|
||||
// search: qs.stringify({
|
||||
// tab: 'live_query',
|
||||
// }),
|
||||
// state: {
|
||||
// query: {
|
||||
// id: item.id,
|
||||
// query: item.attributes.query,
|
||||
// },
|
||||
// },
|
||||
// }),
|
||||
// [push]
|
||||
// );
|
||||
const handlePlayClick = useCallback(
|
||||
(item) =>
|
||||
push('/live_queries/new', {
|
||||
form: {
|
||||
savedQueryId: item.id,
|
||||
},
|
||||
}),
|
||||
[push]
|
||||
);
|
||||
|
||||
const renderEditAction = useCallback(
|
||||
(item: SavedObject<{ name: string }>) => (
|
||||
|
@ -96,45 +92,53 @@ const SavedQueriesPageComponent = () => {
|
|||
() => [
|
||||
{
|
||||
field: 'attributes.id',
|
||||
name: 'Query ID',
|
||||
name: i18n.translate('xpack.osquery.savedQueries.table.queryIdColumnTitle', {
|
||||
defaultMessage: 'Query ID',
|
||||
}),
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
},
|
||||
{
|
||||
field: 'attributes.description',
|
||||
name: 'Description',
|
||||
name: i18n.translate('xpack.osquery.savedQueries.table.descriptionColumnTitle', {
|
||||
defaultMessage: 'Description',
|
||||
}),
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
},
|
||||
{
|
||||
field: 'attributes.created_by',
|
||||
name: 'Created by',
|
||||
name: i18n.translate('xpack.osquery.savedQueries.table.createdByColumnTitle', {
|
||||
defaultMessage: 'Created by',
|
||||
}),
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
},
|
||||
{
|
||||
field: 'attributes.updated_at',
|
||||
name: 'Last updated at',
|
||||
name: i18n.translate('xpack.osquery.savedQueries.table.updatedAtColumnTitle', {
|
||||
defaultMessage: 'Last updated at',
|
||||
}),
|
||||
sortable: (item: SavedObject<{ updated_at: string }>) =>
|
||||
item.attributes.updated_at ? Date.parse(item.attributes.updated_at) : 0,
|
||||
truncateText: true,
|
||||
render: renderUpdatedAt,
|
||||
},
|
||||
{
|
||||
name: 'Actions',
|
||||
name: i18n.translate('xpack.osquery.savedQueries.table.actionsColumnTitle', {
|
||||
defaultMessage: 'Actions',
|
||||
}),
|
||||
actions: [
|
||||
// {
|
||||
// name: 'Live query',
|
||||
// description: 'Run live query',
|
||||
// type: 'icon',
|
||||
// icon: 'play',
|
||||
// onClick: handlePlayClick,
|
||||
// },
|
||||
{
|
||||
type: 'icon',
|
||||
icon: 'play',
|
||||
onClick: handlePlayClick,
|
||||
},
|
||||
{ render: renderEditAction },
|
||||
],
|
||||
},
|
||||
],
|
||||
[renderEditAction, renderUpdatedAt]
|
||||
[handlePlayClick, renderEditAction, renderUpdatedAt]
|
||||
);
|
||||
|
||||
const onTableChange = useCallback(({ page = {}, sort = {} }) => {
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
|
||||
import { find } from 'lodash/fp';
|
||||
import { EuiCodeBlock, EuiFormRow, EuiComboBox, EuiText } from '@elastic/eui';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { SimpleSavedObject } from 'kibana/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
|
||||
import { useSavedQueries } from './use_saved_queries';
|
||||
|
||||
|
@ -29,19 +30,25 @@ const SavedQueriesDropdownComponent: React.FC<SavedQueriesDropdownProps> = ({
|
|||
disabled,
|
||||
onChange,
|
||||
}) => {
|
||||
const { replace } = useHistory();
|
||||
const location = useLocation();
|
||||
const [selectedOptions, setSelectedOptions] = useState([]);
|
||||
|
||||
const { data } = useSavedQueries({});
|
||||
|
||||
const queryOptions =
|
||||
data?.savedObjects?.map((savedQuery) => ({
|
||||
label: savedQuery.attributes.id ?? '',
|
||||
value: {
|
||||
id: savedQuery.attributes.id,
|
||||
description: savedQuery.attributes.description,
|
||||
query: savedQuery.attributes.query,
|
||||
},
|
||||
})) ?? [];
|
||||
const queryOptions = useMemo(
|
||||
() =>
|
||||
data?.savedObjects?.map((savedQuery) => ({
|
||||
label: savedQuery.attributes.id ?? '',
|
||||
value: {
|
||||
savedObjectId: savedQuery.id,
|
||||
id: savedQuery.attributes.id,
|
||||
description: savedQuery.attributes.description,
|
||||
query: savedQuery.attributes.query,
|
||||
},
|
||||
})) ?? [],
|
||||
[data?.savedObjects]
|
||||
);
|
||||
|
||||
const handleSavedQueryChange = useCallback(
|
||||
(newSelectedOptions) => {
|
||||
|
@ -73,6 +80,20 @@ const SavedQueriesDropdownComponent: React.FC<SavedQueriesDropdownProps> = ({
|
|||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const savedQueryId = location.state?.form?.savedQueryId;
|
||||
|
||||
if (savedQueryId) {
|
||||
const savedQueryOption = find(['value.savedObjectId', savedQueryId], queryOptions);
|
||||
|
||||
if (savedQueryOption) {
|
||||
handleSavedQueryChange([savedQueryOption]);
|
||||
}
|
||||
|
||||
replace({ state: null });
|
||||
}
|
||||
}, [handleSavedQueryChange, replace, location.state, queryOptions]);
|
||||
|
||||
return (
|
||||
<EuiFormRow
|
||||
label={
|
||||
|
|
|
@ -258,7 +258,7 @@ const ViewResultsInDiscoverActionComponent: React.FC<ViewResultsInDiscoverAction
|
|||
}
|
||||
: {
|
||||
to: 'now',
|
||||
from: 'now-15m',
|
||||
from: 'now-1d',
|
||||
mode: 'relative',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { estypes } from '@elastic/elasticsearch';
|
||||
|
||||
import { ISearchRequestParams } from '../../../../../../../../../src/plugins/data/common';
|
||||
import { AgentsRequestOptions } from '../../../../../../common/search_strategy';
|
||||
// import { createQueryFilterClauses } from '../../../../../../common/utils/build_query';
|
||||
|
@ -24,10 +26,23 @@ export const buildActionsQuery = ({
|
|||
body: {
|
||||
// query: { bool: { filter } },
|
||||
query: {
|
||||
term: {
|
||||
type: {
|
||||
value: 'INPUT_ACTION',
|
||||
},
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
term: {
|
||||
type: {
|
||||
value: 'INPUT_ACTION',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
term: {
|
||||
input_type: {
|
||||
value: 'osquery',
|
||||
},
|
||||
},
|
||||
},
|
||||
] as estypes.QueryDslQueryContainer[],
|
||||
},
|
||||
},
|
||||
from: cursorStart,
|
||||
|
|
Loading…
Reference in a new issue