mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
# Conflicts: # x-pack/plugins/osquery/public/live_queries/form/index.tsx
This commit is contained in:
parent
b5021e1d02
commit
b82a7001e7
24 changed files with 365 additions and 228 deletions
|
@ -57,7 +57,7 @@ export const ecsMapping = t.record(
|
|||
t.string,
|
||||
t.partial({
|
||||
field: t.string,
|
||||
value: t.string,
|
||||
value: t.union([t.string, t.array(t.string)]),
|
||||
})
|
||||
);
|
||||
export type ECSMapping = t.TypeOf<typeof ecsMapping>;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { isArray, pickBy } from 'lodash';
|
||||
import { isArray, isEmpty, pickBy } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiBasicTable, EuiButtonIcon, EuiCodeBlock, formatDate } from '@elastic/eui';
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
|
@ -72,12 +72,15 @@ const ActionsTableComponent = () => {
|
|||
const handlePlayClick = useCallback(
|
||||
(item) =>
|
||||
push('/live_queries/new', {
|
||||
form: pickBy({
|
||||
agentIds: item.fields.agents,
|
||||
query: item._source.data.query,
|
||||
ecs_mapping: item._source.data.ecs_mapping,
|
||||
savedQueryId: item._source.data.saved_query_id,
|
||||
}),
|
||||
form: pickBy(
|
||||
{
|
||||
agentIds: item.fields.agents,
|
||||
query: item._source.data.query,
|
||||
ecs_mapping: item._source.data.ecs_mapping,
|
||||
savedQueryId: item._source.data.saved_query_id,
|
||||
},
|
||||
(value) => !isEmpty(value)
|
||||
),
|
||||
}),
|
||||
[push]
|
||||
);
|
||||
|
|
|
@ -5,14 +5,25 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { EuiIcon, EuiIconProps } from '@elastic/eui';
|
||||
import OsqueryLogo from './osquery.svg';
|
||||
|
||||
export type OsqueryIconProps = Omit<EuiIconProps, 'type'>;
|
||||
|
||||
const OsqueryIconComponent: React.FC<OsqueryIconProps> = (props) => (
|
||||
<EuiIcon size="xl" type={OsqueryLogo} {...props} />
|
||||
);
|
||||
const OsqueryIconComponent: React.FC<OsqueryIconProps> = (props) => {
|
||||
const [Icon, setIcon] = useState<React.ReactElement | null>(null);
|
||||
|
||||
// FIXME: This is a hack to force the icon to be loaded asynchronously.
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setIcon(<EuiIcon size="xl" type={OsqueryLogo} {...props} />);
|
||||
}, 0);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [props, setIcon]);
|
||||
|
||||
return Icon;
|
||||
};
|
||||
|
||||
export const OsqueryIcon = React.memo(OsqueryIconComponent);
|
||||
|
|
|
@ -315,6 +315,16 @@ export const OsqueryManagedPolicyCreateImportExtension = React.memo<
|
|||
streams: [],
|
||||
policy_template: 'osquery_manager',
|
||||
});
|
||||
} else {
|
||||
if (!draft.inputs[0].type) {
|
||||
set(draft, 'inputs[0].type', 'osquery');
|
||||
}
|
||||
if (!draft.inputs[0].policy_template) {
|
||||
set(draft, 'inputs[0].policy_template', 'osquery_manager');
|
||||
}
|
||||
if (!draft.inputs[0].enabled) {
|
||||
set(draft, 'inputs[0].enabled', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
onChange({
|
||||
|
|
|
@ -137,11 +137,6 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
|
|||
type: FIELD_TYPES.JSON,
|
||||
validations: [],
|
||||
},
|
||||
hidden: {
|
||||
defaultValue: false,
|
||||
type: FIELD_TYPES.TOGGLE,
|
||||
validations: [],
|
||||
},
|
||||
};
|
||||
|
||||
const { form } = useForm({
|
||||
|
@ -152,10 +147,15 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
|
|||
|
||||
if (isValid) {
|
||||
try {
|
||||
await mutateAsync({
|
||||
...formData,
|
||||
...(isEmpty(ecsFieldValue) ? {} : { ecs_mapping: ecsFieldValue }),
|
||||
});
|
||||
await mutateAsync(
|
||||
pickBy(
|
||||
{
|
||||
...formData,
|
||||
...(isEmpty(ecsFieldValue) ? {} : { ecs_mapping: ecsFieldValue }),
|
||||
},
|
||||
(value) => !isEmpty(value)
|
||||
)
|
||||
);
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
}
|
||||
|
@ -163,10 +163,8 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
|
|||
options: {
|
||||
stripEmptyFields: false,
|
||||
},
|
||||
serializer: ({ savedQueryId, hidden, ...formData }) => ({
|
||||
...pickBy({ ...formData, saved_query_id: savedQueryId }),
|
||||
...(hidden != null && hidden ? { hidden } : {}),
|
||||
}),
|
||||
serializer: ({ savedQueryId, ...formData }) =>
|
||||
pickBy({ ...formData, saved_query_id: savedQueryId }, (value) => !isEmpty(value)),
|
||||
defaultValue: deepMerge(
|
||||
{
|
||||
agentSelection: {
|
||||
|
@ -177,7 +175,6 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
|
|||
},
|
||||
query: '',
|
||||
savedQueryId: null,
|
||||
hidden: false,
|
||||
},
|
||||
defaultValue ?? {}
|
||||
),
|
||||
|
@ -419,9 +416,6 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
|
|||
if (defaultValue?.query) {
|
||||
setFieldValue('query', defaultValue?.query);
|
||||
}
|
||||
if (defaultValue?.hidden) {
|
||||
setFieldValue('hidden', defaultValue?.hidden);
|
||||
}
|
||||
// TODO: Set query and ECS mapping from savedQueryId object
|
||||
if (defaultValue?.savedQueryId) {
|
||||
setFieldValue('savedQueryId', defaultValue?.savedQueryId);
|
||||
|
@ -436,7 +430,6 @@ const LiveQueryFormComponent: React.FC<LiveQueryFormProps> = ({
|
|||
<Form form={form}>
|
||||
{formType === 'steps' ? <EuiSteps steps={formSteps} /> : simpleForm}
|
||||
<UseField path="savedQueryId" component={GhostFormField} />
|
||||
<UseField path="hidden" component={GhostFormField} />
|
||||
</Form>
|
||||
{showSavedQueryFlyout ? (
|
||||
<SavedQueryFlyout
|
||||
|
|
|
@ -105,7 +105,7 @@ const PackFormComponent: React.FC<PackFormProps> = ({ defaultValue, editMode = f
|
|||
defaultValue: [],
|
||||
type: FIELD_TYPES.COMBO_BOX,
|
||||
label: i18n.translate('xpack.osquery.pack.form.agentPoliciesFieldLabel', {
|
||||
defaultMessage: 'Agent policies (optional)',
|
||||
defaultMessage: 'Scheduled agent policies (optional)',
|
||||
}),
|
||||
helpText: i18n.translate('xpack.osquery.pack.form.agentPoliciesFieldHelpText', {
|
||||
defaultMessage: 'Queries in this pack are scheduled for agents in the selected policies.',
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { findIndex, forEach, pullAt, pullAllBy, pickBy } from 'lodash';
|
||||
import { isEmpty, findIndex, forEach, pullAt, pullAllBy, pickBy } from 'lodash';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiSpacer } from '@elastic/eui';
|
||||
import { produce } from 'immer';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
|
@ -133,13 +133,16 @@ const QueriesFieldComponent: React.FC<QueriesFieldProps> = ({ field, handleNameC
|
|||
produce((draft) => {
|
||||
forEach(parsedContent.queries, (newQuery, newQueryId) => {
|
||||
draft.push(
|
||||
pickBy({
|
||||
id: newQueryId,
|
||||
interval: newQuery.interval ?? parsedContent.interval,
|
||||
query: newQuery.query,
|
||||
version: newQuery.version ?? parsedContent.version,
|
||||
platform: getSupportedPlatforms(newQuery.platform ?? parsedContent.platform),
|
||||
})
|
||||
pickBy(
|
||||
{
|
||||
id: newQueryId,
|
||||
interval: newQuery.interval ?? parsedContent.interval,
|
||||
query: newQuery.query,
|
||||
version: newQuery.version ?? parsedContent.version,
|
||||
platform: getSupportedPlatforms(newQuery.platform ?? parsedContent.platform),
|
||||
},
|
||||
(value) => !isEmpty(value)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ import {
|
|||
PersistedIndexPatternLayer,
|
||||
PieVisualizationState,
|
||||
} from '../../../lens/public';
|
||||
import { FilterStateStore, IndexPattern } from '../../../../../src/plugins/data/common';
|
||||
import { FilterStateStore, DataView } from '../../../../../src/plugins/data/common';
|
||||
import { useKibana } from '../common/lib/kibana';
|
||||
import { OsqueryManagerPackagePolicyInputStream } from '../../common/types';
|
||||
import { ScheduledQueryErrorsTable } from './scheduled_query_errors_table';
|
||||
|
@ -130,12 +130,12 @@ function getLensAttributes(
|
|||
references: [
|
||||
{
|
||||
id: 'logs-*',
|
||||
name: 'indexpattern-datasource-current-indexpattern',
|
||||
name: 'dataView-datasource-current-dataView',
|
||||
type: 'index-pattern',
|
||||
},
|
||||
{
|
||||
id: 'logs-*',
|
||||
name: 'indexpattern-datasource-layer-layer1',
|
||||
name: 'dataView-datasource-layer-layer1',
|
||||
type: 'index-pattern',
|
||||
},
|
||||
{
|
||||
|
@ -377,7 +377,7 @@ interface ScheduledQueryLastResultsProps {
|
|||
actionId: string;
|
||||
queryId: string;
|
||||
interval: number;
|
||||
logsIndexPattern: IndexPattern | undefined;
|
||||
logsDataView: DataView | undefined;
|
||||
toggleErrors: (payload: { queryId: string; interval: number }) => void;
|
||||
expanded: boolean;
|
||||
}
|
||||
|
@ -386,20 +386,20 @@ const ScheduledQueryLastResults: React.FC<ScheduledQueryLastResultsProps> = ({
|
|||
actionId,
|
||||
queryId,
|
||||
interval,
|
||||
logsIndexPattern,
|
||||
logsDataView,
|
||||
toggleErrors,
|
||||
expanded,
|
||||
}) => {
|
||||
const { data: lastResultsData, isFetched } = usePackQueryLastResults({
|
||||
actionId,
|
||||
interval,
|
||||
logsIndexPattern,
|
||||
logsDataView,
|
||||
});
|
||||
|
||||
const { data: errorsData, isFetched: errorsFetched } = usePackQueryErrors({
|
||||
actionId,
|
||||
interval,
|
||||
logsIndexPattern,
|
||||
logsDataView,
|
||||
});
|
||||
|
||||
const handleErrorsToggle = useCallback(
|
||||
|
@ -512,14 +512,14 @@ interface PackViewInActionProps {
|
|||
id: string;
|
||||
interval: number;
|
||||
};
|
||||
logsIndexPattern: IndexPattern | undefined;
|
||||
logsDataView: DataView | undefined;
|
||||
packName: string;
|
||||
agentIds?: string[];
|
||||
}
|
||||
|
||||
const PackViewInDiscoverActionComponent: React.FC<PackViewInActionProps> = ({
|
||||
item,
|
||||
logsIndexPattern,
|
||||
logsDataView,
|
||||
packName,
|
||||
agentIds,
|
||||
}) => {
|
||||
|
@ -528,7 +528,7 @@ const PackViewInDiscoverActionComponent: React.FC<PackViewInActionProps> = ({
|
|||
const { data: lastResultsData } = usePackQueryLastResults({
|
||||
actionId,
|
||||
interval,
|
||||
logsIndexPattern,
|
||||
logsDataView,
|
||||
});
|
||||
|
||||
const startDate = lastResultsData?.['@timestamp']
|
||||
|
@ -554,7 +554,7 @@ const PackViewInDiscoverAction = React.memo(PackViewInDiscoverActionComponent);
|
|||
|
||||
const PackViewInLensActionComponent: React.FC<PackViewInActionProps> = ({
|
||||
item,
|
||||
logsIndexPattern,
|
||||
logsDataView,
|
||||
packName,
|
||||
agentIds,
|
||||
}) => {
|
||||
|
@ -563,7 +563,7 @@ const PackViewInLensActionComponent: React.FC<PackViewInActionProps> = ({
|
|||
const { data: lastResultsData } = usePackQueryLastResults({
|
||||
actionId,
|
||||
interval,
|
||||
logsIndexPattern,
|
||||
logsDataView,
|
||||
});
|
||||
|
||||
const startDate = lastResultsData?.['@timestamp']
|
||||
|
@ -602,17 +602,17 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
|
|||
Record<string, ReturnType<typeof ScheduledQueryExpandedContent>>
|
||||
>({});
|
||||
|
||||
const indexPatterns = useKibana().services.data.indexPatterns;
|
||||
const [logsIndexPattern, setLogsIndexPattern] = useState<IndexPattern | undefined>(undefined);
|
||||
const dataViews = useKibana().services.data.dataViews;
|
||||
const [logsDataView, setLogsDataView] = useState<DataView | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchLogsIndexPattern = async () => {
|
||||
const indexPattern = await indexPatterns.find('logs-*');
|
||||
const fetchLogsDataView = async () => {
|
||||
const dataView = await dataViews.find('logs-*');
|
||||
|
||||
setLogsIndexPattern(indexPattern[0]);
|
||||
setLogsDataView(dataView[0]);
|
||||
};
|
||||
fetchLogsIndexPattern();
|
||||
}, [indexPatterns]);
|
||||
fetchLogsDataView();
|
||||
}, [dataViews]);
|
||||
|
||||
const renderQueryColumn = useCallback(
|
||||
(query: string) => (
|
||||
|
@ -645,7 +645,7 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
|
|||
const renderLastResultsColumn = useCallback(
|
||||
(item) => (
|
||||
<ScheduledQueryLastResults
|
||||
logsIndexPattern={logsIndexPattern}
|
||||
logsDataView={logsDataView}
|
||||
queryId={item.id}
|
||||
actionId={getPackActionId(item.id, packName)}
|
||||
interval={item.interval}
|
||||
|
@ -653,7 +653,7 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
|
|||
expanded={!!itemIdToExpandedRowMap[item.id]}
|
||||
/>
|
||||
),
|
||||
[itemIdToExpandedRowMap, packName, toggleErrors, logsIndexPattern]
|
||||
[itemIdToExpandedRowMap, packName, toggleErrors, logsDataView]
|
||||
);
|
||||
|
||||
const renderDiscoverResultsAction = useCallback(
|
||||
|
@ -661,11 +661,11 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
|
|||
<PackViewInDiscoverAction
|
||||
item={item}
|
||||
agentIds={agentIds}
|
||||
logsIndexPattern={logsIndexPattern}
|
||||
logsDataView={logsDataView}
|
||||
packName={packName}
|
||||
/>
|
||||
),
|
||||
[agentIds, logsIndexPattern, packName]
|
||||
[agentIds, logsDataView, packName]
|
||||
);
|
||||
|
||||
const renderLensResultsAction = useCallback(
|
||||
|
@ -673,11 +673,11 @@ const PackQueriesStatusTableComponent: React.FC<PackQueriesStatusTableProps> = (
|
|||
<PackViewInLensAction
|
||||
item={item}
|
||||
agentIds={agentIds}
|
||||
logsIndexPattern={logsIndexPattern}
|
||||
logsDataView={logsDataView}
|
||||
packName={packName}
|
||||
/>
|
||||
),
|
||||
[agentIds, logsIndexPattern, packName]
|
||||
[agentIds, logsDataView, packName]
|
||||
);
|
||||
|
||||
const getItemId = useCallback(
|
||||
|
|
|
@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { PlatformIcons } from './queries/platforms';
|
||||
import { OsqueryManagerPackagePolicyInputStream } from '../../common/types';
|
||||
|
||||
interface PackQueriesTableProps {
|
||||
export interface PackQueriesTableProps {
|
||||
data: OsqueryManagerPackagePolicyInputStream[];
|
||||
onDeleteClick?: (item: OsqueryManagerPackagePolicyInputStream) => void;
|
||||
onEditClick?: (item: OsqueryManagerPackagePolicyInputStream) => void;
|
||||
|
@ -184,3 +184,5 @@ const PackQueriesTableComponent: React.FC<PackQueriesTableProps> = ({
|
|||
};
|
||||
|
||||
export const PackQueriesTable = React.memo(PackQueriesTableComponent);
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default PackQueriesTable;
|
||||
|
|
|
@ -52,7 +52,7 @@ export const AgentPoliciesPopover = ({ agentPolicyIds }: { agentPolicyIds: strin
|
|||
|
||||
const button = useMemo(
|
||||
() => (
|
||||
<EuiButtonEmpty flush="both" onClick={onButtonClick}>
|
||||
<EuiButtonEmpty size="s" flush="both" onClick={onButtonClick}>
|
||||
<>{agentPolicyIds?.length ?? 0}</>
|
||||
</EuiButtonEmpty>
|
||||
),
|
||||
|
|
|
@ -6,7 +6,18 @@
|
|||
*/
|
||||
|
||||
import { produce } from 'immer';
|
||||
import { each, isEmpty, find, orderBy, sortedUniqBy, isArray, map, reduce, get } from 'lodash';
|
||||
import {
|
||||
castArray,
|
||||
each,
|
||||
isEmpty,
|
||||
find,
|
||||
orderBy,
|
||||
sortedUniqBy,
|
||||
isArray,
|
||||
map,
|
||||
reduce,
|
||||
get,
|
||||
} from 'lodash';
|
||||
import React, {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
|
@ -81,30 +92,24 @@ const typeMap = {
|
|||
};
|
||||
|
||||
const StyledEuiSuperSelect = styled(EuiSuperSelect)`
|
||||
&.euiFormControlLayout__prepend {
|
||||
padding-left: 8px;
|
||||
padding-right: 24px;
|
||||
box-shadow: none;
|
||||
min-width: 70px;
|
||||
border-radius: 6px 0 0 6px;
|
||||
|
||||
.euiIcon {
|
||||
padding: 0;
|
||||
width: 18px;
|
||||
background: none;
|
||||
}
|
||||
.euiIcon {
|
||||
padding: 0;
|
||||
width: 18px;
|
||||
background: none;
|
||||
}
|
||||
`;
|
||||
|
||||
// @ts-expect-error update types
|
||||
const ResultComboBox = styled(EuiComboBox)`
|
||||
&.euiComboBox--prepended .euiSuperSelect {
|
||||
border-right: 1px solid ${(props) => props.theme.eui.euiBorderColor};
|
||||
&.euiComboBox {
|
||||
position: relative;
|
||||
left: -1px;
|
||||
|
||||
.euiFormControlLayout__childrenWrapper {
|
||||
border-radius: 6px 0 0 6px;
|
||||
|
||||
.euiFormControlLayoutIcons--right {
|
||||
right: 6px;
|
||||
}
|
||||
.euiComboBox__inputWrap {
|
||||
border-radius: 0 6px 6px 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
@ -311,9 +316,11 @@ const OSQUERY_COLUMN_VALUE_TYPE_OPTIONS = [
|
|||
},
|
||||
];
|
||||
|
||||
const EMPTY_ARRAY: EuiComboBoxOptionOption[] = [];
|
||||
|
||||
interface OsqueryColumnFieldProps {
|
||||
resultType: FieldHook<string>;
|
||||
resultValue: FieldHook<string>;
|
||||
resultValue: FieldHook<string | string[]>;
|
||||
euiFieldProps: EuiComboBoxProps<OsquerySchemaOption>;
|
||||
idAria?: string;
|
||||
}
|
||||
|
@ -324,6 +331,7 @@ const OsqueryColumnFieldComponent: React.FC<OsqueryColumnFieldProps> = ({
|
|||
euiFieldProps = {},
|
||||
idAria,
|
||||
}) => {
|
||||
const inputRef = useRef<HTMLInputElement>();
|
||||
const { setValue } = resultValue;
|
||||
const { setValue: setType } = resultType;
|
||||
const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(resultValue);
|
||||
|
@ -367,16 +375,27 @@ const OsqueryColumnFieldComponent: React.FC<OsqueryColumnFieldProps> = ({
|
|||
(newType) => {
|
||||
if (newType !== resultType.value) {
|
||||
setType(newType);
|
||||
setValue(newType === 'value' && euiFieldProps.singleSelection === false ? [] : '');
|
||||
}
|
||||
},
|
||||
[setType, resultType.value]
|
||||
[resultType.value, setType, setValue, euiFieldProps.singleSelection]
|
||||
);
|
||||
|
||||
const handleCreateOption = useCallback(
|
||||
(newOption) => {
|
||||
setValue(newOption);
|
||||
(newOption: string) => {
|
||||
if (euiFieldProps.singleSelection === false) {
|
||||
setValue([newOption]);
|
||||
if (resultValue.value.length) {
|
||||
setValue([...castArray(resultValue.value), newOption]);
|
||||
} else {
|
||||
setValue([newOption]);
|
||||
}
|
||||
inputRef.current?.blur();
|
||||
} else {
|
||||
setValue(newOption);
|
||||
}
|
||||
},
|
||||
[setValue]
|
||||
[euiFieldProps.singleSelection, resultValue.value, setValue]
|
||||
);
|
||||
|
||||
const Prepend = useMemo(
|
||||
|
@ -400,6 +419,11 @@ const OsqueryColumnFieldComponent: React.FC<OsqueryColumnFieldProps> = ({
|
|||
setSelected(() => {
|
||||
if (!resultValue.value.length) return [];
|
||||
|
||||
// Static array values
|
||||
if (isArray(resultValue.value)) {
|
||||
return resultValue.value.map((value) => ({ label: value }));
|
||||
}
|
||||
|
||||
const selectedOption = find(euiFieldProps?.options, ['label', resultValue.value]);
|
||||
|
||||
return selectedOption ? [selectedOption] : [{ label: resultValue.value }];
|
||||
|
@ -416,18 +440,26 @@ const OsqueryColumnFieldComponent: React.FC<OsqueryColumnFieldProps> = ({
|
|||
describedByIds={describedByIds}
|
||||
isDisabled={euiFieldProps.isDisabled}
|
||||
>
|
||||
<ResultComboBox
|
||||
fullWidth
|
||||
prepend={Prepend}
|
||||
singleSelection={singleSelection}
|
||||
selectedOptions={selectedOptions}
|
||||
onChange={handleChange}
|
||||
onCreateOption={handleCreateOption}
|
||||
renderOption={renderOsqueryOption}
|
||||
rowHeight={32}
|
||||
isClearable
|
||||
{...euiFieldProps}
|
||||
/>
|
||||
<EuiFlexGroup gutterSize="none">
|
||||
<EuiFlexItem grow={false}>{Prepend}</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<ResultComboBox
|
||||
// eslint-disable-next-line react/jsx-no-bind, react-perf/jsx-no-new-function-as-prop
|
||||
inputRef={(ref: HTMLInputElement) => {
|
||||
inputRef.current = ref;
|
||||
}}
|
||||
fullWidth
|
||||
selectedOptions={selectedOptions}
|
||||
onChange={handleChange}
|
||||
onCreateOption={handleCreateOption}
|
||||
renderOption={renderOsqueryOption}
|
||||
rowHeight={32}
|
||||
isClearable
|
||||
{...euiFieldProps}
|
||||
options={(resultType.value === 'field' && euiFieldProps.options) || EMPTY_ARRAY}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFormRow>
|
||||
);
|
||||
};
|
||||
|
@ -497,7 +529,7 @@ const getOsqueryResultFieldValidator =
|
|||
) => {
|
||||
const fieldRequiredError = fieldValidators.emptyField(
|
||||
i18n.translate('xpack.osquery.pack.queryFlyoutForm.osqueryResultFieldRequiredErrorMessage', {
|
||||
defaultMessage: 'Osquery result is required.',
|
||||
defaultMessage: 'Value is required.',
|
||||
})
|
||||
)(args);
|
||||
|
||||
|
@ -551,6 +583,7 @@ interface ECSMappingEditorFormRef {
|
|||
export const ECSMappingEditorForm = forwardRef<ECSMappingEditorFormRef, ECSMappingEditorFormProps>(
|
||||
({ isDisabled, osquerySchemaOptions, defaultValue, onAdd, onChange, onDelete }, ref) => {
|
||||
const editForm = !!defaultValue;
|
||||
const multipleValuesField = useRef(false);
|
||||
const currentFormData = useRef(defaultValue);
|
||||
const formSchema = {
|
||||
key: {
|
||||
|
@ -648,6 +681,8 @@ export const ECSMappingEditorForm = forwardRef<ECSMappingEditorFormRef, ECSMappi
|
|||
// @ts-expect-error update types
|
||||
options: osquerySchemaOptions,
|
||||
isDisabled,
|
||||
// @ts-expect-error update types
|
||||
singleSelection: !multipleValuesField.current ? { asPlainText: true } : false,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
@ -687,17 +722,13 @@ export const ECSMappingEditorForm = forwardRef<ECSMappingEditorFormRef, ECSMappi
|
|||
useEffect(() => {
|
||||
if (!deepEqual(formData, currentFormData.current)) {
|
||||
currentFormData.current = formData;
|
||||
const ecsOption = find(ECSSchemaOptions, ['label', formData.key]);
|
||||
multipleValuesField.current =
|
||||
ecsOption?.value?.normalization === 'array' && formData.result.type === 'value';
|
||||
handleSubmit();
|
||||
}
|
||||
}, [handleSubmit, formData, onAdd]);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (defaultValue) {
|
||||
// validate();
|
||||
// __validateFields(['result.value']);
|
||||
// }
|
||||
// }, [defaultValue, osquerySchemaOptions, validate, __validateFields]);
|
||||
|
||||
return (
|
||||
<Form form={form}>
|
||||
<EuiFlexGroup alignItems="flexStart" gutterSize="s">
|
||||
|
|
|
@ -6,16 +6,27 @@
|
|||
*/
|
||||
|
||||
import { EuiIcon } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { getPlatformIconModule } from './helpers';
|
||||
|
||||
interface PlatformIconProps {
|
||||
export interface PlatformIconProps {
|
||||
platform: string;
|
||||
}
|
||||
|
||||
const PlatformIconComponent: React.FC<PlatformIconProps> = ({ platform }) => {
|
||||
const platformIconModule = getPlatformIconModule(platform);
|
||||
return <EuiIcon type={platformIconModule} title={platform} size="l" />;
|
||||
const [Icon, setIcon] = useState<React.ReactElement | null>(null);
|
||||
|
||||
// FIXME: This is a hack to force the icon to be loaded asynchronously.
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
const platformIconModule = getPlatformIconModule(platform);
|
||||
setIcon(<EuiIcon type={platformIconModule} title={platform} size="l" />);
|
||||
}, 0);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [platform, setIcon]);
|
||||
|
||||
return Icon;
|
||||
};
|
||||
|
||||
export const PlatformIcon = React.memo(PlatformIconComponent);
|
||||
|
|
|
@ -6,21 +6,21 @@
|
|||
*/
|
||||
|
||||
import { useQuery } from 'react-query';
|
||||
import { IndexPattern, SortDirection } from '../../../../../src/plugins/data/common';
|
||||
import { DataView, SortDirection } from '../../../../../src/plugins/data/common';
|
||||
|
||||
import { useKibana } from '../common/lib/kibana';
|
||||
|
||||
interface UsePackQueryErrorsProps {
|
||||
actionId: string;
|
||||
interval: number;
|
||||
logsIndexPattern?: IndexPattern;
|
||||
logsDataView?: DataView;
|
||||
skip?: boolean;
|
||||
}
|
||||
|
||||
export const usePackQueryErrors = ({
|
||||
actionId,
|
||||
interval,
|
||||
logsIndexPattern,
|
||||
logsDataView,
|
||||
skip = false,
|
||||
}: UsePackQueryErrorsProps) => {
|
||||
const data = useKibana().services.data;
|
||||
|
@ -29,7 +29,7 @@ export const usePackQueryErrors = ({
|
|||
['scheduledQueryErrors', { actionId, interval }],
|
||||
async () => {
|
||||
const searchSource = await data.search.searchSource.create({
|
||||
index: logsIndexPattern,
|
||||
index: logsDataView,
|
||||
fields: ['*'],
|
||||
sort: [
|
||||
{
|
||||
|
@ -73,7 +73,7 @@ export const usePackQueryErrors = ({
|
|||
},
|
||||
{
|
||||
keepPreviousData: true,
|
||||
enabled: !!(!skip && actionId && interval && logsIndexPattern),
|
||||
enabled: !!(!skip && actionId && interval && logsDataView),
|
||||
select: (response) => response.rawResponse.hits ?? [],
|
||||
refetchOnReconnect: false,
|
||||
refetchOnWindowFocus: false,
|
||||
|
|
|
@ -7,21 +7,21 @@
|
|||
|
||||
import { useQuery } from 'react-query';
|
||||
import moment from 'moment-timezone';
|
||||
import { IndexPattern } from '../../../../../src/plugins/data/common';
|
||||
import { DataView, SortDirection } from '../../../../../src/plugins/data/common';
|
||||
import { useKibana } from '../common/lib/kibana';
|
||||
|
||||
interface UsePackQueryLastResultsProps {
|
||||
actionId: string;
|
||||
agentIds?: string[];
|
||||
interval: number;
|
||||
logsIndexPattern?: IndexPattern;
|
||||
logsDataView?: DataView;
|
||||
skip?: boolean;
|
||||
}
|
||||
|
||||
export const usePackQueryLastResults = ({
|
||||
actionId,
|
||||
interval,
|
||||
logsIndexPattern,
|
||||
logsDataView,
|
||||
skip = false,
|
||||
}: UsePackQueryLastResultsProps) => {
|
||||
const data = useKibana().services.data;
|
||||
|
@ -30,8 +30,9 @@ export const usePackQueryLastResults = ({
|
|||
['scheduledQueryLastResults', { actionId }],
|
||||
async () => {
|
||||
const lastResultsSearchSource = await data.search.searchSource.create({
|
||||
index: logsIndexPattern,
|
||||
index: logsDataView,
|
||||
size: 1,
|
||||
sort: { '@timestamp': SortDirection.desc },
|
||||
query: {
|
||||
// @ts-expect-error update types
|
||||
bool: {
|
||||
|
@ -51,7 +52,7 @@ export const usePackQueryLastResults = ({
|
|||
|
||||
if (timestamp) {
|
||||
const aggsSearchSource = await data.search.searchSource.create({
|
||||
index: logsIndexPattern,
|
||||
index: logsDataView,
|
||||
size: 1,
|
||||
aggs: {
|
||||
unique_agents: { cardinality: { field: 'agent.id' } },
|
||||
|
@ -92,7 +93,7 @@ export const usePackQueryLastResults = ({
|
|||
},
|
||||
{
|
||||
keepPreviousData: true,
|
||||
enabled: !!(!skip && actionId && interval && logsIndexPattern),
|
||||
enabled: !!(!skip && actionId && interval && logsDataView),
|
||||
refetchOnReconnect: false,
|
||||
refetchOnWindowFocus: false,
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { get, isEmpty, isEqual, keys, map, reduce } from 'lodash/fp';
|
||||
import { get, isEmpty, isArray, isObject, isEqual, keys, map, reduce } from 'lodash/fp';
|
||||
import {
|
||||
EuiCallOut,
|
||||
EuiCode,
|
||||
|
@ -123,6 +123,11 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
|
|||
[visibleColumns, setVisibleColumns]
|
||||
);
|
||||
|
||||
const ecsMappingColumns = useMemo(
|
||||
() => keys(get('actionDetails._source.data.ecs_mapping', actionDetails) || {}),
|
||||
[actionDetails]
|
||||
);
|
||||
|
||||
const renderCellValue: EuiDataGridProps['renderCellValue'] = useMemo(
|
||||
() =>
|
||||
// eslint-disable-next-line react/display-name
|
||||
|
@ -140,9 +145,22 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
|
|||
return <EuiLink href={getFleetAppUrl(agentIdValue)}>{value}</EuiLink>;
|
||||
}
|
||||
|
||||
if (ecsMappingColumns.includes(columnId)) {
|
||||
const ecsFieldValue = get(columnId, data[rowIndex % pagination.pageSize]?._source);
|
||||
|
||||
if (isArray(ecsFieldValue) || isObject(ecsFieldValue)) {
|
||||
try {
|
||||
return JSON.stringify(ecsFieldValue, null, 2);
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
return ecsFieldValue ?? '-';
|
||||
}
|
||||
|
||||
return !isEmpty(value) ? value : '-';
|
||||
},
|
||||
[getFleetAppUrl, pagination.pageSize]
|
||||
[ecsMappingColumns, getFleetAppUrl, pagination.pageSize]
|
||||
);
|
||||
|
||||
const tableSorting = useMemo(
|
||||
|
@ -218,12 +236,17 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
|
|||
return;
|
||||
}
|
||||
|
||||
const newColumns = keys(allResultsData?.edges[0]?.fields)
|
||||
.sort()
|
||||
.reduce(
|
||||
(acc, fieldName) => {
|
||||
const { data, seen } = acc;
|
||||
if (fieldName === 'agent.name') {
|
||||
const fields = [
|
||||
'agent.name',
|
||||
...ecsMappingColumns.sort(),
|
||||
...keys(allResultsData?.edges[0]?.fields || {}).sort(),
|
||||
];
|
||||
|
||||
const newColumns = fields.reduce(
|
||||
(acc, fieldName) => {
|
||||
const { data, seen } = acc;
|
||||
if (fieldName === 'agent.name') {
|
||||
if (!seen.has(fieldName)) {
|
||||
data.push({
|
||||
id: fieldName,
|
||||
displayAsText: i18n.translate(
|
||||
|
@ -234,34 +257,48 @@ const ResultsTableComponent: React.FC<ResultsTableComponentProps> = ({
|
|||
),
|
||||
defaultSortDirection: Direction.asc,
|
||||
});
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (fieldName.startsWith('osquery.')) {
|
||||
const displayAsText = fieldName.split('.')[1];
|
||||
if (!seen.has(displayAsText)) {
|
||||
data.push({
|
||||
id: fieldName,
|
||||
displayAsText,
|
||||
display: getHeaderDisplay(displayAsText),
|
||||
defaultSortDirection: Direction.asc,
|
||||
});
|
||||
seen.add(displayAsText);
|
||||
}
|
||||
return acc;
|
||||
seen.add(fieldName);
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ data: [], seen: new Set<string>() } as { data: EuiDataGridColumn[]; seen: Set<string> }
|
||||
).data;
|
||||
}
|
||||
|
||||
if (ecsMappingColumns.includes(fieldName)) {
|
||||
if (!seen.has(fieldName)) {
|
||||
data.push({
|
||||
id: fieldName,
|
||||
displayAsText: fieldName,
|
||||
defaultSortDirection: Direction.asc,
|
||||
});
|
||||
seen.add(fieldName);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (fieldName.startsWith('osquery.')) {
|
||||
const displayAsText = fieldName.split('.')[1];
|
||||
if (!seen.has(displayAsText)) {
|
||||
data.push({
|
||||
id: fieldName,
|
||||
displayAsText,
|
||||
display: getHeaderDisplay(displayAsText),
|
||||
defaultSortDirection: Direction.asc,
|
||||
});
|
||||
seen.add(displayAsText);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ data: [], seen: new Set<string>() } as { data: EuiDataGridColumn[]; seen: Set<string> }
|
||||
).data;
|
||||
|
||||
setColumns((currentColumns) =>
|
||||
!isEqual(map('id', currentColumns), map('id', newColumns)) ? newColumns : currentColumns
|
||||
);
|
||||
setVisibleColumns(map('id', newColumns));
|
||||
}, [allResultsData?.edges, getHeaderDisplay]);
|
||||
}, [allResultsData?.edges, ecsMappingColumns, getHeaderDisplay]);
|
||||
|
||||
const toolbarVisibility = useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -127,7 +127,7 @@ const OsqueryActionComponent: React.FC<OsqueryActionProps> = ({ metadata }) => {
|
|||
);
|
||||
}
|
||||
|
||||
return <LiveQuery agentId={agentId} />;
|
||||
return <LiveQuery formType="simple" agentId={agentId} />;
|
||||
};
|
||||
|
||||
export const OsqueryAction = React.memo(OsqueryActionComponent);
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { produce } from 'immer';
|
||||
import { SavedObjectsType } from '../../../../../../src/core/server';
|
||||
|
||||
import { savedQuerySavedObjectType, packSavedObjectType } from '../../../common/types';
|
||||
|
||||
export const savedQuerySavedObjectMappings: SavedObjectsType['mappings'] = {
|
||||
|
@ -54,9 +54,13 @@ export const savedQueryType: SavedObjectsType = {
|
|||
namespaceType: 'multiple-isolated',
|
||||
mappings: savedQuerySavedObjectMappings,
|
||||
management: {
|
||||
defaultSearchField: 'id',
|
||||
importableAndExportable: true,
|
||||
getTitle: (savedObject) => savedObject.attributes.id,
|
||||
getEditUrl: (savedObject) => `/saved_queries/${savedObject.id}/edit`,
|
||||
getInAppUrl: (savedObject) => ({
|
||||
path: `/app/saved_queries/${savedObject.id}`,
|
||||
uiCapabilitiesPath: 'osquery.read',
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -117,6 +121,19 @@ export const packType: SavedObjectsType = {
|
|||
management: {
|
||||
defaultSearchField: 'name',
|
||||
importableAndExportable: true,
|
||||
getTitle: (savedObject) => savedObject.attributes.name,
|
||||
getTitle: (savedObject) => `Pack: ${savedObject.attributes.name}`,
|
||||
getEditUrl: (savedObject) => `/packs/${savedObject.id}/edit`,
|
||||
getInAppUrl: (savedObject) => ({
|
||||
path: `/app/packs/${savedObject.id}`,
|
||||
uiCapabilitiesPath: 'osquery.read',
|
||||
}),
|
||||
onExport: (context, objects) =>
|
||||
produce(objects, (draft) => {
|
||||
draft.forEach((packSO) => {
|
||||
packSO.references = [];
|
||||
});
|
||||
|
||||
return draft;
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -227,7 +227,7 @@ export class OsqueryPlugin implements Plugin<OsqueryPluginSetup, OsqueryPluginSt
|
|||
security: plugins.security,
|
||||
};
|
||||
|
||||
initSavedObjects(core.savedObjects, osqueryContext);
|
||||
initSavedObjects(core.savedObjects);
|
||||
initUsageCollectors({
|
||||
core,
|
||||
osqueryContext,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { pickBy } from 'lodash';
|
||||
import { pickBy, isEmpty } from 'lodash';
|
||||
import uuid from 'uuid';
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
|
@ -69,12 +69,15 @@ export const createActionRoute = (router: IRouter, osqueryContext: OsqueryAppCon
|
|||
input_type: 'osquery',
|
||||
agents: selectedAgents,
|
||||
user_id: currentUser,
|
||||
data: pickBy({
|
||||
id: uuid.v4(),
|
||||
query: request.body.query,
|
||||
saved_query_id: request.body.saved_query_id,
|
||||
ecs_mapping: request.body.ecs_mapping,
|
||||
}),
|
||||
data: pickBy(
|
||||
{
|
||||
id: uuid.v4(),
|
||||
query: request.body.query,
|
||||
saved_query_id: request.body.saved_query_id,
|
||||
ecs_mapping: request.body.ecs_mapping,
|
||||
},
|
||||
(value) => !isEmpty(value)
|
||||
),
|
||||
};
|
||||
const actionResponse = await esClient.index<{}, {}>({
|
||||
index: '.fleet-actions',
|
||||
|
|
|
@ -43,7 +43,10 @@ export const createPackRoute = (router: IRouter, osqueryContext: OsqueryAppConte
|
|||
schema.recordOf(
|
||||
schema.string(),
|
||||
schema.object({
|
||||
field: schema.string(),
|
||||
field: schema.maybe(schema.string()),
|
||||
value: schema.maybe(
|
||||
schema.oneOf([schema.string(), schema.arrayOf(schema.string())])
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
|
@ -68,8 +71,7 @@ export const createPackRoute = (router: IRouter, osqueryContext: OsqueryAppConte
|
|||
|
||||
const conflictingEntries = await savedObjectsClient.find({
|
||||
type: packSavedObjectType,
|
||||
search: name,
|
||||
searchFields: ['name'],
|
||||
filter: `${packSavedObjectType}.attributes.name: "${name}"`,
|
||||
});
|
||||
|
||||
if (conflictingEntries.saved_objects.length) {
|
||||
|
|
|
@ -6,7 +6,19 @@
|
|||
*/
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
import { set, unset, has, difference, filter, find, map, mapKeys, pickBy, uniq } from 'lodash';
|
||||
import {
|
||||
isEmpty,
|
||||
set,
|
||||
unset,
|
||||
has,
|
||||
difference,
|
||||
filter,
|
||||
find,
|
||||
map,
|
||||
mapKeys,
|
||||
pickBy,
|
||||
uniq,
|
||||
} from 'lodash';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { produce } from 'immer';
|
||||
import {
|
||||
|
@ -51,7 +63,10 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte
|
|||
schema.recordOf(
|
||||
schema.string(),
|
||||
schema.object({
|
||||
field: schema.string(),
|
||||
field: schema.maybe(schema.string()),
|
||||
value: schema.maybe(
|
||||
schema.oneOf([schema.string(), schema.arrayOf(schema.string())])
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
|
@ -82,8 +97,7 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte
|
|||
if (name) {
|
||||
const conflictingEntries = await savedObjectsClient.find({
|
||||
type: packSavedObjectType,
|
||||
search: name,
|
||||
searchFields: ['name'],
|
||||
filter: `${packSavedObjectType}.attributes.name: "${name}"`,
|
||||
});
|
||||
|
||||
if (
|
||||
|
@ -112,13 +126,16 @@ export const updatePackRoute = (router: IRouter, osqueryContext: OsqueryAppConte
|
|||
request.params.id,
|
||||
{
|
||||
enabled,
|
||||
...pickBy({
|
||||
name,
|
||||
description,
|
||||
queries: queries && convertPackQueriesToSO(queries),
|
||||
updated_at: moment().toISOString(),
|
||||
updated_by: currentUser,
|
||||
}),
|
||||
...pickBy(
|
||||
{
|
||||
name,
|
||||
description,
|
||||
queries: queries && convertPackQueriesToSO(queries),
|
||||
updated_at: moment().toISOString(),
|
||||
updated_by: currentUser,
|
||||
},
|
||||
(value) => !isEmpty(value)
|
||||
),
|
||||
},
|
||||
policy_ids
|
||||
? {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { pickBy } from 'lodash';
|
||||
import { isEmpty, pickBy } from 'lodash';
|
||||
import { IRouter } from '../../../../../../src/core/server';
|
||||
import { PLUGIN_ID } from '../../../common';
|
||||
import {
|
||||
|
@ -39,8 +39,7 @@ export const createSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp
|
|||
|
||||
const conflictingEntries = await savedObjectsClient.find({
|
||||
type: savedQuerySavedObjectType,
|
||||
search: id,
|
||||
searchFields: ['id'],
|
||||
filter: `${savedQuerySavedObjectType}.attributes.id: "${id}"`,
|
||||
});
|
||||
|
||||
if (conflictingEntries.saved_objects.length) {
|
||||
|
@ -49,26 +48,32 @@ export const createSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp
|
|||
|
||||
const savedQuerySO = await savedObjectsClient.create(
|
||||
savedQuerySavedObjectType,
|
||||
pickBy({
|
||||
id,
|
||||
description,
|
||||
query,
|
||||
platform,
|
||||
version,
|
||||
interval,
|
||||
ecs_mapping: convertECSMappingToArray(ecs_mapping),
|
||||
created_by: currentUser,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_by: currentUser,
|
||||
updated_at: new Date().toISOString(),
|
||||
})
|
||||
pickBy(
|
||||
{
|
||||
id,
|
||||
description,
|
||||
query,
|
||||
platform,
|
||||
version,
|
||||
interval,
|
||||
ecs_mapping: convertECSMappingToArray(ecs_mapping),
|
||||
created_by: currentUser,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_by: currentUser,
|
||||
updated_at: new Date().toISOString(),
|
||||
},
|
||||
(value) => !isEmpty(value)
|
||||
)
|
||||
);
|
||||
|
||||
return response.ok({
|
||||
body: pickBy({
|
||||
...savedQuerySO,
|
||||
ecs_mapping,
|
||||
}),
|
||||
body: pickBy(
|
||||
{
|
||||
...savedQuerySO,
|
||||
ecs_mapping,
|
||||
},
|
||||
(value) => !isEmpty(value)
|
||||
),
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { filter, pickBy } from 'lodash';
|
||||
import { isEmpty, filter, pickBy } from 'lodash';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
import { PLUGIN_ID } from '../../../common';
|
||||
|
@ -35,7 +35,9 @@ export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp
|
|||
schema.string(),
|
||||
schema.object({
|
||||
field: schema.maybe(schema.string()),
|
||||
value: schema.maybe(schema.string()),
|
||||
value: schema.maybe(
|
||||
schema.oneOf([schema.string(), schema.arrayOf(schema.string())])
|
||||
),
|
||||
})
|
||||
)
|
||||
),
|
||||
|
@ -62,8 +64,7 @@ export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp
|
|||
|
||||
const conflictingEntries = await savedObjectsClient.find<{ id: string }>({
|
||||
type: savedQuerySavedObjectType,
|
||||
search: id,
|
||||
searchFields: ['id'],
|
||||
filter: `${savedQuerySavedObjectType}.attributes.id: "${id}"`,
|
||||
});
|
||||
|
||||
if (
|
||||
|
@ -76,17 +77,20 @@ export const updateSavedQueryRoute = (router: IRouter, osqueryContext: OsqueryAp
|
|||
const updatedSavedQuerySO = await savedObjectsClient.update(
|
||||
savedQuerySavedObjectType,
|
||||
request.params.id,
|
||||
pickBy({
|
||||
id,
|
||||
description,
|
||||
platform,
|
||||
query,
|
||||
version,
|
||||
interval,
|
||||
ecs_mapping: convertECSMappingToArray(ecs_mapping),
|
||||
updated_by: currentUser,
|
||||
updated_at: new Date().toISOString(),
|
||||
}),
|
||||
pickBy(
|
||||
{
|
||||
id,
|
||||
description,
|
||||
platform,
|
||||
query,
|
||||
version,
|
||||
interval,
|
||||
ecs_mapping: convertECSMappingToArray(ecs_mapping),
|
||||
updated_by: currentUser,
|
||||
updated_at: new Date().toISOString(),
|
||||
},
|
||||
(value) => !isEmpty(value)
|
||||
),
|
||||
{
|
||||
refresh: 'wait_for',
|
||||
}
|
||||
|
|
|
@ -7,24 +7,11 @@
|
|||
|
||||
import { CoreSetup } from '../../../../src/core/server';
|
||||
|
||||
import { OsqueryAppContext } from './lib/osquery_app_context_services';
|
||||
import { savedQueryType, packType } from './lib/saved_query/saved_object_mappings';
|
||||
import { usageMetricType } from './routes/usage/saved_object_mappings';
|
||||
|
||||
const types = [savedQueryType, packType];
|
||||
|
||||
export const savedObjectTypes = types.map((type) => type.name);
|
||||
|
||||
export const initSavedObjects = (
|
||||
savedObjects: CoreSetup['savedObjects'],
|
||||
osqueryContext: OsqueryAppContext
|
||||
) => {
|
||||
const config = osqueryContext.config();
|
||||
|
||||
export const initSavedObjects = (savedObjects: CoreSetup['savedObjects']) => {
|
||||
savedObjects.registerType(usageMetricType);
|
||||
savedObjects.registerType(savedQueryType);
|
||||
|
||||
if (config.packs) {
|
||||
savedObjects.registerType(packType);
|
||||
}
|
||||
savedObjects.registerType(packType);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue