Skip to content

Commit

Permalink
[Osquery] Add multiline query support (#131224)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomsonpl authored May 19, 2022
1 parent d8a6258 commit 5ecde4b
Show file tree
Hide file tree
Showing 14 changed files with 428 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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.
*/

export const removeMultilines = (query: string): string =>
query.replaceAll('\n', ' ').replaceAll(/ +/g, ' ');
4 changes: 1 addition & 3 deletions x-pack/plugins/osquery/public/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ const OsqueryEditorComponent: React.FC<OsqueryEditorProps> = ({
}) => {
const [editorValue, setEditorValue] = useState(defaultValue ?? '');

useDebounce(() => onChange(editorValue.replaceAll('\n', ' ').replaceAll(' ', ' ')), 500, [
editorValue,
]);
useDebounce(() => onChange(editorValue), 500, [editorValue]);

useEffect(() => setEditorValue(defaultValue), [defaultValue]);

Expand Down
267 changes: 180 additions & 87 deletions x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
EuiPanel,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage, FormattedDate, FormattedTime, FormattedRelative } from '@kbn/i18n-react';
import { FormattedDate, FormattedTime, FormattedRelative } from '@kbn/i18n-react';
import moment from 'moment-timezone';

import type {
Expand All @@ -32,6 +32,7 @@ import type {
} from '@kbn/lens-plugin/public';
import { DOCUMENT_FIELD_NAME as RECORDS_FIELD } from '@kbn/lens-plugin/common/constants';
import { FilterStateStore, DataView } from '@kbn/data-plugin/common';
import { removeMultilines } from '../../common/utils/build_query/remove_multilines';
import { useKibana } from '../common/lib/kibana';
import { OsqueryManagerPackagePolicyInputStream } from '../../common/types';
import { ScheduledQueryErrorsTable } from './scheduled_query_errors_table';
Expand Down Expand Up @@ -384,6 +385,13 @@ const ScheduledQueryExpandedContent = React.memo<ScheduledQueryExpandedContentPr
ScheduledQueryExpandedContent.displayName = 'ScheduledQueryExpandedContent';

interface ScheduledQueryLastResultsProps {
actionId: string;
queryId?: string;
interval: number;
logsDataView: DataView | undefined;
}

interface ScheduledQueryErrorsProps {
actionId: string;
queryId: string;
interval: number;
Expand All @@ -394,34 +402,20 @@ interface ScheduledQueryLastResultsProps {

const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
actionId,
queryId,
interval,
logsDataView,
toggleErrors,
expanded,
}) => {
const { data: lastResultsData, isLoading } = usePackQueryLastResults({
actionId,
interval,
logsDataView,
});

const { data: errorsData, isLoading: errorsLoading } = usePackQueryErrors({
actionId,
interval,
logsDataView,
});

const handleErrorsToggle = useCallback(
() => toggleErrors({ queryId, interval }),
[queryId, interval, toggleErrors]
);

if (isLoading || errorsLoading) {
if (isLoading) {
return <EuiLoadingSpinner />;
}

if (!lastResultsData && !errorsData?.total) {
if (!lastResultsData) {
return <>{'-'}</>;
}

Expand All @@ -448,73 +442,115 @@ const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
'-'
)}
</EuiFlexItem>
<EuiFlexItem grow={4}>
<EuiFlexGroup gutterSize="s" alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color="subdued">
{lastResultsData?.docCount ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.osquery.queriesStatusTable.documentLabelText"
defaultMessage="{count, plural, one {Document} other {Documents}}"
// eslint-disable-next-line react-perf/jsx-no-new-object-as-prop
values={{ count: lastResultsData?.docCount as number }}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
);
};

<EuiFlexItem grow={4}>
<EuiFlexGroup gutterSize="s" alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color="subdued">
{lastResultsData?.uniqueAgentsCount ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.osquery.queriesStatusTable.agentsLabelText"
defaultMessage="{count, plural, one {Agent} other {Agents}}"
// eslint-disable-next-line react-perf/jsx-no-new-object-as-prop
values={{ count: lastResultsData?.uniqueAgentsCount ?? 0 }}
/>
</EuiFlexItem>
</EuiFlexGroup>
const DocsColumnResults: React.FC<ScheduledQueryLastResultsProps> = ({
actionId,
interval,
logsDataView,
}) => {
const { data: lastResultsData, isLoading } = usePackQueryLastResults({
actionId,
interval,
logsDataView,
});
if (isLoading) {
return <EuiLoadingSpinner />;
}

if (!lastResultsData) {
return <>{'-'}</>;
}

return (
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color="subdued">
{lastResultsData?.docCount ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>
</EuiFlexGroup>
);
};

<EuiFlexItem grow={5}>
<EuiFlexGroup gutterSize="s" alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color={errorsData?.total ? 'accent' : 'subdued'}>
{errorsData?.total ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>

<EuiFlexItem grow={false}>
{' '}
<FormattedMessage
id="xpack.osquery.queriesStatusTable.errorsLabelText"
defaultMessage="{count, plural, one {Error} other {Errors}}"
// eslint-disable-next-line react-perf/jsx-no-new-object-as-prop
values={{ count: errorsData?.total as number }}
/>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<EuiButtonIcon
isDisabled={!errorsData?.total}
onClick={handleErrorsToggle}
iconType={expanded ? 'arrowUp' : 'arrowDown'}
/>
</EuiFlexItem>
</EuiFlexGroup>
const AgentsColumnResults: React.FC<ScheduledQueryLastResultsProps> = ({
actionId,
interval,
logsDataView,
}) => {
const { data: lastResultsData, isLoading } = usePackQueryLastResults({
actionId,
interval,
logsDataView,
});
if (isLoading) {
return <EuiLoadingSpinner />;
}

if (!lastResultsData) {
return <>{'-'}</>;
}

return (
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color="subdued">
{lastResultsData?.uniqueAgentsCount ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>
</EuiFlexGroup>
);
};

const ErrorsColumnResults: React.FC<ScheduledQueryErrorsProps> = ({
actionId,
interval,
queryId,
toggleErrors,
expanded,
logsDataView,
}) => {
const handleErrorsToggle = useCallback(
() => toggleErrors({ queryId, interval }),
[toggleErrors, queryId, interval]
);

const { data: errorsData, isLoading: errorsLoading } = usePackQueryErrors({
actionId,
interval,
logsDataView,
});
if (errorsLoading) {
return <EuiLoadingSpinner />;
}

if (!errorsData?.total) {
return <>{'-'}</>;
}

return (
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s" alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
<EuiNotificationBadge color={errorsData?.total ? 'accent' : 'subdued'}>
{errorsData?.total ?? 0}
</EuiNotificationBadge>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<EuiButtonIcon
isDisabled={!errorsData?.total}
onClick={handleErrorsToggle}
iconType={expanded ? 'arrowUp' : 'arrowDown'}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
);
};

const getPackActionId = (actionId: string, packName: string) => `pack_${packName}_${actionId}`;

interface PackViewInActionProps {
Expand Down Expand Up @@ -625,14 +661,18 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
fetchLogsDataView();
}, [dataViews]);

const renderQueryColumn = useCallback(
(query: string) => (
<EuiCodeBlock language="sql" fontSize="s" paddingSize="none" transparentBackground>
{query}
</EuiCodeBlock>
),
[]
);
const renderQueryColumn = useCallback((query: string, item) => {
const singleLine = removeMultilines(query);
const content = singleLine.length > 55 ? `${singleLine.substring(0, 55)}...` : singleLine;

return (
<EuiToolTip title={item.id} content={<EuiFlexItem>{query}</EuiFlexItem>}>
<EuiCodeBlock language="sql" fontSize="s" paddingSize="none" transparentBackground>
{content}
</EuiCodeBlock>
</EuiToolTip>
);
}, []);

const toggleErrors = useCallback(
({ queryId, interval }: { queryId: string; interval: number }) => {
Expand All @@ -658,14 +698,44 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
(item) => (
<ScheduledQueryLastResults
logsDataView={logsDataView}
queryId={item.id}
actionId={getPackActionId(item.id, packName)}
interval={item.interval}
/>
),
[packName, logsDataView]
);
const renderDocsColumn = useCallback(
(item) => (
<DocsColumnResults
logsDataView={logsDataView}
actionId={getPackActionId(item.id, packName)}
interval={item.interval}
/>
),
[logsDataView, packName]
);
const renderAgentsColumn = useCallback(
(item) => (
<AgentsColumnResults
logsDataView={logsDataView}
actionId={getPackActionId(item.id, packName)}
interval={item.interval}
/>
),
[logsDataView, packName]
);
const renderErrorsColumn = useCallback(
(item) => (
<ErrorsColumnResults
queryId={item.id}
interval={item.interval}
actionId={getPackActionId(item.id, packName)}
toggleErrors={toggleErrors}
logsDataView={logsDataView}
expanded={!!itemIdToExpandedRowMap[item.id]}
/>
),
[itemIdToExpandedRowMap, packName, toggleErrors, logsDataView]
[itemIdToExpandedRowMap, logsDataView, packName, toggleErrors]
);

const renderDiscoverResultsAction = useCallback(
Expand Down Expand Up @@ -705,6 +775,7 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
defaultMessage: 'ID',
}),
width: '15%',
truncateText: true,
},
{
field: 'interval',
Expand All @@ -719,13 +790,32 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
defaultMessage: 'Query',
}),
render: renderQueryColumn,
width: '20%',
width: '40%',
},
{
name: i18n.translate('xpack.osquery.pack.queriesTable.lastResultsColumnTitle', {
defaultMessage: 'Last results',
}),
render: renderLastResultsColumn,
width: '12%',
},
{
name: i18n.translate('xpack.osquery.pack.queriesTable.docsResultsColumnTitle', {
defaultMessage: 'Docs',
}),
render: renderDocsColumn,
},
{
name: i18n.translate('xpack.osquery.pack.queriesTable.agentsResultsColumnTitle', {
defaultMessage: 'Agents',
}),
render: renderAgentsColumn,
},
{
name: i18n.translate('xpack.osquery.pack.queriesTable.errorsResultsColumnTitle', {
defaultMessage: 'Errors',
}),
render: renderErrorsColumn,
},
{
name: i18n.translate('xpack.osquery.pack.queriesTable.viewResultsColumnTitle', {
Expand All @@ -745,6 +835,9 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
[
renderQueryColumn,
renderLastResultsColumn,
renderDocsColumn,
renderAgentsColumn,
renderErrorsColumn,
renderDiscoverResultsAction,
renderLensResultsAction,
]
Expand Down
Loading

0 comments on commit 5ecde4b

Please sign in to comment.