From 1841495322d4abe63c3985e9c7456bda01652292 Mon Sep 17 00:00:00 2001
From: Phillip Burch
Date: Thu, 1 Oct 2020 18:54:46 -0500
Subject: [PATCH 01/21] [Metrics UI] Override anomaly detection partition field
(#79214)
* Add ability to override datafeeds and job config for partition field
* Remove debug
* UX cleanup
* Fix types, delete dead code
* Fix types
---
.../containers/ml/infra_ml_module_types.ts | 4 +-
.../containers/ml/infra_ml_setup_state.ts | 289 ------------------
.../metrics_hosts/module_descriptor.ts | 135 +++++---
.../modules/metrics_k8s/module_descriptor.ts | 143 ++++++---
.../anomoly_detection_flyout.tsx | 4 +-
.../ml/anomaly_detection/flyout_home.tsx | 113 +++----
.../ml/anomaly_detection/job_setup_screen.tsx | 3 +-
7 files changed, 247 insertions(+), 444 deletions(-)
delete mode 100644 x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts
diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts
index a9f2671de82598..e36f38add641aa 100644
--- a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts
+++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts
@@ -33,11 +33,11 @@ export interface ModuleDescriptor {
partitionField?: string
) => Promise;
cleanUpModule: (spaceId: string, sourceId: string) => Promise;
- validateSetupIndices: (
+ validateSetupIndices?: (
indices: string[],
timestampField: string
) => Promise;
- validateSetupDatasets: (
+ validateSetupDatasets?: (
indices: string[],
timestampField: string,
startTime: number,
diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts
deleted file mode 100644
index 0dfe3b301f2400..00000000000000
--- a/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { isEqual } from 'lodash';
-import { useCallback, useEffect, useMemo, useState } from 'react';
-import { usePrevious } from 'react-use';
-import {
- combineDatasetFilters,
- DatasetFilter,
- filterDatasetFilter,
- isExampleDataIndex,
-} from '../../../common/infra_ml';
-import {
- AvailableIndex,
- ValidationIndicesError,
- ValidationUIError,
-} from '../../components/logging/log_analysis_setup/initial_configuration_step';
-import { useTrackedPromise } from '../../utils/use_tracked_promise';
-import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types';
-
-type SetupHandler = (
- indices: string[],
- startTime: number | undefined,
- endTime: number | undefined,
- datasetFilter: DatasetFilter
-) => void;
-
-interface AnalysisSetupStateArguments {
- cleanUpAndSetUpModule: SetupHandler;
- moduleDescriptor: ModuleDescriptor;
- setUpModule: SetupHandler;
- sourceConfiguration: ModuleSourceConfiguration;
-}
-
-const fourWeeksInMs = 86400000 * 7 * 4;
-
-export const useAnalysisSetupState = ({
- cleanUpAndSetUpModule,
- moduleDescriptor: { validateSetupDatasets, validateSetupIndices },
- setUpModule,
- sourceConfiguration,
-}: AnalysisSetupStateArguments) => {
- const [startTime, setStartTime] = useState(Date.now() - fourWeeksInMs);
- const [endTime, setEndTime] = useState(undefined);
-
- const isTimeRangeValid = useMemo(
- () => (startTime != null && endTime != null ? startTime < endTime : true),
- [endTime, startTime]
- );
-
- const [validatedIndices, setValidatedIndices] = useState(
- sourceConfiguration.indices.map((indexName) => ({
- name: indexName,
- validity: 'unknown' as const,
- }))
- );
-
- const updateIndicesWithValidationErrors = useCallback(
- (validationErrors: ValidationIndicesError[]) =>
- setValidatedIndices((availableIndices) =>
- availableIndices.map((previousAvailableIndex) => {
- const indexValiationErrors = validationErrors.filter(
- ({ index }) => index === previousAvailableIndex.name
- );
-
- if (indexValiationErrors.length > 0) {
- return {
- validity: 'invalid',
- name: previousAvailableIndex.name,
- errors: indexValiationErrors,
- };
- } else if (previousAvailableIndex.validity === 'valid') {
- return {
- ...previousAvailableIndex,
- validity: 'valid',
- errors: [],
- };
- } else {
- return {
- validity: 'valid',
- name: previousAvailableIndex.name,
- isSelected: !isExampleDataIndex(previousAvailableIndex.name),
- availableDatasets: [],
- datasetFilter: {
- type: 'includeAll' as const,
- },
- };
- }
- })
- ),
- []
- );
-
- const updateIndicesWithAvailableDatasets = useCallback(
- (availableDatasets: Array<{ indexName: string; datasets: string[] }>) =>
- setValidatedIndices((availableIndices) =>
- availableIndices.map((previousAvailableIndex) => {
- if (previousAvailableIndex.validity !== 'valid') {
- return previousAvailableIndex;
- }
-
- const availableDatasetsForIndex = availableDatasets.filter(
- ({ indexName }) => indexName === previousAvailableIndex.name
- );
- const newAvailableDatasets = availableDatasetsForIndex.flatMap(
- ({ datasets }) => datasets
- );
-
- // filter out datasets that have disappeared if this index' datasets were updated
- const newDatasetFilter: DatasetFilter =
- availableDatasetsForIndex.length > 0
- ? filterDatasetFilter(previousAvailableIndex.datasetFilter, (dataset) =>
- newAvailableDatasets.includes(dataset)
- )
- : previousAvailableIndex.datasetFilter;
-
- return {
- ...previousAvailableIndex,
- availableDatasets: newAvailableDatasets,
- datasetFilter: newDatasetFilter,
- };
- })
- ),
- []
- );
-
- const validIndexNames = useMemo(
- () => validatedIndices.filter((index) => index.validity === 'valid').map((index) => index.name),
- [validatedIndices]
- );
-
- const selectedIndexNames = useMemo(
- () =>
- validatedIndices
- .filter((index) => index.validity === 'valid' && index.isSelected)
- .map((i) => i.name),
- [validatedIndices]
- );
-
- const datasetFilter = useMemo(
- () =>
- validatedIndices
- .flatMap((validatedIndex) =>
- validatedIndex.validity === 'valid'
- ? validatedIndex.datasetFilter
- : { type: 'includeAll' as const }
- )
- .reduce(combineDatasetFilters, { type: 'includeAll' as const }),
- [validatedIndices]
- );
-
- const [validateIndicesRequest, validateIndices] = useTrackedPromise(
- {
- cancelPreviousOn: 'resolution',
- createPromise: async () => {
- return await validateSetupIndices(
- sourceConfiguration.indices,
- sourceConfiguration.timestampField
- );
- },
- onResolve: ({ data: { errors } }) => {
- updateIndicesWithValidationErrors(errors);
- },
- onReject: () => {
- setValidatedIndices([]);
- },
- },
- [sourceConfiguration.indices, sourceConfiguration.timestampField]
- );
-
- const [validateDatasetsRequest, validateDatasets] = useTrackedPromise(
- {
- cancelPreviousOn: 'resolution',
- createPromise: async () => {
- if (validIndexNames.length === 0) {
- return { data: { datasets: [] } };
- }
-
- return await validateSetupDatasets(
- validIndexNames,
- sourceConfiguration.timestampField,
- startTime ?? 0,
- endTime ?? Date.now()
- );
- },
- onResolve: ({ data: { datasets } }) => {
- updateIndicesWithAvailableDatasets(datasets);
- },
- },
- [validIndexNames, sourceConfiguration.timestampField, startTime, endTime]
- );
-
- const setUp = useCallback(() => {
- return setUpModule(selectedIndexNames, startTime, endTime, datasetFilter);
- }, [setUpModule, selectedIndexNames, startTime, endTime, datasetFilter]);
-
- const cleanUpAndSetUp = useCallback(() => {
- return cleanUpAndSetUpModule(selectedIndexNames, startTime, endTime, datasetFilter);
- }, [cleanUpAndSetUpModule, selectedIndexNames, startTime, endTime, datasetFilter]);
-
- const isValidating = useMemo(
- () => validateIndicesRequest.state === 'pending' || validateDatasetsRequest.state === 'pending',
- [validateDatasetsRequest.state, validateIndicesRequest.state]
- );
-
- const validationErrors = useMemo(() => {
- if (isValidating) {
- return [];
- }
-
- return [
- // validate request status
- ...(validateIndicesRequest.state === 'rejected' ||
- validateDatasetsRequest.state === 'rejected'
- ? [{ error: 'NETWORK_ERROR' as const }]
- : []),
- // validation request results
- ...validatedIndices.reduce((errors, index) => {
- return index.validity === 'invalid' && selectedIndexNames.includes(index.name)
- ? [...errors, ...index.errors]
- : errors;
- }, []),
- // index count
- ...(selectedIndexNames.length === 0 ? [{ error: 'TOO_FEW_SELECTED_INDICES' as const }] : []),
- // time range
- ...(!isTimeRangeValid ? [{ error: 'INVALID_TIME_RANGE' as const }] : []),
- ];
- }, [
- isValidating,
- validateIndicesRequest.state,
- validateDatasetsRequest.state,
- validatedIndices,
- selectedIndexNames,
- isTimeRangeValid,
- ]);
-
- const prevStartTime = usePrevious(startTime);
- const prevEndTime = usePrevious(endTime);
- const prevValidIndexNames = usePrevious(validIndexNames);
-
- useEffect(() => {
- if (!isTimeRangeValid) {
- return;
- }
-
- validateIndices();
- }, [isTimeRangeValid, validateIndices]);
-
- useEffect(() => {
- if (!isTimeRangeValid) {
- return;
- }
-
- if (
- startTime !== prevStartTime ||
- endTime !== prevEndTime ||
- !isEqual(validIndexNames, prevValidIndexNames)
- ) {
- validateDatasets();
- }
- }, [
- endTime,
- isTimeRangeValid,
- prevEndTime,
- prevStartTime,
- prevValidIndexNames,
- startTime,
- validIndexNames,
- validateDatasets,
- ]);
-
- return {
- cleanUpAndSetUp,
- datasetFilter,
- endTime,
- isValidating,
- selectedIndexNames,
- setEndTime,
- setStartTime,
- setUp,
- startTime,
- validatedIndices,
- setValidatedIndices,
- validationErrors,
- };
-};
diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts
index cec87fb1144e33..7ea87c3d21322d 100644
--- a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts
+++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts
@@ -10,17 +10,27 @@ import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup';
import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api';
import { callGetMlModuleAPI } from '../../api/ml_get_module';
import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api';
-import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices';
-import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets';
import {
metricsHostsJobTypes,
getJobId,
MetricsHostsJobType,
DatasetFilter,
bucketSpan,
- partitionField,
} from '../../../../../common/infra_ml';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import MemoryJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_memory_usage.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import MemoryDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_memory_usage.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkInJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_network_in.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkInDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_network_in.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkOutJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_network_out.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkOutDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_network_out.json';
+type JobType = 'hosts_memory_usage' | 'hosts_network_in' | 'hosts_network_out';
const moduleId = 'metrics_ui_hosts';
const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', {
defaultMessage: 'Metrics anomanly detection',
@@ -54,23 +64,68 @@ const setUpModule = async (
end: number | undefined,
datasetFilter: DatasetFilter,
{ spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration,
- pField?: string
+ partitionField?: string
) => {
const indexNamePattern = indices.join(',');
- const jobIds = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out'];
- const jobOverrides = jobIds.map((id) => ({
- job_id: id,
- data_description: {
- time_field: timestampField,
- },
- custom_settings: {
- metrics_source_config: {
- indexPattern: indexNamePattern,
- timestampField,
- bucketSpan,
+ const jobIds: JobType[] = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out'];
+
+ const jobOverrides = jobIds.map((id) => {
+ const { job: defaultJobConfig } = getDefaultJobConfigs(id);
+
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const analysis_config: any = {
+ ...defaultJobConfig.analysis_config,
+ };
+
+ if (partitionField) {
+ analysis_config.detectors[0].partition_field_name = partitionField;
+ if (analysis_config.influencers.indexOf(partitionField) === -1) {
+ analysis_config.influencers.push(partitionField);
+ }
+ }
+
+ return {
+ job_id: id,
+ data_description: {
+ time_field: timestampField,
+ },
+ analysis_config,
+ custom_settings: {
+ metrics_source_config: {
+ indexPattern: indexNamePattern,
+ timestampField,
+ bucketSpan,
+ },
+ },
+ };
+ });
+
+ const datafeedOverrides = jobIds.map((id) => {
+ const { datafeed: defaultDatafeedConfig } = getDefaultJobConfigs(id);
+
+ if (!partitionField || id === 'hosts_memory_usage') {
+ // Since the host memory usage doesn't have custom aggs, we don't need to do anything to add a partition field
+ return defaultDatafeedConfig;
+ }
+
+ // If we have a partition field, we need to change the aggregation to do a terms agg at the top level
+ const aggregations = {
+ [partitionField]: {
+ terms: {
+ field: partitionField,
+ },
+ aggregations: {
+ ...defaultDatafeedConfig.aggregations,
+ },
},
- },
- }));
+ };
+
+ return {
+ ...defaultDatafeedConfig,
+ job_id: id,
+ aggregations,
+ };
+ });
return callSetupMlModuleAPI(
moduleId,
@@ -80,34 +135,32 @@ const setUpModule = async (
sourceId,
indexNamePattern,
jobOverrides,
- []
+ datafeedOverrides
);
};
-const cleanUpModule = async (spaceId: string, sourceId: string) => {
- return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes);
-};
-
-const validateSetupIndices = async (indices: string[], timestampField: string) => {
- return await callValidateIndicesAPI(indices, [
- {
- name: timestampField,
- validTypes: ['date'],
- },
- {
- name: partitionField,
- validTypes: ['keyword'],
- },
- ]);
+const getDefaultJobConfigs = (jobId: JobType): { datafeed: any; job: any } => {
+ switch (jobId) {
+ case 'hosts_memory_usage':
+ return {
+ datafeed: MemoryDatafeed,
+ job: MemoryJob,
+ };
+ case 'hosts_network_in':
+ return {
+ datafeed: NetworkInDatafeed,
+ job: NetworkInJob,
+ };
+ case 'hosts_network_out':
+ return {
+ datafeed: NetworkOutDatafeed,
+ job: NetworkOutJob,
+ };
+ }
};
-const validateSetupDatasets = async (
- indices: string[],
- timestampField: string,
- startTime: number,
- endTime: number
-) => {
- return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime);
+const cleanUpModule = async (spaceId: string, sourceId: string) => {
+ return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes);
};
export const metricHostsModule: ModuleDescriptor = {
@@ -121,6 +174,4 @@ export const metricHostsModule: ModuleDescriptor = {
getModuleDefinition,
setUpModule,
cleanUpModule,
- validateSetupDatasets,
- validateSetupIndices,
};
diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts
index cbcff1c307af6e..eaf7489c84eb4d 100644
--- a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts
+++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts
@@ -10,17 +10,28 @@ import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup';
import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api';
import { callGetMlModuleAPI } from '../../api/ml_get_module';
import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api';
-import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices';
-import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets';
import {
metricsK8SJobTypes,
getJobId,
MetricK8sJobType,
DatasetFilter,
bucketSpan,
- partitionField,
} from '../../../../../common/infra_ml';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import MemoryJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_memory_usage.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import MemoryDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_memory_usage.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkInJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_network_in.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkInDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_network_in.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkOutJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_network_out.json';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import NetworkOutDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_network_out.json';
+type JobType = 'k8s_memory_usage' | 'k8s_network_in' | 'k8s_network_out';
+export const DEFAULT_K8S_PARTITION_FIELD = 'kubernetes.namespace';
const moduleId = 'metrics_ui_k8s';
const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', {
defaultMessage: 'Metrics anomanly detection',
@@ -54,26 +65,72 @@ const setUpModule = async (
end: number | undefined,
datasetFilter: DatasetFilter,
{ spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration,
- pField?: string
+ partitionField?: string
) => {
const indexNamePattern = indices.join(',');
- const jobIds = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out'];
- const jobOverrides = jobIds.map((id) => ({
- job_id: id,
- analysis_config: {
- bucket_span: `${bucketSpan}ms`,
- },
- data_description: {
- time_field: timestampField,
- },
- custom_settings: {
- metrics_source_config: {
- indexPattern: indexNamePattern,
- timestampField,
- bucketSpan,
+ const jobIds: JobType[] = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out'];
+ const jobOverrides = jobIds.map((id) => {
+ const { job: defaultJobConfig } = getDefaultJobConfigs(id);
+
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ const analysis_config: any = {
+ ...defaultJobConfig.analysis_config,
+ };
+
+ if (partitionField) {
+ analysis_config.detectors[0].partition_field_name = partitionField;
+ if (analysis_config.influencers.indexOf(partitionField) === -1) {
+ analysis_config.influencers.push(partitionField);
+ }
+ }
+
+ return {
+ job_id: id,
+ data_description: {
+ time_field: timestampField,
+ },
+ analysis_config,
+ custom_settings: {
+ metrics_source_config: {
+ indexPattern: indexNamePattern,
+ timestampField,
+ bucketSpan,
+ },
+ },
+ };
+ });
+
+ const datafeedOverrides = jobIds.map((id) => {
+ const { datafeed: defaultDatafeedConfig } = getDefaultJobConfigs(id);
+
+ if (!partitionField || id === 'k8s_memory_usage') {
+ // Since the host memory usage doesn't have custom aggs, we don't need to do anything to add a partition field
+ return defaultDatafeedConfig;
+ }
+
+ // Because the ML K8s jobs ship with a default partition field of {kubernetes.namespace}, ignore that agg and wrap it in our own agg.
+ const innerAggregation =
+ defaultDatafeedConfig.aggregations[DEFAULT_K8S_PARTITION_FIELD].aggregations;
+
+ // If we have a partition field, we need to change the aggregation to do a terms agg to partition the data at the top level
+ const aggregations = {
+ [partitionField]: {
+ terms: {
+ field: partitionField,
+ size: 25, // 25 is arbitratry and only used to keep the number of buckets to a managable level in the event that the user choose a high cardinality partition field.
+ },
+ aggregations: {
+ ...innerAggregation,
+ },
},
- },
- }));
+ };
+
+ return {
+ ...defaultDatafeedConfig,
+ job_id: id,
+ aggregations,
+ };
+ });
return callSetupMlModuleAPI(
moduleId,
@@ -83,34 +140,32 @@ const setUpModule = async (
sourceId,
indexNamePattern,
jobOverrides,
- []
+ datafeedOverrides
);
};
-const cleanUpModule = async (spaceId: string, sourceId: string) => {
- return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes);
-};
-
-const validateSetupIndices = async (indices: string[], timestampField: string) => {
- return await callValidateIndicesAPI(indices, [
- {
- name: timestampField,
- validTypes: ['date'],
- },
- {
- name: partitionField,
- validTypes: ['keyword'],
- },
- ]);
+const getDefaultJobConfigs = (jobId: JobType): { datafeed: any; job: any } => {
+ switch (jobId) {
+ case 'k8s_memory_usage':
+ return {
+ datafeed: MemoryDatafeed,
+ job: MemoryJob,
+ };
+ case 'k8s_network_in':
+ return {
+ datafeed: NetworkInDatafeed,
+ job: NetworkInJob,
+ };
+ case 'k8s_network_out':
+ return {
+ datafeed: NetworkOutDatafeed,
+ job: NetworkOutJob,
+ };
+ }
};
-const validateSetupDatasets = async (
- indices: string[],
- timestampField: string,
- startTime: number,
- endTime: number
-) => {
- return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime);
+const cleanUpModule = async (spaceId: string, sourceId: string) => {
+ return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes);
};
export const metricHostsModule: ModuleDescriptor = {
@@ -124,6 +179,4 @@ export const metricHostsModule: ModuleDescriptor = {
getModuleDefinition,
setUpModule,
cleanUpModule,
- validateSetupDatasets,
- validateSetupIndices,
};
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx
index b063713fa2c971..b5d224910e819d 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx
@@ -50,10 +50,10 @@ export const AnomalyDetectionFlyout = () => {
return (
<>
-
+
{showFlyout && (
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx
index 801dff9c4a17a5..5b520084ebb744 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx
@@ -5,7 +5,7 @@
*/
import React, { useState, useCallback, useEffect } from 'react';
-import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiTabs, EuiTab, EuiSpacer } from '@elastic/eui';
+import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiText, EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@@ -30,7 +30,7 @@ interface Props {
}
export const FlyoutHome = (props: Props) => {
- const [tab, setTab] = useState<'jobs' | 'anomalies'>('jobs');
+ const [tab] = useState<'jobs' | 'anomalies'>('jobs');
const { goToSetup } = props;
const {
fetchJobStatus: fetchHostJobStatus,
@@ -56,18 +56,10 @@ export const FlyoutHome = (props: Props) => {
goToSetup('kubernetes');
}, [goToSetup]);
- const goToJobs = useCallback(() => {
- setTab('jobs');
- }, []);
-
const jobIds = [
...(k8sJobSummaries || []).map((k) => k.id),
...(hostJobSummaries || []).map((h) => h.id),
];
- const anomaliesUrl = useLinkProps({
- app: 'ml',
- pathname: `/explorer?_g=${createResultsUrl(jobIds)}`,
- });
useEffect(() => {
if (hasInfraMLReadCapabilities) {
@@ -105,30 +97,24 @@ export const FlyoutHome = (props: Props) => {
-
-
-
-
-
-
-
-
+
+
{hostJobSummaries.length > 0 && (
<>
0}
hasK8sJobs={k8sJobSummaries.length > 0}
+ jobIds={jobIds}
/>
>
@@ -151,6 +137,7 @@ export const FlyoutHome = (props: Props) => {
interface CalloutProps {
hasHostJobs: boolean;
hasK8sJobs: boolean;
+ jobIds: string[];
}
const JobsEnabledCallout = (props: CalloutProps) => {
let target = '';
@@ -175,8 +162,34 @@ const JobsEnabledCallout = (props: CalloutProps) => {
pathname: '/jobs',
});
+ const anomaliesUrl = useLinkProps({
+ app: 'ml',
+ pathname: `/explorer?_g=${createResultsUrl(props.jobIds)}`,
+ });
+
return (
<>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
}
iconType="check"
/>
-
-
-
-
>
);
};
@@ -211,30 +217,11 @@ interface CreateJobTab {
const CreateJobTab = (props: CreateJobTab) => {
return (
<>
-
-
-
+ {/* */}
}
// title="Hosts"
title={
@@ -245,7 +232,7 @@ const CreateJobTab = (props: CreateJobTab) => {
}
description={
}
@@ -254,7 +241,7 @@ const CreateJobTab = (props: CreateJobTab) => {
{props.hasHostJobs && (
@@ -262,7 +249,7 @@ const CreateJobTab = (props: CreateJobTab) => {
{!props.hasHostJobs && (
@@ -273,7 +260,7 @@ const CreateJobTab = (props: CreateJobTab) => {
}
title={
{
}
description={
}
@@ -292,7 +279,7 @@ const CreateJobTab = (props: CreateJobTab) => {
{props.hasK8sJobs && (
@@ -300,7 +287,7 @@ const CreateJobTab = (props: CreateJobTab) => {
{!props.hasK8sJobs && (
diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx
index 428c002da63838..c327d187f6bc20 100644
--- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx
+++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx
@@ -20,6 +20,7 @@ import { useSourceViaHttp } from '../../../../../../containers/source/use_source
import { useMetricK8sModuleContext } from '../../../../../../containers/ml/modules/metrics_k8s/module';
import { useMetricHostsModuleContext } from '../../../../../../containers/ml/modules/metrics_hosts/module';
import { FixedDatePicker } from '../../../../../../components/fixed_datepicker';
+import { DEFAULT_K8S_PARTITION_FIELD } from '../../../../../../containers/ml/modules/metrics_k8s/module_descriptor';
interface Props {
jobType: 'hosts' | 'kubernetes';
@@ -107,7 +108,7 @@ export const JobSetupScreen = (props: Props) => {
useEffect(() => {
if (props.jobType === 'kubernetes') {
- setPartitionField(['kubernetes.namespace']);
+ setPartitionField([DEFAULT_K8S_PARTITION_FIELD]);
}
}, [props.jobType]);
From 6c015cfbef12189eb5aec8fa42f5ea2743be2971 Mon Sep 17 00:00:00 2001
From: Nathan Reese
Date: Thu, 1 Oct 2020 19:04:26 -0600
Subject: [PATCH 02/21] Convert VectorLayer to typescript (#78490)
* [maps] convert VectorLayer to TS
* more tslint fixes
* clean up
* more tslint fixes
* more tslint fixes
* remove unneeded casts
* remove unneeded VectorStyle casts
* revert changes to layer.getQuery
* fix
* update tile layer constructor
* review feedback
Co-authored-by: Elastic Machine
---
.../data_request_descriptor_types.ts | 19 +-
.../common/descriptor_types/map_descriptor.ts | 2 +-
.../common/elasticsearch_util/es_agg_utils.ts | 4 +-
.../public/actions/data_request_actions.ts | 2 +-
.../maps/public/classes/joins/inner_join.d.ts | 23 +-
.../plugins/maps/public/classes/joins/join.ts | 32 +-
.../blended_vector_layer.ts | 11 +-
.../layers/heatmap_layer/heatmap_layer.js | 12 +
.../maps/public/classes/layers/layer.test.ts | 6 -
.../maps/public/classes/layers/layer.tsx | 19 +-
.../classes/layers/tile_layer/tile_layer.js | 15 +-
.../tiled_vector_layer/tiled_vector_layer.tsx | 43 +--
.../layers/vector_layer/vector_layer.d.ts | 85 -----
.../{vector_layer.js => vector_layer.tsx} | 291 ++++++++++++------
.../sources/es_agg_source/es_agg_source.ts | 2 +-
.../es_geo_grid_source.d.ts | 9 +-
.../es_geo_grid_source.test.ts | 3 +-
.../classes/sources/es_source/es_source.d.ts | 48 ++-
.../classes/sources/es_source/es_source.js | 15 +-
.../es_term_source/es_term_source.d.ts | 13 +-
.../sources/es_term_source/es_term_source.js | 4 +-
.../kibana_regionmap_source.js | 1 +
.../mvt_single_layer_vector_source.tsx | 14 +-
.../sources/vector_source/vector_source.d.ts | 28 +-
.../classes/styles/vector/vector_style.tsx | 55 +++-
.../toc_entry_actions_popover.test.tsx | 14 +-
26 files changed, 478 insertions(+), 292 deletions(-)
delete mode 100644 x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.d.ts
rename x-pack/plugins/maps/public/classes/layers/vector_layer/{vector_layer.js => vector_layer.tsx} (78%)
diff --git a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts
index f3521cca2e456f..16b60492c9b78c 100644
--- a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts
+++ b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts
@@ -38,15 +38,21 @@ export type VectorSourceRequestMeta = MapFilters & {
applyGlobalQuery: boolean;
fieldNames: string[];
geogridPrecision?: number;
- sourceQuery: MapQuery;
+ sourceQuery?: MapQuery;
sourceMeta: VectorSourceSyncMeta;
};
+export type VectorJoinSourceRequestMeta = MapFilters & {
+ applyGlobalQuery: boolean;
+ fieldNames: string[];
+ sourceQuery: MapQuery;
+};
+
export type VectorStyleRequestMeta = MapFilters & {
dynamicStyleFields: string[];
isTimeAware: boolean;
sourceQuery: MapQuery;
- timeFilters: unknown;
+ timeFilters: TimeRange;
};
export type ESSearchSourceResponseMeta = {
@@ -59,9 +65,12 @@ export type ESSearchSourceResponseMeta = {
};
// Partial because objects are justified downstream in constructors
-export type DataMeta = Partial &
- Partial &
- Partial;
+export type DataMeta = Partial<
+ VectorSourceRequestMeta &
+ VectorJoinSourceRequestMeta &
+ VectorStyleRequestMeta &
+ ESSearchSourceResponseMeta
+>;
type NumericalStyleFieldData = {
avg: number;
diff --git a/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts b/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts
index d064dfb1c4a372..b769b125cf0f81 100644
--- a/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts
+++ b/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts
@@ -17,7 +17,7 @@ export type MapExtent = {
};
export type MapQuery = Query & {
- queryLastTriggeredAt: string;
+ queryLastTriggeredAt?: string;
};
export type MapRefreshConfig = {
diff --git a/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts b/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts
index 7828c3cc6410b5..f157ffe9f1c809 100644
--- a/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts
+++ b/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts
@@ -33,8 +33,10 @@ export function addFieldToDSL(dsl: object, field: IFieldType) {
};
}
+export type BucketProperties = Record;
+
export function extractPropertiesFromBucket(bucket: any, ignoreKeys: string[] = []) {
- const properties: Record = {};
+ const properties: BucketProperties = {};
for (const key in bucket) {
if (ignoreKeys.includes(key) || !bucket.hasOwnProperty(key)) {
continue;
diff --git a/x-pack/plugins/maps/public/actions/data_request_actions.ts b/x-pack/plugins/maps/public/actions/data_request_actions.ts
index 14d81969005068..d7d9259e1539e4 100644
--- a/x-pack/plugins/maps/public/actions/data_request_actions.ts
+++ b/x-pack/plugins/maps/public/actions/data_request_actions.ts
@@ -47,7 +47,7 @@ const FIT_TO_BOUNDS_SCALE_FACTOR = 0.1;
export type DataRequestContext = {
startLoading(dataId: string, requestToken: symbol, meta: DataMeta): void;
- stopLoading(dataId: string, requestToken: symbol, data: object, meta: DataMeta): void;
+ stopLoading(dataId: string, requestToken: symbol, data: object, meta?: DataMeta): void;
onLoadError(dataId: string, requestToken: symbol, errorMessage: string): void;
updateSourceData(newData: unknown): void;
isRequestStillActive(dataId: string, requestToken: symbol): boolean;
diff --git a/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts b/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts
index befff0965fb70c..3e2ceac4971c45 100644
--- a/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts
+++ b/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts
@@ -4,19 +4,40 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { Feature, GeoJsonProperties } from 'geojson';
import { IESTermSource } from '../sources/es_term_source';
-import { IJoin } from './join';
+import { IJoin, PropertiesMap } from './join';
import { JoinDescriptor } from '../../../common/descriptor_types';
import { ISource } from '../sources/source';
+import { ITooltipProperty } from '../tooltips/tooltip_property';
+import { IField } from '../fields/field';
export class InnerJoin implements IJoin {
constructor(joinDescriptor: JoinDescriptor, leftSource: ISource);
+ destroy: () => void;
+
getRightJoinSource(): IESTermSource;
toDescriptor(): JoinDescriptor;
+ getJoinFields: () => IField[];
+
+ getLeftField: () => IField;
+
+ getIndexPatternIds: () => string[];
+
+ getQueryableIndexPatternIds: () => string[];
+
+ getSourceDataRequestId: () => string;
+
getSourceMetaDataRequestId(): string;
getSourceFormattersDataRequestId(): string;
+
+ getTooltipProperties(properties: GeoJsonProperties): Promise;
+
+ hasCompleteConfig: () => boolean;
+
+ joinPropertiesToFeature: (feature: Feature, propertiesMap?: PropertiesMap) => boolean;
}
diff --git a/x-pack/plugins/maps/public/classes/joins/join.ts b/x-pack/plugins/maps/public/classes/joins/join.ts
index 5bcc4bfdec87e5..df6f6f684f4d2c 100644
--- a/x-pack/plugins/maps/public/classes/joins/join.ts
+++ b/x-pack/plugins/maps/public/classes/joins/join.ts
@@ -4,15 +4,39 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { Feature, GeoJsonProperties } from 'geojson';
import { IESTermSource } from '../sources/es_term_source';
import { JoinDescriptor } from '../../../common/descriptor_types';
+import { ITooltipProperty } from '../tooltips/tooltip_property';
+import { IField } from '../fields/field';
+import { BucketProperties } from '../../../common/elasticsearch_util';
+
+export type PropertiesMap = Map;
export interface IJoin {
- getRightJoinSource(): IESTermSource;
+ destroy: () => void;
+
+ getRightJoinSource: () => IESTermSource;
+
+ toDescriptor: () => JoinDescriptor;
+
+ getJoinFields: () => IField[];
+
+ getLeftField: () => IField;
+
+ getIndexPatternIds: () => string[];
+
+ getQueryableIndexPatternIds: () => string[];
+
+ getSourceDataRequestId: () => string;
+
+ getSourceMetaDataRequestId: () => string;
+
+ getSourceFormattersDataRequestId: () => string;
- toDescriptor(): JoinDescriptor;
+ getTooltipProperties: (properties: GeoJsonProperties) => Promise;
- getSourceMetaDataRequestId(): string;
+ hasCompleteConfig: () => boolean;
- getSourceFormattersDataRequestId(): string;
+ joinPropertiesToFeature: (feature: Feature, propertiesMap?: PropertiesMap) => boolean;
}
diff --git a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts
index 90e8d25a77958d..9b6a67ac28ad09 100644
--- a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts
+++ b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts
@@ -37,7 +37,6 @@ import {
LayerDescriptor,
VectorLayerDescriptor,
} from '../../../../common/descriptor_types';
-import { IStyle } from '../../styles/style';
import { IVectorSource } from '../../sources/vector_source';
const ACTIVE_COUNT_DATA_ID = 'ACTIVE_COUNT_DATA_ID';
@@ -257,7 +256,7 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {
return clonedDescriptor;
}
- getSource() {
+ getSource(): IVectorSource {
return this._isClustered ? this._clusterSource : this._documentSource;
}
@@ -268,11 +267,11 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {
return this._documentSource;
}
- getCurrentStyle(): IStyle {
+ getCurrentStyle(): IVectorStyle {
return this._isClustered ? this._clusterStyle : this._documentStyle;
}
- getStyleForEditing(): IStyle {
+ getStyleForEditing(): IVectorStyle {
return this._documentStyle;
}
@@ -281,8 +280,8 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer {
const requestToken = Symbol(`layer-active-count:${this.getId()}`);
const searchFilters = this._getSearchFilters(
syncContext.dataFilters,
- this.getSource() as IVectorSource,
- this.getCurrentStyle() as IVectorStyle
+ this.getSource(),
+ this.getCurrentStyle()
);
const canSkipFetch = await canSkipSourceUpdate({
source: this.getSource(),
diff --git a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.js b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.js
index adcc86b9d1546e..33e82db79f3cfa 100644
--- a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.js
+++ b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.js
@@ -31,6 +31,18 @@ export class HeatmapLayer extends VectorLayer {
}
}
+ getStyleForEditing() {
+ return this._style;
+ }
+
+ getStyle() {
+ return this._style;
+ }
+
+ getCurrentStyle() {
+ return this._style;
+ }
+
_getPropKeyOfSelectedMetric() {
const metricfields = this.getSource().getMetricFields();
return metricfields[0].getName();
diff --git a/x-pack/plugins/maps/public/classes/layers/layer.test.ts b/x-pack/plugins/maps/public/classes/layers/layer.test.ts
index 7bc91d71f83e22..76df7c2af840a7 100644
--- a/x-pack/plugins/maps/public/classes/layers/layer.test.ts
+++ b/x-pack/plugins/maps/public/classes/layers/layer.test.ts
@@ -7,7 +7,6 @@
import { AbstractLayer } from './layer';
import { ISource } from '../sources/source';
-import { IStyle } from '../styles/style';
import { AGG_TYPE, FIELD_ORIGIN, LAYER_STYLE_TYPE, VECTOR_STYLES } from '../../../common/constants';
import { ESTermSourceDescriptor, VectorStyleDescriptor } from '../../../common/descriptor_types';
import { getDefaultDynamicProperties } from '../styles/vector/vector_style_defaults';
@@ -38,8 +37,6 @@ class MockSource {
}
}
-class MockStyle {}
-
describe('cloneDescriptor', () => {
describe('with joins', () => {
const styleDescriptor = {
@@ -84,7 +81,6 @@ describe('cloneDescriptor', () => {
const layer = new MockLayer({
layerDescriptor,
source: (new MockSource() as unknown) as ISource,
- style: (new MockStyle() as unknown) as IStyle,
});
const clonedDescriptor = await layer.cloneDescriptor();
const clonedStyleProps = (clonedDescriptor.style as VectorStyleDescriptor).properties;
@@ -122,7 +118,6 @@ describe('cloneDescriptor', () => {
const layer = new MockLayer({
layerDescriptor,
source: (new MockSource() as unknown) as ISource,
- style: (new MockStyle() as unknown) as IStyle,
});
const clonedDescriptor = await layer.cloneDescriptor();
const clonedStyleProps = (clonedDescriptor.style as VectorStyleDescriptor).properties;
@@ -165,7 +160,6 @@ describe('isFittable', () => {
const layer = new MockLayer({
layerDescriptor,
source: (new MockSource({ fitToBounds: test.fitToBounds }) as unknown) as ISource,
- style: (new MockStyle() as unknown) as IStyle,
});
expect(await layer.isFittable()).toBe(test.canFit);
});
diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx
index cd720063c67031..d6bd5180375ce5 100644
--- a/x-pack/plugins/maps/public/classes/layers/layer.tsx
+++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx
@@ -110,13 +110,11 @@ export type CustomIconAndTooltipContent = {
export interface ILayerArguments {
layerDescriptor: LayerDescriptor;
source: ISource;
- style: IStyle;
}
export class AbstractLayer implements ILayer {
protected readonly _descriptor: LayerDescriptor;
protected readonly _source: ISource;
- protected readonly _style: IStyle;
protected readonly _dataRequests: DataRequest[];
static createDescriptor(options: Partial): LayerDescriptor {
@@ -140,10 +138,9 @@ export class AbstractLayer implements ILayer {
}
}
- constructor({ layerDescriptor, source, style }: ILayerArguments) {
+ constructor({ layerDescriptor, source }: ILayerArguments) {
this._descriptor = AbstractLayer.createDescriptor(layerDescriptor);
this._source = source;
- this._style = style;
if (this._descriptor.__dataRequests) {
this._dataRequests = this._descriptor.__dataRequests.map(
(dataRequest) => new DataRequest(dataRequest)
@@ -257,11 +254,15 @@ export class AbstractLayer implements ILayer {
}
getStyleForEditing(): IStyle {
- return this._style;
+ throw new Error('Should implement AbstractLayer#getStyleForEditing');
}
- getStyle() {
- return this._style;
+ getStyle(): IStyle {
+ throw new Error('Should implement AbstractLayer#getStyle');
+ }
+
+ getCurrentStyle(): IStyle {
+ throw new Error('Should implement AbstractLayer#getCurrentStyle');
}
getLabel(): string {
@@ -412,10 +413,6 @@ export class AbstractLayer implements ILayer {
return this._descriptor.query ? this._descriptor.query : null;
}
- getCurrentStyle(): IStyle {
- return this._style;
- }
-
async getImmutableSourceProperties() {
const source = this.getSource();
return await source.getImmutableProperties();
diff --git a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js
index 3e2009c24a2e4d..fa60017f0eaf7b 100644
--- a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js
+++ b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js
@@ -21,7 +21,20 @@ export class TileLayer extends AbstractLayer {
}
constructor({ source, layerDescriptor }) {
- super({ source, layerDescriptor, style: new TileStyle() });
+ super({ source, layerDescriptor });
+ this._style = new TileStyle();
+ }
+
+ getStyleForEditing() {
+ return this._style;
+ }
+
+ getStyle() {
+ return this._style;
+ }
+
+ getCurrentStyle() {
+ return this._style;
}
async syncData({ startLoading, stopLoading, onLoadError, dataFilters }) {
diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx
index 70bf8ea3883b72..68b9f2931f398c 100644
--- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx
+++ b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx
@@ -5,9 +5,14 @@
*/
import React from 'react';
+import {
+ Map as MbMap,
+ GeoJSONSource as MbGeoJSONSource,
+ VectorSource as MbVectorSource,
+} from 'mapbox-gl';
import { EuiIcon } from '@elastic/eui';
import { Feature } from 'geojson';
-import { VectorStyle } from '../../styles/vector/vector_style';
+import { IVectorStyle, VectorStyle } from '../../styles/vector/vector_style';
import { SOURCE_DATA_REQUEST_ID, LAYER_TYPE } from '../../../../common/constants';
import { VectorLayer, VectorLayerArguments } from '../vector_layer/vector_layer';
import { ITiledSingleLayerVectorSource } from '../../sources/vector_source';
@@ -59,7 +64,7 @@ export class TiledVectorLayer extends VectorLayer {
const searchFilters: VectorSourceRequestMeta = this._getSearchFilters(
dataFilters,
this.getSource(),
- this._style
+ this._style as IVectorStyle
);
const prevDataRequest = this.getSourceDataRequest();
@@ -88,13 +93,12 @@ export class TiledVectorLayer extends VectorLayer {
}
async syncData(syncContext: DataRequestContext) {
- await this._syncSourceStyleMeta(syncContext, this._source, this._style);
- await this._syncSourceFormatters(syncContext, this._source, this._style);
+ await this._syncSourceStyleMeta(syncContext, this._source, this._style as IVectorStyle);
+ await this._syncSourceFormatters(syncContext, this._source, this._style as IVectorStyle);
await this._syncMVTUrlTemplate(syncContext);
}
- _syncSourceBindingWithMb(mbMap: unknown) {
- // @ts-expect-error
+ _syncSourceBindingWithMb(mbMap: MbMap) {
const mbSource = mbMap.getSource(this._getMbSourceId());
if (mbSource) {
return;
@@ -113,7 +117,6 @@ export class TiledVectorLayer extends VectorLayer {
}
const mbSourceId = this._getMbSourceId();
- // @ts-expect-error
mbMap.addSource(mbSourceId, {
type: 'vector',
tiles: [sourceMeta.urlTemplate],
@@ -126,7 +129,7 @@ export class TiledVectorLayer extends VectorLayer {
return this._getMbSourceId() === mbSourceId;
}
- _syncStylePropertiesWithMb(mbMap: unknown) {
+ _syncStylePropertiesWithMb(mbMap: MbMap) {
// @ts-ignore
const mbSource = mbMap.getSource(this._getMbSourceId());
if (!mbSource) {
@@ -146,12 +149,16 @@ export class TiledVectorLayer extends VectorLayer {
this._setMbLinePolygonProperties(mbMap, sourceMeta.layerName);
}
- _requiresPrevSourceCleanup(mbMap: unknown): boolean {
- // @ts-expect-error
- const mbTileSource = mbMap.getSource(this._getMbSourceId());
- if (!mbTileSource) {
+ _requiresPrevSourceCleanup(mbMap: MbMap): boolean {
+ const mbSource = mbMap.getSource(this._getMbSourceId()) as MbVectorSource | MbGeoJSONSource;
+ if (!mbSource) {
return false;
}
+ if (!('tiles' in mbSource)) {
+ // Expected source is not compatible, so remove.
+ return true;
+ }
+ const mbTileSource = mbSource as MbVectorSource;
const dataRequest = this.getSourceDataRequest();
if (!dataRequest) {
@@ -163,13 +170,8 @@ export class TiledVectorLayer extends VectorLayer {
return false;
}
- if (!mbTileSource.tiles) {
- // Expected source is not compatible, so remove.
- return true;
- }
-
const isSourceDifferent =
- mbTileSource.tiles[0] !== tiledSourceMeta.urlTemplate ||
+ mbTileSource.tiles?.[0] !== tiledSourceMeta.urlTemplate ||
mbTileSource.minzoom !== tiledSourceMeta.minSourceZoom ||
mbTileSource.maxzoom !== tiledSourceMeta.maxSourceZoom;
@@ -179,9 +181,8 @@ export class TiledVectorLayer extends VectorLayer {
const layerIds = this.getMbLayerIds();
for (let i = 0; i < layerIds.length; i++) {
- // @ts-expect-error
const mbLayer = mbMap.getLayer(layerIds[i]);
- if (mbLayer && mbLayer.sourceLayer !== tiledSourceMeta.layerName) {
+ if (mbLayer && mbLayer['source-layer'] !== tiledSourceMeta.layerName) {
// If the source-pointer of one of the layers is stale, they will all be stale.
// In this case, all the mb-layers need to be removed and re-added.
return true;
@@ -191,7 +192,7 @@ export class TiledVectorLayer extends VectorLayer {
return false;
}
- syncLayerWithMB(mbMap: unknown) {
+ syncLayerWithMB(mbMap: MbMap) {
this._removeStaleMbSourcesAndLayers(mbMap);
this._syncSourceBindingWithMb(mbMap);
this._syncStylePropertiesWithMb(mbMap);
diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.d.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.d.ts
deleted file mode 100644
index fa614ae87b2905..00000000000000
--- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.d.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-/* eslint-disable @typescript-eslint/consistent-type-definitions */
-
-import { Feature, GeoJsonProperties } from 'geojson';
-import { AbstractLayer } from '../layer';
-import { IVectorSource } from '../../sources/vector_source';
-import {
- MapFilters,
- VectorLayerDescriptor,
- VectorSourceRequestMeta,
-} from '../../../../common/descriptor_types';
-import { ILayer } from '../layer';
-import { IJoin } from '../../joins/join';
-import { IVectorStyle } from '../../styles/vector/vector_style';
-import { IField } from '../../fields/field';
-import { DataRequestContext } from '../../../actions';
-import { ITooltipProperty } from '../../tooltips/tooltip_property';
-
-export type VectorLayerArguments = {
- source: IVectorSource;
- joins?: IJoin[];
- layerDescriptor: VectorLayerDescriptor;
-};
-
-export interface IVectorLayer extends ILayer {
- getFields(): Promise;
- getStyleEditorFields(): Promise;
- getJoins(): IJoin[];
- getValidJoins(): IJoin[];
- getSource(): IVectorSource;
- getFeatureById(id: string | number): Feature | null;
- getPropertiesForTooltip(properties: GeoJsonProperties): Promise;
- hasJoins(): boolean;
-}
-
-export class VectorLayer extends AbstractLayer implements IVectorLayer {
- static type: string;
-
- protected readonly _style: IVectorStyle;
- static createDescriptor(
- options: Partial,
- mapColors?: string[]
- ): VectorLayerDescriptor;
-
- constructor(options: VectorLayerArguments);
- getLayerTypeIconName(): string;
- getFields(): Promise;
- getStyleEditorFields(): Promise;
- getJoins(): IJoin[];
- getValidJoins(): IJoin[];
- _syncSourceStyleMeta(
- syncContext: DataRequestContext,
- source: IVectorSource,
- style: IVectorStyle
- ): Promise;
- _syncSourceFormatters(
- syncContext: DataRequestContext,
- source: IVectorSource,
- style: IVectorStyle
- ): Promise;
- syncLayerWithMB(mbMap: unknown): void;
- _getSearchFilters(
- dataFilters: MapFilters,
- source: IVectorSource,
- style: IVectorStyle
- ): VectorSourceRequestMeta;
- _syncData(
- syncContext: DataRequestContext,
- source: IVectorSource,
- style: IVectorStyle
- ): Promise;
- ownsMbSourceId(sourceId: string): boolean;
- ownsMbLayerId(sourceId: string): boolean;
- _setMbPointsProperties(mbMap: unknown, mvtSourceLayer?: string): void;
- _setMbLinePolygonProperties(mbMap: unknown, mvtSourceLayer?: string): void;
- getSource(): IVectorSource;
- getFeatureById(id: string | number): Feature | null;
- getPropertiesForTooltip(properties: GeoJsonProperties): Promise;
- hasJoins(): boolean;
- isFittable(): Promise;
-}
diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx
similarity index 78%
rename from x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js
rename to x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx
index 27c344b713a60f..a2532d4e7b10ef 100644
--- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js
+++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx
@@ -5,8 +5,13 @@
*/
import React from 'react';
+import { Map as MbMap, Layer as MbLayer, GeoJSONSource as MbGeoJSONSource } from 'mapbox-gl';
+import { Feature, FeatureCollection, GeoJsonProperties } from 'geojson';
+import _ from 'lodash';
+import { EuiIcon } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
import { AbstractLayer } from '../layer';
-import { VectorStyle } from '../../styles/vector/vector_style';
+import { IVectorStyle, VectorStyle } from '../../styles/vector/vector_style';
import {
FEATURE_ID_PROPERTY_NAME,
SOURCE_DATA_REQUEST_ID,
@@ -20,11 +25,9 @@ import {
FIELD_ORIGIN,
LAYER_STYLE_TYPE,
KBN_TOO_MANY_FEATURES_IMAGE_ID,
+ FieldFormatter,
} from '../../../../common/constants';
-import _ from 'lodash';
import { JoinTooltipProperty } from '../../tooltips/join_tooltip_property';
-import { EuiIcon } from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
import { DataRequestAbortError } from '../../util/data_request';
import {
canSkipSourceUpdate,
@@ -39,15 +42,66 @@ import {
getPointFilterExpression,
} from '../../util/mb_filter_expressions';
+import {
+ DynamicStylePropertyOptions,
+ MapFilters,
+ MapQuery,
+ VectorLayerDescriptor,
+ VectorSourceRequestMeta,
+ VectorStyleRequestMeta,
+} from '../../../../common/descriptor_types';
+import { IVectorSource } from '../../sources/vector_source';
+import { CustomIconAndTooltipContent, ILayer } from '../layer';
+import { IJoin, PropertiesMap } from '../../joins/join';
+import { IField } from '../../fields/field';
+import { DataRequestContext } from '../../../actions';
+import { ITooltipProperty } from '../../tooltips/tooltip_property';
+import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property';
+import { IESSource } from '../../sources/es_source';
+
+interface SourceResult {
+ refreshed: boolean;
+ featureCollection?: FeatureCollection;
+}
+
+interface JoinState {
+ dataHasChanged: boolean;
+ join: IJoin;
+ propertiesMap?: PropertiesMap;
+}
+
+export interface VectorLayerArguments {
+ source: IVectorSource;
+ joins?: IJoin[];
+ layerDescriptor: VectorLayerDescriptor;
+}
+
+export interface IVectorLayer extends ILayer {
+ getFields(): Promise;
+ getStyleEditorFields(): Promise;
+ getJoins(): IJoin[];
+ getValidJoins(): IJoin[];
+ getSource(): IVectorSource;
+ getFeatureById(id: string | number): Feature | null;
+ getPropertiesForTooltip(properties: GeoJsonProperties): Promise;
+ hasJoins(): boolean;
+}
+
export class VectorLayer extends AbstractLayer {
static type = LAYER_TYPE.VECTOR;
- static createDescriptor(options, mapColors) {
+ protected readonly _style: IVectorStyle;
+ private readonly _joins: IJoin[];
+
+ static createDescriptor(
+ options: Partial,
+ mapColors?: string[]
+ ): VectorLayerDescriptor {
const layerDescriptor = super.createDescriptor(options);
layerDescriptor.type = VectorLayer.type;
if (!options.style) {
- const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors);
+ const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors ? mapColors : []);
layerDescriptor.style = VectorStyle.createDescriptor(styleProperties);
}
@@ -55,16 +109,31 @@ export class VectorLayer extends AbstractLayer {
layerDescriptor.joins = [];
}
- return layerDescriptor;
+ return layerDescriptor as VectorLayerDescriptor;
}
- constructor({ layerDescriptor, source, joins = [] }) {
- super({ layerDescriptor, source });
+ constructor({ layerDescriptor, source, joins = [] }: VectorLayerArguments) {
+ super({
+ layerDescriptor,
+ source,
+ });
this._joins = joins;
- this._style = new VectorStyle(this._descriptor.style, source, this);
+ this._style = new VectorStyle(layerDescriptor.style, source, this);
+ }
+
+ getSource(): IVectorSource {
+ return super.getSource() as IVectorSource;
+ }
+
+ getStyleForEditing(): IVectorStyle {
+ return this._style;
+ }
+
+ getStyle(): IVectorStyle {
+ return this._style;
}
- getStyle() {
+ getCurrentStyle(): IVectorStyle {
return this._style;
}
@@ -108,7 +177,7 @@ export class VectorLayer extends AbstractLayer {
return true;
}
- getCustomIconAndTooltipContent() {
+ getCustomIconAndTooltipContent(): CustomIconAndTooltipContent {
const featureCollection = this._getSourceFeatureCollection();
const noResultsIcon = ;
@@ -124,7 +193,7 @@ export class VectorLayer extends AbstractLayer {
if (
this.getJoins().length &&
!featureCollection.features.some(
- (feature) => feature.properties[FEATURE_VISIBLE_PROPERTY_NAME]
+ (feature) => feature.properties?.[FEATURE_VISIBLE_PROPERTY_NAME]
)
) {
return {
@@ -141,8 +210,8 @@ export class VectorLayer extends AbstractLayer {
);
return {
icon: this.getCurrentStyle().getIcon(),
- tooltipContent: tooltipContent,
- areResultsTrimmed: areResultsTrimmed,
+ tooltipContent,
+ areResultsTrimmed,
};
}
@@ -158,7 +227,12 @@ export class VectorLayer extends AbstractLayer {
return this.getCurrentStyle().renderLegendDetails();
}
- async getBounds({ startLoading, stopLoading, registerCancelCallback, dataFilters }) {
+ async getBounds({
+ startLoading,
+ stopLoading,
+ registerCancelCallback,
+ dataFilters,
+ }: DataRequestContext) {
const isStaticLayer = !this.getSource().isBoundsAware();
if (isStaticLayer || this.hasJoins()) {
return getFeatureCollectionBounds(this._getSourceFeatureCollection(), this.hasJoins());
@@ -190,7 +264,7 @@ export class VectorLayer extends AbstractLayer {
} finally {
// Use stopLoading callback instead of onLoadError callback.
// Function is loading bounds and not feature data.
- stopLoading(SOURCE_BOUNDS_DATA_REQUEST_ID, requestToken, bounds, boundsFilters);
+ stopLoading(SOURCE_BOUNDS_DATA_REQUEST_ID, requestToken, bounds ? bounds : {}, boundsFilters);
}
return bounds;
}
@@ -205,7 +279,7 @@ export class VectorLayer extends AbstractLayer {
}
_getJoinFields() {
- const joinFields = [];
+ const joinFields: IField[] = [];
this.getValidJoins().forEach((join) => {
const fields = join.getJoinFields();
joinFields.push(...fields);
@@ -219,7 +293,7 @@ export class VectorLayer extends AbstractLayer {
}
async getStyleEditorFields() {
- const sourceFields = await this.getSourceForEditing().getFields();
+ const sourceFields = await (this.getSourceForEditing() as IVectorSource).getFields();
return [...sourceFields, ...this._getJoinFields()];
}
@@ -246,7 +320,7 @@ export class VectorLayer extends AbstractLayer {
onLoadError,
registerCancelCallback,
dataFilters,
- }) {
+ }: { join: IJoin } & DataRequestContext): Promise {
const joinSource = join.getRightJoinSource();
const sourceDataId = join.getSourceDataRequestId();
const requestToken = Symbol(`layer-join-refresh:${this.getId()} - ${sourceDataId}`);
@@ -266,15 +340,15 @@ export class VectorLayer extends AbstractLayer {
if (canSkipFetch) {
return {
dataHasChanged: false,
- join: join,
- propertiesMap: prevDataRequest.getData(),
+ join,
+ propertiesMap: prevDataRequest?.getData() as PropertiesMap,
};
}
try {
startLoading(sourceDataId, requestToken, searchFilters);
const leftSourceName = await this._source.getDisplayName();
- const { propertiesMap } = await joinSource.getPropertiesMap(
+ const propertiesMap = await joinSource.getPropertiesMap(
searchFilters,
leftSourceName,
join.getLeftField().getName(),
@@ -283,8 +357,8 @@ export class VectorLayer extends AbstractLayer {
stopLoading(sourceDataId, requestToken, propertiesMap);
return {
dataHasChanged: true,
- join: join,
- propertiesMap: propertiesMap,
+ join,
+ propertiesMap,
};
} catch (e) {
if (!(e instanceof DataRequestAbortError)) {
@@ -292,13 +366,12 @@ export class VectorLayer extends AbstractLayer {
}
return {
dataHasChanged: true,
- join: join,
- propertiesMap: null,
+ join,
};
}
}
- async _syncJoins(syncContext, style) {
+ async _syncJoins(syncContext: DataRequestContext, style: IVectorStyle) {
const joinSyncs = this.getValidJoins().map(async (join) => {
await this._syncJoinStyleMeta(syncContext, join, style);
await this._syncJoinFormatters(syncContext, join, style);
@@ -308,28 +381,37 @@ export class VectorLayer extends AbstractLayer {
return await Promise.all(joinSyncs);
}
- _getSearchFilters(dataFilters, source, style) {
+ _getSearchFilters(
+ dataFilters: MapFilters,
+ source: IVectorSource,
+ style: IVectorStyle
+ ): VectorSourceRequestMeta {
const fieldNames = [
...source.getFieldNames(),
...(style.getType() === LAYER_STYLE_TYPE.VECTOR ? style.getSourceFieldNames() : []),
...this.getValidJoins().map((join) => join.getLeftField().getName()),
];
+ const sourceQuery = this.getQuery() as MapQuery;
return {
...dataFilters,
fieldNames: _.uniq(fieldNames).sort(),
geogridPrecision: source.getGeoGridPrecision(dataFilters.zoom),
- sourceQuery: this.getQuery(),
+ sourceQuery: sourceQuery ? sourceQuery : undefined,
applyGlobalQuery: source.getApplyGlobalQuery(),
sourceMeta: source.getSyncMeta(),
};
}
- async _performInnerJoins(sourceResult, joinStates, updateSourceData) {
- //should update the store if
- //-- source result was refreshed
- //-- any of the join configurations changed (joinState changed)
- //-- visibility of any of the features has changed
+ async _performInnerJoins(
+ sourceResult: SourceResult,
+ joinStates: JoinState[],
+ updateSourceData: DataRequestContext['updateSourceData']
+ ) {
+ // should update the store if
+ // -- source result was refreshed
+ // -- any of the join configurations changed (joinState changed)
+ // -- visibility of any of the features has changed
let shouldUpdateStore =
sourceResult.refreshed || joinStates.some((joinState) => joinState.dataHasChanged);
@@ -338,8 +420,11 @@ export class VectorLayer extends AbstractLayer {
return;
}
- for (let i = 0; i < sourceResult.featureCollection.features.length; i++) {
- const feature = sourceResult.featureCollection.features[i];
+ for (let i = 0; i < sourceResult.featureCollection!.features.length; i++) {
+ const feature = sourceResult.featureCollection!.features[i];
+ if (!feature.properties) {
+ feature.properties = {};
+ }
const oldVisbility = feature.properties[FEATURE_VISIBLE_PROPERTY_NAME];
let isFeatureVisible = true;
for (let j = 0; j < joinStates.length; j++) {
@@ -364,7 +449,11 @@ export class VectorLayer extends AbstractLayer {
}
}
- async _syncSource(syncContext, source, style) {
+ async _syncSource(
+ syncContext: DataRequestContext,
+ source: IVectorSource,
+ style: IVectorStyle
+ ): Promise {
const {
startLoading,
stopLoading,
@@ -385,7 +474,9 @@ export class VectorLayer extends AbstractLayer {
if (canSkipFetch) {
return {
refreshed: false,
- featureCollection: prevDataRequest.getData(),
+ featureCollection: prevDataRequest
+ ? (prevDataRequest.getData() as FeatureCollection)
+ : EMPTY_FEATURE_COLLECTION,
};
}
@@ -416,15 +507,20 @@ export class VectorLayer extends AbstractLayer {
}
}
- async _syncSourceStyleMeta(syncContext, source, style) {
+ async _syncSourceStyleMeta(
+ syncContext: DataRequestContext,
+ source: IVectorSource,
+ style: IVectorStyle
+ ) {
if (this.getCurrentStyle().getType() !== LAYER_STYLE_TYPE.VECTOR) {
return;
}
+ const sourceQuery = this.getQuery() as MapQuery;
return this._syncStyleMeta({
source,
style,
- sourceQuery: this.getQuery(),
+ sourceQuery: sourceQuery ? sourceQuery : undefined,
dataRequestId: SOURCE_META_DATA_REQUEST_ID,
dynamicStyleProps: style.getDynamicPropertiesArray().filter((dynamicStyleProp) => {
return (
@@ -436,7 +532,7 @@ export class VectorLayer extends AbstractLayer {
});
}
- async _syncJoinStyleMeta(syncContext, join, style) {
+ async _syncJoinStyleMeta(syncContext: DataRequestContext, join: IJoin, style: IVectorStyle) {
const joinSource = join.getRightJoinSource();
return this._syncStyleMeta({
source: joinSource,
@@ -446,9 +542,7 @@ export class VectorLayer extends AbstractLayer {
dynamicStyleProps: this.getCurrentStyle()
.getDynamicPropertiesArray()
.filter((dynamicStyleProp) => {
- const matchingField = joinSource.getMetricFieldForName(
- dynamicStyleProp.getField().getName()
- );
+ const matchingField = joinSource.getMetricFieldForName(dynamicStyleProp.getFieldName());
return (
dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN &&
!!matchingField &&
@@ -470,13 +564,19 @@ export class VectorLayer extends AbstractLayer {
stopLoading,
onLoadError,
registerCancelCallback,
- }) {
+ }: {
+ dataRequestId: string;
+ dynamicStyleProps: Array>;
+ source: IVectorSource;
+ sourceQuery?: MapQuery;
+ style: IVectorStyle;
+ } & DataRequestContext) {
if (!source.isESSource() || dynamicStyleProps.length === 0) {
return;
}
const dynamicStyleFields = dynamicStyleProps.map((dynamicStyleProp) => {
- return `${dynamicStyleProp.getField().getName()}${dynamicStyleProp.getNumberOfCategories()}`;
+ return `${dynamicStyleProp.getFieldName()}${dynamicStyleProp.getNumberOfCategories()}`;
});
const nextMeta = {
@@ -484,7 +584,7 @@ export class VectorLayer extends AbstractLayer {
sourceQuery,
isTimeAware: this.getCurrentStyle().isTimeAware() && (await source.isTimeAware()),
timeFilters: dataFilters.timeFilters,
- };
+ } as VectorStyleRequestMeta;
const prevDataRequest = this.getDataRequest(dataRequestId);
const canSkipFetch = canSkipStyleMetaUpdate({ prevDataRequest, nextMeta });
if (canSkipFetch) {
@@ -496,14 +596,14 @@ export class VectorLayer extends AbstractLayer {
startLoading(dataRequestId, requestToken, nextMeta);
const layerName = await this.getDisplayName(source);
- //todo: cast source to ESSource when migrating to TS
- const styleMeta = await source.loadStylePropsMeta(
+ const styleMeta = await (source as IESSource).loadStylePropsMeta({
layerName,
style,
dynamicStyleProps,
- registerCancelCallback.bind(null, requestToken),
- nextMeta
- );
+ registerCancelCallback: registerCancelCallback.bind(null, requestToken),
+ sourceQuery: nextMeta.sourceQuery,
+ timeFilters: nextMeta.timeFilters,
+ });
stopLoading(dataRequestId, requestToken, styleMeta, nextMeta);
} catch (error) {
if (!(error instanceof DataRequestAbortError)) {
@@ -512,7 +612,11 @@ export class VectorLayer extends AbstractLayer {
}
}
- async _syncSourceFormatters(syncContext, source, style) {
+ async _syncSourceFormatters(
+ syncContext: DataRequestContext,
+ source: IVectorSource,
+ style: IVectorStyle
+ ) {
if (style.getType() !== LAYER_STYLE_TYPE.VECTOR) {
return;
}
@@ -526,13 +630,13 @@ export class VectorLayer extends AbstractLayer {
return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.SOURCE;
})
.map((dynamicStyleProp) => {
- return dynamicStyleProp.getField();
+ return dynamicStyleProp.getField()!;
}),
...syncContext,
});
}
- async _syncJoinFormatters(syncContext, join, style) {
+ async _syncJoinFormatters(syncContext: DataRequestContext, join: IJoin, style: IVectorStyle) {
const joinSource = join.getRightJoinSource();
return this._syncFormatters({
source: joinSource,
@@ -540,19 +644,28 @@ export class VectorLayer extends AbstractLayer {
fields: style
.getDynamicPropertiesArray()
.filter((dynamicStyleProp) => {
- const matchingField = joinSource.getMetricFieldForName(
- dynamicStyleProp.getField().getName()
- );
+ const matchingField = joinSource.getMetricFieldForName(dynamicStyleProp.getFieldName());
return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN && !!matchingField;
})
.map((dynamicStyleProp) => {
- return dynamicStyleProp.getField();
+ return dynamicStyleProp.getField()!;
}),
...syncContext,
});
}
- async _syncFormatters({ source, dataRequestId, fields, startLoading, stopLoading, onLoadError }) {
+ async _syncFormatters({
+ source,
+ dataRequestId,
+ fields,
+ startLoading,
+ stopLoading,
+ onLoadError,
+ }: {
+ dataRequestId: string;
+ fields: IField[];
+ source: IVectorSource;
+ } & DataRequestContext) {
if (fields.length === 0) {
return;
}
@@ -573,7 +686,7 @@ export class VectorLayer extends AbstractLayer {
try {
startLoading(dataRequestId, requestToken, nextMeta);
- const formatters = {};
+ const formatters: { [key: string]: FieldFormatter | null } = {};
const promises = fields
.filter((field) => {
return field.canValueBeFormatted();
@@ -589,7 +702,7 @@ export class VectorLayer extends AbstractLayer {
}
}
- async syncData(syncContext) {
+ async syncData(syncContext: DataRequestContext) {
await this._syncData(syncContext, this.getSource(), this.getCurrentStyle());
}
@@ -603,7 +716,7 @@ export class VectorLayer extends AbstractLayer {
// Given 1 above, which source/style to use can not be stored in Layer instance state.
// Given 2 above, which source/style to use can not be pulled from data request state.
// Therefore, source and style are provided as arugments and must be used instead of calling getSource or getCurrentStyle.
- async _syncData(syncContext, source, style) {
+ async _syncData(syncContext: DataRequestContext, source: IVectorSource, style: IVectorStyle) {
if (this.isLoadingBounds()) {
return;
}
@@ -624,11 +737,11 @@ export class VectorLayer extends AbstractLayer {
_getSourceFeatureCollection() {
const sourceDataRequest = this.getSourceDataRequest();
- return sourceDataRequest ? sourceDataRequest.getData() : null;
+ return sourceDataRequest ? (sourceDataRequest.getData() as FeatureCollection) : null;
}
- _syncFeatureCollectionWithMb(mbMap) {
- const mbGeoJSONSource = mbMap.getSource(this.getId());
+ _syncFeatureCollectionWithMb(mbMap: MbMap) {
+ const mbGeoJSONSource = mbMap.getSource(this.getId()) as MbGeoJSONSource;
const featureCollection = this._getSourceFeatureCollection();
const featureCollectionOnMap = AbstractLayer.getBoundDataForSource(mbMap, this.getId());
@@ -653,7 +766,7 @@ export class VectorLayer extends AbstractLayer {
}
}
- _setMbPointsProperties(mbMap, mvtSourceLayer) {
+ _setMbPointsProperties(mbMap: MbMap, mvtSourceLayer?: string) {
const pointLayerId = this._getMbPointLayerId();
const symbolLayerId = this._getMbSymbolLayerId();
const pointLayer = mbMap.getLayer(pointLayerId);
@@ -689,12 +802,12 @@ export class VectorLayer extends AbstractLayer {
}
}
- _setMbCircleProperties(mbMap, mvtSourceLayer) {
+ _setMbCircleProperties(mbMap: MbMap, mvtSourceLayer?: string) {
const sourceId = this.getId();
const pointLayerId = this._getMbPointLayerId();
const pointLayer = mbMap.getLayer(pointLayerId);
if (!pointLayer) {
- const mbLayer = {
+ const mbLayer: MbLayer = {
id: pointLayerId,
type: 'circle',
source: sourceId,
@@ -710,7 +823,7 @@ export class VectorLayer extends AbstractLayer {
const textLayerId = this._getMbTextLayerId();
const textLayer = mbMap.getLayer(textLayerId);
if (!textLayer) {
- const mbLayer = {
+ const mbLayer: MbLayer = {
id: textLayerId,
type: 'symbol',
source: sourceId,
@@ -740,13 +853,13 @@ export class VectorLayer extends AbstractLayer {
});
}
- _setMbSymbolProperties(mbMap, mvtSourceLayer) {
+ _setMbSymbolProperties(mbMap: MbMap, mvtSourceLayer?: string) {
const sourceId = this.getId();
const symbolLayerId = this._getMbSymbolLayerId();
const symbolLayer = mbMap.getLayer(symbolLayerId);
if (!symbolLayer) {
- const mbLayer = {
+ const mbLayer: MbLayer = {
id: symbolLayerId,
type: 'symbol',
source: sourceId,
@@ -775,7 +888,7 @@ export class VectorLayer extends AbstractLayer {
});
}
- _setMbLinePolygonProperties(mbMap, mvtSourceLayer) {
+ _setMbLinePolygonProperties(mbMap: MbMap, mvtSourceLayer?: string) {
const sourceId = this.getId();
const fillLayerId = this._getMbPolygonLayerId();
const lineLayerId = this._getMbLineLayerId();
@@ -783,7 +896,7 @@ export class VectorLayer extends AbstractLayer {
const hasJoins = this.hasJoins();
if (!mbMap.getLayer(fillLayerId)) {
- const mbLayer = {
+ const mbLayer: MbLayer = {
id: fillLayerId,
type: 'fill',
source: sourceId,
@@ -795,7 +908,7 @@ export class VectorLayer extends AbstractLayer {
mbMap.addLayer(mbLayer);
}
if (!mbMap.getLayer(lineLayerId)) {
- const mbLayer = {
+ const mbLayer: MbLayer = {
id: lineLayerId,
type: 'line',
source: sourceId,
@@ -807,7 +920,7 @@ export class VectorLayer extends AbstractLayer {
mbMap.addLayer(mbLayer);
}
if (!mbMap.getLayer(tooManyFeaturesLayerId)) {
- const mbLayer = {
+ const mbLayer: MbLayer = {
id: tooManyFeaturesLayerId,
type: 'fill',
source: sourceId,
@@ -855,12 +968,12 @@ export class VectorLayer extends AbstractLayer {
mbMap.setLayerZoomRange(tooManyFeaturesLayerId, this.getMinZoom(), this.getMaxZoom());
}
- _syncStylePropertiesWithMb(mbMap) {
+ _syncStylePropertiesWithMb(mbMap: MbMap) {
this._setMbPointsProperties(mbMap);
this._setMbLinePolygonProperties(mbMap);
}
- _syncSourceBindingWithMb(mbMap) {
+ _syncSourceBindingWithMb(mbMap: MbMap) {
const mbSource = mbMap.getSource(this._getMbSourceId());
if (!mbSource) {
mbMap.addSource(this._getMbSourceId(), {
@@ -883,7 +996,7 @@ export class VectorLayer extends AbstractLayer {
}
}
- syncLayerWithMB(mbMap) {
+ syncLayerWithMB(mbMap: MbMap) {
this._syncSourceBindingWithMb(mbMap);
this._syncFeatureCollectionWithMb(mbMap);
this._syncStylePropertiesWithMb(mbMap);
@@ -924,15 +1037,15 @@ export class VectorLayer extends AbstractLayer {
];
}
- ownsMbLayerId(mbLayerId) {
+ ownsMbLayerId(mbLayerId: string) {
return this.getMbLayerIds().includes(mbLayerId);
}
- ownsMbSourceId(mbSourceId) {
+ ownsMbSourceId(mbSourceId: string) {
return this.getId() === mbSourceId;
}
- _addJoinsToSourceTooltips(tooltipsFromSource) {
+ _addJoinsToSourceTooltips(tooltipsFromSource: ITooltipProperty[]) {
for (let i = 0; i < tooltipsFromSource.length; i++) {
const tooltipProperty = tooltipsFromSource[i];
const matchingJoins = [];
@@ -947,7 +1060,7 @@ export class VectorLayer extends AbstractLayer {
}
}
- async getPropertiesForTooltip(properties) {
+ async getPropertiesForTooltip(properties: GeoJsonProperties) {
const vectorSource = this.getSource();
let allProperties = await vectorSource.getTooltipProperties(properties);
this._addJoinsToSourceTooltips(allProperties);
@@ -961,18 +1074,20 @@ export class VectorLayer extends AbstractLayer {
canShowTooltip() {
return (
- this.isVisible() && (this.getSource().canFormatFeatureProperties() || this.getJoins().length)
+ this.isVisible() &&
+ (this.getSource().canFormatFeatureProperties() || this.getJoins().length > 0)
);
}
- getFeatureById(id) {
+ getFeatureById(id: string | number) {
const featureCollection = this._getSourceFeatureCollection();
if (!featureCollection) {
return null;
}
- return featureCollection.features.find((feature) => {
- return feature.properties[FEATURE_ID_PROPERTY_NAME] === id;
+ const targetFeature = featureCollection.features.find((feature) => {
+ return feature.properties?.[FEATURE_ID_PROPERTY_NAME] === id;
});
+ return targetFeature ? targetFeature : null;
}
}
diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts
index be947d79f4e395..5c062f3419e28d 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts
@@ -29,7 +29,7 @@ export interface IESAggSource extends IESSource {
getValueAggsDsl(indexPattern: IndexPattern): { [key: string]: unknown };
}
-export class AbstractESAggSource extends AbstractESSource {
+export abstract class AbstractESAggSource extends AbstractESSource {
private readonly _metricFields: IESAggField[];
private readonly _canReadFromGeoJson: boolean;
diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts
index ada76b8e4e674e..b221d13bb0f8ac 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts
@@ -10,6 +10,7 @@ import {
MapFilters,
MapQuery,
VectorSourceSyncMeta,
+ VectorSourceRequestMeta,
} from '../../../../common/descriptor_types';
import { GRID_RESOLUTION } from '../../../../common/constants';
import { IField } from '../../fields/field';
@@ -35,13 +36,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements ITiledSingle
getLayerName(): string;
getUrlTemplateWithMeta(
- searchFilters: MapFilters & {
- applyGlobalQuery: boolean;
- fieldNames: string[];
- geogridPrecision?: number;
- sourceQuery: MapQuery;
- sourceMeta: VectorSourceSyncMeta;
- }
+ searchFilters: VectorSourceRequestMeta
): Promise<{
layerName: string;
urlTemplate: string;
diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts
index 189e7dea1b0c1b..06df68283c434e 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts
@@ -160,7 +160,8 @@ describe('ESGeoGridSource', () => {
const { data, meta } = await geogridSource.getGeoJsonWithMeta(
'foobarLayer',
vectorSourceRequestMeta,
- () => {}
+ () => {},
+ () => true
);
expect(meta && meta.areResultsTrimmed).toEqual(false);
diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts
index 01fde589dcb84d..c11b6f0853cc7a 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts
@@ -6,12 +6,14 @@
import { AbstractVectorSource } from '../vector_source';
import { IVectorSource } from '../vector_source';
+import { TimeRange } from '../../../../../../../src/plugins/data/common';
import { IndexPattern, ISearchSource } from '../../../../../../../src/plugins/data/public';
import {
DynamicStylePropertyOptions,
+ MapQuery,
VectorSourceRequestMeta,
} from '../../../../common/descriptor_types';
-import { VectorStyle } from '../../styles/vector/vector_style';
+import { IVectorStyle } from '../../styles/vector/vector_style';
import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property';
export interface IESSource extends IVectorSource {
@@ -25,13 +27,21 @@ export interface IESSource extends IVectorSource {
limit: number,
initialSearchContext?: object
): Promise;
- loadStylePropsMeta(
- layerName: string,
- style: VectorStyle,
- dynamicStyleProps: Array>,
- registerCancelCallback: (requestToken: symbol, callback: () => void) => void,
- searchFilters: VectorSourceRequestMeta
- ): Promise;
+ loadStylePropsMeta({
+ layerName,
+ style,
+ dynamicStyleProps,
+ registerCancelCallback,
+ sourceQuery,
+ timeFilters,
+ }: {
+ layerName: string;
+ style: IVectorStyle;
+ dynamicStyleProps: Array>;
+ registerCancelCallback: (callback: () => void) => void;
+ sourceQuery?: MapQuery;
+ timeFilters: TimeRange;
+ }): Promise;
}
export class AbstractESSource extends AbstractVectorSource implements IESSource {
@@ -45,13 +55,21 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
limit: number,
initialSearchContext?: object
): Promise;
- loadStylePropsMeta(
- layerName: string,
- style: VectorStyle,
- dynamicStyleProps: Array>,
- registerCancelCallback: (requestToken: symbol, callback: () => void) => void,
- searchFilters: VectorSourceRequestMeta
- ): Promise;
+ loadStylePropsMeta({
+ layerName,
+ style,
+ dynamicStyleProps,
+ registerCancelCallback,
+ sourceQuery,
+ timeFilters,
+ }: {
+ layerName: string;
+ style: IVectorStyle;
+ dynamicStyleProps: Array>;
+ registerCancelCallback: (callback: () => void) => void;
+ sourceQuery?: MapQuery;
+ timeFilters: TimeRange;
+ }): Promise;
_runEsQuery: ({
requestId,
requestName,
diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js
index ab56ceeab4e771..0c8cb5f5142470 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js
@@ -284,13 +284,14 @@ export class AbstractESSource extends AbstractVectorSource {
return indexPattern.getFormatterForField(fieldFromIndexPattern).getConverterFor('text');
}
- async loadStylePropsMeta(
+ async loadStylePropsMeta({
layerName,
style,
dynamicStyleProps,
registerCancelCallback,
- searchFilters
- ) {
+ sourceQuery,
+ timeFilters,
+ }) {
const promises = dynamicStyleProps.map((dynamicStyleProp) => {
return dynamicStyleProp.getFieldMetaRequest();
});
@@ -307,13 +308,11 @@ export class AbstractESSource extends AbstractVectorSource {
searchSource.setField('index', indexPattern);
searchSource.setField('size', 0);
searchSource.setField('aggs', aggs);
- if (searchFilters.sourceQuery) {
- searchSource.setField('query', searchFilters.sourceQuery);
+ if (sourceQuery) {
+ searchSource.setField('query', sourceQuery);
}
if (style.isTimeAware() && (await this.isTimeAware())) {
- searchSource.setField('filter', [
- getTimeFilter().createFilter(indexPattern, searchFilters.timeFilters),
- ]);
+ searchSource.setField('filter', [getTimeFilter().createFilter(indexPattern, timeFilters)]);
}
const resp = await this._runEsQuery({
diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.d.ts b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.d.ts
index 248ca2b9212b4d..ef1ada8da82899 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.d.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.d.ts
@@ -4,10 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { MapQuery, VectorJoinSourceRequestMeta } from '../../../../common/descriptor_types';
import { IField } from '../../fields/field';
import { IESAggSource } from '../es_agg_source';
+import { PropertiesMap } from '../../joins/join';
export interface IESTermSource extends IESAggSource {
- getTermField(): IField;
- hasCompleteConfig(): boolean;
+ getTermField: () => IField;
+ hasCompleteConfig: () => boolean;
+ getWhereQuery: () => MapQuery;
+ getPropertiesMap: (
+ searchFilters: VectorJoinSourceRequestMeta,
+ leftSourceName: string,
+ leftFieldName: string,
+ registerCancelCallback: (callback: () => void) => void
+ ) => PropertiesMap;
}
diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js
index 359d22d2c44ce7..ff52dccdd2ef40 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js
+++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js
@@ -119,9 +119,7 @@ export class ESTermSource extends AbstractESAggSource {
});
const countPropertyName = this.getAggKey(AGG_TYPE.COUNT);
- return {
- propertiesMap: extractPropertiesMap(rawEsData, countPropertyName),
- };
+ return extractPropertiesMap(rawEsData, countPropertyName);
}
isFilterByMapBounds() {
diff --git a/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_source.js b/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_source.js
index eeb34ed672221c..d937edb4ed3620 100644
--- a/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_source.js
+++ b/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_source.js
@@ -74,6 +74,7 @@ export class KibanaRegionmapSource extends AbstractVectorSource {
});
return {
data: featureCollection,
+ meta: {},
};
}
diff --git a/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source.tsx b/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source.tsx
index 3e515613b3fd03..440f0cb4457e84 100644
--- a/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source.tsx
+++ b/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source.tsx
@@ -179,7 +179,7 @@ export class MVTSingleLayerVectorSource
getBoundsForFilters(
boundsFilters: BoundsFilters,
- registerCancelCallback: (requestToken: symbol, callback: () => void) => void
+ registerCancelCallback: (callback: () => void) => void
): MapExtent | null {
return null;
}
@@ -192,6 +192,18 @@ export class MVTSingleLayerVectorSource
return false;
}
+ isBoundsAware() {
+ return false;
+ }
+
+ getSourceTooltipContent() {
+ return { tooltipContent: null, areResultsTrimmed: false };
+ }
+
+ async getLeftJoinFields() {
+ return [];
+ }
+
async getTooltipProperties(
properties: GeoJsonProperties,
featureId?: string | number
diff --git a/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.d.ts b/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.d.ts
index a481e273bc33e0..7bf1db43c28712 100644
--- a/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.d.ts
+++ b/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.d.ts
@@ -19,6 +19,12 @@ import {
} from '../../../../common/descriptor_types';
import { VECTOR_SHAPE_TYPE } from '../../../../common/constants';
import { ITooltipProperty } from '../../tooltips/tooltip_property';
+import { DataRequest } from '../../util/data_request';
+
+export interface SourceTooltipConfig {
+ tooltipContent: string | null;
+ areResultsTrimmed: boolean;
+}
export type GeoJsonFetchMeta = ESSearchSourceResponseMeta;
@@ -30,8 +36,8 @@ export type GeoJsonWithMeta = {
export type BoundsFilters = {
applyGlobalQuery: boolean;
filters: Filter[];
- query: MapQuery;
- sourceQuery: MapQuery;
+ query?: MapQuery;
+ sourceQuery?: MapQuery;
timeFilters: TimeRange;
};
@@ -39,44 +45,52 @@ export interface IVectorSource extends ISource {
getTooltipProperties(properties: GeoJsonProperties): Promise;
getBoundsForFilters(
boundsFilters: BoundsFilters,
- registerCancelCallback: (requestToken: symbol, callback: () => void) => void
+ registerCancelCallback: (callback: () => void) => void
): MapExtent | null;
getGeoJsonWithMeta(
- layerName: 'string',
+ layerName: string,
searchFilters: MapFilters,
- registerCancelCallback: (callback: () => void) => void
+ registerCancelCallback: (callback: () => void) => void,
+ isRequestStillActive: () => boolean
): Promise;
getFields(): Promise;
getFieldByName(fieldName: string): IField | null;
+ getLeftJoinFields(): Promise;
getSyncMeta(): VectorSourceSyncMeta;
getFieldNames(): string[];
getApplyGlobalQuery(): boolean;
createField({ fieldName }: { fieldName: string }): IField;
canFormatFeatureProperties(): boolean;
getSupportedShapeTypes(): Promise;
+ isBoundsAware(): boolean;
+ getSourceTooltipContent(sourceDataRequest?: DataRequest): SourceTooltipConfig;
}
export class AbstractVectorSource extends AbstractSource implements IVectorSource {
getTooltipProperties(properties: GeoJsonProperties): Promise;
getBoundsForFilters(
boundsFilters: BoundsFilters,
- registerCancelCallback: (requestToken: symbol, callback: () => void) => void
+ registerCancelCallback: (callback: () => void) => void
): MapExtent | null;
getGeoJsonWithMeta(
layerName: string,
searchFilters: VectorSourceRequestMeta,
- registerCancelCallback: (callback: () => void) => void
+ registerCancelCallback: (callback: () => void) => void,
+ isRequestStillActive: () => boolean
): Promise;
getFields(): Promise;
getFieldByName(fieldName: string): IField | null;
+ getLeftJoinFields(): Promise;
getSyncMeta(): VectorSourceSyncMeta;
getSupportedShapeTypes(): Promise;
canFormatFeatureProperties(): boolean;
getApplyGlobalQuery(): boolean;
getFieldNames(): string[];
createField({ fieldName }: { fieldName: string }): IField;
+ isBoundsAware(): boolean;
+ getSourceTooltipContent(sourceDataRequest?: DataRequest): SourceTooltipConfig;
}
export interface ITiledSingleLayerVectorSource extends IVectorSource {
diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx
index 1244c53afe9a6c..5d0d9712ef988f 100644
--- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx
+++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx
@@ -5,7 +5,7 @@
*/
import _ from 'lodash';
-import React from 'react';
+import React, { ReactElement } from 'react';
import { Map as MbMap, FeatureIdentifier } from 'mapbox-gl';
import { FeatureCollection } from 'geojson';
// @ts-expect-error
@@ -92,6 +92,55 @@ export interface IVectorStyle extends IStyle {
mapColors: string[]
): { hasChanges: boolean; nextStyleDescriptor?: VectorStyleDescriptor };
pluckStyleMetaFromSourceDataRequest(sourceDataRequest: DataRequest): Promise;
+ isTimeAware: () => boolean;
+ getIcon: () => ReactElement;
+ hasLegendDetails: () => Promise;
+ renderLegendDetails: () => ReactElement;
+ clearFeatureState: (featureCollection: FeatureCollection, mbMap: MbMap, sourceId: string) => void;
+ setFeatureStateAndStyleProps: (
+ featureCollection: FeatureCollection,
+ mbMap: MbMap,
+ mbSourceId: string
+ ) => boolean;
+ arePointsSymbolizedAsCircles: () => boolean;
+ setMBPaintProperties: ({
+ alpha,
+ mbMap,
+ fillLayerId,
+ lineLayerId,
+ }: {
+ alpha: number;
+ mbMap: MbMap;
+ fillLayerId: string;
+ lineLayerId: string;
+ }) => void;
+ setMBPaintPropertiesForPoints: ({
+ alpha,
+ mbMap,
+ pointLayerId,
+ }: {
+ alpha: number;
+ mbMap: MbMap;
+ pointLayerId: string;
+ }) => void;
+ setMBPropertiesForLabelText: ({
+ alpha,
+ mbMap,
+ textLayerId,
+ }: {
+ alpha: number;
+ mbMap: MbMap;
+ textLayerId: string;
+ }) => void;
+ setMBSymbolPropertiesForPoints: ({
+ mbMap,
+ symbolLayerId,
+ alpha,
+ }: {
+ alpha: number;
+ mbMap: MbMap;
+ symbolLayerId: string;
+ }) => void;
}
export class VectorStyle implements IVectorStyle {
@@ -594,12 +643,12 @@ export class VectorStyle implements IVectorStyle {
mbSourceId: string
) {
if (!featureCollection) {
- return;
+ return false;
}
const dynamicStyleProps = this.getDynamicPropertiesArray();
if (dynamicStyleProps.length === 0) {
- return;
+ return false;
}
const tmpFeatureIdentifier: FeatureIdentifier = {
diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx
index 6c6cb6ba143cda..24728465de3bdd 100644
--- a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx
+++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx
@@ -9,7 +9,6 @@ import React from 'react';
import { shallow } from 'enzyme';
import { AbstractLayer, ILayer } from '../../../../../../classes/layers/layer';
import { AbstractSource, ISource } from '../../../../../../classes/sources/source';
-import { IStyle } from '../../../../../../classes/styles/style';
import { TOCEntryActionsPopover } from './toc_entry_actions_popover';
@@ -17,28 +16,17 @@ let supportsFitToBounds: boolean;
class MockSource extends AbstractSource implements ISource {}
-class MockStyle implements IStyle {
- renderEditor() {
- return null;
- }
-
- getType() {
- return 'mockStyle';
- }
-}
-
class LayerMock extends AbstractLayer implements ILayer {
constructor() {
const sourceDescriptor = {
type: 'mySourceType',
};
const source = new MockSource(sourceDescriptor);
- const style = new MockStyle();
const layerDescriptor = {
id: 'testLayer',
sourceDescriptor,
};
- super({ layerDescriptor, source, style });
+ super({ layerDescriptor, source });
}
async supportsFitToBounds(): Promise {
From e92a4ab4bf5ec175fc572f4d14b5da173de266a7 Mon Sep 17 00:00:00 2001
From: Ashik Meerankutty
Date: Fri, 2 Oct 2020 07:34:22 +0530
Subject: [PATCH 03/21] [APM] Service Inventory Updated the `EuiBadge` to use
the `behind_text` vars instead of the base colors for the health status
badges (#77844)
* Use behind_text colors in health status
* Separated badge color usage from getSeverityColor
---
.../plugins/apm/common/service_health_status.ts | 16 ++++++++++++++++
.../ServiceOverview/ServiceList/HealthBadge.tsx | 4 ++--
.../__snapshots__/ServiceOverview.test.tsx.snap | 2 +-
3 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/x-pack/plugins/apm/common/service_health_status.ts b/x-pack/plugins/apm/common/service_health_status.ts
index 1d4bcfb3b0e07b..f66e03a9733a3e 100644
--- a/x-pack/plugins/apm/common/service_health_status.ts
+++ b/x-pack/plugins/apm/common/service_health_status.ts
@@ -54,6 +54,22 @@ export function getServiceHealthStatusColor(
}
}
+export function getServiceHealthStatusBadgeColor(
+ theme: EuiTheme,
+ status: ServiceHealthStatus
+) {
+ switch (status) {
+ case ServiceHealthStatus.healthy:
+ return theme.eui.euiColorVis0_behindText;
+ case ServiceHealthStatus.warning:
+ return theme.eui.euiColorVis5_behindText;
+ case ServiceHealthStatus.critical:
+ return theme.eui.euiColorVis9_behindText;
+ case ServiceHealthStatus.unknown:
+ return theme.eui.euiColorMediumShade;
+ }
+}
+
export function getServiceHealthStatusLabel(status: ServiceHealthStatus) {
switch (status) {
case ServiceHealthStatus.critical:
diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx
index c6be0a352ef666..e8ad3e65b1a47e 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx
+++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx
@@ -6,7 +6,7 @@
import React from 'react';
import { EuiBadge } from '@elastic/eui';
import {
- getServiceHealthStatusColor,
+ getServiceHealthStatusBadgeColor,
getServiceHealthStatusLabel,
ServiceHealthStatus,
} from '../../../../../common/service_health_status';
@@ -20,7 +20,7 @@ export function HealthBadge({
const theme = useTheme();
return (
-
+
{getServiceHealthStatusLabel(healthStatus)}
);
diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap
index 40a2b6a5fa81b1..ee3a4fce0dbaae 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap
+++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap
@@ -153,7 +153,7 @@ NodeList [
>
Date: Fri, 2 Oct 2020 09:03:08 +0300
Subject: [PATCH 04/21] [Functional] Add retry on custom formatter test
(#78729)
Co-authored-by: Elastic Machine
---
test/functional/apps/visualize/_tsvb_time_series.ts | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts
index 0b2a52b367a20a..d4a079a38c8143 100644
--- a/test/functional/apps/visualize/_tsvb_time_series.ts
+++ b/test/functional/apps/visualize/_tsvb_time_series.ts
@@ -84,8 +84,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
await visualBuilder.clickSeriesOption();
await visualBuilder.enterSeriesTemplate('$ {{value}}');
- const actualCount = await visualBuilder.getRhythmChartLegendValue();
- expect(actualCount).to.be(expectedLegendValue);
+ await retry.try(async () => {
+ const actualCount = await visualBuilder.getRhythmChartLegendValue();
+ expect(actualCount).to.be(expectedLegendValue);
+ });
});
it('should show the correct count in the legend with percent formatter', async () => {
From 4ddcd1d2a6bf3ffbec37844468c4e4af827f739f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?=
Date: Fri, 2 Oct 2020 09:45:50 +0200
Subject: [PATCH 05/21] [APM] Fix anomalies not showing up on transaction
charts (#76930)
* [APM] Fix anomalies not showing up on transaction charts
* Added API tests to check transaction groups charts for anomaly data
* Improve test names and assertions from PR feedback
* Updated the transaction groups chart API to make `environment` a
required param while making `uiFilters` optional
* updates the basic API tests for transaction_groups/charts with the
required `environment` param
* makes uiFIltersES default to [] on core setup and removes SetupUIFilters type
* fixes vertical shade
* - replaces uiFiltersES with esFilter & uiFilters and cleans up related code around these
- deduplicates the required environment in the transaction_groups/charts API
* updates basic apm_api_integration tests
* pr feedback
* updates api test snapshots with correct anomaly data
* removed environment query param from useTransactionCharts and ensures
it's included in uiFilters returned from useUrlParams
Co-authored-by: Oliver Gupte
---
.../example_response_opbeans_beats.json | 42 ++---
.../public/context/UrlParamsContext/index.tsx | 7 +-
.../plugins/apm/public/utils/testHelpers.tsx | 7 +-
.../errors/__snapshots__/queries.test.ts.snap | 6 +-
.../__snapshots__/queries.test.ts.snap | 4 +-
.../__tests__/get_buckets.test.ts | 5 +-
.../lib/errors/distribution/get_buckets.ts | 12 +-
.../errors/distribution/get_distribution.ts | 8 +-
.../apm/server/lib/errors/get_error_group.ts | 12 +-
.../apm/server/lib/errors/get_error_groups.ts | 8 +-
...{get_ui_filters_es.ts => get_es_filter.ts} | 2 +-
.../get_parsed_ui_filters.ts | 23 ---
.../apm/server/lib/helpers/setup_request.ts | 41 ++---
.../__snapshots__/queries.test.ts.snap | 54 +++---
.../server/lib/metrics/by_agent/default.ts | 8 +-
.../java/gc/fetch_and_transform_gc_metrics.ts | 8 +-
.../by_agent/java/gc/get_gc_rate_chart.ts | 8 +-
.../by_agent/java/gc/get_gc_time_chart.ts | 8 +-
.../by_agent/java/heap_memory/index.ts | 8 +-
.../server/lib/metrics/by_agent/java/index.ts | 8 +-
.../by_agent/java/non_heap_memory/index.ts | 8 +-
.../by_agent/java/thread_count/index.ts | 8 +-
.../lib/metrics/by_agent/shared/cpu/index.ts | 8 +-
.../metrics/by_agent/shared/memory/index.ts | 10 +-
.../metrics/fetch_and_transform_metrics.ts | 8 +-
.../get_metrics_chart_data_by_agent.ts | 8 +-
.../__snapshots__/queries.test.ts.snap | 14 +-
.../lib/rum_client/get_client_metrics.ts | 8 +-
.../server/lib/rum_client/get_js_errors.ts | 8 +-
.../lib/rum_client/get_long_task_metrics.ts | 8 +-
.../rum_client/get_page_load_distribution.ts | 10 +-
.../lib/rum_client/get_page_view_trends.ts | 8 +-
.../lib/rum_client/get_pl_dist_breakdown.ts | 8 +-
.../server/lib/rum_client/get_rum_services.ts | 8 +-
.../server/lib/rum_client/get_url_search.ts | 8 +-
.../lib/rum_client/get_visitor_breakdown.ts | 8 +-
.../lib/rum_client/get_web_core_vitals.ts | 8 +-
.../server/lib/service_map/get_service_map.ts | 2 +-
.../get_service_map_service_node_info.test.ts | 6 +-
.../get_service_map_service_node_info.ts | 9 +-
.../group_resource_nodes_grouped.json | 4 +-
.../group_resource_nodes_pregrouped.json | 4 +-
.../__snapshots__/queries.test.ts.snap | 6 +-
.../apm/server/lib/service_nodes/index.ts | 8 +-
.../__snapshots__/queries.test.ts.snap | 10 +-
.../lib/services/get_service_node_metadata.ts | 8 +-
.../get_services/get_services_items.ts | 12 +-
.../server/lib/services/get_services/index.ts | 11 +-
.../__snapshots__/queries.test.ts.snap | 14 +-
.../server/lib/transaction_groups/fetcher.ts | 8 +-
.../lib/transaction_groups/get_error_rate.ts | 12 +-
.../get_transaction_sample_for_group.ts | 12 +-
.../server/lib/transaction_groups/index.ts | 8 +-
.../__snapshots__/queries.test.ts.snap | 12 +-
.../lib/transactions/breakdown/index.test.ts | 3 +-
.../lib/transactions/breakdown/index.ts | 12 +-
.../charts/get_anomaly_data/fetcher.ts | 11 +-
.../charts/get_anomaly_data/index.ts | 40 ++---
.../get_timeseries_data/fetcher.test.ts | 5 +-
.../charts/get_timeseries_data/fetcher.ts | 12 +-
.../charts/get_timeseries_data/index.ts | 8 +-
.../server/lib/transactions/charts/index.ts | 10 +-
.../distribution/get_buckets/index.ts | 12 +-
.../distribution/get_distribution_max.ts | 12 +-
.../lib/transactions/distribution/index.ts | 8 +-
.../lib/transactions/get_transaction/index.ts | 8 +-
.../server/lib/transactions/queries.test.ts | 3 -
.../__snapshots__/queries.test.ts.snap | 2 +-
.../get_local_filter_query.ts | 4 +-
.../local_ui_filters/queries.test.ts | 2 +-
.../plugins/apm/server/projections/errors.ts | 12 +-
.../plugins/apm/server/projections/metrics.ts | 12 +-
.../projections/rum_page_load_transactions.ts | 18 +-
.../apm/server/projections/service_nodes.ts | 8 +-
.../apm/server/projections/services.ts | 12 +-
.../server/projections/transaction_groups.ts | 8 +-
.../apm/server/projections/transactions.ts | 12 +-
.../plugins/apm/server/routes/service_map.ts | 5 -
x-pack/plugins/apm/server/routes/services.ts | 12 +-
.../apm/server/routes/transaction_groups.ts | 21 +--
.../plugins/apm/server/routes/ui_filters.ts | 13 +-
.../plugins/apm/server/utils/test_helpers.tsx | 7 +-
.../basic/tests/feature_controls.ts | 6 +-
.../transaction_groups/transaction_charts.ts | 2 +-
.../apm_api_integration/trial/tests/index.ts | 1 +
.../transaction_groups_charts.snap | 43 +++++
.../services/transaction_groups_charts.ts | 161 ++++++++++++++++++
87 files changed, 528 insertions(+), 548 deletions(-)
rename x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/{get_ui_filters_es.ts => get_es_filter.ts} (96%)
delete mode 100644 x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_parsed_ui_filters.ts
create mode 100644 x-pack/test/apm_api_integration/trial/tests/services/__snapshots__/transaction_groups_charts.snap
create mode 100644 x-pack/test/apm_api_integration/trial/tests/services/transaction_groups_charts.ts
diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json
index 153fa57bb05e70..cfd905f145fe2c 100644
--- a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json
+++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json
@@ -83,7 +83,7 @@
"id": "opbeans-go~>postgresql",
"sourceData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
@@ -103,7 +103,7 @@
"id": "opbeans-go~opbeans-java",
"sourceData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
@@ -123,13 +123,13 @@
"id": "opbeans-go~opbeans-node",
"sourceData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
"targetData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -143,7 +143,7 @@
"id": "opbeans-go~opbeans-ruby",
"sourceData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
@@ -189,7 +189,7 @@
},
"targetData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
@@ -209,7 +209,7 @@
},
"targetData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
}
@@ -242,7 +242,7 @@
"id": "opbeans-node~>postgresql",
"sourceData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -262,7 +262,7 @@
"id": "opbeans-node~>redis",
"sourceData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -282,13 +282,13 @@
"id": "opbeans-node~opbeans-go",
"sourceData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
"targetData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
@@ -302,7 +302,7 @@
"id": "opbeans-node~opbeans-python",
"sourceData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -322,7 +322,7 @@
"id": "opbeans-node~opbeans-ruby",
"sourceData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -408,7 +408,7 @@
},
"targetData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
}
@@ -427,7 +427,7 @@
},
"targetData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -487,7 +487,7 @@
},
"targetData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
},
@@ -527,7 +527,7 @@
},
"targetData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
},
@@ -566,7 +566,7 @@
},
"targetData": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go"
}
@@ -602,7 +602,7 @@
},
"targetData": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
}
@@ -673,7 +673,7 @@
{
"data": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs",
"anomaly_score": 41.31593099784474,
@@ -733,7 +733,7 @@
{
"data": {
"id": "opbeans-go",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-go",
"agent.name": "go",
"anomaly_score": 0.2633884161762746,
diff --git a/x-pack/plugins/apm/public/context/UrlParamsContext/index.tsx b/x-pack/plugins/apm/public/context/UrlParamsContext/index.tsx
index 9eb4704a2ca29a..5682009019d7f8 100644
--- a/x-pack/plugins/apm/public/context/UrlParamsContext/index.tsx
+++ b/x-pack/plugins/apm/public/context/UrlParamsContext/index.tsx
@@ -25,6 +25,7 @@ import {
import { pickKeys } from '../../../common/utils/pick_keys';
import { useDeepObjectIdentity } from '../../hooks/useDeepObjectIdentity';
import { LocalUIFilterName } from '../../../common/ui_filter';
+import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values';
interface TimeRange {
rangeFrom: string;
@@ -38,7 +39,11 @@ function useUiFilters(params: IUrlParams): UIFilters {
(val) => (val ? val.split(',') : [])
) as Partial>;
- return useDeepObjectIdentity({ kuery, environment, ...localUiFilters });
+ return useDeepObjectIdentity({
+ kuery,
+ environment: environment || ENVIRONMENT_ALL.value,
+ ...localUiFilters,
+ });
}
const defaultRefresh = (_time: TimeRange) => {};
diff --git a/x-pack/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx
index 971455fde39462..7826e9672a3bbe 100644
--- a/x-pack/plugins/apm/public/utils/testHelpers.tsx
+++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx
@@ -25,6 +25,7 @@ import {
} from '../../typings/elasticsearch';
import { MockApmPluginContextWrapper } from '../context/ApmPluginContext/MockApmPluginContext';
import { UrlParamsProvider } from '../context/UrlParamsContext';
+import { UIFilters } from '../../typings/ui_filters';
const originalConsoleWarn = console.warn; // eslint-disable-line no-console
/**
@@ -118,7 +119,8 @@ interface MockSetup {
apmEventClient: any;
internalClient: any;
config: APMConfig;
- uiFiltersES: ESFilter[];
+ uiFilters: UIFilters;
+ esFilter: ESFilter[];
indices: {
/* eslint-disable @typescript-eslint/naming-convention */
'apm_oss.sourcemapIndices': string;
@@ -179,7 +181,8 @@ export async function inspectSearchParams(
},
}
) as APMConfig,
- uiFiltersES: [{ term: { 'my.custom.ui.filter': 'foo-bar' } }],
+ uiFilters: { environment: 'test' },
+ esFilter: [{ term: { 'service.environment': 'test' } }],
indices: {
/* eslint-disable @typescript-eslint/naming-convention */
'apm_oss.sourcemapIndices': 'myIndex',
diff --git a/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap
index 63b6c9cde4d0d1..632232ffb075d8 100644
--- a/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap
@@ -32,7 +32,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -119,7 +119,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -194,7 +194,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap
index ea142ca2acc002..b329499c8b0452 100644
--- a/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap
@@ -40,7 +40,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -91,7 +91,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts b/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts
index 1a83113de35f2a..50da1f9c20d16c 100644
--- a/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts
@@ -41,7 +41,10 @@ describe('timeseriesFetcher', () => {
get: () => 'myIndex',
}
) as APMConfig,
- uiFiltersES: [
+ uiFilters: {
+ environment: 'prod',
+ },
+ esFilter: [
{
term: { 'service.environment': 'prod' },
},
diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
index de6df15354e79c..a42710947a7924 100644
--- a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts
@@ -11,11 +11,7 @@ import {
SERVICE_NAME,
} from '../../../../common/elasticsearch_fieldnames';
import { rangeFilter } from '../../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
export async function getBuckets({
serviceName,
@@ -26,13 +22,13 @@ export async function getBuckets({
serviceName: string;
groupId?: string;
bucketSize: number;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
- const { start, end, uiFiltersES, apmEventClient } = setup;
+ const { start, end, esFilter, apmEventClient } = setup;
const filter: ESFilter[] = [
{ term: { [SERVICE_NAME]: serviceName } },
{ range: rangeFilter(start, end) },
- ...uiFiltersES,
+ ...esFilter,
];
if (groupId) {
diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts
index 3b48b6c5be5944..dea518cad8e405 100644
--- a/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts
+++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts
@@ -5,11 +5,7 @@
*/
import { PromiseReturnType } from '../../../../typings/common';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { getBuckets } from './get_buckets';
import { BUCKET_TARGET_COUNT } from '../../transactions/constants';
@@ -28,7 +24,7 @@ export async function getErrorDistribution({
}: {
serviceName: string;
groupId?: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
const bucketSize = getBucketSize({ start: setup.start, end: setup.end });
const { buckets, noHits } = await getBuckets({
diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_group.ts b/x-pack/plugins/apm/server/lib/errors/get_error_group.ts
index b23c955b57183b..0fbc7720f7111f 100644
--- a/x-pack/plugins/apm/server/lib/errors/get_error_group.ts
+++ b/x-pack/plugins/apm/server/lib/errors/get_error_group.ts
@@ -12,11 +12,7 @@ import {
} from '../../../common/elasticsearch_fieldnames';
import { PromiseReturnType } from '../../../typings/common';
import { rangeFilter } from '../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getTransaction } from '../transactions/get_transaction';
export type ErrorGroupAPIResponse = PromiseReturnType;
@@ -29,9 +25,9 @@ export async function getErrorGroup({
}: {
serviceName: string;
groupId: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
- const { start, end, uiFiltersES, apmEventClient } = setup;
+ const { start, end, esFilter, apmEventClient } = setup;
const params = {
apm: {
@@ -45,7 +41,7 @@ export async function getErrorGroup({
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [ERROR_GROUP_ID]: groupId } },
{ range: rangeFilter(start, end) },
- ...uiFiltersES,
+ ...esFilter,
],
should: [{ term: { [TRANSACTION_SAMPLED]: true } }],
},
diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
index ab1c2149be3436..006d2fae3d4fb1 100644
--- a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
+++ b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts
@@ -13,11 +13,7 @@ import {
ERROR_LOG_MESSAGE,
} from '../../../common/elasticsearch_fieldnames';
import { PromiseReturnType } from '../../../typings/common';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getErrorGroupsProjection } from '../../projections/errors';
import { mergeProjection } from '../../projections/util/merge_projection';
import { SortOptions } from '../../../typings/elasticsearch/aggregations';
@@ -35,7 +31,7 @@ export async function getErrorGroups({
serviceName: string;
sortField?: string;
sortDirection?: 'asc' | 'desc';
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
const { apmEventClient } = setup;
diff --git a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts
similarity index 96%
rename from x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts
rename to x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts
index c1405b44f2a8ac..1b8f32d4de8b93 100644
--- a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts
@@ -13,7 +13,7 @@ import {
} from '../../ui_filters/local_ui_filters/config';
import { esKuery } from '../../../../../../../src/plugins/data/server';
-export function getUiFiltersES(uiFilters: UIFilters) {
+export function getEsFilter(uiFilters: UIFilters) {
const { kuery, environment, ...localFilterValues } = uiFilters;
const mappedFilters = localUIFilterNames
.filter((name) => name in localFilterValues)
diff --git a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_parsed_ui_filters.ts b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_parsed_ui_filters.ts
deleted file mode 100644
index 324da199807c7d..00000000000000
--- a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_parsed_ui_filters.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { Logger } from 'src/core/server';
-import { UIFilters } from '../../../../typings/ui_filters';
-
-export function getParsedUiFilters({
- uiFilters,
- logger,
-}: {
- uiFilters: string;
- logger: Logger;
-}): UIFilters {
- try {
- return JSON.parse(uiFilters);
- } catch (error) {
- logger.error(error);
- }
- return {};
-}
diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts
index eba75433a51486..26896a050dd882 100644
--- a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts
+++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts
@@ -5,6 +5,7 @@
*/
import moment from 'moment';
+import { Logger } from 'kibana/server';
import { isActivePlatinumLicense } from '../../../common/service_map';
import { UI_SETTINGS } from '../../../../../../src/plugins/data/common';
import { KibanaRequest } from '../../../../../../src/core/server';
@@ -14,7 +15,7 @@ import {
ApmIndicesConfig,
} from '../settings/apm_indices/get_apm_indices';
import { ESFilter } from '../../../typings/elasticsearch';
-import { getUiFiltersES } from './convert_ui_filters/get_ui_filters_es';
+import { getEsFilter } from './convert_ui_filters/get_es_filter';
import { APMRequestHandlerContext } from '../../routes/typings';
import { ProcessorEvent } from '../../../common/processor_event';
import {
@@ -25,14 +26,8 @@ import {
APMInternalClient,
createInternalESClient,
} from './create_es_client/create_internal_es_client';
+import { UIFilters } from '../../../typings/ui_filters';
-function decodeUiFilters(uiFiltersEncoded?: string) {
- if (!uiFiltersEncoded) {
- return [];
- }
- const uiFilters = JSON.parse(uiFiltersEncoded);
- return getUiFiltersES(uiFilters);
-}
// Explicitly type Setup to prevent TS initialization errors
// https://github.com/microsoft/TypeScript/issues/34933
@@ -42,6 +37,8 @@ export interface Setup {
ml?: ReturnType;
config: APMConfig;
indices: ApmIndicesConfig;
+ uiFilters: UIFilters;
+ esFilter: ESFilter[];
}
export interface SetupTimeRange {
@@ -49,10 +46,6 @@ export interface SetupTimeRange {
end: number;
}
-export interface SetupUIFilters {
- uiFiltersES: ESFilter[];
-}
-
interface SetupRequestParams {
query?: {
_debug?: boolean;
@@ -65,16 +58,13 @@ interface SetupRequestParams {
type InferSetup = Setup &
(TParams extends { query: { start: string } } ? { start: number } : {}) &
- (TParams extends { query: { end: string } } ? { end: number } : {}) &
- (TParams extends { query: { uiFilters: string } }
- ? { uiFiltersES: ESFilter[] }
- : {});
+ (TParams extends { query: { end: string } } ? { end: number } : {});
export async function setupRequest(
context: APMRequestHandlerContext,
request: KibanaRequest
): Promise> {
- const { config } = context;
+ const { config, logger } = context;
const { query } = context.params;
const [indices, includeFrozen] = await Promise.all([
@@ -85,7 +75,7 @@ export async function setupRequest(
context.core.uiSettings.client.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN),
]);
- const uiFiltersES = decodeUiFilters(query.uiFilters);
+ const uiFilters = decodeUiFilters(logger, query.uiFilters);
const coreSetupRequest = {
indices,
@@ -108,12 +98,13 @@ export async function setupRequest(
)
: undefined,
config,
+ uiFilters,
+ esFilter: getEsFilter(uiFilters),
};
return {
...('start' in query ? { start: moment.utc(query.start).valueOf() } : {}),
...('end' in query ? { end: moment.utc(query.end).valueOf() } : {}),
- ...('uiFilters' in query ? { uiFiltersES } : {}),
...coreSetupRequest,
} as InferSetup;
}
@@ -129,3 +120,15 @@ function getMlSetup(
modules: ml.modulesProvider(request, savedObjectsClient),
};
}
+
+function decodeUiFilters(logger: Logger, uiFiltersEncoded?: string): UIFilters {
+ if (!uiFiltersEncoded) {
+ return {};
+ }
+ try {
+ return JSON.parse(uiFiltersEncoded);
+ } catch (error) {
+ logger.error(error);
+ return {};
+ }
+}
diff --git a/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
index 2868dcfda97b6e..961a1eee61d1d5 100644
--- a/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap
@@ -87,7 +87,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -175,7 +175,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -206,7 +206,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -231,7 +231,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -258,7 +258,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -283,7 +283,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -338,7 +338,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -431,7 +431,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -514,7 +514,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -623,7 +623,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -717,7 +717,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -748,7 +748,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -773,7 +773,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -800,7 +800,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -825,7 +825,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -886,7 +886,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -985,7 +985,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -1074,7 +1074,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -1172,7 +1172,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -1255,7 +1255,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -1286,7 +1286,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -1311,7 +1311,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -1338,7 +1338,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -1363,7 +1363,7 @@ Object {
"lang": "painless",
"source": "
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -1413,7 +1413,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -1501,7 +1501,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -1579,7 +1579,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts
index 6ee507d7b9bb16..fbcbc9f12791fa 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts
@@ -4,16 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { getCPUChartData } from './shared/cpu';
import { getMemoryChartData } from './shared/memory';
export async function getDefaultMetricsCharts(
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string
) {
const charts = await Promise.all([
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts
index d7e64bdcacd12b..2ed11480a75857 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts
@@ -11,11 +11,7 @@
import { sum, round } from 'lodash';
import theme from '@elastic/eui/dist/eui_theme_light.json';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { getMetricsDateHistogramParams } from '../../../../helpers/metrics';
import { ChartBase } from '../../../types';
import { getMetricsProjection } from '../../../../../projections/metrics';
@@ -36,7 +32,7 @@ export async function fetchAndTransformGcMetrics({
chartBase,
fieldName,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
chartBase: ChartBase;
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts
index 6e562b9a8ee870..7cedeb828e3b73 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts
@@ -7,11 +7,7 @@
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import { METRIC_JAVA_GC_COUNT } from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { fetchAndTransformGcMetrics } from './fetch_and_transform_gc_metrics';
import { ChartBase } from '../../../types';
@@ -35,7 +31,7 @@ const chartBase: ChartBase = {
};
const getGcRateChart = (
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) => {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts
index 0b9d6240fc1c9b..f21d3d8e7c0565 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts
@@ -7,11 +7,7 @@
import theme from '@elastic/eui/dist/eui_theme_light.json';
import { i18n } from '@kbn/i18n';
import { METRIC_JAVA_GC_TIME } from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { fetchAndTransformGcMetrics } from './fetch_and_transform_gc_metrics';
import { ChartBase } from '../../../types';
@@ -35,7 +31,7 @@ const chartBase: ChartBase = {
};
const getGcTimeChart = (
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) => {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts
index ba3183c0fa7d7a..eb79897f9f0558 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts
@@ -12,11 +12,7 @@ import {
METRIC_JAVA_HEAP_MEMORY_USED,
AGENT_NAME,
} from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics';
import { ChartBase } from '../../../types';
@@ -55,7 +51,7 @@ const chartBase: ChartBase = {
};
export async function getHeapMemoryChart(
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts
index 21caab6590fc46..d4084701f0f49a 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts
@@ -5,11 +5,7 @@
*/
import { getHeapMemoryChart } from './heap_memory';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../helpers/setup_request';
import { getNonHeapMemoryChart } from './non_heap_memory';
import { getThreadCountChart } from './thread_count';
import { getCPUChartData } from '../shared/cpu';
@@ -18,7 +14,7 @@ import { getGcRateChart } from './gc/get_gc_rate_chart';
import { getGcTimeChart } from './gc/get_gc_time_chart';
export async function getJavaMetricsCharts(
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts
index 1a2d5bd0b0e68a..50cc449da3c159 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts
@@ -12,11 +12,7 @@ import {
METRIC_JAVA_NON_HEAP_MEMORY_USED,
AGENT_NAME,
} from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { ChartBase } from '../../../types';
import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics';
@@ -52,7 +48,7 @@ const chartBase: ChartBase = {
};
export async function getNonHeapMemoryChart(
- setup: Setup & SetupUIFilters & SetupTimeRange,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts
index 01cc6d84952446..0062f0a423970c 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts
@@ -10,11 +10,7 @@ import {
METRIC_JAVA_THREAD_COUNT,
AGENT_NAME,
} from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { ChartBase } from '../../../types';
import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics';
@@ -44,7 +40,7 @@ const chartBase: ChartBase = {
};
export async function getThreadCountChart(
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts
index 066ef40b4ab6ce..ca642aa12fff1d 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts
@@ -10,11 +10,7 @@ import {
METRIC_SYSTEM_CPU_PERCENT,
METRIC_PROCESS_CPU_PERCENT,
} from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { ChartBase } from '../../../types';
import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics';
@@ -56,7 +52,7 @@ const chartBase: ChartBase = {
};
export async function getCPUChartData(
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) {
diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts
index a60576ca0c175e..e6ee47cc815efd 100644
--- a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts
@@ -11,11 +11,7 @@ import {
METRIC_SYSTEM_FREE_MEMORY,
METRIC_SYSTEM_TOTAL_MEMORY,
} from '../../../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../../helpers/setup_request';
import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics';
import { ChartBase } from '../../../types';
@@ -54,7 +50,7 @@ export const percentCgroupMemoryUsedScript = {
lang: 'painless',
source: `
/*
- When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
+ When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants.
This number represents the max possible value for the limit field.
*/
double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L;
@@ -73,7 +69,7 @@ export const percentCgroupMemoryUsedScript = {
};
export async function getMemoryChartData(
- setup: Setup & SetupTimeRange & SetupUIFilters,
+ setup: Setup & SetupTimeRange,
serviceName: string,
serviceNodeName?: string
) {
diff --git a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
index a42a10d6518a01..3ccba8c7586dcf 100644
--- a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts
@@ -5,11 +5,7 @@
*/
import { Unionize, Overwrite } from 'utility-types';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getMetricsDateHistogramParams } from '../helpers/metrics';
import { ChartBase } from './types';
import { transformDataToMetricsChart } from './transform_metrics_chart';
@@ -58,7 +54,7 @@ export async function fetchAndTransformMetrics({
aggs,
additionalFilters = [],
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
chartBase: ChartBase;
diff --git a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts
index 059e1ce48c83df..72cd65deebff63 100644
--- a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts
+++ b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts
@@ -3,11 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getJavaMetricsCharts } from './by_agent/java';
import { getDefaultMetricsCharts } from './by_agent/default';
import { GenericMetricsChart } from './transform_metrics_chart';
@@ -22,7 +18,7 @@ export async function getMetricsChartDataByAgent({
serviceNodeName,
agentName,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
agentName: string;
diff --git a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap
index dcafe092211646..1fafa080824439 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap
@@ -61,7 +61,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -151,7 +151,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -230,7 +230,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -500,7 +500,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -552,7 +552,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -667,7 +667,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -723,7 +723,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts b/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts
index 6566ea4f5e29bf..6d596246d6af92 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts
@@ -7,11 +7,7 @@
import { TRANSACTION_DURATION } from '../../../common/elasticsearch_fieldnames';
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
TRANSACTION_DOM_INTERACTIVE,
TRANSACTION_TIME_TO_FIRST_BYTE,
@@ -22,7 +18,7 @@ export async function getClientMetrics({
urlQuery,
percentile = 50,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
urlQuery?: string;
percentile?: number;
}) {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_js_errors.ts b/x-pack/plugins/apm/server/lib/rum_client/get_js_errors.ts
index 0540ea4bf09dd8..a8a4e2714c86e0 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_js_errors.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_js_errors.ts
@@ -5,11 +5,7 @@
*/
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getRumErrorsProjection } from '../../projections/rum_page_load_transactions';
import {
ERROR_EXC_MESSAGE,
@@ -23,7 +19,7 @@ export async function getJSErrors({
pageSize,
pageIndex,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
pageSize: number;
pageIndex: number;
}) {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts b/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts
index c2c86ae05d57c6..dfb31de8f10f78 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts
@@ -6,11 +6,7 @@
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
const LONG_TASK_SUM_FIELD = 'transaction.experience.longtask.sum';
const LONG_TASK_COUNT_FIELD = 'transaction.experience.longtask.count';
@@ -21,7 +17,7 @@ export async function getLongTaskMetrics({
urlQuery,
percentile = 50,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
urlQuery?: string;
percentile?: number;
}) {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts b/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts
index 5f666feb8a18f2..225afff2818abb 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts
@@ -7,11 +7,7 @@
import { TRANSACTION_DURATION } from '../../../common/elasticsearch_fieldnames';
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
export const MICRO_TO_SEC = 1000000;
@@ -56,7 +52,7 @@ export async function getPageLoadDistribution({
maxPercentile,
urlQuery,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
minPercentile?: string;
maxPercentile?: string;
urlQuery?: string;
@@ -168,7 +164,7 @@ const getPercentilesDistribution = async ({
minDuration,
maxDuration,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
minDuration: number;
maxDuration: number;
}) => {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts b/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts
index 40f8a8bc58a546..c1a602c33feae1 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts
@@ -5,11 +5,7 @@
*/
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { BreakdownItem } from '../../../typings/ui_filters';
export async function getPageViewTrends({
@@ -17,7 +13,7 @@ export async function getPageViewTrends({
breakdowns,
urlQuery,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
breakdowns?: string;
urlQuery?: string;
}) {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts b/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts
index bebf9c0bc99c92..e2ec59d232b211 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts
@@ -7,11 +7,7 @@
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { ProcessorEvent } from '../../../common/processor_event';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
CLIENT_GEO_COUNTRY_ISO_CODE,
USER_AGENT_DEVICE,
@@ -46,7 +42,7 @@ export const getPageLoadDistBreakdown = async ({
breakdown,
urlQuery,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
minPercentile: number;
maxPercentile: number;
breakdown: string;
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_rum_services.ts b/x-pack/plugins/apm/server/lib/rum_client/get_rum_services.ts
index 3adad0868ed4b5..e9bd203e354cb7 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_rum_services.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_rum_services.ts
@@ -5,18 +5,14 @@
*/
import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
export async function getRumServices({
setup,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
const projection = getRumPageLoadTransactionsProjection({
setup,
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_url_search.ts b/x-pack/plugins/apm/server/lib/rum_client/get_url_search.ts
index 6aa39c7ef961fc..febfd66897e182 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_url_search.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_url_search.ts
@@ -5,11 +5,7 @@
*/
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import {
TRANSACTION_DURATION,
@@ -21,7 +17,7 @@ export async function getUrlSearch({
urlQuery,
percentile,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
urlQuery?: string;
percentile: number;
}) {
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts b/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts
index 52d089e4e29c99..6350bc2c070160 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts
@@ -6,11 +6,7 @@
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
USER_AGENT_NAME,
USER_AGENT_OS,
@@ -20,7 +16,7 @@ export async function getVisitorBreakdown({
setup,
urlQuery,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
urlQuery?: string;
}) {
const projection = getRumPageLoadTransactionsProjection({
diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts b/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts
index 676b3506397a71..c5baf0b529eb42 100644
--- a/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts
+++ b/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts
@@ -6,11 +6,7 @@
import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions';
import { mergeProjection } from '../../projections/util/merge_projection';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
CLS_FIELD,
FCP_FIELD,
@@ -25,7 +21,7 @@ export async function getWebCoreVitals({
urlQuery,
percentile = 50,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
urlQuery?: string;
percentile?: number;
}) {
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts
index 75acebe7ed56ce..330bb936c9e889 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts
@@ -82,7 +82,7 @@ async function getServicesData(options: IEnvOptions) {
const { setup, searchAggregatedTransactions } = options;
const projection = getServicesProjection({
- setup: { ...setup, uiFiltersES: [] },
+ setup: { ...setup, esFilter: [] },
searchAggregatedTransactions,
});
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts
index 7af1607697ef39..eb2ddbf38b2746 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts
@@ -19,11 +19,10 @@ describe('getServiceMapServiceNodeInfo', () => {
}),
},
indices: {},
+ uiFilters: { environment: 'test environment' },
} as unknown) as Setup & SetupTimeRange;
- const environment = 'test environment';
const serviceName = 'test service name';
const result = await getServiceMapServiceNodeInfo({
- uiFilters: { environment },
setup,
serviceName,
searchAggregatedTransactions: false,
@@ -67,11 +66,10 @@ describe('getServiceMapServiceNodeInfo', () => {
config: {
'xpack.apm.metricsInterval': 30,
},
+ uiFilters: { environment: 'test environment' },
} as unknown) as Setup & SetupTimeRange;
- const environment = 'test environment';
const serviceName = 'test service name';
const result = await getServiceMapServiceNodeInfo({
- uiFilters: { environment },
setup,
serviceName,
searchAggregatedTransactions: false,
diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
index 7c2137ce65d839..37b34641435fbc 100644
--- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
+++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts
@@ -8,7 +8,6 @@ import {
TRANSACTION_REQUEST,
TRANSACTION_PAGE_LOAD,
} from '../../../common/transaction_types';
-import { UIFilters } from '../../../typings/ui_filters';
import {
SERVICE_NAME,
METRIC_SYSTEM_CPU_PERCENT,
@@ -53,9 +52,8 @@ export async function getServiceMapServiceNodeInfo({
serviceName,
setup,
searchAggregatedTransactions,
- uiFilters,
-}: Options & { serviceName: string; uiFilters: UIFilters }) {
- const { start, end } = setup;
+}: Options & { serviceName: string }) {
+ const { start, end, uiFilters } = setup;
const filter: ESFilter[] = [
{ range: rangeFilter(start, end) },
@@ -105,7 +103,8 @@ async function getErrorStats({
}) {
const setupWithBlankUiFilters = {
...setup,
- uiFiltersES: getEnvironmentUiFilterES(environment),
+ uiFilters: { environment },
+ esFilter: getEnvironmentUiFilterES(environment),
};
const { noHits, average } = await getErrorRate({
setup: setupWithBlankUiFilters,
diff --git a/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_grouped.json b/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_grouped.json
index e7bba585de1807..94c508fe90230d 100644
--- a/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_grouped.json
+++ b/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_grouped.json
@@ -3,7 +3,7 @@
{
"data": {
"id": "opbeans-rum",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-rum",
"agent.name": "rum-js"
}
@@ -18,7 +18,7 @@
{
"data": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
}
diff --git a/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_pregrouped.json b/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_pregrouped.json
index 22c5c50de74727..58469f607ac130 100644
--- a/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_pregrouped.json
+++ b/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_pregrouped.json
@@ -3,7 +3,7 @@
{
"data": {
"id": "opbeans-rum",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-rum",
"agent.name": "rum-js"
}
@@ -18,7 +18,7 @@
{
"data": {
"id": "opbeans-node",
- "service.environment": "testing",
+ "service.environment": "test",
"service.name": "opbeans-node",
"agent.name": "nodejs"
}
diff --git a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
index 87aca0d0569091..d83e558775be40 100644
--- a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap
@@ -51,7 +51,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -119,7 +119,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -188,7 +188,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/service_nodes/index.ts b/x-pack/plugins/apm/server/lib/service_nodes/index.ts
index a83aba192dba93..d5e29532e3d7b9 100644
--- a/x-pack/plugins/apm/server/lib/service_nodes/index.ts
+++ b/x-pack/plugins/apm/server/lib/service_nodes/index.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getServiceNodesProjection } from '../../projections/service_nodes';
import { mergeProjection } from '../../projections/util/merge_projection';
import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes';
@@ -23,7 +19,7 @@ const getServiceNodes = async ({
setup,
serviceName,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
}) => {
const { apmEventClient } = setup;
diff --git a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
index 431f11066aaff0..3a38f80c87b35e 100644
--- a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap
@@ -144,7 +144,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -194,7 +194,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -257,7 +257,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -334,7 +334,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -389,7 +389,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts
index fca472b0ce8c22..d6ba9f5447ba50 100644
--- a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
HOST_NAME,
CONTAINER_ID,
@@ -24,7 +20,7 @@ export async function getServiceNodeMetadata({
}: {
serviceName: string;
serviceNodeName: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
const { apmEventClient } = setup;
diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts
index c09be7aacc784f..092485c46fb083 100644
--- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts
@@ -5,11 +5,7 @@
*/
import { joinByKey } from '../../../../common/utils/join_by_key';
import { PromiseReturnType } from '../../../../typings/common';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { getServicesProjection } from '../../../projections/services';
import {
getTransactionDurationAverages,
@@ -21,17 +17,15 @@ import {
} from './get_services_items_stats';
export type ServiceListAPIResponse = PromiseReturnType;
-export type ServicesItemsSetup = Setup & SetupTimeRange & SetupUIFilters;
+export type ServicesItemsSetup = Setup & SetupTimeRange;
export type ServicesItemsProjection = ReturnType;
export async function getServicesItems({
setup,
searchAggregatedTransactions,
- mlAnomaliesEnvironment,
}: {
setup: ServicesItemsSetup;
searchAggregatedTransactions: boolean;
- mlAnomaliesEnvironment?: string;
}) {
const params = {
projection: getServicesProjection({
@@ -55,7 +49,7 @@ export async function getServicesItems({
getTransactionRates(params),
getTransactionErrorRates(params),
getEnvironments(params),
- getHealthStatuses(params, mlAnomaliesEnvironment),
+ getHealthStatuses(params, setup.uiFilters.environment),
]);
const allMetrics = [
diff --git a/x-pack/plugins/apm/server/lib/services/get_services/index.ts b/x-pack/plugins/apm/server/lib/services/get_services/index.ts
index 351457b2a815ee..04744a9c791bb1 100644
--- a/x-pack/plugins/apm/server/lib/services/get_services/index.ts
+++ b/x-pack/plugins/apm/server/lib/services/get_services/index.ts
@@ -6,11 +6,7 @@
import { isEmpty } from 'lodash';
import { PromiseReturnType } from '../../../../typings/common';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { hasHistoricalAgentData } from './has_historical_agent_data';
import { getLegacyDataStatus } from './get_legacy_data_status';
import { getServicesItems } from './get_services_items';
@@ -20,17 +16,14 @@ export type ServiceListAPIResponse = PromiseReturnType;
export async function getServices({
setup,
searchAggregatedTransactions,
- mlAnomaliesEnvironment,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
- mlAnomaliesEnvironment?: string;
}) {
const [items, hasLegacyData] = await Promise.all([
getServicesItems({
setup,
searchAggregatedTransactions,
- mlAnomaliesEnvironment,
}),
getLegacyDataStatus(setup),
]);
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
index bd6cefa793467f..c678e7db711b65 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap
@@ -61,7 +61,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -128,7 +128,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -195,7 +195,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -270,7 +270,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -325,7 +325,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -380,7 +380,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -441,7 +441,7 @@ Array [
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
index 5d581149db6678..0a4d9748f25977 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts
@@ -15,11 +15,7 @@ import { getTransactionGroupsProjection } from '../../projections/transaction_gr
import { mergeProjection } from '../../projections/util/merge_projection';
import { PromiseReturnType } from '../../../../observability/typings/common';
import { AggregationOptionsByType } from '../../../typings/elasticsearch/aggregations';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import {
getAverages,
getSums,
@@ -57,7 +53,7 @@ export type TransactionGroupRequestBase = ReturnType<
};
};
-export type TransactionGroupSetup = Setup & SetupTimeRange & SetupUIFilters;
+export type TransactionGroupSetup = Setup & SetupTimeRange;
function getItemsWithRelativeImpact(
setup: TransactionGroupSetup,
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts
index 3dc126c45d328a..d5289430b2698d 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts
@@ -12,11 +12,7 @@ import {
EVENT_OUTCOME,
} from '../../../common/elasticsearch_fieldnames';
import { rangeFilter } from '../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { getBucketSize } from '../helpers/get_bucket_size';
import {
getProcessorEventForAggregatedTransactions,
@@ -33,10 +29,10 @@ export async function getErrorRate({
serviceName: string;
transactionType?: string;
transactionName?: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
- const { start, end, uiFiltersES, apmEventClient } = setup;
+ const { start, end, esFilter, apmEventClient } = setup;
const transactionNamefilter = transactionName
? [{ term: { [TRANSACTION_NAME]: transactionName } }]
@@ -53,7 +49,7 @@ export async function getErrorRate({
},
...transactionNamefilter,
...transactionTypefilter,
- ...uiFiltersES,
+ ...esFilter,
];
const params = {
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_sample_for_group.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_sample_for_group.ts
index 6c9b23b3dc0793..7e1aad075fb161 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_sample_for_group.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_sample_for_group.ts
@@ -12,11 +12,7 @@ import {
} from '../../../common/elasticsearch_fieldnames';
import { ProcessorEvent } from '../../../common/processor_event';
import { rangeFilter } from '../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
export async function getTransactionSampleForGroup({
serviceName,
@@ -25,9 +21,9 @@ export async function getTransactionSampleForGroup({
}: {
serviceName: string;
transactionName: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
- const { apmEventClient, start, end, uiFiltersES } = setup;
+ const { apmEventClient, start, end, esFilter } = setup;
const filter = [
{
@@ -43,7 +39,7 @@ export async function getTransactionSampleForGroup({
[TRANSACTION_NAME]: transactionName,
},
},
- ...uiFiltersES,
+ ...esFilter,
];
const getSampledTransaction = async () => {
diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/index.ts b/x-pack/plugins/apm/server/lib/transaction_groups/index.ts
index 6e0d619268d444..3796511029243a 100644
--- a/x-pack/plugins/apm/server/lib/transaction_groups/index.ts
+++ b/x-pack/plugins/apm/server/lib/transaction_groups/index.ts
@@ -4,16 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../helpers/setup_request';
import { transactionGroupsFetcher, Options } from './fetcher';
export async function getTransactionGroupList(
options: Options,
- setup: Setup & SetupTimeRange & SetupUIFilters
+ setup: Setup & SetupTimeRange
) {
const bucketSize = setup.config['xpack.apm.ui.transactionGroupBucketSize'];
return await transactionGroupsFetcher(options, setup, bucketSize);
diff --git a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
index c63dfcc0c0ec70..3e0a7317afd706 100644
--- a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap
@@ -161,7 +161,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -295,7 +295,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -401,7 +401,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
@@ -502,7 +502,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -608,7 +608,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
Object {
@@ -673,7 +673,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts
index 34863c64f98041..8bbcaebe06513d 100644
--- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts
@@ -36,7 +36,8 @@ function getMockSetup(esResponse: any) {
get: () => 'myIndex',
}
) as APMConfig,
- uiFiltersES: [],
+ uiFilters: {},
+ esFilter: [],
indices: mockIndices,
dynamicIndexPattern: null as any,
};
diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
index 9730ddbbf38d7c..8febdc898ab97f 100644
--- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts
@@ -16,11 +16,7 @@ import {
TRANSACTION_NAME,
TRANSACTION_BREAKDOWN_COUNT,
} from '../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { rangeFilter } from '../../../../common/utils/range_filter';
import { getMetricsDateHistogramParams } from '../../helpers/metrics';
import { MAX_KPIS } from './constants';
@@ -32,12 +28,12 @@ export async function getTransactionBreakdown({
transactionName,
transactionType,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
transactionName?: string;
transactionType: string;
}) {
- const { uiFiltersES, apmEventClient, start, end, config } = setup;
+ const { esFilter, apmEventClient, start, end, config } = setup;
const subAggs = {
sum_all_self_times: {
@@ -84,7 +80,7 @@ export async function getTransactionBreakdown({
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
{ range: rangeFilter(start, end) },
- ...uiFiltersES,
+ ...esFilter,
];
if (transactionName) {
diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts
index 3cf9a54e3fe9be..287c7bc2c47f9f 100644
--- a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts
@@ -5,6 +5,7 @@
*/
import { Logger } from 'kibana/server';
+import { ESSearchResponse } from '../../../../../typings/elasticsearch';
import { PromiseReturnType } from '../../../../../../observability/typings/common';
import { Setup, SetupTimeRange } from '../../../helpers/setup_request';
@@ -47,7 +48,7 @@ export async function anomalySeriesFetcher({
filter: [
{ term: { job_id: jobId } },
{ exists: { field: 'bucket_span' } },
- { term: { result_type: 'model_plot' } },
+ { terms: { result_type: ['model_plot', 'record'] } },
{ term: { partition_field_value: serviceName } },
{ term: { by_field_value: transactionType } },
{
@@ -67,7 +68,7 @@ export async function anomalySeriesFetcher({
extended_bounds: { min: newStart, max: end },
},
aggs: {
- anomaly_score: { max: { field: 'anomaly_score' } },
+ anomaly_score: { max: { field: 'record_score' } },
lower: { min: { field: 'model_lower' } },
upper: { max: { field: 'model_upper' } },
},
@@ -77,7 +78,11 @@ export async function anomalySeriesFetcher({
};
try {
- const response = await ml.mlSystem.mlAnomalySearch(params);
+ const response: ESSearchResponse<
+ unknown,
+ typeof params
+ > = (await ml.mlSystem.mlAnomalySearch(params)) as any;
+
return response;
} catch (err) {
const isHttpError = 'statusCode' in err;
diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts
index d8865f0049d357..f11623eaa2daea 100644
--- a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts
@@ -5,17 +5,13 @@
*/
import { Logger } from 'kibana/server';
import { isNumber } from 'lodash';
+import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values';
import { getBucketSize } from '../../../helpers/get_bucket_size';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../helpers/setup_request';
import { anomalySeriesFetcher } from './fetcher';
import { getMlBucketSize } from './get_ml_bucket_size';
import { anomalySeriesTransform } from './transform';
import { getMLJobIds } from '../../../service_map/get_service_anomalies';
-import { UIFilters } from '../../../../../typings/ui_filters';
export async function getAnomalySeries({
serviceName,
@@ -24,15 +20,13 @@ export async function getAnomalySeries({
timeSeriesDates,
setup,
logger,
- uiFilters,
}: {
serviceName: string;
transactionType: string | undefined;
transactionName: string | undefined;
timeSeriesDates: number[];
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
logger: Logger;
- uiFilters: UIFilters;
}) {
// don't fetch anomalies for transaction details page
if (transactionName) {
@@ -44,12 +38,22 @@ export async function getAnomalySeries({
return;
}
+ const { uiFilters, start, end } = setup;
+ const { environment } = uiFilters;
+
+ // don't fetch anomalies when no specific environment is selected
+ if (environment === ENVIRONMENT_ALL.value) {
+ return;
+ }
+
// don't fetch anomalies if unknown uiFilters are applied
const knownFilters = ['environment', 'serviceName'];
- const uiFilterNames = Object.keys(uiFilters);
- if (
- uiFilterNames.some((uiFilterName) => !knownFilters.includes(uiFilterName))
- ) {
+ const hasUnknownFiltersApplied = Object.entries(setup.uiFilters)
+ .filter(([key, value]) => !!value)
+ .map(([key]) => key)
+ .some((uiFilterName) => !knownFilters.includes(uiFilterName));
+
+ if (hasUnknownFiltersApplied) {
return;
}
@@ -64,15 +68,8 @@ export async function getAnomalySeries({
return;
}
- const mlJobIds = await getMLJobIds(
- setup.ml.anomalyDetectors,
- uiFilters.environment
- );
+ const mlJobIds = await getMLJobIds(setup.ml.anomalyDetectors, environment);
- // don't fetch anomalies if there are isn't exaclty 1 ML job match for the given environment
- if (mlJobIds.length !== 1) {
- return;
- }
const jobId = mlJobIds[0];
const mlBucketSize = await getMlBucketSize({ setup, jobId, logger });
@@ -80,7 +77,6 @@ export async function getAnomalySeries({
return;
}
- const { start, end } = setup;
const { intervalString, bucketSize } = getBucketSize(start, end);
const esResponse = await anomalySeriesFetcher({
diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts
index fdbd99bf274d6d..75dfae3e7375f3 100644
--- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts
@@ -29,7 +29,10 @@ describe('timeseriesFetcher', () => {
get: () => 'myIndex',
}
) as APMConfig,
- uiFiltersES: [
+ uiFilters: {
+ environment: 'test',
+ },
+ esFilter: [
{
term: { 'service.environment': 'test' },
},
diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts
index 5a3948f5774306..e2edbbec63d475 100644
--- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts
@@ -14,11 +14,7 @@ import {
import { PromiseReturnType } from '../../../../../../observability/typings/common';
import { getBucketSize } from '../../../helpers/get_bucket_size';
import { rangeFilter } from '../../../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../helpers/setup_request';
import {
getProcessorEventForAggregatedTransactions,
getTransactionDurationFieldForAggregatedTransactions,
@@ -36,10 +32,10 @@ export function timeseriesFetcher({
serviceName: string;
transactionType: string | undefined;
transactionName: string | undefined;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
- const { start, end, uiFiltersES, apmEventClient } = setup;
+ const { start, end, apmEventClient } = setup;
const { intervalString } = getBucketSize(start, end);
const filter: ESFilter[] = [
@@ -48,7 +44,7 @@ export function timeseriesFetcher({
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
- ...uiFiltersES,
+ ...setup.esFilter,
];
if (transactionName) {
diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts
index 81dca447f16cab..c0421005dd06eb 100644
--- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts
@@ -5,11 +5,7 @@
*/
import { getBucketSize } from '../../../helpers/get_bucket_size';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../helpers/setup_request';
import { timeseriesFetcher } from './fetcher';
import { timeseriesTransformer } from './transform';
@@ -17,7 +13,7 @@ export async function getApmTimeseriesData(options: {
serviceName: string;
transactionType: string | undefined;
transactionName: string | undefined;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
const { start, end } = options.setup;
diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/index.ts
index 43abf0b1a1d33d..d8593612c05828 100644
--- a/x-pack/plugins/apm/server/lib/transactions/charts/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/charts/index.ts
@@ -6,15 +6,10 @@
import { Logger } from 'kibana/server';
import { PromiseReturnType } from '../../../../../observability/typings/common';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { getAnomalySeries } from './get_anomaly_data';
import { getApmTimeseriesData } from './get_timeseries_data';
import { ApmTimeSeriesResponse } from './get_timeseries_data/transform';
-import { UIFilters } from '../../../../typings/ui_filters';
function getDates(apmTimeseries: ApmTimeSeriesResponse) {
return apmTimeseries.responseTimes.avg.map((p) => p.x);
@@ -27,10 +22,9 @@ export async function getTransactionCharts(options: {
serviceName: string;
transactionType: string | undefined;
transactionName: string | undefined;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
logger: Logger;
- uiFilters: UIFilters;
}) {
const apmTimeseries = await getApmTimeseriesData(options);
const anomalyTimeseries = await getAnomalySeries({
diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
index 6e2fe34a5f5ef5..34d01627a2869d 100644
--- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts
@@ -17,11 +17,7 @@ import {
TRANSACTION_TYPE,
} from '../../../../../common/elasticsearch_fieldnames';
import { rangeFilter } from '../../../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../../helpers/setup_request';
import {
getDocumentTypeFilterForAggregatedTransactions,
getProcessorEventForAggregatedTransactions,
@@ -66,17 +62,17 @@ export async function getBuckets({
traceId: string;
distributionMax: number;
bucketSize: number;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
- const { start, end, uiFiltersES, apmEventClient } = setup;
+ const { start, end, esFilter, apmEventClient } = setup;
const commonFilters = [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
{ term: { [TRANSACTION_NAME]: transactionName } },
{ range: rangeFilter(start, end) },
- ...uiFiltersES,
+ ...esFilter,
];
async function getSamplesForDistributionBuckets() {
diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts
index 24ca2a4a07b68c..249b1c4fbb20a5 100644
--- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts
@@ -9,11 +9,7 @@ import {
TRANSACTION_NAME,
TRANSACTION_TYPE,
} from '../../../../common/elasticsearch_fieldnames';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import {
getProcessorEventForAggregatedTransactions,
getTransactionDurationFieldForAggregatedTransactions,
@@ -29,10 +25,10 @@ export async function getDistributionMax({
serviceName: string;
transactionName: string;
transactionType: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
- const { start, end, uiFiltersES, apmEventClient } = setup;
+ const { start, end, esFilter, apmEventClient } = setup;
const params = {
apm: {
@@ -59,7 +55,7 @@ export async function getDistributionMax({
},
},
},
- ...uiFiltersES,
+ ...esFilter,
],
},
},
diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts
index b9ab36fb08d422..deafc37ee42e20 100644
--- a/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts
@@ -5,11 +5,7 @@
*/
import { PromiseReturnType } from '../../../../../observability/typings/common';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { getBuckets } from './get_buckets';
import { getDistributionMax } from './get_distribution_max';
import { roundToNearestFiveOrTen } from '../../helpers/round_to_nearest_five_or_ten';
@@ -39,7 +35,7 @@ export async function getTransactionDistribution({
transactionType: string;
transactionId: string;
traceId: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
const distributionMax = await getDistributionMax({
diff --git a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
index 9aa1a8f4de87fc..8958be0819613d 100644
--- a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts
@@ -9,11 +9,7 @@ import {
TRANSACTION_ID,
} from '../../../../common/elasticsearch_fieldnames';
import { rangeFilter } from '../../../../common/utils/range_filter';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../helpers/setup_request';
import { ProcessorEvent } from '../../../../common/processor_event';
export async function getTransaction({
@@ -23,7 +19,7 @@ export async function getTransaction({
}: {
transactionId: string;
traceId: string;
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
const { start, end, apmEventClient } = setup;
diff --git a/x-pack/plugins/apm/server/lib/transactions/queries.test.ts b/x-pack/plugins/apm/server/lib/transactions/queries.test.ts
index 87b8bc7c4ae90f..eff9451c9e1cd4 100644
--- a/x-pack/plugins/apm/server/lib/transactions/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/transactions/queries.test.ts
@@ -56,7 +56,6 @@ describe('transaction queries', () => {
setup,
searchAggregatedTransactions: false,
logger: loggerMock.create(),
- uiFilters: {},
})
);
expect(mock.params).toMatchSnapshot();
@@ -71,7 +70,6 @@ describe('transaction queries', () => {
setup,
searchAggregatedTransactions: false,
logger: loggerMock.create(),
- uiFilters: {},
})
);
expect(mock.params).toMatchSnapshot();
@@ -86,7 +84,6 @@ describe('transaction queries', () => {
setup,
searchAggregatedTransactions: false,
logger: loggerMock.create(),
- uiFilters: {},
})
);
diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap
index 5f384327192800..e7ca65eb740b6a 100644
--- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap
+++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap
@@ -46,7 +46,7 @@ Object {
},
Object {
"term": Object {
- "my.custom.ui.filter": "foo-bar",
+ "service.environment": "test",
},
},
],
diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
index 10f6e93c1cfc13..9fbdba679b6675 100644
--- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
+++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts
@@ -8,7 +8,7 @@ import { omit } from 'lodash';
import { mergeProjection } from '../../../projections/util/merge_projection';
import { Projection } from '../../../projections/typings';
import { UIFilters } from '../../../../typings/ui_filters';
-import { getUiFiltersES } from '../../helpers/convert_ui_filters/get_ui_filters_es';
+import { getEsFilter } from '../../helpers/convert_ui_filters/get_es_filter';
import { localUIFilters } from './config';
import { LocalUIFilterName } from '../../../../common/ui_filter';
@@ -22,7 +22,7 @@ export const getLocalFilterQuery = ({
localUIFilterName: LocalUIFilterName;
}) => {
const field = localUIFilters[localUIFilterName];
- const filter = getUiFiltersES(omit(uiFilters, field.name));
+ const filter = getEsFilter(omit(uiFilters, field.name));
const bucketCountAggregation = projection.body.aggs
? {
diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts
index 22fa20e255f6ed..f4e8aafc1bcf5f 100644
--- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts
+++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts
@@ -15,7 +15,7 @@ describe('local ui filter queries', () => {
let mock: SearchParamsMock;
beforeEach(() => {
- jest.mock('../../helpers/convert_ui_filters/get_ui_filters_es', () => {
+ jest.mock('../../helpers/convert_ui_filters/get_es_filter', () => {
return [];
});
});
diff --git a/x-pack/plugins/apm/server/projections/errors.ts b/x-pack/plugins/apm/server/projections/errors.ts
index 49a0e9f479d263..173dc94a0840cd 100644
--- a/x-pack/plugins/apm/server/projections/errors.ts
+++ b/x-pack/plugins/apm/server/projections/errors.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import {
SERVICE_NAME,
ERROR_GROUP_ID,
@@ -20,10 +16,10 @@ export function getErrorGroupsProjection({
setup,
serviceName,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
}) {
- const { start, end, uiFiltersES } = setup;
+ const { start, end, esFilter } = setup;
return {
apm: {
@@ -35,7 +31,7 @@ export function getErrorGroupsProjection({
filter: [
{ term: { [SERVICE_NAME]: serviceName } },
{ range: rangeFilter(start, end) },
- ...uiFiltersES,
+ ...esFilter,
],
},
},
diff --git a/x-pack/plugins/apm/server/projections/metrics.ts b/x-pack/plugins/apm/server/projections/metrics.ts
index eb80a6bc73248d..c3b5db5be6af88 100644
--- a/x-pack/plugins/apm/server/projections/metrics.ts
+++ b/x-pack/plugins/apm/server/projections/metrics.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import {
SERVICE_NAME,
SERVICE_NODE_NAME,
@@ -34,17 +30,17 @@ export function getMetricsProjection({
serviceName,
serviceNodeName,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
}) {
- const { start, end, uiFiltersES } = setup;
+ const { start, end, esFilter } = setup;
const filter = [
{ term: { [SERVICE_NAME]: serviceName } },
{ range: rangeFilter(start, end) },
...getServiceNodeNameFilters(serviceNodeName),
- ...uiFiltersES,
+ ...esFilter,
];
return {
diff --git a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts
index c27314923f6bd6..96ee26c6e65f58 100644
--- a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts
+++ b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import {
AGENT_NAME,
TRANSACTION_TYPE,
@@ -22,10 +18,10 @@ export function getRumPageLoadTransactionsProjection({
setup,
urlQuery,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
urlQuery?: string;
}) {
- const { start, end, uiFiltersES } = setup;
+ const { start, end, esFilter } = setup;
const bool = {
filter: [
@@ -49,7 +45,7 @@ export function getRumPageLoadTransactionsProjection({
},
]
: []),
- ...uiFiltersES,
+ ...esFilter,
],
};
@@ -68,9 +64,9 @@ export function getRumPageLoadTransactionsProjection({
export function getRumErrorsProjection({
setup,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
}) {
- const { start, end, uiFiltersES } = setup;
+ const { start, end, esFilter: esFilter } = setup;
const bool = {
filter: [
@@ -82,7 +78,7 @@ export function getRumErrorsProjection({
[SERVICE_LANGUAGE_NAME]: 'javascript',
},
},
- ...uiFiltersES,
+ ...esFilter,
],
};
diff --git a/x-pack/plugins/apm/server/projections/service_nodes.ts b/x-pack/plugins/apm/server/projections/service_nodes.ts
index 87fe815a12d0de..ed8d4c7409eda0 100644
--- a/x-pack/plugins/apm/server/projections/service_nodes.ts
+++ b/x-pack/plugins/apm/server/projections/service_nodes.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import { SERVICE_NODE_NAME } from '../../common/elasticsearch_fieldnames';
import { mergeProjection } from './util/merge_projection';
import { getMetricsProjection } from './metrics';
@@ -18,7 +14,7 @@ export function getServiceNodesProjection({
serviceName,
serviceNodeName,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName: string;
serviceNodeName?: string;
}) {
diff --git a/x-pack/plugins/apm/server/projections/services.ts b/x-pack/plugins/apm/server/projections/services.ts
index ba61f72519a237..d912a95546515e 100644
--- a/x-pack/plugins/apm/server/projections/services.ts
+++ b/x-pack/plugins/apm/server/projections/services.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupUIFilters,
- SetupTimeRange,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import { SERVICE_NAME } from '../../common/elasticsearch_fieldnames';
import { rangeFilter } from '../../common/utils/range_filter';
import { ProcessorEvent } from '../../common/processor_event';
@@ -18,10 +14,10 @@ export function getServicesProjection({
setup,
searchAggregatedTransactions,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
searchAggregatedTransactions: boolean;
}) {
- const { start, end, uiFiltersES } = setup;
+ const { start, end, esFilter } = setup;
return {
apm: {
@@ -37,7 +33,7 @@ export function getServicesProjection({
size: 0,
query: {
bool: {
- filter: [{ range: rangeFilter(start, end) }, ...uiFiltersES],
+ filter: [{ range: rangeFilter(start, end) }, ...esFilter],
},
},
aggs: {
diff --git a/x-pack/plugins/apm/server/projections/transaction_groups.ts b/x-pack/plugins/apm/server/projections/transaction_groups.ts
index 0cc3a7a35d2143..2ce720eb121672 100644
--- a/x-pack/plugins/apm/server/projections/transaction_groups.ts
+++ b/x-pack/plugins/apm/server/projections/transaction_groups.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { omit } from 'lodash';
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import {
TRANSACTION_NAME,
PARENT_ID,
@@ -22,7 +18,7 @@ export function getTransactionGroupsProjection({
setup,
options,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
options: Options;
}) {
const transactionsProjection = getTransactionsProjection({
diff --git a/x-pack/plugins/apm/server/projections/transactions.ts b/x-pack/plugins/apm/server/projections/transactions.ts
index 8e9bb3bf321f62..548e77b5d2cd99 100644
--- a/x-pack/plugins/apm/server/projections/transactions.ts
+++ b/x-pack/plugins/apm/server/projections/transactions.ts
@@ -4,11 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import {
- Setup,
- SetupTimeRange,
- SetupUIFilters,
-} from '../../server/lib/helpers/setup_request';
+import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request';
import {
SERVICE_NAME,
TRANSACTION_TYPE,
@@ -27,13 +23,13 @@ export function getTransactionsProjection({
transactionType,
searchAggregatedTransactions,
}: {
- setup: Setup & SetupTimeRange & SetupUIFilters;
+ setup: Setup & SetupTimeRange;
serviceName?: string;
transactionName?: string;
transactionType?: string;
searchAggregatedTransactions: boolean;
}) {
- const { start, end, uiFiltersES } = setup;
+ const { start, end, esFilter } = setup;
const transactionNameFilter = transactionName
? [{ term: { [TRANSACTION_NAME]: transactionName } }]
@@ -51,7 +47,7 @@ export function getTransactionsProjection({
...transactionNameFilter,
...transactionTypeFilter,
...serviceNameFilter,
- ...uiFiltersES,
+ ...esFilter,
...getDocumentTypeFilterForAggregatedTransactions(
searchAggregatedTransactions
),
diff --git a/x-pack/plugins/apm/server/routes/service_map.ts b/x-pack/plugins/apm/server/routes/service_map.ts
index 1996d4d4a262dc..6e86ececd1bfe4 100644
--- a/x-pack/plugins/apm/server/routes/service_map.ts
+++ b/x-pack/plugins/apm/server/routes/service_map.ts
@@ -17,7 +17,6 @@ import { createRoute } from './create_route';
import { rangeRt, uiFiltersRt } from './default_api_types';
import { notifyFeatureUsage } from '../feature';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
-import { getParsedUiFilters } from '../lib/helpers/convert_ui_filters/get_parsed_ui_filters';
export const serviceMapRoute = createRoute(() => ({
path: '/api/apm/service-map',
@@ -77,24 +76,20 @@ export const serviceMapServiceNodeRoute = createRoute(() => ({
if (!isActivePlatinumLicense(context.licensing.license)) {
throw Boom.forbidden(invalidLicenseMessage);
}
- const logger = context.logger;
const setup = await setupRequest(context, request);
const {
- query: { uiFilters: uiFiltersJson },
path: { serviceName },
} = context.params;
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
- const uiFilters = getParsedUiFilters({ uiFilters: uiFiltersJson, logger });
return getServiceMapServiceNodeInfo({
setup,
serviceName,
searchAggregatedTransactions,
- uiFilters,
});
},
}));
diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts
index 4bb10f31ba6a1a..538ba3926c792d 100644
--- a/x-pack/plugins/apm/server/routes/services.ts
+++ b/x-pack/plugins/apm/server/routes/services.ts
@@ -17,7 +17,6 @@ import { uiFiltersRt, rangeRt } from './default_api_types';
import { getServiceAnnotations } from '../lib/services/annotations';
import { dateAsStringRt } from '../../common/runtime_types/date_as_string_rt';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
-import { getParsedUiFilters } from '../lib/helpers/convert_ui_filters/get_parsed_ui_filters';
export const servicesRoute = createRoute(() => ({
path: '/api/apm/services',
@@ -25,22 +24,13 @@ export const servicesRoute = createRoute(() => ({
query: t.intersection([uiFiltersRt, rangeRt]),
},
handler: async ({ context, request }) => {
- const { environment } = getParsedUiFilters({
- uiFilters: context.params.query.uiFilters,
- logger: context.logger,
- });
-
const setup = await setupRequest(context, request);
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
- const services = await getServices({
- setup,
- searchAggregatedTransactions,
- mlAnomaliesEnvironment: environment,
- });
+ const services = await getServices({ setup, searchAggregatedTransactions });
return services;
},
diff --git a/x-pack/plugins/apm/server/routes/transaction_groups.ts b/x-pack/plugins/apm/server/routes/transaction_groups.ts
index dd1335fb2c2a19..18fc73b468cd4d 100644
--- a/x-pack/plugins/apm/server/routes/transaction_groups.ts
+++ b/x-pack/plugins/apm/server/routes/transaction_groups.ts
@@ -5,6 +5,7 @@
*/
import * as t from 'io-ts';
+import Boom from 'boom';
import { setupRequest } from '../lib/helpers/setup_request';
import { getTransactionCharts } from '../lib/transactions/charts';
import { getTransactionDistribution } from '../lib/transactions/distribution';
@@ -15,7 +16,6 @@ import { uiFiltersRt, rangeRt } from './default_api_types';
import { getTransactionSampleForGroup } from '../lib/transaction_groups/get_transaction_sample_for_group';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
import { getErrorRate } from '../lib/transaction_groups/get_error_rate';
-import { getParsedUiFilters } from '../lib/helpers/convert_ui_filters/get_parsed_ui_filters';
export const transactionGroupsRoute = createRoute(() => ({
path: '/api/apm/services/{serviceName}/transaction_groups',
@@ -71,27 +71,28 @@ export const transactionGroupsChartsRoute = createRoute(() => ({
const setup = await setupRequest(context, request);
const logger = context.logger;
const { serviceName } = context.params.path;
- const {
- transactionType,
- transactionName,
- uiFilters: uiFiltersJson,
- } = context.params.query;
+ const { transactionType, transactionName } = context.params.query;
- const uiFilters = getParsedUiFilters({ uiFilters: uiFiltersJson, logger });
+ if (!setup.uiFilters.environment) {
+ throw Boom.badRequest(
+ `environment is a required property of the ?uiFilters JSON for transaction_groups/charts.`
+ );
+ }
const searchAggregatedTransactions = await getSearchAggregatedTransactions(
setup
);
- return getTransactionCharts({
+ const options = {
serviceName,
transactionType,
transactionName,
setup,
searchAggregatedTransactions,
logger,
- uiFilters,
- });
+ };
+
+ return getTransactionCharts(options);
},
}));
diff --git a/x-pack/plugins/apm/server/routes/ui_filters.ts b/x-pack/plugins/apm/server/routes/ui_filters.ts
index 936d460102dcec..26fe0118c02ed9 100644
--- a/x-pack/plugins/apm/server/routes/ui_filters.ts
+++ b/x-pack/plugins/apm/server/routes/ui_filters.ts
@@ -9,13 +9,12 @@ import { omit } from 'lodash';
import {
setupRequest,
Setup,
- SetupUIFilters,
SetupTimeRange,
} from '../lib/helpers/setup_request';
import { getEnvironments } from '../lib/ui_filters/get_environments';
import { Projection } from '../projections/typings';
import { localUIFilterNames } from '../lib/ui_filters/local_ui_filters/config';
-import { getUiFiltersES } from '../lib/helpers/convert_ui_filters/get_ui_filters_es';
+import { getEsFilter } from '../lib/helpers/convert_ui_filters/get_es_filter';
import { getLocalUIFilters } from '../lib/ui_filters/local_ui_filters';
import { getServicesProjection } from '../projections/services';
import { getTransactionGroupsProjection } from '../projections/transaction_groups';
@@ -97,23 +96,23 @@ function createLocalFiltersRoute<
},
handler: async ({ context, request }) => {
const setup = await setupRequest(context, request);
+ const { uiFilters } = setup;
const { query } = context.params;
- const { uiFilters, filterNames } = query;
- const parsedUiFilters = JSON.parse(uiFilters);
+ const { filterNames } = query;
const projection = await getProjection({
query,
context,
setup: {
...setup,
- uiFiltersES: getUiFiltersES(omit(parsedUiFilters, filterNames)),
+ esFilter: getEsFilter(omit(uiFilters, filterNames)),
},
});
return getLocalUIFilters({
projection,
setup,
- uiFilters: parsedUiFilters,
+ uiFilters,
localFilterNames: filterNames,
});
},
@@ -271,6 +270,6 @@ type GetProjection<
context,
}: {
query: t.TypeOf;
- setup: Setup & SetupUIFilters & SetupTimeRange;
+ setup: Setup & SetupTimeRange;
context: APMRequestHandlerContext;
}) => Promise | TProjection;
diff --git a/x-pack/plugins/apm/server/utils/test_helpers.tsx b/x-pack/plugins/apm/server/utils/test_helpers.tsx
index 98c1436b2b9b8f..18b990b35b5a58 100644
--- a/x-pack/plugins/apm/server/utils/test_helpers.tsx
+++ b/x-pack/plugins/apm/server/utils/test_helpers.tsx
@@ -9,6 +9,7 @@ import {
ESSearchRequest,
} from '../../typings/elasticsearch';
import { PromiseReturnType } from '../../typings/common';
+import { UIFilters } from '../../typings/ui_filters';
import { APMConfig } from '..';
interface Options {
@@ -23,7 +24,8 @@ interface MockSetup {
apmEventClient: any;
internalClient: any;
config: APMConfig;
- uiFiltersES: ESFilter[];
+ uiFilters: UIFilters;
+ esFilter: ESFilter[];
indices: {
/* eslint-disable @typescript-eslint/naming-convention */
'apm_oss.sourcemapIndices': string;
@@ -78,7 +80,8 @@ export async function inspectSearchParams(
},
}
) as APMConfig,
- uiFiltersES: [{ term: { 'my.custom.ui.filter': 'foo-bar' } }],
+ uiFilters: { environment: 'test' },
+ esFilter: [{ term: { 'service.environment': 'test' } }],
indices: {
/* eslint-disable @typescript-eslint/naming-convention */
'apm_oss.sourcemapIndices': 'myIndex',
diff --git a/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts b/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts
index e0e13b7b7fb98d..a2223e55602888 100644
--- a/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts
+++ b/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts
@@ -107,21 +107,21 @@ export default function featureControlsTests({ getService }: FtrProviderContext)
},
{
req: {
- url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%7D`,
+ url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%22environment%22%3A%22testing%22%7D`,
},
expectForbidden: expect404,
expectResponse: expect200,
},
{
req: {
- url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&uiFilters=%7B%7D`,
+ url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&uiFilters=%7B%22environment%22%3A%22testing%22%7D`,
},
expectForbidden: expect404,
expectResponse: expect200,
},
{
req: {
- url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%7D`,
+ url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%22environment%22%3A%22testing%22%7D`,
},
expectForbidden: expect404,
expectResponse: expect200,
diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts
index c9581079b99520..d7d6d613281ef7 100644
--- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts
+++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts
@@ -19,7 +19,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
// url parameters
const start = encodeURIComponent(metadata.start);
const end = encodeURIComponent(metadata.end);
- const uiFilters = encodeURIComponent(JSON.stringify({}));
+ const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'testing' }));
describe('Transaction charts', () => {
describe('when data is not loaded ', () => {
diff --git a/x-pack/test/apm_api_integration/trial/tests/index.ts b/x-pack/test/apm_api_integration/trial/tests/index.ts
index a6a031def34ea4..e609279366390f 100644
--- a/x-pack/test/apm_api_integration/trial/tests/index.ts
+++ b/x-pack/test/apm_api_integration/trial/tests/index.ts
@@ -16,6 +16,7 @@ export default function observabilityApiIntegrationTests({ loadTestFile }: FtrPr
describe('Services', function () {
loadTestFile(require.resolve('./services/annotations'));
loadTestFile(require.resolve('./services/top_services.ts'));
+ loadTestFile(require.resolve('./services/transaction_groups_charts'));
});
describe('Settings', function () {
diff --git a/x-pack/test/apm_api_integration/trial/tests/services/__snapshots__/transaction_groups_charts.snap b/x-pack/test/apm_api_integration/trial/tests/services/__snapshots__/transaction_groups_charts.snap
new file mode 100644
index 00000000000000..8169e73202fbc7
--- /dev/null
+++ b/x-pack/test/apm_api_integration/trial/tests/services/__snapshots__/transaction_groups_charts.snap
@@ -0,0 +1,43 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`APM Transaction Overview when data is loaded and fetching transaction groups charts with uiFilters when not defined environments selected should return the correct anomaly boundaries 1`] = `Array []`;
+
+exports[`APM Transaction Overview when data is loaded and fetching transaction groups charts with uiFilters with environment selected and empty kuery filter should return a non-empty anomaly series 1`] = `
+Array [
+ Object {
+ "x": 1601389800000,
+ "y": 1206111.33487531,
+ "y0": 10555.1290143587,
+ },
+ Object {
+ "x": 1601390700000,
+ "y": 1223987.49321778,
+ "y0": 10177.4677901726,
+ },
+ Object {
+ "x": 1601391600000,
+ "y": 1223987.49321778,
+ "y0": 10177.4677901726,
+ },
+]
+`;
+
+exports[`APM Transaction Overview when data is loaded and fetching transaction groups charts with uiFilters with environment selected in uiFilters should return a non-empty anomaly series 1`] = `
+Array [
+ Object {
+ "x": 1601389800000,
+ "y": 1206111.33487531,
+ "y0": 10555.1290143587,
+ },
+ Object {
+ "x": 1601390700000,
+ "y": 1223987.49321778,
+ "y0": 10177.4677901726,
+ },
+ Object {
+ "x": 1601391600000,
+ "y": 1223987.49321778,
+ "y0": 10177.4677901726,
+ },
+]
+`;
diff --git a/x-pack/test/apm_api_integration/trial/tests/services/transaction_groups_charts.ts b/x-pack/test/apm_api_integration/trial/tests/services/transaction_groups_charts.ts
new file mode 100644
index 00000000000000..c35dfcc3817a42
--- /dev/null
+++ b/x-pack/test/apm_api_integration/trial/tests/services/transaction_groups_charts.ts
@@ -0,0 +1,161 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import expect from '@kbn/expect';
+import { expectSnapshot } from '../../../common/match_snapshot';
+import { PromiseReturnType } from '../../../../../plugins/apm/typings/common';
+import { FtrProviderContext } from '../../../common/ftr_provider_context';
+import archives_metadata from '../../../common/archives_metadata';
+
+export default function ApiTest({ getService }: FtrProviderContext) {
+ const supertest = getService('supertest');
+ const esArchiver = getService('esArchiver');
+
+ const archiveName = 'apm_8.0.0';
+
+ const range = archives_metadata[archiveName];
+
+ // url parameters
+ const start = encodeURIComponent(range.start);
+ const end = encodeURIComponent(range.end);
+ const transactionType = 'request';
+
+ describe('APM Transaction Overview', () => {
+ describe('when data is loaded', () => {
+ before(() => esArchiver.load(archiveName));
+ after(() => esArchiver.unload(archiveName));
+
+ describe('and fetching transaction groups charts with uiFilters', () => {
+ const serviceName = 'opbeans-java';
+ let response: PromiseReturnType;
+
+ describe('without environment', () => {
+ const uiFilters = encodeURIComponent(JSON.stringify({}));
+ before(async () => {
+ response = await supertest.get(
+ `/api/apm/services/${serviceName}/transaction_groups/charts?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}`
+ );
+ });
+ it('should return an error response', () => {
+ expect(response.status).to.eql(400);
+ });
+ });
+
+ describe('without uiFilters', () => {
+ before(async () => {
+ response = await supertest.get(
+ `/api/apm/services/${serviceName}/transaction_groups/charts?start=${start}&end=${end}&transactionType=${transactionType}`
+ );
+ });
+ it('should return an error response', () => {
+ expect(response.status).to.eql(400);
+ });
+ });
+
+ describe('with environment selected in uiFilters', () => {
+ const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'production' }));
+ before(async () => {
+ response = await supertest.get(
+ `/api/apm/services/${serviceName}/transaction_groups/charts?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}`
+ );
+ });
+
+ it('should have a successful response', () => {
+ expect(response.status).to.eql(200);
+ });
+
+ it('should return the ML job id for anomalies of the selected environment', () => {
+ expect(response.body).to.have.property('anomalyTimeseries');
+ expect(response.body.anomalyTimeseries).to.have.property('jobId');
+ expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline(
+ `"apm-production-229a-high_mean_transaction_duration"`
+ );
+ });
+
+ it('should return a non-empty anomaly series', () => {
+ expect(response.body).to.have.property('anomalyTimeseries');
+ expect(response.body.anomalyTimeseries.anomalyBoundaries?.length).to.be.greaterThan(0);
+ expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch();
+ });
+ });
+
+ describe('when not defined environments selected', () => {
+ const uiFilters = encodeURIComponent(
+ JSON.stringify({ environment: 'ENVIRONMENT_NOT_DEFINED' })
+ );
+ before(async () => {
+ response = await supertest.get(
+ `/api/apm/services/${serviceName}/transaction_groups/charts?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}`
+ );
+ });
+
+ it('should have a successful response', () => {
+ expect(response.status).to.eql(200);
+ });
+
+ it('should return the ML job id for anomalies with no defined environment', () => {
+ expect(response.body).to.have.property('anomalyTimeseries');
+ expect(response.body.anomalyTimeseries).to.have.property('jobId');
+ expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline(
+ `"apm-environment_not_defined-7ed6-high_mean_transaction_duration"`
+ );
+ });
+
+ it('should return the correct anomaly boundaries', () => {
+ expect(response.body).to.have.property('anomalyTimeseries');
+ expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch();
+ });
+ });
+
+ describe('with all environments selected', () => {
+ const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'ENVIRONMENT_ALL' }));
+ before(async () => {
+ response = await supertest.get(
+ `/api/apm/services/${serviceName}/transaction_groups/charts?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}`
+ );
+ });
+
+ it('should have a successful response', () => {
+ expect(response.status).to.eql(200);
+ });
+
+ it('should not return anomaly timeseries data', () => {
+ expect(response.body).to.not.have.property('anomalyTimeseries');
+ });
+ });
+
+ describe('with environment selected and empty kuery filter', () => {
+ const uiFilters = encodeURIComponent(
+ JSON.stringify({ kuery: '', environment: 'production' })
+ );
+ before(async () => {
+ response = await supertest.get(
+ `/api/apm/services/${serviceName}/transaction_groups/charts?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}`
+ );
+ });
+
+ it('should have a successful response', () => {
+ expect(response.status).to.eql(200);
+ });
+
+ it('should return the ML job id for anomalies of the selected environment', () => {
+ expect(response.body).to.have.property('anomalyTimeseries');
+ expect(response.body.anomalyTimeseries).to.have.property('jobId');
+ expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline(
+ `"apm-production-229a-high_mean_transaction_duration"`
+ );
+ });
+
+ it('should return a non-empty anomaly series', () => {
+ expect(response.body).to.have.property('anomalyTimeseries');
+ expect(response.body.anomalyTimeseries.anomalyBoundaries?.length).to.be.greaterThan(0);
+ expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch();
+ });
+ });
+ });
+ });
+ });
+}
From bd9a9a7a2bf65c465dd805815a2b1a6eea03bc9d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?=
Date: Fri, 2 Oct 2020 10:45:53 +0200
Subject: [PATCH 06/21] [Security Solution] Refactor react-beautiful-dnd render
props (#78128)
---
.../timeline/events/all/index.ts | 7 +-
.../common/search_strategy/timeline/index.ts | 18 +-
.../add_filter_to_global_search_bar/index.tsx | 56 +-
.../drag_drop_context_wrapper.tsx | 13 +-
.../drag_and_drop/draggable_wrapper.tsx | 131 +-
.../drag_and_drop/droppable_wrapper.tsx | 33 +-
.../common/components/draggables/index.tsx | 2 +-
.../events_viewer/events_viewer.tsx | 28 +-
.../common/components/events_viewer/mock.ts | 2 +-
.../common/components/ml/entity_draggable.tsx | 67 +-
.../components/ml/score/draggable_score.tsx | 76 +-
.../recent_timelines/recent_timelines.tsx | 160 +-
.../components/flyout/button/index.tsx | 34 +-
.../components/formatted_ip/index.tsx | 157 +-
.../__snapshots__/timeline.test.tsx.snap | 34 +-
.../timeline/auto_save_warning/index.tsx | 121 +-
.../body/column_headers/actions/index.tsx | 29 +-
.../body/column_headers/column_header.tsx | 43 +-
.../timeline/body/column_headers/index.tsx | 29 +-
.../body/events/event_column_view.tsx | 18 +-
.../timeline/body/events/stateful_event.tsx | 228 +-
.../renderers/suricata/suricata_signature.tsx | 82 +-
.../body/renderers/zeek/zeek_signature.tsx | 76 +-
.../data_providers.test.tsx.snap | 5 -
.../__snapshots__/providers.test.tsx.snap | 2243 +++++++++++++----
.../data_providers/data_providers.test.tsx | 27 +-
.../timeline/data_providers/index.tsx | 80 +-
.../data_providers/provider_badge.tsx | 4 +-
.../data_providers/provider_item_badge.tsx | 47 +-
.../data_providers/providers.test.tsx | 264 +-
.../timeline/data_providers/providers.tsx | 460 ++--
.../timelines/components/timeline/events.ts | 32 +-
.../footer/__snapshots__/index.test.tsx.snap | 2 +-
.../components/timeline/footer/index.test.tsx | 39 +-
.../components/timeline/footer/index.tsx | 90 +-
.../header/__snapshots__/index.test.tsx.snap | 5 -
.../components/timeline/header/index.tsx | 27 -
.../timelines/components/timeline/index.tsx | 78 +-
.../timeline/properties/helpers.tsx | 42 +-
.../components/timeline/timeline.test.tsx | 210 --
.../components/timeline/timeline.tsx | 26 +-
.../public/timelines/containers/index.tsx | 20 +-
.../timeline/epic_local_storage.test.tsx | 5 -
.../timelines/store/timeline/helpers.ts | 15 +-
.../timeline/factory/events/all/index.ts | 9 +-
45 files changed, 3039 insertions(+), 2135 deletions(-)
diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts
index 0503a9c327467e..f673fca290a292 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts
+++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts
@@ -6,7 +6,7 @@
import { IEsSearchResponse } from '../../../../../../../../src/plugins/data/common';
import { Ecs } from '../../../../ecs';
-import { CursorType, Inspect, Maybe } from '../../../common';
+import { CursorType, Inspect, Maybe, PageInfoPaginated } from '../../../common';
import { TimelineRequestOptionsPaginated } from '../..';
export interface TimelineEdges {
@@ -29,10 +29,7 @@ export interface TimelineNonEcsData {
export interface TimelineEventsAllStrategyResponse extends IEsSearchResponse {
edges: TimelineEdges[];
totalCount: number;
- pageInfo: {
- activePage: number;
- totalPages: number;
- };
+ pageInfo: PageInfoPaginated;
inspect?: Maybe;
}
diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts
index 773ee608558866..6b96783adc25a3 100644
--- a/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts
+++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts
@@ -14,7 +14,13 @@ import {
TimelineEventsLastEventTimeRequestOptions,
TimelineEventsLastEventTimeStrategyResponse,
} from './events';
-import { DocValueFields, TimerangeInput, SortField } from '../common';
+import {
+ DocValueFields,
+ PaginationInput,
+ PaginationInputPaginated,
+ TimerangeInput,
+ SortField,
+} from '../common';
export * from './events';
@@ -29,19 +35,13 @@ export interface TimelineRequestBasicOptions extends IEsSearchRequest {
}
export interface TimelineRequestOptions extends TimelineRequestBasicOptions {
- pagination: {
- activePage: number;
- querySize: number;
- };
+ pagination: PaginationInput;
sort: SortField;
}
export interface TimelineRequestOptionsPaginated
extends TimelineRequestBasicOptions {
- pagination: {
- activePage: number;
- querySize: number;
- };
+ pagination: PaginationInputPaginated;
sort: SortField;
}
diff --git a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.tsx
index 8a294ec1b71fdd..9f273b4f293bac 100644
--- a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.tsx
@@ -5,7 +5,7 @@
*/
import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
-import React, { useCallback } from 'react';
+import React, { useCallback, useMemo } from 'react';
import { Filter } from '../../../../../../../src/plugins/data/public';
import { WithHoverActions } from '../with_hover_actions';
@@ -47,34 +47,36 @@ export const AddFilterToGlobalSearchBar = React.memo(
}
}, [filterManager, filter, onFilterAdded]);
- return (
-
-
-
-
+ const HoverContent = useMemo(
+ () => (
+
+
+
+
-
-
-
-
- }
- render={() => children}
- />
+
+
+
+
+ ),
+ [filterForValue, filterOutValue]
);
+
+ const render = useCallback(() => children, [children]);
+
+ return ;
}
);
diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx
index 74efe2d34fcca7..4efb662a4aab60 100644
--- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx
@@ -9,6 +9,7 @@ import React, { useCallback } from 'react';
import { DropResult, DragDropContext } from 'react-beautiful-dnd';
import { connect, ConnectedProps } from 'react-redux';
import { Dispatch } from 'redux';
+import deepEqual from 'fast-deep-equal';
import { BeforeCapture } from './drag_drop_context';
import { BrowserFields } from '../../containers/source';
@@ -134,13 +135,11 @@ export const DragDropContextWrapperComponent = React.memo
);
},
- (prevProps, nextProps) => {
- return (
- prevProps.children === nextProps.children &&
- prevProps.dataProviders === nextProps.dataProviders &&
- prevProps.activeTimelineDataProviders === nextProps.activeTimelineDataProviders
- ); // prevent re-renders when data providers are added or removed, but all other props are the same
- }
+ // prevent re-renders when data providers are added or removed, but all other props are the same
+ (prevProps, nextProps) =>
+ prevProps.children === nextProps.children &&
+ deepEqual(prevProps.dataProviders, nextProps.dataProviders) &&
+ prevProps.activeTimelineDataProviders === nextProps.activeTimelineDataProviders
);
DragDropContextWrapperComponent.displayName = 'DragDropContextWrapperComponent';
diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx
index 64f6699d21dacc..bd22811612a672 100644
--- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx
@@ -196,76 +196,93 @@ const DraggableWrapperComponent: React.FC = ({
]
);
- const renderContent = useCallback(
+ const RenderClone = useCallback(
+ (provided, snapshot) => (
+
+
+
+ {render(dataProvider, provided, snapshot)}
+
+
+
+ ),
+ [dataProvider, registerProvider, render]
+ );
+
+ const DraggableContent = useCallback(
+ (provided, snapshot) => (
+ {
+ provided.innerRef(e);
+ draggableRef.current = e;
+ }}
+ data-test-subj="providerContainer"
+ isDragging={snapshot.isDragging}
+ registerProvider={registerProvider}
+ >
+ {truncate && !snapshot.isDragging ? (
+
+ {render(dataProvider, provided, snapshot)}
+
+ ) : (
+
+ {render(dataProvider, provided, snapshot)}
+
+ )}
+
+ ),
+ [dataProvider, registerProvider, render, truncate]
+ );
+
+ const DroppableContent = useCallback(
+ (droppableProvided) => (
+
+
+ {DraggableContent}
+
+ {droppableProvided.placeholder}
+
+ ),
+ [DraggableContent, dataProvider.id, isDisabled]
+ );
+
+ const content = useMemo(
() => (
(
-
-
-
- {render(dataProvider, provided, snapshot)}
-
-
-
- )}
+ renderClone={RenderClone}
>
- {(droppableProvided) => (
-
-
- {(provided, snapshot) => (
- {
- provided.innerRef(e);
- draggableRef.current = e;
- }}
- data-test-subj="providerContainer"
- isDragging={snapshot.isDragging}
- registerProvider={registerProvider}
- >
- {truncate && !snapshot.isDragging ? (
-
- {render(dataProvider, provided, snapshot)}
-
- ) : (
-
- {render(dataProvider, provided, snapshot)}
-
- )}
-
- )}
-
- {droppableProvided.placeholder}
-
- )}
+ {DroppableContent}
),
- [dataProvider, registerProvider, render, isDisabled, truncate]
+ [DroppableContent, RenderClone, dataProvider.id, isDisabled]
);
- if (isDisabled) return <>{renderContent()}>;
+ const renderContent = useCallback(() => content, [content]);
+
+ if (isDisabled) return <>{content}>;
return (
(
type,
render = null,
renderClone,
- }) => (
-
- {(provided, snapshot) => (
+ }) => {
+ const DroppableContent = useCallback(
+ (provided, snapshot) => (
(
{render == null ? children : render({ isDraggingOver: snapshot.isDraggingOver })}
{provided.placeholder}
- )}
-
- )
+ ),
+ [children, height, render]
+ );
+
+ return (
+
+ {DroppableContent}
+
+ );
+ }
);
DroppableWrapper.displayName = 'DroppableWrapper';
diff --git a/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx b/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx
index 4dc3c6fcbe440c..d37de2cd3ec3da 100644
--- a/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx
@@ -135,7 +135,7 @@ DefaultDraggable.displayName = 'DefaultDraggable';
export const Badge = styled(EuiBadge)`
vertical-align: top;
-` as any; // eslint-disable-line @typescript-eslint/no-explicit-any
+`;
Badge.displayName = 'Badge';
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
index 2c8c8136a47332..7859f5584b0e54 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx
@@ -5,7 +5,7 @@
*/
import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui';
-import { getOr, isEmpty, union } from 'lodash/fp';
+import { isEmpty, union } from 'lodash/fp';
import React, { useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import deepEqual from 'fast-deep-equal';
@@ -239,6 +239,19 @@ const EventsViewerComponent: React.FC = ({
events,
]);
+ const HeaderSectionContent = useMemo(
+ () =>
+ headerFilterGroup && (
+
+ {headerFilterGroup}
+
+ ),
+ [graphEventId, headerFilterGroup]
+ );
+
useEffect(() => {
setIsQueryLoading(loading);
}, [loading]);
@@ -257,14 +270,7 @@ const EventsViewerComponent: React.FC = ({
subtitle={utilityBar ? undefined : subtitle}
title={inspect ? justTitle : titleWithExitFullScreen}
>
- {headerFilterGroup && (
-
- {headerFilterGroup}
-
- )}
+ {HeaderSectionContent}
{utilityBar && !resolverIsShowing(graphEventId) && (
{utilityBar?.(refetch, totalCountMinusDeleted)}
@@ -293,7 +299,7 @@ const EventsViewerComponent: React.FC = ({
/** Hide the footer if Resolver is showing. */
!graphEventId && (
= ({
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadPage}
serverSideEventCount={totalCountMinusDeleted}
- totalPages={getOr(0, 'totalPages', pageInfo)}
+ totalCount={pageInfo.fakeTotalCount}
/>
)
}
diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts b/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts
index f6fb01be4371f1..d2bd940dcc266b 100644
--- a/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts
+++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts
@@ -8,7 +8,7 @@ export const mockEventViewerResponse = {
totalCount: 12,
pageInfo: {
activePage: 0,
- totalPages: 10,
+ fakeTotalCount: 100,
},
events: [],
};
diff --git a/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.tsx b/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.tsx
index 9024aec17400c8..1c5b13acb0c227 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.tsx
@@ -4,9 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import { DraggableWrapper, DragEffects } from '../drag_and_drop/draggable_wrapper';
-import { IS_OPERATOR } from '../../../timelines/components/timeline/data_providers/data_provider';
+import {
+ IS_OPERATOR,
+ QueryOperator,
+} from '../../../timelines/components/timeline/data_providers/data_provider';
import { Provider } from '../../../timelines/components/timeline/data_providers/provider';
import { escapeDataProviderId } from '../drag_and_drop/helpers';
@@ -16,39 +19,43 @@ interface Props {
entityValue: string;
}
-export const EntityDraggableComponent = ({
+export const EntityDraggableComponent: React.FC = ({
idPrefix,
entityName,
entityValue,
-}: Props): JSX.Element => {
+}) => {
const id = escapeDataProviderId(`entity-draggable-${idPrefix}-${entityName}-${entityValue}`);
- return (
-
- snapshot.isDragging ? (
-
-
-
- ) : (
- <>{`${entityName}: "${entityValue}"`}>
- )
- }
- />
+
+ const dataProviderProp = useMemo(
+ () => ({
+ and: [],
+ enabled: true,
+ id,
+ name: entityValue,
+ excluded: false,
+ kqlQuery: '',
+ queryMatch: {
+ field: entityName,
+ value: entityValue,
+ operator: IS_OPERATOR as QueryOperator,
+ },
+ }),
+ [entityName, entityValue, id]
+ );
+
+ const render = useCallback(
+ (dataProvider, _, snapshot) =>
+ snapshot.isDragging ? (
+
+
+
+ ) : (
+ <>{`${entityName}: "${entityValue}"`}>
+ ),
+ [entityName, entityValue]
);
+
+ return ;
};
EntityDraggableComponent.displayName = 'EntityDraggableComponent';
diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/draggable_score.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/draggable_score.tsx
index c849476f0c3db7..668a374e57f0d6 100644
--- a/x-pack/plugins/security_solution/public/common/components/ml/score/draggable_score.tsx
+++ b/x-pack/plugins/security_solution/public/common/components/ml/score/draggable_score.tsx
@@ -4,10 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import { DraggableWrapper, DragEffects } from '../../drag_and_drop/draggable_wrapper';
import { Anomaly } from '../types';
-import { IS_OPERATOR } from '../../../../timelines/components/timeline/data_providers/data_provider';
+import {
+ IS_OPERATOR,
+ QueryOperator,
+} from '../../../../timelines/components/timeline/data_providers/data_provider';
import { Provider } from '../../../../timelines/components/timeline/data_providers/provider';
import { Spacer } from '../../page';
import { getScoreString } from './score_health';
@@ -23,39 +26,48 @@ export const DraggableScoreComponent = ({
}): JSX.Element => {
const scoreString = getScoreString(score.severity);
+ const dataProviderProp = useMemo(
+ () => ({
+ and: [],
+ enabled: true,
+ id,
+ name: score.entityName,
+ excluded: false,
+ kqlQuery: '',
+ queryMatch: {
+ field: score.entityName,
+ value: score.entityValue,
+ operator: IS_OPERATOR as QueryOperator,
+ },
+ }),
+ [id, score.entityName, score.entityValue]
+ );
+
+ const render = useCallback(
+ (dataProvider, _, snapshot) =>
+ snapshot.isDragging ? (
+
+
+
+ ) : (
+ <>
+ {index !== 0 && (
+ <>
+ {','}
+
+ >
+ )}
+ {scoreString}
+ >
+ ),
+ [index, scoreString]
+ );
+
return (
- snapshot.isDragging ? (
-
-
-
- ) : (
- <>
- {index !== 0 && (
- <>
- {','}
-
- >
- )}
- {scoreString}
- >
- )
- }
+ dataProvider={dataProviderProp}
+ render={render}
/>
);
};
diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/recent_timelines.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/recent_timelines.tsx
index ddad72081645bf..598849c917d335 100644
--- a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/recent_timelines.tsx
+++ b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/recent_timelines.tsx
@@ -12,7 +12,7 @@ import {
EuiToolTip,
EuiButtonIcon,
} from '@elastic/eui';
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import { RecentTimelineHeader } from './header';
import {
@@ -25,76 +25,110 @@ import { TimelineType } from '../../../../common/types/timeline';
import { RecentTimelineCounts } from './counts';
import * as i18n from './translations';
-export const RecentTimelines = React.memo<{
- noTimelinesMessage: string;
+interface RecentTimelinesItemProps {
+ timeline: OpenTimelineResult;
onOpenTimeline: OnOpenTimeline;
- timelines: OpenTimelineResult[];
-}>(({ noTimelinesMessage, onOpenTimeline, timelines }) => {
- if (timelines.length === 0) {
+ isLastItem: boolean;
+}
+
+const RecentTimelinesItem = React.memo(
+ ({ timeline, onOpenTimeline, isLastItem }) => {
+ const handleClick = useCallback(
+ () =>
+ onOpenTimeline({
+ duplicate: true,
+ timelineId: `${timeline.savedObjectId}`,
+ }),
+ [onOpenTimeline, timeline.savedObjectId]
+ );
+
+ const render = useCallback(
+ (showHoverContent) => (
+
+
+
+
+ {timeline.description && timeline.description.length && (
+
+ {timeline.description}
+
+ )}
+
+
+ {showHoverContent && (
+
+
+
+
+
+ )}
+
+ ),
+ [handleClick, onOpenTimeline, timeline]
+ );
+
return (
<>
-
- {noTimelinesMessage}
-
+
+ <>{!isLastItem && }>
>
);
}
+);
- return (
- <>
- {timelines.map((t, i) => (
-
- (
-
-
-
-
- {t.description && t.description.length && (
-
- {t.description}
-
- )}
-
+RecentTimelinesItem.displayName = 'RecentTimelinesItem';
- {showHoverContent && (
-
-
-
- onOpenTimeline({
- duplicate: true,
- timelineId: `${t.savedObjectId}`,
- })
- }
- size="s"
- />
-
-
- )}
-
- )}
+interface RecentTimelinesProps {
+ noTimelinesMessage: string;
+ onOpenTimeline: OnOpenTimeline;
+ timelines: OpenTimelineResult[];
+}
+
+export const RecentTimelines = React.memo(
+ ({ noTimelinesMessage, onOpenTimeline, timelines }) => {
+ const content = useMemo(
+ () =>
+ timelines.map((timeline, index) => (
+
- <>{i !== timelines.length - 1 && }>
-
- ))}
- >
- );
-});
+ )),
+ [onOpenTimeline, timelines]
+ );
+
+ if (timelines.length === 0) {
+ return (
+ <>
+
+ {noTimelinesMessage}
+
+ >
+ );
+ }
+
+ return <>{content}>;
+ }
+);
RecentTimelines.displayName = 'RecentTimelines';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx
index 954ae0b6a0d40e..72fa20c9f152d9 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx
@@ -4,11 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { noop } from 'lodash/fp';
import { EuiButton, EuiNotificationBadge, EuiPanel } from '@elastic/eui';
import { rgba } from 'polished';
import React, { useMemo } from 'react';
import styled from 'styled-components';
+import deepEqual from 'fast-deep-equal';
import { IS_DRAGGING_CLASS_NAME } from '../../../../common/components/drag_and_drop/helpers';
import { DataProvider } from '../../timeline/data_providers/data_provider';
@@ -88,6 +88,18 @@ export const FlyoutButton = React.memo(
const badgeCount = useMemo(() => getBadgeCount(dataProviders), [dataProviders]);
const { browserFields } = useSourcererScope(SourcererScopeName.timeline);
+ const badgeStyles: React.CSSProperties = useMemo(
+ () => ({
+ left: '-9px',
+ position: 'relative',
+ top: '-6px',
+ transform: 'rotate(90deg)',
+ visibility: dataProviders.length !== 0 ? 'inherit' : 'hidden',
+ zIndex: 10,
+ }),
+ [dataProviders.length]
+ );
+
if (!show) {
return null;
}
@@ -108,18 +120,7 @@ export const FlyoutButton = React.memo(
>
{i18n.FLYOUT_BUTTON}
-
+
{badgeCount}
@@ -128,11 +129,6 @@ export const FlyoutButton = React.memo(
browserFields={browserFields}
timelineId={timelineId}
dataProviders={dataProviders}
- onDataProviderEdited={noop}
- onDataProviderRemoved={noop}
- onToggleDataProviderEnabled={noop}
- onToggleDataProviderExcluded={noop}
- onToggleDataProviderType={noop}
/>
@@ -140,7 +136,7 @@ export const FlyoutButton = React.memo(
},
(prevProps, nextProps) =>
prevProps.show === nextProps.show &&
- prevProps.dataProviders === nextProps.dataProviders &&
+ deepEqual(prevProps.dataProviders, nextProps.dataProviders) &&
prevProps.timelineId === nextProps.timelineId
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx
index e4148b55814356..091bb41bc20804 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx
@@ -5,7 +5,7 @@
*/
import { isArray, isEmpty, isString, uniq } from 'lodash/fp';
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import {
DragEffects,
@@ -71,16 +71,25 @@ const NonDecoratedIpComponent: React.FC<{
fieldName: string;
truncate?: boolean;
value: string | object | null | undefined;
-}> = ({ contextId, eventId, fieldName, truncate, value }) => (
-
+}> = ({ contextId, eventId, fieldName, truncate, value }) => {
+ const key = useMemo(
+ () =>
+ `non-decorated-ip-draggable-wrapper-${getUniqueId({
+ contextId,
+ eventId,
+ fieldName,
+ address: value,
+ })}`,
+ [contextId, eventId, fieldName, value]
+ );
+
+ const dataProviderProp = useMemo(
+ () => getDataProvider({ contextId, eventId, fieldName, address: value }),
+ [contextId, eventId, fieldName, value]
+ );
+
+ const render = useCallback(
+ (dataProvider, _, snapshot) =>
snapshot.isDragging ? (
@@ -89,47 +98,107 @@ const NonDecoratedIpComponent: React.FC<{
getOrEmptyTagFromValue(value)
) : (
getOrEmptyTagFromValue(tryStringify(value))
- )
- }
- truncate={truncate}
- />
-);
+ ),
+ [value]
+ );
+
+ return (
+
+ );
+};
const NonDecoratedIp = React.memo(NonDecoratedIpComponent);
-const AddressLinksComponent: React.FC<{
+interface AddressLinksItemProps extends Omit {
+ address: string;
+}
+
+const AddressLinksItemComponent: React.FC = ({
+ address,
+ contextId,
+ eventId,
+ fieldName,
+ truncate,
+}) => {
+ const key = useMemo(
+ () =>
+ `address-links-draggable-wrapper-${getUniqueId({
+ contextId,
+ eventId,
+ fieldName,
+ address,
+ })}`,
+ [address, contextId, eventId, fieldName]
+ );
+
+ const dataProviderProp = useMemo(
+ () => getDataProvider({ contextId, eventId, fieldName, address }),
+ [address, contextId, eventId, fieldName]
+ );
+
+ const render = useCallback(
+ (_props, _provided, snapshot) =>
+ snapshot.isDragging ? (
+
+
+
+ ) : (
+
+ ),
+ [address, dataProviderProp]
+ );
+
+ return (
+
+ );
+};
+
+const AddressLinksItem = React.memo(AddressLinksItemComponent);
+
+interface AddressLinksProps {
addresses: string[];
contextId: string;
eventId: string;
fieldName: string;
truncate?: boolean;
-}> = ({ addresses, contextId, eventId, fieldName, truncate }) => (
- <>
- {uniq(addresses).map((address) => (
-
- snapshot.isDragging ? (
-
-
-
- ) : (
-
- )
- }
- truncate={truncate}
- />
- ))}
- >
-);
+}
+
+const AddressLinksComponent: React.FC = ({
+ addresses,
+ contextId,
+ eventId,
+ fieldName,
+ truncate,
+}) => {
+ const uniqAddresses = useMemo(() => uniq(addresses), [addresses]);
+
+ const content = useMemo(
+ () =>
+ uniqAddresses.map((address) => (
+
+ )),
+ [contextId, eventId, fieldName, truncate, uniqAddresses]
+ );
+
+ return <>{content}>;
+};
const AddressLinks = React.memo(AddressLinksComponent);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap
index 18a648f2abfaab..153128fb418262 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap
@@ -646,34 +646,7 @@ In other use cases the message field can be used to concatenate different values
dataProviders={
Array [
Object {
- "and": Array [
- Object {
- "and": Array [],
- "enabled": true,
- "excluded": false,
- "id": "id-Provider 2",
- "kqlQuery": "",
- "name": "Provider 2",
- "queryMatch": Object {
- "field": "name",
- "operator": ":",
- "value": "Provider 2",
- },
- },
- Object {
- "and": Array [],
- "enabled": true,
- "excluded": false,
- "id": "id-Provider 3",
- "kqlQuery": "",
- "name": "Provider 3",
- "queryMatch": Object {
- "field": "name",
- "operator": ":",
- "value": "Provider 3",
- },
- },
- ],
+ "and": Array [],
"enabled": true,
"excluded": false,
"id": "id-Provider 1",
@@ -921,11 +894,6 @@ In other use cases the message field can be used to concatenate different values
loadingSourcerer={false}
onChangeItemsPerPage={[MockFunction]}
onClose={[MockFunction]}
- onDataProviderEdited={[MockFunction]}
- onDataProviderRemoved={[MockFunction]}
- onToggleDataProviderEnabled={[MockFunction]}
- onToggleDataProviderExcluded={[MockFunction]}
- onToggleDataProviderType={[MockFunction]}
show={true}
showCallOutUnauthorizedMsg={false}
sort={
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/auto_save_warning/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/auto_save_warning/index.tsx
index 210af7a571569a..98faa84db851e6 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/auto_save_warning/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/auto_save_warning/index.tsx
@@ -11,84 +11,69 @@ import {
EuiGlobalToastListToast as Toast,
} from '@elastic/eui';
import { getOr } from 'lodash/fp';
-import React from 'react';
-import { connect, ConnectedProps } from 'react-redux';
+import React, { useCallback, useMemo } from 'react';
+import { useDispatch, useSelector, shallowEqual } from 'react-redux';
-import { State } from '../../../../common/store';
-import { setTimelineRangeDatePicker as dispatchSetTimelineRangeDatePicker } from '../../../../common/store/inputs/actions';
+import { setTimelineRangeDatePicker } from '../../../../common/store/inputs/actions';
import { timelineActions, timelineSelectors } from '../../../store/timeline';
-import { AutoSavedWarningMsg } from '../../../store/timeline/types';
import { useStateToaster } from '../../../../common/components/toasters';
import * as i18n from './translations';
-const AutoSaveWarningMsgComponent = React.memo(
- ({
- newTimelineModel,
- setTimelineRangeDatePicker,
- timelineId,
- updateAutoSaveMsg,
- updateTimeline,
- }) => {
- const dispatchToaster = useStateToaster()[1];
+const AutoSaveWarningMsgComponent = () => {
+ const dispatch = useDispatch();
+ const dispatchToaster = useStateToaster()[1];
+ const { timelineId, newTimelineModel } = useSelector(
+ timelineSelectors.autoSaveMsgSelector,
+ shallowEqual
+ );
+
+ const handleClick = useCallback(() => {
if (timelineId != null && newTimelineModel != null) {
- const toast: Toast = {
- id: 'AutoSaveWarningMsg',
- title: i18n.TITLE,
- color: 'warning',
- iconType: 'alert',
- toastLifeTimeMs: 10000,
- text: (
- <>
- {i18n.DESCRIPTION}
-
-
- {
- updateTimeline({ id: timelineId, timeline: newTimelineModel });
- updateAutoSaveMsg({ timelineId: null, newTimelineModel: null });
- setTimelineRangeDatePicker({
- from: getOr(0, 'dateRange.start', newTimelineModel),
- to: getOr(0, 'dateRange.end', newTimelineModel),
- });
- }}
- >
- {i18n.REFRESH_TIMELINE}
-
-
-
- >
- ),
- };
- dispatchToaster({
- type: 'addToaster',
- toast,
- });
+ dispatch(timelineActions.updateTimeline({ id: timelineId, timeline: newTimelineModel }));
+ dispatch(timelineActions.updateAutoSaveMsg({ timelineId: null, newTimelineModel: null }));
+ dispatch(
+ setTimelineRangeDatePicker({
+ from: getOr(0, 'dateRange.start', newTimelineModel),
+ to: getOr(0, 'dateRange.end', newTimelineModel),
+ })
+ );
}
+ }, [dispatch, newTimelineModel, timelineId]);
- return null;
- }
-);
-
-AutoSaveWarningMsgComponent.displayName = 'AutoSaveWarningMsgComponent';
-
-const mapStateToProps = (state: State) => {
- const autoSaveMessage: AutoSavedWarningMsg = timelineSelectors.autoSaveMsgSelector(state);
+ const TextComponent = useMemo(
+ () => (
+ <>
+ {i18n.DESCRIPTION}
+
+
+
+ {i18n.REFRESH_TIMELINE}
+
+
+
+ >
+ ),
+ [handleClick]
+ );
- return {
- timelineId: autoSaveMessage.timelineId,
- newTimelineModel: autoSaveMessage.newTimelineModel,
- };
-};
+ if (timelineId != null && newTimelineModel != null) {
+ const toast: Toast = {
+ id: 'AutoSaveWarningMsg',
+ title: i18n.TITLE,
+ color: 'warning',
+ iconType: 'alert',
+ toastLifeTimeMs: 10000,
+ text: TextComponent,
+ };
+ dispatchToaster({
+ type: 'addToaster',
+ toast,
+ });
+ }
-const mapDispatchToProps = {
- setTimelineRangeDatePicker: dispatchSetTimelineRangeDatePicker,
- updateAutoSaveMsg: timelineActions.updateAutoSaveMsg,
- updateTimeline: timelineActions.updateTimeline,
+ return null;
};
-const connector = connect(mapStateToProps, mapDispatchToProps);
-
-type PropsFromRedux = ConnectedProps;
+AutoSaveWarningMsgComponent.displayName = 'AutoSaveWarningMsgComponent';
-export const AutoSaveWarningMsg = connector(AutoSaveWarningMsgComponent);
+export const AutoSaveWarningMsg = React.memo(AutoSaveWarningMsgComponent);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx
index 3352639fa95f82..c4c4e0e0c70657 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx
@@ -5,7 +5,7 @@
*/
import { EuiButtonIcon } from '@elastic/eui';
-import React from 'react';
+import React, { useCallback } from 'react';
import { ColumnHeaderOptions } from '../../../../../../timelines/store/timeline/model';
import { OnColumnRemoved } from '../../../events';
@@ -26,20 +26,27 @@ interface Props {
export const CloseButton = React.memo<{
columnId: string;
onColumnRemoved: OnColumnRemoved;
-}>(({ columnId, onColumnRemoved }) => (
- ) => {
+}>(({ columnId, onColumnRemoved }) => {
+ const handleClick = useCallback(
+ (event: React.MouseEvent) => {
// To avoid a re-sorting when you delete a column
event.preventDefault();
event.stopPropagation();
onColumnRemoved(columnId);
- }}
- />
-));
+ },
+ [columnId, onColumnRemoved]
+ );
+
+ return (
+
+ );
+});
CloseButton.displayName = 'CloseButton';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx
index 617b2935ee75cc..6e21446944573b 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx
@@ -78,6 +78,29 @@ const ColumnHeaderComponent: React.FC = ({
[timelineId, header.id]
);
+ const DraggableContent = useCallback(
+ (dragProvided) => (
+
+
+
+
+
+ ),
+ [header, onColumnRemoved, onColumnSorted, onFilterChange, sort, timelineId]
+ );
+
return (
= ({
index={draggableIndex}
key={header.id}
>
- {(dragProvided) => (
-
-
-
-
-
- )}
+ {DraggableContent}
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx
index 6e802053ab29f6..f4d4cf29ba38bc 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx
@@ -200,6 +200,22 @@ export const ColumnHeadersComponent = ({
[globalFullScreen, timelineId, timelineFullScreen]
);
+ const DroppableContent = useCallback(
+ (dropProvided, snapshot) => (
+ <>
+
+ {ColumnHeaderList}
+
+ >
+ ),
+ [ColumnHeaderList]
+ );
+
return (
@@ -275,18 +291,7 @@ export const ColumnHeadersComponent = ({
type={DRAG_TYPE_FIELD}
renderClone={renderClone}
>
- {(dropProvided, snapshot) => (
- <>
-
- {ColumnHeaderList}
-
- >
- )}
+ {DroppableContent}
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx
index 3b6585013c8d36..df5c48ad012a6b 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx
@@ -151,13 +151,17 @@ export const EventColumnView = React.memo(
/>,
]
: []),
- ,
+ ...(eventType !== 'raw'
+ ? [
+ ,
+ ]
+ : []),
],
[
associateNote,
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx
index d71af86c802476..ee68c270e9abad 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx
@@ -97,6 +97,8 @@ const TOP_OFFSET = 50;
*/
const BOTTOM_OFFSET = -500;
+const VISIBILITY_SENSOR_OFFSET = { top: TOP_OFFSET, bottom: BOTTOM_OFFSET };
+
const emptyNotes: string[] = [];
const EventsTrSupplementContainerWrapper = React.memo(({ children }) => {
@@ -173,105 +175,145 @@ const StatefulEventComponent: React.FC = ({
// Number of current columns plus one for actions.
const columnCount = columnHeaders.length + 1;
+ const VisibilitySensorContent = useCallback(
+ ({ isVisible }) => {
+ if (isVisible || disableSensorVisibility) {
+ return (
+
+
+
+
+
+
+
+
+ {getRowRenderer(event.ecs, rowRenderers).renderRow({
+ browserFields,
+ data: event.ecs,
+ timelineId,
+ })}
+
+
+
+
+
+
+ );
+ } else {
+ // Height place holder for visibility detection as well as re-rendering sections.
+ const height =
+ divElement.current != null && divElement.current!.clientHeight
+ ? `${divElement.current!.clientHeight}px`
+ : DEFAULT_ROW_HEIGHT;
+
+ return ;
+ }
+ },
+ [
+ actionsColumnWidth,
+ associateNote,
+ browserFields,
+ columnCount,
+ columnHeaders,
+ columnRenderers,
+ detailsData,
+ disableSensorVisibility,
+ event._id,
+ event.data,
+ event.ecs,
+ eventIdToNoteIds,
+ expanded,
+ getNotesByIds,
+ isEventPinned,
+ isEventViewer,
+ loading,
+ loadingEventIds,
+ onColumnResized,
+ onPinEvent,
+ onRowSelected,
+ onToggleExpanded,
+ onToggleShowNotes,
+ onUnPinEvent,
+ onUpdateColumns,
+ refetch,
+ rowRenderers,
+ selectedEventIds,
+ showCheckboxes,
+ showNotes,
+ timelineId,
+ timelineStatus,
+ toggleColumn,
+ updateNote,
+ ]
+ );
+
return (
- {({ isVisible }) => {
- if (isVisible || disableSensorVisibility) {
- return (
-
-
-
-
-
-
-
-
- {getRowRenderer(event.ecs, rowRenderers).renderRow({
- browserFields,
- data: event.ecs,
- timelineId,
- })}
-
-
-
-
-
-
- );
- } else {
- // Height place holder for visibility detection as well as re-rendering sections.
- const height =
- divElement.current != null && divElement.current!.clientHeight
- ? `${divElement.current!.clientHeight}px`
- : DEFAULT_ROW_HEIGHT;
-
- return ;
- }
- }}
+ {VisibilitySensorContent}
);
};
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx
index 1cd78178d017f6..5d4821757d7749 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx
@@ -5,7 +5,7 @@
*/
import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
import {
@@ -19,7 +19,7 @@ import { Provider } from '../../../data_providers/provider';
import { TokensFlexItem } from '../helpers';
import { getBeginningTokens } from './suricata_links';
import { DefaultDraggable } from '../../../../../../common/components/draggables';
-import { IS_OPERATOR } from '../../../data_providers/data_provider';
+import { IS_OPERATOR, QueryOperator } from '../../../data_providers/data_provider';
export const SURICATA_SIGNATURE_FIELD_NAME = 'suricata.eve.alert.signature';
export const SURICATA_SIGNATURE_ID_FIELD_NAME = 'suricata.eve.alert.signature_id';
@@ -57,41 +57,49 @@ export const Tokens = React.memo<{ tokens: string[] }>(({ tokens }) => (
Tokens.displayName = 'Tokens';
export const DraggableSignatureId = React.memo<{ id: string; signatureId: number }>(
- ({ id, signatureId }) => (
-
-
- snapshot.isDragging ? (
-
-
-
- ) : (
-
-
- {signatureId}
-
-
- )
- }
- />
-
- )
+ ({ id, signatureId }) => {
+ const dataProviderProp = useMemo(
+ () => ({
+ and: [],
+ enabled: true,
+ id: escapeDataProviderId(`suricata-draggable-signature-id-${id}-sig-${signatureId}`),
+ name: String(signatureId),
+ excluded: false,
+ kqlQuery: '',
+ queryMatch: {
+ field: SURICATA_SIGNATURE_ID_FIELD_NAME,
+ value: signatureId,
+ operator: IS_OPERATOR as QueryOperator,
+ },
+ }),
+ [id, signatureId]
+ );
+
+ const render = useCallback(
+ (dataProvider, _, snapshot) =>
+ snapshot.isDragging ? (
+
+
+
+ ) : (
+
+
+ {signatureId}
+
+
+ ),
+ [signatureId]
+ );
+
+ return (
+
+
+
+ );
+ }
);
DraggableSignatureId.displayName = 'DraggableSignatureId';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx
index 07e32a9a4e5d1f..9ef579706f1189 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx
@@ -6,7 +6,7 @@
import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import { get } from 'lodash/fp';
-import React from 'react';
+import React, { useCallback, useMemo } from 'react';
import styled from 'styled-components';
import { Ecs } from '../../../../../../../common/ecs';
@@ -17,7 +17,7 @@ import {
import { escapeDataProviderId } from '../../../../../../common/components/drag_and_drop/helpers';
import { GoogleLink, ReputationLink } from '../../../../../../common/components/links';
import { Provider } from '../../../data_providers/provider';
-import { IS_OPERATOR } from '../../../data_providers/data_provider';
+import { IS_OPERATOR, QueryOperator } from '../../../data_providers/data_provider';
import * as i18n from './translations';
@@ -68,42 +68,46 @@ export const DraggableZeekElement = React.memo<{
field: string;
value: string | null | undefined;
stringRenderer?: StringRenderer;
-}>(({ id, field, value, stringRenderer = defaultStringRenderer }) =>
- value != null ? (
+}>(({ id, field, value, stringRenderer = defaultStringRenderer }) => {
+ const dataProviderProp = useMemo(
+ () => ({
+ and: [],
+ enabled: true,
+ id: escapeDataProviderId(`draggable-zeek-element-draggable-wrapper-${id}-${field}-${value}`),
+ name: value!,
+ excluded: false,
+ kqlQuery: '',
+ queryMatch: {
+ field,
+ value: value!,
+ operator: IS_OPERATOR as QueryOperator,
+ },
+ }),
+ [field, id, value]
+ );
+
+ const render = useCallback(
+ (dataProvider, _, snapshot) =>
+ snapshot.isDragging ? (
+
+
+
+ ) : (
+
+
+ {stringRenderer(value!)}
+
+
+ ),
+ [field, stringRenderer, value]
+ );
+
+ return value != null ? (
-
- snapshot.isDragging ? (
-
-
-
- ) : (
-
-
- {stringRenderer(value)}
-
-
- )
- }
- />
+
- ) : null
-);
+ ) : null;
+});
DraggableZeekElement.displayName = 'DraggableZeekElement';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/data_providers.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/data_providers.test.tsx.snap
index 14304b99263acb..a8818517fb94b1 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/data_providers.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/data_providers.test.tsx.snap
@@ -144,11 +144,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = `
},
]
}
- onDataProviderEdited={[MockFunction]}
- onDataProviderRemoved={[MockFunction]}
- onToggleDataProviderEnabled={[MockFunction]}
- onToggleDataProviderExcluded={[MockFunction]}
- onToggleDataProviderType={[MockFunction]}
timelineId="foo"
/>
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/providers.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/providers.test.tsx.snap
index a86c99cbc094ae..281a26b08df670 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/providers.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/providers.test.tsx.snap
@@ -2,556 +2,1717 @@
exports[`Providers rendering renders correctly against snapshot 1`] = `
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
+
-
-
-
-
-
-
-
-
- (
-
-
-
-
-
-
-
-
-
- )
-
-
-
-
`;
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_providers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_providers.test.tsx
index d48be25b08897d..a7ae14dea510f7 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_providers.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_providers.test.tsx
@@ -41,11 +41,6 @@ describe('DataProviders', () => {
data-test-subj="dataProviders-container"
dataProviders={mockDataProviders}
timelineId="foo"
- onDataProviderEdited={jest.fn()}
- onDataProviderRemoved={jest.fn()}
- onToggleDataProviderEnabled={jest.fn()}
- onToggleDataProviderExcluded={jest.fn()}
- onToggleDataProviderType={jest.fn()}
/>
@@ -58,16 +53,7 @@ describe('DataProviders', () => {
const wrapper = mount(
-
+
);
@@ -77,16 +63,7 @@ describe('DataProviders', () => {
test('it renders the data providers', () => {
const wrapper = mount(
-
+
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx
index c9e06f89af41c7..b892ca089eb4c4 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx
@@ -14,13 +14,6 @@ import {
droppableTimelineProvidersPrefix,
IS_DRAGGING_CLASS_NAME,
} from '../../../../common/components/drag_and_drop/helpers';
-import {
- OnDataProviderEdited,
- OnDataProviderRemoved,
- OnToggleDataProviderEnabled,
- OnToggleDataProviderExcluded,
- OnToggleDataProviderType,
-} from '../events';
import { DataProvider } from './data_provider';
import { Empty } from './empty';
@@ -31,11 +24,6 @@ interface Props {
browserFields: BrowserFields;
timelineId: string;
dataProviders: DataProvider[];
- onDataProviderEdited: OnDataProviderEdited;
- onDataProviderRemoved: OnDataProviderRemoved;
- onToggleDataProviderEnabled: OnToggleDataProviderEnabled;
- onToggleDataProviderExcluded: OnToggleDataProviderExcluded;
- onToggleDataProviderType: OnToggleDataProviderType;
}
const DropTargetDataProvidersContainer = styled.div`
@@ -91,48 +79,32 @@ const getDroppableId = (id: string): string => `${droppableTimelineProvidersPref
* the user to drop anything with a facet count into
* the data pro section.
*/
-export const DataProviders = React.memo(
- ({
- browserFields,
- dataProviders,
+export const DataProviders = React.memo(({ browserFields, dataProviders, timelineId }) => {
+ const { getManageTimelineById } = useManageTimeline();
+ const isLoading = useMemo(() => getManageTimelineById(timelineId).isLoading, [
+ getManageTimelineById,
timelineId,
- onDataProviderEdited,
- onDataProviderRemoved,
- onToggleDataProviderEnabled,
- onToggleDataProviderExcluded,
- onToggleDataProviderType,
- }) => {
- const { getManageTimelineById } = useManageTimeline();
- const isLoading = useMemo(() => getManageTimelineById(timelineId).isLoading, [
- getManageTimelineById,
- timelineId,
- ]);
- return (
-
-
- {dataProviders != null && dataProviders.length ? (
-
- ) : (
-
-
-
- )}
-
-
- );
- }
-);
+ ]);
+ return (
+
+
+ {dataProviders != null && dataProviders.length ? (
+
+ ) : (
+
+
+
+ )}
+
+
+ );
+});
DataProviders.displayName = 'DataProviders';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx
index bf2094e7659eee..b66a332fc977d7 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx
@@ -91,7 +91,7 @@ const ConvertFieldBadge = styled(ProviderFieldBadge)`
}
`;
-const TemplateFieldBadge: React.FC = ({ type, toggleType }) => {
+const TemplateFieldBadgeComponent: React.FC = ({ type, toggleType }) => {
if (type !== DataProviderType.template) {
return (
{i18n.CONVERT_TO_TEMPLATE_FIELD}
@@ -101,6 +101,8 @@ const TemplateFieldBadge: React.FC = ({ type, toggleTyp
return {i18n.TEMPLATE_FIELD_LABEL} ;
};
+const TemplateFieldBadge = React.memo(TemplateFieldBadgeComponent);
+
interface ProviderBadgeProps {
deleteProvider: () => void;
field: string;
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx
index 0093c43a17137e..fc06d37b9663fd 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx
@@ -117,21 +117,38 @@ export const ProviderItemBadge = React.memo(
[unRegisterProvider]
);
- const button = (
-
+ const button = useMemo(
+ () => (
+
+ ),
+ [
+ deleteProvider,
+ field,
+ isEnabled,
+ isExcluded,
+ isLoading,
+ kqlQuery,
+ onToggleTypeProvider,
+ operator,
+ providerId,
+ timelineType,
+ togglePopover,
+ type,
+ val,
+ ]
);
return (
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx
index 3f371349aa7502..2df19605f813c1 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx
@@ -12,6 +12,7 @@ import { TestProviders } from '../../../../common/mock/test_providers';
import { DroppableWrapper } from '../../../../common/components/drag_and_drop/droppable_wrapper';
import { FilterManager } from '../../../../../../../../src/plugins/data/public';
+import { timelineActions } from '../../../store/timeline';
import { mockDataProviders } from './mock/mock_data_providers';
import { Providers } from './providers';
import { DELETE_CLASS_NAME, ENABLE_CLASS_NAME, EXCLUDE_CLASS_NAME } from './provider_item_actions';
@@ -24,27 +25,24 @@ describe('Providers', () => {
const isLoading: boolean = true;
const mount = useMountAppended();
const filterManager = new FilterManager(mockUiSettingsForFilterManager);
+ const mockOnDataProviderRemoved = jest.spyOn(timelineActions, 'removeProvider');
const manageTimelineForTesting = {
- foo: {
- ...getTimelineDefaults('foo'),
+ test: {
+ ...getTimelineDefaults('test'),
filterManager,
isLoading,
},
};
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
describe('rendering', () => {
test('renders correctly against snapshot', () => {
const wrapper = shallow(
-
+
);
expect(wrapper).toMatchSnapshot();
});
@@ -53,16 +51,7 @@ describe('Providers', () => {
const wrapper = mount(
-
+
);
@@ -77,20 +66,10 @@ describe('Providers', () => {
describe('#onDataProviderRemoved', () => {
test('it invokes the onDataProviderRemoved callback when the close button is clicked', () => {
- const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
);
@@ -98,25 +77,15 @@ describe('Providers', () => {
.find('[data-test-subj="providerBadge"] [data-euiicon-type]')
.first()
.simulate('click');
- expect(mockOnDataProviderRemoved.mock.calls[0][0]).toEqual('id-Provider 1');
+ expect(mockOnDataProviderRemoved.mock.calls[0][0].providerId).toEqual('id-Provider 1');
});
test('while loading data, it does NOT invoke the onDataProviderRemoved callback when the close button is clicked', () => {
- const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
@@ -131,20 +100,10 @@ describe('Providers', () => {
});
test('it invokes the onDataProviderRemoved callback when you click on the option "Delete" in the provider menu', () => {
- const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
);
@@ -156,25 +115,15 @@ describe('Providers', () => {
.find(`[data-test-subj="providerActions"] .${DELETE_CLASS_NAME}`)
.first()
.simulate('click');
- expect(mockOnDataProviderRemoved.mock.calls[0][0]).toEqual('id-Provider 1');
+ expect(mockOnDataProviderRemoved.mock.calls[0][0].providerId).toEqual('id-Provider 1');
});
test('while loading data, it does NOT invoke the onDataProviderRemoved callback when you click on the option "Delete" in the provider menu', () => {
- const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
@@ -194,20 +143,14 @@ describe('Providers', () => {
describe('#onToggleDataProviderEnabled', () => {
test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => {
- const mockOnToggleDataProviderEnabled = jest.fn();
+ const mockOnToggleDataProviderEnabled = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderEnabled'
+ );
const wrapper = mount(
-
+
);
@@ -220,27 +163,23 @@ describe('Providers', () => {
.first()
.simulate('click');
expect(mockOnToggleDataProviderEnabled.mock.calls[0][0]).toEqual({
+ andProviderId: undefined,
enabled: false,
+ id: 'test',
providerId: 'id-Provider 1',
});
});
test('while loading data, it does NOT invoke the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => {
- const mockOnToggleDataProviderEnabled = jest.fn();
+ const mockOnToggleDataProviderEnabled = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderEnabled'
+ );
const wrapper = mount(
-
+
@@ -260,21 +199,15 @@ describe('Providers', () => {
describe('#onToggleDataProviderExcluded', () => {
test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => {
- const onToggleDataProviderExcluded = jest.fn();
+ const mockOnToggleDataProviderExcluded = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderExcluded'
+ );
const wrapper = mount(
-
+
);
@@ -288,29 +221,25 @@ describe('Providers', () => {
.first()
.simulate('click');
- expect(onToggleDataProviderExcluded.mock.calls[0][0]).toEqual({
+ expect(mockOnToggleDataProviderExcluded.mock.calls[0][0]).toEqual({
+ andProviderId: undefined,
excluded: true,
+ id: 'test',
providerId: 'id-Provider 1',
});
});
test('while loading data, it does NOT invoke the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => {
- const onToggleDataProviderExcluded = jest.fn();
+ const mockOnToggleDataProviderExcluded = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderExcluded'
+ );
const wrapper = mount(
-
+
@@ -325,7 +254,7 @@ describe('Providers', () => {
.first()
.simulate('click');
- expect(onToggleDataProviderExcluded).not.toBeCalled();
+ expect(mockOnToggleDataProviderExcluded).not.toBeCalled();
});
});
@@ -337,16 +266,7 @@ describe('Providers', () => {
const wrapper = mount(
-
+
);
@@ -364,21 +284,11 @@ describe('Providers', () => {
test('it invokes the onDataProviderRemoved callback when you click on the close button is clicked', () => {
const dataProviders = mockDataProviders.slice(0, 1);
dataProviders[0].and = mockDataProviders.slice(1, 3);
- const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
);
@@ -392,28 +302,22 @@ describe('Providers', () => {
wrapper.update();
- expect(mockOnDataProviderRemoved.mock.calls[0]).toEqual(['id-Provider 1', 'id-Provider 2']);
+ expect(mockOnDataProviderRemoved.mock.calls[0][0]).toEqual({
+ andProviderId: 'id-Provider 2',
+ id: 'test',
+ providerId: 'id-Provider 1',
+ });
});
test('while loading data, it does NOT invoke the onDataProviderRemoved callback when you click on the close button is clicked', () => {
const dataProviders = mockDataProviders.slice(0, 1);
dataProviders[0].and = mockDataProviders.slice(1, 3);
- const mockOnDataProviderRemoved = jest.fn();
const wrapper = mount(
-
+
@@ -434,21 +338,15 @@ describe('Providers', () => {
test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => {
const dataProviders = mockDataProviders.slice(0, 1);
dataProviders[0].and = mockDataProviders.slice(1, 3);
- const mockOnToggleDataProviderEnabled = jest.fn();
+ const mockOnToggleDataProviderEnabled = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderEnabled'
+ );
const wrapper = mount(
-
+
);
@@ -470,6 +368,7 @@ describe('Providers', () => {
expect(mockOnToggleDataProviderEnabled.mock.calls[0][0]).toEqual({
andProviderId: 'id-Provider 2',
enabled: false,
+ id: 'test',
providerId: 'id-Provider 1',
});
});
@@ -477,22 +376,16 @@ describe('Providers', () => {
test('while loading data, it does NOT invoke the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => {
const dataProviders = mockDataProviders.slice(0, 1);
dataProviders[0].and = mockDataProviders.slice(1, 3);
- const mockOnToggleDataProviderEnabled = jest.fn();
+ const mockOnToggleDataProviderEnabled = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderEnabled'
+ );
const wrapper = mount(
-
+
@@ -518,21 +411,15 @@ describe('Providers', () => {
test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => {
const dataProviders = mockDataProviders.slice(0, 1);
dataProviders[0].and = mockDataProviders.slice(1, 3);
- const mockOnToggleDataProviderExcluded = jest.fn();
+ const mockOnToggleDataProviderExcluded = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderExcluded'
+ );
const wrapper = mount(
-
+
);
@@ -554,6 +441,7 @@ describe('Providers', () => {
expect(mockOnToggleDataProviderExcluded.mock.calls[0][0]).toEqual({
andProviderId: 'id-Provider 2',
excluded: true,
+ id: 'test',
providerId: 'id-Provider 1',
});
});
@@ -561,22 +449,16 @@ describe('Providers', () => {
test('while loading data, it does NOT invoke the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => {
const dataProviders = mockDataProviders.slice(0, 1);
dataProviders[0].and = mockDataProviders.slice(1, 3);
- const mockOnToggleDataProviderExcluded = jest.fn();
+ const mockOnToggleDataProviderExcluded = jest.spyOn(
+ timelineActions,
+ 'updateDataProviderExcluded'
+ );
const wrapper = mount(
-
+
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx
index 1142bbc214d74e..4b6f3c6701794b 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx
@@ -6,9 +6,12 @@
import { EuiFlexGroup, EuiFlexItem, EuiFormHelpText, EuiSpacer } from '@elastic/eui';
import { rgba } from 'polished';
-import React, { Fragment, useMemo } from 'react';
+import React, { useCallback, useMemo } from 'react';
import { Draggable, DraggingStyle, Droppable, NotDraggingStyle } from 'react-beautiful-dnd';
-import styled, { css } from 'styled-components';
+import styled from 'styled-components';
+import { useDispatch } from 'react-redux';
+
+import { timelineActions } from '../../../store/timeline';
import { AndOrBadge } from '../../../../common/components/and_or_badge';
import { AddDataProviderPopover } from './add_data_provider_popover';
@@ -18,13 +21,6 @@ import {
IS_DRAGGING_CLASS_NAME,
getTimelineProviderDraggableId,
} from '../../../../common/components/drag_and_drop/helpers';
-import {
- OnDataProviderEdited,
- OnDataProviderRemoved,
- OnToggleDataProviderEnabled,
- OnToggleDataProviderExcluded,
- OnToggleDataProviderType,
-} from '../events';
import { DataProvider, DataProviderType, DataProvidersAnd, IS_OPERATOR } from './data_provider';
import { EMPTY_GROUP, flattenIntoAndGroups } from './helpers';
@@ -36,11 +32,6 @@ interface Props {
browserFields: BrowserFields;
timelineId: string;
dataProviders: DataProvider[];
- onDataProviderEdited: OnDataProviderEdited;
- onDataProviderRemoved: OnDataProviderRemoved;
- onToggleDataProviderEnabled: OnToggleDataProviderEnabled;
- onToggleDataProviderExcluded: OnToggleDataProviderExcluded;
- onToggleDataProviderType: OnToggleDataProviderType;
}
/**
@@ -74,12 +65,10 @@ const DroppableContainer = styled.div`
`;
const Parens = styled.span`
- ${({ theme }) => css`
- color: ${theme.eui.euiColorMediumShade};
- font-size: 32px;
- padding: 2px;
- user-select: none;
- `}
+ color: ${({ theme }) => theme.eui.euiColorMediumShade};
+ font-size: 32px;
+ padding: 2px;
+ user-select: none;
`;
const AndOrBadgeContainer = styled.div<{ hideBadge: boolean }>`
@@ -123,182 +112,269 @@ const getDataProviderValue = (dataProvider: DataProvidersAnd) =>
* 2) temporarily disabling a data provider
* 3) applying boolean negation to the data provider
*/
-export const Providers = React.memo(
- ({
- browserFields,
- timelineId,
- dataProviders,
- onDataProviderEdited,
- onDataProviderRemoved,
- onToggleDataProviderEnabled,
- onToggleDataProviderExcluded,
- onToggleDataProviderType,
- }) => {
- // Transform the dataProviders into flattened groups, and append an empty group
- const dataProviderGroups: DataProvidersAnd[][] = useMemo(
- () => [...flattenIntoAndGroups(dataProviders), ...EMPTY_GROUP],
- [dataProviders]
+export const Providers = React.memo(({ browserFields, timelineId, dataProviders }) => {
+ // Transform the dataProviders into flattened groups, and append an empty group
+ const dataProviderGroups: DataProvidersAnd[][] = useMemo(
+ () => [...flattenIntoAndGroups(dataProviders), ...EMPTY_GROUP],
+ [dataProviders]
+ );
+
+ const content = useMemo(
+ () =>
+ dataProviderGroups.map((group, groupIndex) => (
+
+ )),
+ [browserFields, dataProviderGroups, dataProviders, timelineId]
+ );
+
+ return {content}
;
+});
+
+Providers.displayName = 'Providers';
+
+interface DataProvidersGroupItem extends Omit {
+ index: number;
+ dataProvider: DataProvidersAnd;
+ group: DataProvidersAnd[];
+ groupIndex: number;
+}
+
+export const DataProvidersGroupItem = React.memo(
+ ({ browserFields, group, groupIndex, dataProvider, index, timelineId }) => {
+ const dispatch = useDispatch();
+ const draggableId = useMemo(
+ () =>
+ getTimelineProviderDraggableId({
+ dataProviderId: dataProvider.id,
+ groupIndex,
+ timelineId,
+ }),
+ [dataProvider.id, groupIndex, timelineId]
+ );
+
+ const handleDeleteProvider = useCallback(() => {
+ const payload = {
+ id: timelineId,
+ providerId: index > 0 ? group[0].id : dataProvider.id,
+ andProviderId: index > 0 ? dataProvider.id : undefined,
+ };
+
+ dispatch(timelineActions.removeProvider(payload));
+ }, [dataProvider.id, dispatch, group, index, timelineId]);
+
+ const handleToggleEnabledProvider = useCallback(() => {
+ const payload = {
+ id: timelineId,
+ providerId: index > 0 ? group[0].id : dataProvider.id,
+ enabled: !dataProvider.enabled,
+ andProviderId: index > 0 ? dataProvider.id : undefined,
+ };
+
+ dispatch(timelineActions.updateDataProviderEnabled(payload));
+ }, [dataProvider.enabled, dataProvider.id, dispatch, group, index, timelineId]);
+
+ const handleToggleExcludedProvider = useCallback(() => {
+ const payload = {
+ id: timelineId,
+ providerId: index > 0 ? group[0].id : dataProvider.id,
+ excluded: !dataProvider.excluded,
+ andProviderId: index > 0 ? dataProvider.id : undefined,
+ };
+
+ dispatch(timelineActions.updateDataProviderExcluded(payload));
+ }, [dataProvider.excluded, dataProvider.id, dispatch, group, index, timelineId]);
+
+ const handleToggleTypeProvider = useCallback(() => {
+ const payload = {
+ id: timelineId,
+ providerId: index > 0 ? group[0].id : dataProvider.id,
+ type:
+ dataProvider.type === DataProviderType.template
+ ? DataProviderType.default
+ : DataProviderType.template,
+ andProviderId: index > 0 ? dataProvider.id : undefined,
+ };
+
+ dispatch(timelineActions.updateDataProviderType(payload));
+ }, [dataProvider.id, dataProvider.type, dispatch, group, index, timelineId]);
+
+ const handleDataProviderEdited = useCallback(
+ ({ andProviderId, excluded, field, operator, providerId, value }) =>
+ dispatch(
+ timelineActions.dataProviderEdited({
+ andProviderId,
+ excluded,
+ field,
+ id: timelineId,
+ operator,
+ providerId,
+ value,
+ })
+ ),
+ [dispatch, timelineId]
+ );
+
+ const DraggableContent = useCallback(
+ (provided, snapshot) => (
+
+
+
+ 0 ? dataProvider.id : undefined}
+ browserFields={browserFields}
+ deleteProvider={handleDeleteProvider}
+ field={
+ index > 0
+ ? dataProvider.queryMatch.displayField ?? dataProvider.queryMatch.field
+ : group[0].queryMatch.displayField ?? group[0].queryMatch.field
+ }
+ kqlQuery={index > 0 ? dataProvider.kqlQuery : group[0].kqlQuery}
+ isEnabled={index > 0 ? dataProvider.enabled : group[0].enabled}
+ isExcluded={index > 0 ? dataProvider.excluded : group[0].excluded}
+ onDataProviderEdited={handleDataProviderEdited}
+ operator={
+ index > 0
+ ? dataProvider.queryMatch.operator ?? IS_OPERATOR
+ : group[0].queryMatch.operator ?? IS_OPERATOR
+ }
+ register={dataProvider}
+ providerId={index > 0 ? group[0].id : dataProvider.id}
+ timelineId={timelineId}
+ toggleEnabledProvider={handleToggleEnabledProvider}
+ toggleExcludedProvider={handleToggleExcludedProvider}
+ toggleTypeProvider={handleToggleTypeProvider}
+ val={getDataProviderValue(dataProvider)}
+ type={dataProvider.type}
+ />
+
+
+ {!snapshot.isDragging &&
+ (index < group.length - 1 ? (
+
+ ) : (
+
+
+
+ ))}
+
+
+
+ ),
+ [
+ browserFields,
+ dataProvider,
+ group,
+ handleDataProviderEdited,
+ handleDeleteProvider,
+ handleToggleEnabledProvider,
+ handleToggleExcludedProvider,
+ handleToggleTypeProvider,
+ index,
+ timelineId,
+ ]
);
return (
-
- {dataProviderGroups.map((group, groupIndex) => (
-
- {groupIndex !== 0 && }
-
-
-
-
-
-
-
-
- {'('}
-
-
-
- {(droppableProvided) => (
-
- {group.map((dataProvider, index) => (
-
- {(provided, snapshot) => (
-
-
-
- 0 ? dataProvider.id : undefined}
- browserFields={browserFields}
- deleteProvider={() =>
- index > 0
- ? onDataProviderRemoved(group[0].id, dataProvider.id)
- : onDataProviderRemoved(dataProvider.id)
- }
- field={
- index > 0
- ? dataProvider.queryMatch.displayField ??
- dataProvider.queryMatch.field
- : group[0].queryMatch.displayField ??
- group[0].queryMatch.field
- }
- kqlQuery={index > 0 ? dataProvider.kqlQuery : group[0].kqlQuery}
- isEnabled={index > 0 ? dataProvider.enabled : group[0].enabled}
- isExcluded={
- index > 0 ? dataProvider.excluded : group[0].excluded
- }
- onDataProviderEdited={onDataProviderEdited}
- operator={
- index > 0
- ? dataProvider.queryMatch.operator ?? IS_OPERATOR
- : group[0].queryMatch.operator ?? IS_OPERATOR
- }
- register={dataProvider}
- providerId={index > 0 ? group[0].id : dataProvider.id}
- timelineId={timelineId}
- toggleEnabledProvider={() =>
- index > 0
- ? onToggleDataProviderEnabled({
- providerId: group[0].id,
- enabled: !dataProvider.enabled,
- andProviderId: dataProvider.id,
- })
- : onToggleDataProviderEnabled({
- providerId: dataProvider.id,
- enabled: !dataProvider.enabled,
- })
- }
- toggleExcludedProvider={() =>
- index > 0
- ? onToggleDataProviderExcluded({
- providerId: group[0].id,
- excluded: !dataProvider.excluded,
- andProviderId: dataProvider.id,
- })
- : onToggleDataProviderExcluded({
- providerId: dataProvider.id,
- excluded: !dataProvider.excluded,
- })
- }
- toggleTypeProvider={() =>
- index > 0
- ? onToggleDataProviderType({
- providerId: group[0].id,
- type:
- dataProvider.type === DataProviderType.template
- ? DataProviderType.default
- : DataProviderType.template,
- andProviderId: dataProvider.id,
- })
- : onToggleDataProviderType({
- providerId: dataProvider.id,
- type:
- dataProvider.type === DataProviderType.template
- ? DataProviderType.default
- : DataProviderType.template,
- })
- }
- val={getDataProviderValue(dataProvider)}
- type={dataProvider.type}
- />
-
-
- {!snapshot.isDragging &&
- (index < group.length - 1 ? (
-
- ) : (
-
-
-
- ))}
-
-
-
- )}
-
- ))}
- {droppableProvided.placeholder}
-
- )}
-
-
-
- {')'}
-
- {groupIndex === dataProviderGroups.length - 1 && (
-
- )}
-
-
- ))}
-
+
+ {DraggableContent}
+
);
}
);
-Providers.displayName = 'Providers';
+DataProvidersGroupItem.displayName = 'DataProvidersGroupItem';
+
+interface DataProvidersGroup extends Props {
+ group: DataProvidersAnd[];
+ groupIndex: number;
+ isLastGroup: boolean;
+}
+
+const DataProvidersGroup = React.memo(
+ ({ browserFields, timelineId, group, groupIndex, isLastGroup }) => {
+ const droppableId = useMemo(() => getTimelineProviderDroppableId({ groupIndex, timelineId }), [
+ groupIndex,
+ timelineId,
+ ]);
+
+ const GroupDataProviders = useMemo(
+ () =>
+ group.map((dataProvider, index) => (
+
+ )),
+ [browserFields, group, groupIndex, timelineId]
+ );
+
+ const DroppableContent = useCallback(
+ (droppableProvided) => (
+
+ {GroupDataProviders}
+ {droppableProvided.placeholder}
+
+ ),
+ [GroupDataProviders, isLastGroup]
+ );
+
+ return (
+ <>
+ {groupIndex !== 0 && }
+
+
+
+
+
+
+
+
+ {'('}
+
+
+
+ {DroppableContent}
+
+
+
+ {')'}
+
+ {isLastGroup && (
+
+ )}
+
+ >
+ );
+ }
+);
+
+DataProvidersGroup.displayName = 'DataProvidersGroup';
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/events.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/events.ts
index f894ac4e73939d..8ab3a71604bf12 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/events.ts
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/events.ts
@@ -7,33 +7,8 @@
import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model';
import { ColumnId } from './body/column_id';
import { SortDirection } from './body/sort';
-import { DataProvider, DataProviderType, QueryOperator } from './data_providers/data_provider';
+import { DataProvider, QueryOperator } from './data_providers/data_provider';
-/** Invoked when a user clicks the close button to remove a data provider */
-export type OnDataProviderRemoved = (providerId: string, andProviderId?: string) => void;
-
-/** Invoked when a user temporarily disables or re-enables a data provider */
-export type OnToggleDataProviderEnabled = (toggled: {
- providerId: string;
- enabled: boolean;
- andProviderId?: string;
-}) => void;
-
-/** Invoked when a user toggles negation ("boolean NOT") of a data provider */
-export type OnToggleDataProviderExcluded = (excluded: {
- providerId: string;
- excluded: boolean;
- andProviderId?: string;
-}) => void;
-
-/** Invoked when a user toggles type (can "default" or "template") of a data provider */
-export type OnToggleDataProviderType = (type: {
- providerId: string;
- type: DataProviderType;
- andProviderId?: string;
-}) => void;
-
-/** Invoked when a user edits the properties of a data provider */
export type OnDataProviderEdited = ({
andProviderId,
excluded,
@@ -54,9 +29,6 @@ export type OnDataProviderEdited = ({
type: DataProvider['type'];
}) => void;
-/** Invoked when a user change the kql query of our data provider */
-export type OnChangeDataProviderKqlQuery = (edit: { providerId: string; kqlQuery: string }) => void;
-
/** Invoked when a user selects a new minimap time range */
export type OnRangeSelected = (range: string) => void;
@@ -76,8 +48,6 @@ export type OnChangeItemsPerPage = (itemsPerPage: number) => void;
/** Invoked when a user clicks to load more item */
export type OnChangePage = (nextPage: number) => void;
-export type OnChangeDroppableAndProvider = (providerId: string) => void;
-
/** Invoked when a user pins an event */
export type OnPinEvent = (eventId: string) => void;
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap
index f81934f9a1d91a..5b14edf818fdcc 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap
@@ -77,7 +77,7 @@ exports[`Footer Timeline Component rendering it renders the default timeline foo
data-test-subj="paging-control"
isLoading={false}
onPageClick={[Function]}
- totalPages={2}
+ totalPages={5}
/>
{
const loadMore = jest.fn();
const onChangeItemsPerPage = jest.fn();
const updatedAt = 1546878704036;
- const totalCount = 15546;
+ const serverSideEventCount = 15546;
const itemsCount = 2;
+ const totalCount = 10;
describe('rendering', () => {
test('it renders the default timeline footer', () => {
@@ -33,8 +34,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -55,8 +56,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -78,8 +79,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -129,8 +130,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -152,8 +153,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -179,8 +180,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -204,8 +205,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -231,8 +232,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
@@ -256,8 +257,8 @@ describe('Footer Timeline Component', () => {
itemsPerPageOptions={[1, 5, 10, 20]}
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadMore}
- serverSideEventCount={totalCount}
- totalPages={2}
+ serverSideEventCount={serverSideEventCount}
+ totalCount={totalCount}
/>
);
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx
index 7174e9b2121e55..7c10168da3c627 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx
@@ -174,30 +174,36 @@ export const EventsCount = React.memo(EventsCountComponent);
EventsCount.displayName = 'EventsCount';
-export const PagingControlComponent = ({
- activePage,
- isLoading,
- onPageClick,
- totalPages,
-}: {
+interface PagingControlProps {
activePage: number;
isLoading: boolean;
onPageClick: OnChangePage;
totalPages: number;
-}) => (
- <>
- {isLoading ? (
- `${i18n.LOADING}...`
- ) : (
-
- )}
- >
-);
+}
+
+export const PagingControlComponent: React.FC = ({
+ activePage,
+ isLoading,
+ onPageClick,
+ totalPages,
+}) => {
+ if (isLoading) {
+ return <>{`${i18n.LOADING}...`}>;
+ }
+
+ if (!totalPages) {
+ return null;
+ }
+
+ return (
+
+ );
+};
PagingControlComponent.displayName = 'PagingControlComponent';
@@ -217,7 +223,7 @@ interface FooterProps {
onChangeItemsPerPage: OnChangeItemsPerPage;
onChangePage: OnChangePage;
serverSideEventCount: number;
- totalPages: number;
+ totalCount: number;
}
/** Renders a loading indicator and paging controls */
@@ -234,7 +240,7 @@ export const FooterComponent = ({
onChangeItemsPerPage,
onChangePage,
serverSideEventCount,
- totalPages,
+ totalCount,
}: FooterProps) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const [paginationLoading, setPaginationLoading] = useState(false);
@@ -259,6 +265,30 @@ export const FooterComponent = ({
]);
const closePopover = useCallback(() => setIsPopoverOpen(false), [setIsPopoverOpen]);
+ const rowItems = useMemo(
+ () =>
+ itemsPerPageOptions &&
+ itemsPerPageOptions.map((item) => (
+ {
+ closePopover();
+ onChangeItemsPerPage(item);
+ }}
+ >
+ {`${item} ${i18n.ROWS}`}
+
+ )),
+ [closePopover, itemsPerPage, itemsPerPageOptions, onChangeItemsPerPage]
+ );
+
+ const totalPages = useMemo(() => Math.ceil(totalCount / itemsPerPage), [
+ itemsPerPage,
+ totalCount,
+ ]);
+
useEffect(() => {
if (paginationLoading && !isLoading) {
setPaginationLoading(false);
@@ -279,22 +309,6 @@ export const FooterComponent = ({
);
}
- const rowItems =
- itemsPerPageOptions &&
- itemsPerPageOptions.map((item) => (
- {
- closePopover();
- onChangeItemsPerPage(item);
- }}
- >
- {`${item} ${i18n.ROWS}`}
-
- ));
-
return (
= ({
dataProviders,
filterManager,
graphEventId,
- onDataProviderEdited,
- onDataProviderRemoved,
- onToggleDataProviderEnabled,
- onToggleDataProviderExcluded,
- onToggleDataProviderType,
show,
showCallOutUnauthorizedMsg,
status,
@@ -85,11 +68,6 @@ const TimelineHeaderComponent: React.FC = ({
browserFields={browserFields}
timelineId={timelineId}
dataProviders={dataProviders}
- onDataProviderEdited={onDataProviderEdited}
- onDataProviderRemoved={onDataProviderRemoved}
- onToggleDataProviderEnabled={onToggleDataProviderEnabled}
- onToggleDataProviderExcluded={onToggleDataProviderExcluded}
- onToggleDataProviderType={onToggleDataProviderType}
/>
(
kqlMode,
kqlQueryExpression,
onClose,
- onDataProviderEdited,
removeColumn,
- removeProvider,
show,
showCallOutUnauthorizedMsg,
sort,
start,
status,
timelineType,
- updateDataProviderEnabled,
- updateDataProviderExcluded,
- updateDataProviderType,
updateItemsPerPage,
upsertColumn,
usersViewing,
@@ -75,59 +63,6 @@ const StatefulTimelineComponent = React.memo(
selectedPatterns,
} = useSourcererScope(SourcererScopeName.timeline);
- const onDataProviderRemoved: OnDataProviderRemoved = useCallback(
- (providerId: string, andProviderId?: string) =>
- removeProvider!({ id, providerId, andProviderId }),
- [id, removeProvider]
- );
-
- const onToggleDataProviderEnabled: OnToggleDataProviderEnabled = useCallback(
- ({ providerId, enabled, andProviderId }) =>
- updateDataProviderEnabled!({
- id,
- enabled,
- providerId,
- andProviderId,
- }),
- [id, updateDataProviderEnabled]
- );
-
- const onToggleDataProviderExcluded: OnToggleDataProviderExcluded = useCallback(
- ({ providerId, excluded, andProviderId }) =>
- updateDataProviderExcluded!({
- id,
- excluded,
- providerId,
- andProviderId,
- }),
- [id, updateDataProviderExcluded]
- );
-
- const onToggleDataProviderType: OnToggleDataProviderType = useCallback(
- ({ providerId, type, andProviderId }) =>
- updateDataProviderType!({
- id,
- type,
- providerId,
- andProviderId,
- }),
- [id, updateDataProviderType]
- );
-
- const onDataProviderEditedLocal: OnDataProviderEdited = useCallback(
- ({ andProviderId, excluded, field, operator, providerId, value }) =>
- onDataProviderEdited!({
- andProviderId,
- excluded,
- field,
- id,
- operator,
- providerId,
- value,
- }),
- [id, onDataProviderEdited]
- );
-
const onChangeItemsPerPage: OnChangeItemsPerPage = useCallback(
(itemsChangedPerPage) => updateItemsPerPage!({ id, itemsPerPage: itemsChangedPerPage }),
[id, updateItemsPerPage]
@@ -183,11 +118,6 @@ const StatefulTimelineComponent = React.memo(
loadingSourcerer={loading}
onChangeItemsPerPage={onChangeItemsPerPage}
onClose={onClose}
- onDataProviderEdited={onDataProviderEditedLocal}
- onDataProviderRemoved={onDataProviderRemoved}
- onToggleDataProviderEnabled={onToggleDataProviderEnabled}
- onToggleDataProviderExcluded={onToggleDataProviderExcluded}
- onToggleDataProviderType={onToggleDataProviderType}
show={show!}
showCallOutUnauthorizedMsg={showCallOutUnauthorizedMsg}
sort={sort!}
@@ -287,14 +217,8 @@ const makeMapStateToProps = () => {
const mapDispatchToProps = {
addProvider: timelineActions.addProvider,
createTimeline: timelineActions.createTimeline,
- onDataProviderEdited: timelineActions.dataProviderEdited,
removeColumn: timelineActions.removeColumn,
- removeProvider: timelineActions.removeProvider,
updateColumns: timelineActions.updateColumns,
- updateDataProviderEnabled: timelineActions.updateDataProviderEnabled,
- updateDataProviderExcluded: timelineActions.updateDataProviderExcluded,
- updateDataProviderKqlQuery: timelineActions.updateDataProviderKqlQuery,
- updateDataProviderType: timelineActions.updateDataProviderType,
updateHighlightedDropAndProviderId: timelineActions.updateHighlightedDropAndProviderId,
updateItemsPerPage: timelineActions.updateItemsPerPage,
updateItemsPerPageOptions: timelineActions.updateItemsPerPageOptions,
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx
index 43ab8ab203e116..a28f4240d3a2f4 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx
@@ -73,23 +73,31 @@ export const StarIcon = React.memo<{
isFavorite: boolean;
timelineId: string;
updateIsFavorite: UpdateIsFavorite;
-}>(({ isFavorite, timelineId: id, updateIsFavorite }) => (
- // TODO: 1 error is: Visible, non-interactive elements with click handlers must have at least one keyboard listener
- // TODO: 2 error is: Elements with the 'button' interactive role must be focusable
- // TODO: Investigate this error
- // eslint-disable-next-line
- updateIsFavorite({ id, isFavorite: !isFavorite })}>
- {isFavorite ? (
-
-
-
- ) : (
-
-
-
- )}
-
-));
+}>(({ isFavorite, timelineId: id, updateIsFavorite }) => {
+ const handleClick = useCallback(() => updateIsFavorite({ id, isFavorite: !isFavorite }), [
+ id,
+ isFavorite,
+ updateIsFavorite,
+ ]);
+
+ return (
+ // TODO: 1 error is: Visible, non-interactive elements with click handlers must have at least one keyboard listener
+ // TODO: 2 error is: Elements with the 'button' interactive role must be focusable
+ // TODO: Investigate this error
+ // eslint-disable-next-line
+
+ {isFavorite ? (
+
+
+
+ ) : (
+
+
+
+ )}
+
+ );
+});
StarIcon.displayName = 'StarIcon';
interface DescriptionProps {
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx
index bde1e7bf5829ad..630a71693d182c 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx
@@ -19,11 +19,6 @@ import {
import '../../../common/mock/match_media';
import { TestProviders } from '../../../common/mock/test_providers';
-import {
- DELETE_CLASS_NAME,
- ENABLE_CLASS_NAME,
- EXCLUDE_CLASS_NAME,
-} from './data_providers/provider_item_actions';
import { TimelineComponent, Props as TimelineComponentProps } from './timeline';
import { Sort } from './body/sort';
import { mockDataProviders } from './data_providers/mock/mock_data_providers';
@@ -115,11 +110,6 @@ describe('Timeline', () => {
loadingSourcerer: false,
onChangeItemsPerPage: jest.fn(),
onClose: jest.fn(),
- onDataProviderEdited: jest.fn(),
- onDataProviderRemoved: jest.fn(),
- onToggleDataProviderEnabled: jest.fn(),
- onToggleDataProviderExcluded: jest.fn(),
- onToggleDataProviderType: jest.fn(),
show: true,
showCallOutUnauthorizedMsg: false,
sort,
@@ -238,204 +228,4 @@ describe('Timeline', () => {
});
});
});
-
- describe('event wire up', () => {
- describe('onDataProviderRemoved', () => {
- test('it invokes the onDataProviderRemoved callback when the delete button on a provider is clicked', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper
- .find('[data-test-subj="providerBadge"] [data-euiicon-type]')
- .first()
- .simulate('click');
-
- expect((props.onDataProviderRemoved as jest.Mock).mock.calls[0][0]).toEqual(
- 'id-Provider 1'
- );
- });
-
- test('it invokes the onDataProviderRemoved callback when you click on the option "Delete" in the provider menu', () => {
- const wrapper = mount(
-
-
-
- );
- wrapper.find('button[data-test-subj="providerBadge"]').first().simulate('click');
-
- wrapper.update();
-
- wrapper
- .find(`[data-test-subj="providerActions"] .${DELETE_CLASS_NAME}`)
- .first()
- .simulate('click');
-
- expect((props.onDataProviderRemoved as jest.Mock).mock.calls[0][0]).toEqual(
- 'id-Provider 1'
- );
- });
- });
-
- describe('onToggleDataProviderEnabled', () => {
- test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper.find('button[data-test-subj="providerBadge"]').first().simulate('click');
-
- wrapper.update();
-
- wrapper
- .find(`[data-test-subj="providerActions"] .${ENABLE_CLASS_NAME}`)
- .first()
- .simulate('click');
-
- expect((props.onToggleDataProviderEnabled as jest.Mock).mock.calls[0][0]).toEqual({
- providerId: 'id-Provider 1',
- enabled: false,
- });
- });
- });
-
- describe('onToggleDataProviderExcluded', () => {
- test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper
- .find('[data-test-subj="providerBadge"]')
- .first()
- .find('button')
- .first()
- .simulate('click');
-
- wrapper.update();
-
- wrapper
- .find(`[data-test-subj="providerActions"] .${EXCLUDE_CLASS_NAME}`)
- .first()
- .simulate('click');
-
- expect((props.onToggleDataProviderExcluded as jest.Mock).mock.calls[0][0]).toEqual({
- providerId: 'id-Provider 1',
- excluded: true,
- });
- });
- });
-
- describe('#ProviderWithAndProvider', () => {
- const dataProviders = mockDataProviders.slice(0, 1);
- dataProviders[0].and = mockDataProviders.slice(1, 3);
-
- test('Rendering And Provider', () => {
- const wrapper = mount(
-
-
-
- );
-
- const andProviderBadges = wrapper.find(
- '[data-test-subj="providerBadge"] .euiBadge__content span.field-value'
- );
-
- const andProviderBadgesText = andProviderBadges.map((node) => node.text()).join(' ');
- expect(andProviderBadges.length).toEqual(3);
- expect(andProviderBadgesText).toEqual(
- 'name: "Provider 1" name: "Provider 2" name: "Provider 3"'
- );
- });
-
- test('it invokes the onDataProviderRemoved callback when you click on the option "Delete" in the accordion menu', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper
- .find('[data-test-subj="providerBadge"]')
- .at(3)
- .find('button')
- .first()
- .simulate('click');
-
- wrapper.update();
-
- wrapper
- .find(`[data-test-subj="providerActions"] .${DELETE_CLASS_NAME}`)
- .first()
- .simulate('click');
-
- expect((props.onDataProviderRemoved as jest.Mock).mock.calls[0]).toEqual([
- 'id-Provider 1',
- 'id-Provider 2',
- ]);
- });
-
- test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the accordion menu', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper
- .find('[data-test-subj="providerBadge"]')
- .at(3)
- .find('button')
- .first()
- .simulate('click');
-
- wrapper.update();
-
- wrapper
- .find(`[data-test-subj="providerActions"] .${ENABLE_CLASS_NAME}`)
- .first()
- .simulate('click');
-
- expect((props.onToggleDataProviderEnabled as jest.Mock).mock.calls[0][0]).toEqual({
- andProviderId: 'id-Provider 2',
- enabled: false,
- providerId: 'id-Provider 1',
- });
- });
-
- test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the accordion menu', () => {
- const wrapper = mount(
-
-
-
- );
-
- wrapper
- .find('[data-test-subj="providerBadge"]')
- .at(3)
- .find('button')
- .first()
- .simulate('click');
-
- wrapper.update();
-
- wrapper
- .find(`[data-test-subj="providerActions"] .${EXCLUDE_CLASS_NAME}`)
- .first()
- .simulate('click');
-
- expect((props.onToggleDataProviderExcluded as jest.Mock).mock.calls[0][0]).toEqual({
- andProviderId: 'id-Provider 2',
- excluded: true,
- providerId: 'id-Provider 1',
- });
- });
- });
- });
});
diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx
index d1a25e6f3e1a48..1097d58b227a86 100644
--- a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx
@@ -19,14 +19,7 @@ import { defaultHeaders } from './body/column_headers/default_headers';
import { Sort } from './body/sort';
import { StatefulBody } from './body/stateful_body';
import { DataProvider } from './data_providers/data_provider';
-import {
- OnChangeItemsPerPage,
- OnDataProviderRemoved,
- OnDataProviderEdited,
- OnToggleDataProviderEnabled,
- OnToggleDataProviderExcluded,
- OnToggleDataProviderType,
-} from './events';
+import { OnChangeItemsPerPage } from './events';
import { TimelineKqlFetch } from './fetch_kql_timeline';
import { Footer, footerHeight } from './footer';
import { TimelineHeader } from './header';
@@ -113,11 +106,6 @@ export interface Props {
loadingSourcerer: boolean;
onChangeItemsPerPage: OnChangeItemsPerPage;
onClose: () => void;
- onDataProviderEdited: OnDataProviderEdited;
- onDataProviderRemoved: OnDataProviderRemoved;
- onToggleDataProviderEnabled: OnToggleDataProviderEnabled;
- onToggleDataProviderExcluded: OnToggleDataProviderExcluded;
- onToggleDataProviderType: OnToggleDataProviderType;
show: boolean;
showCallOutUnauthorizedMsg: boolean;
sort: Sort;
@@ -149,11 +137,6 @@ export const TimelineComponent: React.FC = ({
kqlQueryExpression,
onChangeItemsPerPage,
onClose,
- onDataProviderEdited,
- onDataProviderRemoved,
- onToggleDataProviderEnabled,
- onToggleDataProviderExcluded,
- onToggleDataProviderType,
show,
showCallOutUnauthorizedMsg,
start,
@@ -270,11 +253,6 @@ export const TimelineComponent: React.FC = ({
dataProviders={dataProviders}
filterManager={filterManager}
graphEventId={graphEventId}
- onDataProviderEdited={onDataProviderEdited}
- onDataProviderRemoved={onDataProviderRemoved}
- onToggleDataProviderEnabled={onToggleDataProviderEnabled}
- onToggleDataProviderExcluded={onToggleDataProviderExcluded}
- onToggleDataProviderType={onToggleDataProviderType}
show={show}
showCallOutUnauthorizedMsg={showCallOutUnauthorizedMsg}
timelineId={id}
@@ -324,7 +302,7 @@ export const TimelineComponent: React.FC = ({
onChangeItemsPerPage={onChangeItemsPerPage}
onChangePage={loadPage}
serverSideEventCount={totalCount}
- totalPages={pageInfo.totalPages}
+ totalCount={pageInfo.fakeTotalCount}
/>
)
diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx
index 54db52b985c314..53944fd29a6877 100644
--- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx
@@ -15,11 +15,13 @@ import { inputsModel } from '../../common/store';
import { useKibana } from '../../common/lib/kibana';
import { createFilter } from '../../common/containers/helpers';
import { DocValueFields } from '../../common/containers/query_template';
+import { generateTablePaginationOptions } from '../../common/components/paginated_table/helpers';
import { timelineActions } from '../../timelines/store/timeline';
import { detectionsTimelineIds, skipQueryForDetectionsPage } from './helpers';
import { getInspectResponse } from '../../helpers';
import {
Direction,
+ PageInfoPaginated,
TimelineEventsQueries,
TimelineEventsAllStrategyResponse,
TimelineEventsAllRequestOptions,
@@ -35,10 +37,7 @@ export interface TimelineArgs {
id: string;
inspect: InspectResponse;
loadPage: LoadPage;
- pageInfo: {
- activePage: number;
- totalPages: number;
- };
+ pageInfo: PageInfoPaginated;
refetch: inputsModel.Refetch;
totalCount: number;
updatedAt: number;
@@ -97,10 +96,7 @@ export const useTimelineEvents = ({
from: startDate,
to: endDate,
},
- pagination: {
- activePage,
- querySize: limit,
- },
+ pagination: generateTablePaginationOptions(activePage, limit),
sort,
defaultIndex: indexNames,
docValueFields: docValueFields ?? [],
@@ -134,7 +130,8 @@ export const useTimelineEvents = ({
totalCount: -1,
pageInfo: {
activePage: 0,
- totalPages: 0,
+ fakeTotalCount: 0,
+ showMorePagesIndicator: false,
},
events: [],
loadPage: wrappedLoadPage,
@@ -218,10 +215,7 @@ export const useTimelineEvents = ({
defaultIndex: indexNames,
docValueFields: docValueFields ?? [],
filterQuery: createFilter(filterQuery),
- pagination: {
- activePage,
- querySize: limit,
- },
+ pagination: generateTablePaginationOptions(activePage, limit),
timerange: {
interval: '12h',
from: startDate,
diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx
index 1f79b26394a69e..1992b1f88f0641 100644
--- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx
+++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx
@@ -96,11 +96,6 @@ describe('epicLocalStorage', () => {
loadingSourcerer: false,
onChangeItemsPerPage: jest.fn(),
onClose: jest.fn(),
- onDataProviderEdited: jest.fn(),
- onDataProviderRemoved: jest.fn(),
- onToggleDataProviderEnabled: jest.fn(),
- onToggleDataProviderExcluded: jest.fn(),
- onToggleDataProviderType: jest.fn(),
show: true,
showCallOutUnauthorizedMsg: false,
start: startDate,
diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts
index fc178df86362b2..30d0796443ab50 100644
--- a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts
+++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts
@@ -1209,17 +1209,20 @@ export const updateTimelinePerPageOptions = ({
const removeAndProvider = (andProviderId: string, providerId: string, timeline: TimelineModel) => {
const providerIndex = timeline.dataProviders.findIndex((p) => p.id === providerId);
- const providerAndIndex = timeline.dataProviders[providerIndex].and.findIndex(
+ const providerAndIndex = timeline.dataProviders[providerIndex]?.and.findIndex(
(p) => p.id === andProviderId
);
+
return [
...timeline.dataProviders.slice(0, providerIndex),
{
...timeline.dataProviders[providerIndex],
- and: [
- ...timeline.dataProviders[providerIndex].and.slice(0, providerAndIndex),
- ...timeline.dataProviders[providerIndex].and.slice(providerAndIndex + 1),
- ],
+ and: timeline.dataProviders[providerIndex]?.and
+ ? [
+ ...timeline.dataProviders[providerIndex]?.and.slice(0, providerAndIndex),
+ ...timeline.dataProviders[providerIndex]?.and.slice(providerAndIndex + 1),
+ ]
+ : [],
},
...timeline.dataProviders.slice(providerIndex + 1),
];
@@ -1229,7 +1232,7 @@ const removeProvider = (providerId: string, timeline: TimelineModel) => {
const providerIndex = timeline.dataProviders.findIndex((p) => p.id === providerId);
return [
...timeline.dataProviders.slice(0, providerIndex),
- ...(timeline.dataProviders[providerIndex].and.length
+ ...(timeline.dataProviders[providerIndex]?.and.length
? [
{
...timeline.dataProviders[providerIndex].and.slice(0, 1)[0],
diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts
index 12729eb88a6669..6b28fc2598d410 100644
--- a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts
+++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts
@@ -42,17 +42,19 @@ export const timelineEventsAll: SecuritySolutionTimelineFactory
+ const edges: TimelineEdges[] = hits.splice(cursorStart, querySize - cursorStart).map((hit) =>
// @ts-expect-error
formatTimelineData(options.fieldRequested, TIMELINE_EVENTS_FIELDS, hit, eventFieldsMap)
);
const inspect = {
dsl: [inspectStringifyObject(buildTimelineEventsAllQuery(queryOptions))],
};
+ const fakeTotalCount = fakePossibleCount <= totalCount ? fakePossibleCount : totalCount;
+ const showMorePagesIndicator = totalCount > fakeTotalCount;
return {
...response,
@@ -61,7 +63,8 @@ export const timelineEventsAll: SecuritySolutionTimelineFactory
Date: Fri, 2 Oct 2020 11:22:41 +0200
Subject: [PATCH 07/21] [Discover] Change context query to prevent duplicates
(#77014)
---
.../application/angular/context/api/_stubs.js | 2 +-
.../context/api/context.predecessors.test.js | 8 ++++++--
.../context/api/context.successors.test.js | 8 ++++++--
.../angular/context/api/context.ts | 4 +++-
.../api/utils/fetch_hits_in_interval.ts | 20 ++++++++++++++-----
5 files changed, 31 insertions(+), 11 deletions(-)
diff --git a/src/plugins/discover/public/application/angular/context/api/_stubs.js b/src/plugins/discover/public/application/angular/context/api/_stubs.js
index 35ddf396c2dbad..d82189db609356 100644
--- a/src/plugins/discover/public/application/angular/context/api/_stubs.js
+++ b/src/plugins/discover/public/application/angular/context/api/_stubs.js
@@ -74,7 +74,7 @@ export function createContextSearchSourceStub(hits, timeField = '@timestamp') {
searchSourceStub.fetch = sinon.spy(() => {
const timeField = searchSourceStub._stubTimeField;
const lastQuery = searchSourceStub.setField.withArgs('query').lastCall.args[1];
- const timeRange = lastQuery.query.constant_score.filter.range[timeField];
+ const timeRange = lastQuery.query.bool.must.constant_score.filter.range[timeField];
const lastSort = searchSourceStub.setField.withArgs('sort').lastCall.args[1];
const sortDirection = lastSort[0][timeField];
const sortFunction =
diff --git a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js
index 4987c77f4bf256..4c0515906a4947 100644
--- a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js
+++ b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js
@@ -124,7 +124,9 @@ describe('context app', function () {
).then((hits) => {
const intervals = mockSearchSource.setField.args
.filter(([property]) => property === 'query')
- .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp']));
+ .map(([, { query }]) =>
+ get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp'])
+ );
expect(
intervals.every(({ gte, lte }) => (gte && lte ? moment(gte).isBefore(lte) : true))
@@ -160,7 +162,9 @@ describe('context app', function () {
).then((hits) => {
const intervals = mockSearchSource.setField.args
.filter(([property]) => property === 'query')
- .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp']));
+ .map(([, { query }]) =>
+ get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp'])
+ );
// should have started at the given time
expect(intervals[0].gte).toEqual(moment(MS_PER_DAY * 1000).toISOString());
diff --git a/src/plugins/discover/public/application/angular/context/api/context.successors.test.js b/src/plugins/discover/public/application/angular/context/api/context.successors.test.js
index ebf6e78585962d..285d39cd4d8a44 100644
--- a/src/plugins/discover/public/application/angular/context/api/context.successors.test.js
+++ b/src/plugins/discover/public/application/angular/context/api/context.successors.test.js
@@ -125,7 +125,9 @@ describe('context app', function () {
).then((hits) => {
const intervals = mockSearchSource.setField.args
.filter(([property]) => property === 'query')
- .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp']));
+ .map(([, { query }]) =>
+ get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp'])
+ );
expect(
intervals.every(({ gte, lte }) => (gte && lte ? moment(gte).isBefore(lte) : true))
@@ -163,7 +165,9 @@ describe('context app', function () {
).then((hits) => {
const intervals = mockSearchSource.setField.args
.filter(([property]) => property === 'query')
- .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp']));
+ .map(([, { query }]) =>
+ get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp'])
+ );
// should have started at the given time
expect(intervals[0].lte).toEqual(moment(MS_PER_DAY * 3000).toISOString());
diff --git a/src/plugins/discover/public/application/angular/context/api/context.ts b/src/plugins/discover/public/application/angular/context/api/context.ts
index e244176914a9b7..ba8cffd1d7558f 100644
--- a/src/plugins/discover/public/application/angular/context/api/context.ts
+++ b/src/plugins/discover/public/application/angular/context/api/context.ts
@@ -31,6 +31,7 @@ export interface EsHitRecord {
fields: Record;
sort: number[];
_source: Record;
+ _id: string;
}
export type EsHitRecordList = EsHitRecord[];
@@ -100,7 +101,8 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract) {
interval,
searchAfter,
remainingSize,
- nanos
+ nanos,
+ anchor._id
);
documents =
diff --git a/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts b/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts
index 9a199ea4a62fc6..5ac41641916339 100644
--- a/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts
+++ b/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts
@@ -43,7 +43,8 @@ export async function fetchHitsInInterval(
interval: IntervalValue[],
searchAfter: EsQuerySearchAfter,
maxCount: number,
- nanosValue: string
+ nanosValue: string,
+ anchorId: string
): Promise {
const range: RangeQuery = {
format: 'strict_date_optional_time',
@@ -61,10 +62,19 @@ export async function fetchHitsInInterval(
.setField('size', maxCount)
.setField('query', {
query: {
- constant_score: {
- filter: {
- range: {
- [timeField]: range,
+ bool: {
+ must: {
+ constant_score: {
+ filter: {
+ range: {
+ [timeField]: range,
+ },
+ },
+ },
+ },
+ must_not: {
+ ids: {
+ values: [anchorId],
},
},
},
From e52884cfa285ee0aeae6ba1ad7655efefddb67c3 Mon Sep 17 00:00:00 2001
From: Pete Hampton
Date: Fri, 2 Oct 2020 10:28:37 +0100
Subject: [PATCH 08/21] [7.10][Telemetry] Display collected security event
sample (#78963)
* Add security example to usage data opt in panel.
* Update translations.
* Fix docs.
* Fix broken type.
Co-authored-by: Elastic Machine
---
docs/developer/plugin-list.asciidoc | 2 +-
.../telemetry_management_section/README.md | 2 +-
...t_in_security_example_flyout.test.tsx.snap | 134 ++++++++++
...telemetry_management_section.test.tsx.snap | 35 ++-
.../opt_in_security_example_flyout.test.tsx | 27 ++
.../opt_in_security_example_flyout.tsx | 235 ++++++++++++++++++
.../telemetry_management_section.test.tsx | 38 ++-
.../telemetry_management_section.tsx | 37 ++-
.../translations/translations/ja-JP.json | 1 -
.../translations/translations/zh-CN.json | 1 -
10 files changed, 491 insertions(+), 21 deletions(-)
create mode 100644 src/plugins/telemetry_management_section/public/components/__snapshots__/opt_in_security_example_flyout.test.tsx.snap
create mode 100644 src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.test.tsx
create mode 100644 src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.tsx
diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc
index ed58e77427d47d..bf11f87b96ce9f 100644
--- a/docs/developer/plugin-list.asciidoc
+++ b/docs/developer/plugin-list.asciidoc
@@ -168,7 +168,7 @@ It also provides a stateful version of it on the start contract.
|{kib-repo}blob/{branch}/src/plugins/telemetry_management_section/README.md[telemetryManagementSection]
-|This plugin adds the Advanced Settings section for the Usage Data collection (aka Telemetry).
+|This plugin adds the Advanced Settings section for the Usage and Security Data collection (aka Telemetry).
|{kib-repo}blob/{branch}/src/plugins/tile_map[tileMap]
diff --git a/src/plugins/telemetry_management_section/README.md b/src/plugins/telemetry_management_section/README.md
index 0f795786720c93..c23a8591f6794a 100644
--- a/src/plugins/telemetry_management_section/README.md
+++ b/src/plugins/telemetry_management_section/README.md
@@ -1,5 +1,5 @@
# Telemetry Management Section
-This plugin adds the Advanced Settings section for the Usage Data collection (aka Telemetry).
+This plugin adds the Advanced Settings section for the Usage and Security Data collection (aka Telemetry).
The reason for having it separated from the `telemetry` plugin is to avoid circular dependencies. The plugin `advancedSettings` depends on the `home` app that depends on the `telemetry` plugin because of the telemetry banner in the welcome screen.
diff --git a/src/plugins/telemetry_management_section/public/components/__snapshots__/opt_in_security_example_flyout.test.tsx.snap b/src/plugins/telemetry_management_section/public/components/__snapshots__/opt_in_security_example_flyout.test.tsx.snap
new file mode 100644
index 00000000000000..0b9d426008ca43
--- /dev/null
+++ b/src/plugins/telemetry_management_section/public/components/__snapshots__/opt_in_security_example_flyout.test.tsx.snap
@@ -0,0 +1,134 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`security flyout renders as expected renders as expected 1`] = `
+
+
+
+
+
+ Endpoint security data
+
+
+
+
+ This is a representative sample of the endpoint security alert event that we collect. Endpoint security data is collected only when the Elastic Endpoint is enabled. It includes information about the endpoint configuration and detection events.
+
+
+
+
+
+ {
+ "@timestamp": "2020-09-22T14:34:56.82202300Z",
+ "agent": {
+ "build": {
+ "original": "version: 7.9.1, compiled: Thu Aug 27 14:50:21 2020, branch: 7.9, commit: b594beb958817dee9b9d908191ed766d483df3ea"
+ },
+ "id": "22dd8544-bcac-46cb-b970-5e681bb99e0b",
+ "type": "endpoint",
+ "version": "7.9.1"
+ },
+ "Endpoint": {
+ "policy": {
+ "applied": {
+ "artifacts": {
+ "global": {
+ "identifiers": [
+ {
+ "sha256": "6a546aade5563d3e8dffc1fe2d93d33edda8f9ca3e17ac3cc9ac707620cb9ecd",
+ "name": "endpointpe-v4-blocklist"
+ },
+ {
+ "sha256": "04f9f87accc5d5aea433427bd1bd4ec6908f8528c78ceed26f70df7875a99385",
+ "name": "endpointpe-v4-exceptionlist"
+ },
+ {
+ "sha256": "1471838597fcd79a54ea4a3ec9a9beee1a86feaedab6c98e61102559ced822a8",
+ "name": "endpointpe-v4-model"
+ },
+ {
+ "sha256": "824859b0c6749cc31951d92a73bbdddfcfe9f38abfe432087934d4dab9766ce8",
+ "name": "global-exceptionlist-windows"
+ }
+ ],
+ "version": "1.0.0"
+ },
+ "user": {
+ "identifiers": [
+ {
+ "sha256": "d801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658",
+ "name": "endpoint-exceptionlist-windows-v1"
+ }
+ ],
+ "version": "1.0.0"
+ }
+ }
+ }
+ }
+ },
+ "ecs": {
+ "version": "1.5.0"
+ },
+ "elastic": {
+ "agent": {
+ "id": "b2e88aea-2671-402a-828a-957526bac315"
+ }
+ },
+ "file": {
+ "path": "C:\\\\Windows\\\\Temp\\\\mimikatz.exe",
+ "size": 1263880,
+ "created": "2020-05-19T07:50:06.0Z",
+ "accessed": "2020-09-22T14:29:19.93531400Z",
+ "mtime": "2020-09-22T14:29:03.6040000Z",
+ "directory": "C:\\\\Windows\\\\Temp",
+ "hash": {
+ "sha1": "c9fb7f8a4c6b7b12b493a99a8dc6901d17867388",
+ "sha256": "cb1553a3c88817e4cc774a5a93f9158f6785bd3815447d04b6c3f4c2c4b21ed7",
+ "md5": "465d5d850f54d9cde767bda90743df30"
+ },
+ "Ext": {
+ "code_signature": {
+ "trusted": true,
+ "subject_name": "Open Source Developer, Benjamin Delpy",
+ "exists": true,
+ "status": "trusted"
+ },
+ "malware_classification": {
+ "identifier": "endpointpe-v4-model",
+ "score": 0.99956864118576,
+ "threshold": 0.71,
+ "version": "0.0.0"
+ }
+ }
+ },
+ "host": {
+ "os": {
+ "Ext": {
+ "variant": "Windows 10 Enterprise Evaluation"
+ },
+ "kernel": "2004 (10.0.19041.388)",
+ "name": "Windows",
+ "family": "windows",
+ "version": "2004 (10.0.19041.388)",
+ "platform": "windows",
+ "full": "Windows 10 Enterprise Evaluation 2004 (10.0.19041.388)"
+ }
+ },
+ "event": {
+ "kind": "alert"
+ },
+ "cluster_uuid": "kLbKvSMcRiiFAR0t8LebDA",
+ "cluster_name": "elasticsearch"
+}
+
+
+
+
+`;
diff --git a/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap
index bed1bbeabb0449..7357598c8495fa 100644
--- a/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap
+++ b/src/plugins/telemetry_management_section/public/components/__snapshots__/telemetry_management_section.test.tsx.snap
@@ -80,15 +80,32 @@ exports[`TelemetryManagementSectionComponent renders as expected 1`] = `
/>
-
-
-
+
+
+ ,
+ "endpointSecurityData":
+
+ ,
+ }
+ }
+ />
,
"displayName": "Provide usage statistics",
diff --git a/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.test.tsx b/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.test.tsx
new file mode 100644
index 00000000000000..c80d0daf5a6955
--- /dev/null
+++ b/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { shallowWithIntl } from 'test_utils/enzyme_helpers';
+import { OptInSecurityExampleFlyout } from './opt_in_security_example_flyout';
+
+describe('security flyout renders as expected', () => {
+ it('renders as expected', () => {
+ expect(shallowWithIntl( )).toMatchSnapshot();
+ });
+});
diff --git a/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.tsx b/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.tsx
new file mode 100644
index 00000000000000..af0de5b268ddc5
--- /dev/null
+++ b/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.tsx
@@ -0,0 +1,235 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import * as React from 'react';
+
+import {
+ EuiCallOut,
+ EuiCodeBlock,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiFlyout,
+ EuiFlyoutHeader,
+ EuiFlyoutBody,
+ EuiLoadingSpinner,
+ EuiPortal, // EuiPortal is a temporary requirement to use EuiFlyout with "ownFocus"
+ EuiText,
+ EuiTextColor,
+ EuiTitle,
+} from '@elastic/eui';
+
+import { FormattedMessage } from '@kbn/i18n/react';
+
+interface Props {
+ onClose: () => void;
+}
+
+interface State {
+ isLoading: boolean;
+ hasPrivilegeToRead: boolean;
+}
+
+/**
+ * React component for displaying the example data associated with the Telemetry opt-in banner.
+ */
+export class OptInSecurityExampleFlyout extends React.PureComponent {
+ public readonly state: State = {
+ isLoading: true,
+ hasPrivilegeToRead: false,
+ };
+
+ async componentDidMount() {
+ try {
+ this.setState({
+ isLoading: false,
+ hasPrivilegeToRead: true,
+ });
+ } catch (err) {
+ this.setState({
+ isLoading: false,
+ hasPrivilegeToRead: err.status !== 403,
+ });
+ }
+ }
+
+ renderBody({ isLoading, hasPrivilegeToRead }: State) {
+ if (isLoading) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ if (!hasPrivilegeToRead) {
+ return (
+
+ }
+ color="danger"
+ iconType="cross"
+ >
+
+
+ );
+ }
+
+ return (
+
+ {JSON.stringify(this.exampleSecurityPayload, null, 2)}
+
+ );
+ }
+
+ render() {
+ return (
+
+
+
+
+ Endpoint security data
+
+
+
+ This is a representative sample of the endpoint security alert event that we
+ collect. Endpoint security data is collected only when the Elastic Endpoint is
+ enabled. It includes information about the endpoint configuration and detection
+ events.
+
+
+
+ {this.renderBody(this.state)}
+
+
+ );
+ }
+
+ exampleSecurityPayload = {
+ '@timestamp': '2020-09-22T14:34:56.82202300Z',
+ agent: {
+ build: {
+ original:
+ 'version: 7.9.1, compiled: Thu Aug 27 14:50:21 2020, branch: 7.9, commit: b594beb958817dee9b9d908191ed766d483df3ea',
+ },
+ id: '22dd8544-bcac-46cb-b970-5e681bb99e0b',
+ type: 'endpoint',
+ version: '7.9.1',
+ },
+ Endpoint: {
+ policy: {
+ applied: {
+ artifacts: {
+ global: {
+ identifiers: [
+ {
+ sha256: '6a546aade5563d3e8dffc1fe2d93d33edda8f9ca3e17ac3cc9ac707620cb9ecd',
+ name: 'endpointpe-v4-blocklist',
+ },
+ {
+ sha256: '04f9f87accc5d5aea433427bd1bd4ec6908f8528c78ceed26f70df7875a99385',
+ name: 'endpointpe-v4-exceptionlist',
+ },
+ {
+ sha256: '1471838597fcd79a54ea4a3ec9a9beee1a86feaedab6c98e61102559ced822a8',
+ name: 'endpointpe-v4-model',
+ },
+ {
+ sha256: '824859b0c6749cc31951d92a73bbdddfcfe9f38abfe432087934d4dab9766ce8',
+ name: 'global-exceptionlist-windows',
+ },
+ ],
+ version: '1.0.0',
+ },
+ user: {
+ identifiers: [
+ {
+ sha256: 'd801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658',
+ name: 'endpoint-exceptionlist-windows-v1',
+ },
+ ],
+ version: '1.0.0',
+ },
+ },
+ },
+ },
+ },
+ ecs: {
+ version: '1.5.0',
+ },
+ elastic: {
+ agent: {
+ id: 'b2e88aea-2671-402a-828a-957526bac315',
+ },
+ },
+ file: {
+ path: 'C:\\Windows\\Temp\\mimikatz.exe',
+ size: 1263880,
+ created: '2020-05-19T07:50:06.0Z',
+ accessed: '2020-09-22T14:29:19.93531400Z',
+ mtime: '2020-09-22T14:29:03.6040000Z',
+ directory: 'C:\\Windows\\Temp',
+ hash: {
+ sha1: 'c9fb7f8a4c6b7b12b493a99a8dc6901d17867388',
+ sha256: 'cb1553a3c88817e4cc774a5a93f9158f6785bd3815447d04b6c3f4c2c4b21ed7',
+ md5: '465d5d850f54d9cde767bda90743df30',
+ },
+ Ext: {
+ code_signature: {
+ trusted: true,
+ subject_name: 'Open Source Developer, Benjamin Delpy',
+ exists: true,
+ status: 'trusted',
+ },
+ malware_classification: {
+ identifier: 'endpointpe-v4-model',
+ score: 0.99956864118576,
+ threshold: 0.71,
+ version: '0.0.0',
+ },
+ },
+ },
+ host: {
+ os: {
+ Ext: {
+ variant: 'Windows 10 Enterprise Evaluation',
+ },
+ kernel: '2004 (10.0.19041.388)',
+ name: 'Windows',
+ family: 'windows',
+ version: '2004 (10.0.19041.388)',
+ platform: 'windows',
+ full: 'Windows 10 Enterprise Evaluation 2004 (10.0.19041.388)',
+ },
+ },
+ event: {
+ kind: 'alert',
+ },
+ cluster_uuid: 'kLbKvSMcRiiFAR0t8LebDA',
+ cluster_name: 'elasticsearch',
+ };
+}
diff --git a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx
index 0e2855f055540a..993295746ea5ba 100644
--- a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx
+++ b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx
@@ -212,7 +212,7 @@ describe('TelemetryManagementSectionComponent', () => {
/>
);
try {
- const toggleExampleComponent = component.find('p > EuiLink[onClick]');
+ const toggleExampleComponent = component.find('FormattedMessage > EuiLink[onClick]').at(0);
const updatedView = toggleExampleComponent.simulate('click');
updatedView.find('OptInExampleFlyout');
updatedView.simulate('close');
@@ -221,6 +221,42 @@ describe('TelemetryManagementSectionComponent', () => {
}
});
+ it('shows the OptInSecurityExampleFlyout', () => {
+ const onQueryMatchChange = jest.fn();
+ const telemetryService = new TelemetryService({
+ config: {
+ enabled: true,
+ url: '',
+ banner: true,
+ allowChangingOptInStatus: true,
+ optIn: false,
+ optInStatusUrl: '',
+ sendUsageFrom: 'browser',
+ },
+ reportOptInStatusChange: false,
+ notifications: coreStart.notifications,
+ http: coreSetup.http,
+ });
+
+ const component = mountWithIntl(
+
+ );
+ try {
+ const toggleExampleComponent = component.find('FormattedMessage > EuiLink[onClick]').at(1);
+ const updatedView = toggleExampleComponent.simulate('click');
+ updatedView.find('OptInSecurityExampleFlyout');
+ updatedView.simulate('close');
+ } finally {
+ component.unmount();
+ }
+ });
+
it('toggles the OptIn button', async () => {
const onQueryMatchChange = jest.fn();
const telemetryService = new TelemetryService({
diff --git a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx
index 9ae0a3d12fbb52..822d8b49661c1d 100644
--- a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx
+++ b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.tsx
@@ -34,6 +34,7 @@ import { i18n } from '@kbn/i18n';
import { TelemetryPluginSetup } from 'src/plugins/telemetry/public';
import { PRIVACY_STATEMENT_URL } from '../../../telemetry/common/constants';
import { OptInExampleFlyout } from './opt_in_example_flyout';
+import { OptInSecurityExampleFlyout } from './opt_in_security_example_flyout';
import { LazyField } from '../../../advanced_settings/public';
import { ToastsStart } from '../../../../core/public';
@@ -53,6 +54,7 @@ interface Props {
interface State {
processing: boolean;
showExample: boolean;
+ showSecurityExample: boolean;
queryMatches: boolean | null;
enabled: boolean;
}
@@ -61,6 +63,7 @@ export class TelemetryManagementSection extends Component {
state: State = {
processing: false,
showExample: false,
+ showSecurityExample: false,
queryMatches: null,
enabled: this.props.telemetryService.getIsOptedIn() || false,
};
@@ -87,7 +90,7 @@ export class TelemetryManagementSection extends Component {
render() {
const { telemetryService } = this.props;
- const { showExample, queryMatches, enabled, processing } = this.state;
+ const { showExample, showSecurityExample, queryMatches, enabled, processing } = this.state;
if (!telemetryService.getCanChangeOptInStatus()) {
return null;
@@ -105,6 +108,7 @@ export class TelemetryManagementSection extends Component {
onClose={this.toggleExample}
/>
)}
+ {showSecurityExample && }
@@ -197,12 +201,25 @@ export class TelemetryManagementSection extends Component {
/>
-
-
-
+
+
+
+ ),
+ endpointSecurityData: (
+
+
+
+ ),
+ }}
+ />
);
@@ -245,6 +262,12 @@ export class TelemetryManagementSection extends Component {
showExample: !this.state.showExample,
});
};
+
+ toggleSecurityExample = () => {
+ this.setState({
+ showSecurityExample: !this.state.showSecurityExample,
+ });
+ };
}
// required for lazy loading
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index bd9a66b48f6332..e344d18213ae59 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -2962,7 +2962,6 @@
"telemetry.provideUsageStatisticsAriaName": "使用統計を提供",
"telemetry.provideUsageStatisticsTitle": "使用統計を提供",
"telemetry.readOurUsageDataPrivacyStatementLinkText": "プライバシーポリシー",
- "telemetry.seeExampleOfWhatWeCollectLinkText": "収集されるデータの例を見る",
"telemetry.telemetryBannerDescription": "Elastic Stackの改善にご協力ください使用状況データの収集は現在無効です。使用状況データの収集を有効にすると、製品とサービスを管理して改善することができます。詳細については、{privacyStatementLink}をご覧ください。",
"telemetry.telemetryConfigAndLinkDescription": "使用状況データの収集を有効にすると、製品とサービスを管理して改善することができます。詳細については、{privacyStatementLink}をご覧ください。",
"telemetry.telemetryConfigDescription": "基本的な機能の利用状況に関する統計情報を提供して、Elastic Stack の改善にご協力ください。このデータは Elastic 社外と共有されません。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index 3ce92782340058..ab7b558afbbf21 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -2963,7 +2963,6 @@
"telemetry.provideUsageStatisticsAriaName": "提供使用情况统计",
"telemetry.provideUsageStatisticsTitle": "提供使用情况统计",
"telemetry.readOurUsageDataPrivacyStatementLinkText": "隐私声明",
- "telemetry.seeExampleOfWhatWeCollectLinkText": "查看我们收集的内容示例",
"telemetry.telemetryBannerDescription": "想帮助我们改进 Elastic Stack?数据使用情况收集当前已禁用。启用数据使用情况收集可帮助我们管理并改善产品和服务。有关详情,请参阅我们的{privacyStatementLink}。",
"telemetry.telemetryConfigAndLinkDescription": "启用使用情况数据收集可帮助我们管理并改善产品和服务。有关更多详情,请参阅我们的{privacyStatementLink}。",
"telemetry.telemetryConfigDescription": "通过提供基本功能的使用情况统计信息,来帮助我们改进 Elastic Stack。我们不会在 Elastic 之外共享此数据。",
From 94ef651d7b03f0d31c1b704a66cdb9191e094d88 Mon Sep 17 00:00:00 2001
From: PavithraCP <31021423+PavithraCP@users.noreply.github.com>
Date: Fri, 2 Oct 2020 07:16:46 -0400
Subject: [PATCH 09/21] [Lens]Do not enable histogram mode for multiple
un-stacked bar series (#78525)
---
.../xy_visualization/expression.test.tsx | 50 ++++++++++++++++++-
.../public/xy_visualization/expression.tsx | 14 +++++-
2 files changed, 61 insertions(+), 3 deletions(-)
diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx
index 3bd6cc73d6320a..5fc89d831a961c 100644
--- a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx
+++ b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx
@@ -896,7 +896,12 @@ describe('xy_expression', () => {
test('it applies histogram mode to the series for single series', () => {
const { data, args } = sampleArgs();
- const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true };
+ const firstLayer: LayerArgs = {
+ ...args.layers[0],
+ accessors: ['b'],
+ seriesType: 'bar',
+ isHistogram: true,
+ };
delete firstLayer.splitAccessor;
const component = shallow(
{
/>
);
expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true);
- expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(true);
+ });
+
+ test('it does not apply histogram mode to more than one bar series for unstacked bar chart', () => {
+ const { data, args } = sampleArgs();
+ const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true };
+ delete firstLayer.splitAccessor;
+ const component = shallow(
+
+ );
+ expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false);
+ expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false);
+ });
+
+ test('it applies histogram mode to more than one the series for unstacked line/area chart', () => {
+ const { data, args } = sampleArgs();
+ const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'line', isHistogram: true };
+ delete firstLayer.splitAccessor;
+ const secondLayer: LayerArgs = { ...args.layers[0], seriesType: 'line', isHistogram: true };
+ delete secondLayer.splitAccessor;
+ const component = shallow(
+
+ );
+ expect(component.find(LineSeries).at(0).prop('enableHistogramMode')).toEqual(true);
+ expect(component.find(LineSeries).at(1).prop('enableHistogramMode')).toEqual(true);
});
test('it applies histogram mode to the series for stacked series', () => {
diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx
index f36525a9a0b146..a59739ec78f7fb 100644
--- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx
+++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx
@@ -299,6 +299,13 @@ export function XYChart({
yRight: true,
};
+ const filteredBarLayers = filteredLayers.filter((layer) => layer.seriesType.includes('bar'));
+
+ const chartHasMoreThanOneBarSeries =
+ filteredBarLayers.length > 1 ||
+ filteredBarLayers.some((layer) => layer.accessors.length > 1) ||
+ filteredBarLayers.some((layer) => layer.splitAccessor);
+
function calculateMinInterval() {
// check all the tables to see if all of the rows have the same timestamp
// that would mean that chart will draw a single bar
@@ -599,7 +606,12 @@ export function XYChart({
groupId: yAxesConfiguration.find((axisConfiguration) =>
axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor)
)?.groupId,
- enableHistogramMode: isHistogram && (seriesType.includes('stacked') || !splitAccessor),
+ enableHistogramMode:
+ isHistogram &&
+ (seriesType.includes('stacked') || !splitAccessor) &&
+ (seriesType.includes('stacked') ||
+ !seriesType.includes('bar') ||
+ !chartHasMoreThanOneBarSeries),
stackMode: seriesType.includes('percentage') ? StackMode.Percentage : undefined,
timeZone,
areaSeriesStyle: {
From 70dac72ad311d595b0f9f6bc871f718656296da6 Mon Sep 17 00:00:00 2001
From: Tim Roes
Date: Fri, 2 Oct 2020 13:39:38 +0200
Subject: [PATCH 10/21] Move legacy plugins to appropriate teams (#79078)
* Move legacy plugins to appropriate teams
* More cleanup
---
.github/CODEOWNERS | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index fc9c55e7868f42..d1cf0300b9e17b 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -17,6 +17,7 @@
/src/plugins/input_control_vis/ @elastic/kibana-app
/src/plugins/management/ @elastic/kibana-app
/src/plugins/kibana_legacy/ @elastic/kibana-app
+/src/plugins/timelion/ @elastic/kibana-app
/src/plugins/vis_default_editor/ @elastic/kibana-app
/src/plugins/vis_type_markdown/ @elastic/kibana-app
/src/plugins/vis_type_metric/ @elastic/kibana-app
@@ -30,32 +31,23 @@
/src/plugins/visualize/ @elastic/kibana-app
/src/plugins/visualizations/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app
-#CC# /src/plugins/vis_type @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/common/utils @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/migrations @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public/dashboard/ @elastic/kibana-app
-#CC# /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public/discover/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app
-#CC# /src/legacy/core_plugins/console_legacy @elastic/kibana-app
#CC# /src/legacy/core_plugins/input_control_vis @elastic/kibana-app
#CC# /src/legacy/core_plugins/timelion @elastic/kibana-app
#CC# /src/legacy/core_plugins/vis_type_tagcloud @elastic/kibana-app
#CC# /src/legacy/core_plugins/vis_type_vega @elastic/kibana-app
#CC# /src/legacy/core_plugins/vis_type_vislib/ @elastic/kibana-app
-#CC# /src/legacy/server/sample_data/ @elastic/kibana-app
#CC# /src/legacy/server/url_shortening/ @elastic/kibana-app
#CC# /src/legacy/ui/public/state_management @elastic/kibana-app
-#CC# /src/plugins/charts/public/static/color_maps @elastic/kibana-app
#CC# /src/plugins/index_pattern_management/public @elastic/kibana-app
-#CC# /src/plugins/input_control_vis/ @elastic/kibana-app
-#CC# /src/plugins/kibana_legacy/ @elastic/kibana-app
-#CC# /src/plugins/timelion @elastic/kibana-app
#CC# /x-pack/legacy/plugins/dashboard_mode/ @elastic/kibana-app
#CC# /x-pack/plugins/dashboard_mode @elastic/kibana-app
-#CC# /x-pack/plugins/lens/ @elastic/kibana-app
# App Architecture
/examples/bfetch_explorer/ @elastic/kibana-app-arch
@@ -147,6 +139,7 @@
/src/plugins/home/server/services/ @elastic/kibana-core-ui
/x-pack/plugins/global_search_bar/ @elastic/kibana-core-ui
#CC# /src/legacy/core_plugins/newsfeed @elastic/kibana-core-ui
+#CC# /src/legacy/server/sample_data/ @elastic/kibana-core-ui
#CC# /src/plugins/newsfeed @elastic/kibana-core-ui
#CC# /src/plugins/home/public @elastic/kibana-core-ui
#CC# /src/plugins/home/server/services/ @elastic/kibana-core-ui
@@ -351,6 +344,8 @@ x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kib
/x-pack/plugins/ingest_pipelines/ @elastic/es-ui
/packages/kbn-ace/ @elastic/es-ui
/packages/kbn-monaco/ @elastic/es-ui
+#CC# /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/es-ui
+#CC# /src/legacy/core_plugins/console_legacy @elastic/es-ui
#CC# /x-pack/legacy/plugins/rollup/ @elastic/es-ui
#CC# /x-pack/legacy/server/lib/create_router/ @elastic/es-ui
#CC# /x-pack/legacy/server/lib/check_license/ @elastic/es-ui
From 09b0b6630abd6fe03c0be504a8e7a9ee5fc83e24 Mon Sep 17 00:00:00 2001
From: ymao1
Date: Fri, 2 Oct 2020 07:40:47 -0400
Subject: [PATCH 11/21] Rearranged PagerDuty action params so non-optional
params are at the top (#79026)
---
.../pagerduty/pagerduty_params.tsx | 47 ++++++++++---------
1 file changed, 25 insertions(+), 22 deletions(-)
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx
index 39800865ed761b..32f16760dd461e 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { Fragment } from 'react';
-import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect } from '@elastic/eui';
+import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect, EuiSpacer } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { ActionParamsProps } from '../../../../types';
import { PagerDutyActionParams } from '.././types';
@@ -143,6 +143,29 @@ const PagerDutyParamsFields: React.FunctionComponent
+
+
+
+
-
-
+
{this.getFeatureListLabel(this.state.selectedBasePrivilege.length > 0)}
@@ -338,7 +287,7 @@ export class PrivilegeSpaceForm extends Component {
buttonText = (
);
}
diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx
index 64b7fe3e2e3a90..6bb9840fd343c5 100644
--- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx
+++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx
@@ -23,7 +23,6 @@ import { FormattedMessage } from '@kbn/i18n/react';
import React, { Component } from 'react';
import { Space, getSpaceColor } from '../../../../../../../../spaces/public';
import { FeaturesPrivileges, Role, copyRole } from '../../../../../../../common/model';
-import { SpacesPopoverList } from '../../../spaces_popover_list';
import { PrivilegeDisplay } from './privilege_display';
import { isGlobalPrivilegeDefinition } from '../../../privilege_utils';
import { PrivilegeFormCalculator } from '../privilege_form_calculator';
@@ -118,19 +117,7 @@ export class PrivilegeSpaceTable extends Component {
const displayedSpaces = isExpanded ? spaces : spaces.slice(0, SPACES_DISPLAY_COUNT);
let button = null;
- if (record.isGlobal) {
- button = (
- s.id !== '*')}
- buttonText={i18n.translate(
- 'xpack.security.management.editRole.spacePrivilegeTable.showAllSpacesLink',
- {
- defaultMessage: 'show spaces',
- }
- )}
- />
- );
- } else if (spaces.length > displayedSpaces.length) {
+ if (spaces.length > displayedSpaces.length) {
button = (
{
name: i18n.translate(
'xpack.security.management.editRole.spaceAwarePrivilegeForm.globalSpacesName',
{
- defaultMessage: '* Global (all spaces)',
+ defaultMessage: '* All Spaces',
}
),
color: '#D3DAE6',
@@ -198,7 +198,7 @@ export class SpaceAwarePrivilegeSection extends Component {
>
);
diff --git a/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx
index 911a6b78a49449..95295b8e64732a 100644
--- a/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx
+++ b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx
@@ -58,7 +58,7 @@ export class CustomizeSpace extends Component {
};
return (
-
+
diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap
index ee1eb7c5e9aba7..063a34091c4e74 100644
--- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap
+++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap
@@ -2,10 +2,8 @@
exports[`EnabledFeatures renders as expected 1`] = `
{
return (
-
-
- hide
-
-
diff --git a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx
index 0b8085ff1ad168..576881398a63c7 100644
--- a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx
+++ b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx
@@ -4,14 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { EuiLink } from '@elastic/eui';
import React from 'react';
import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers';
import { SectionPanel } from './section_panel';
test('it renders without blowing up', () => {
const wrapper = shallowWithIntl(
-
+
child
);
@@ -19,9 +18,9 @@ test('it renders without blowing up', () => {
expect(wrapper).toMatchSnapshot();
});
-test('it renders children by default', () => {
+test('it renders children', () => {
const wrapper = mountWithIntl(
-
+
child 1
child 2
@@ -30,19 +29,3 @@ test('it renders children by default', () => {
expect(wrapper.find(SectionPanel)).toHaveLength(1);
expect(wrapper.find('.child')).toHaveLength(2);
});
-
-test('it hides children when the "hide" link is clicked', () => {
- const wrapper = mountWithIntl(
-
- child 1
- child 2
-
- );
-
- expect(wrapper.find(SectionPanel)).toHaveLength(1);
- expect(wrapper.find('.child')).toHaveLength(2);
-
- wrapper.find(EuiLink).simulate('click');
-
- expect(wrapper.find('.child')).toHaveLength(0);
-});
diff --git a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx
index a6d25511acba67..492932aa957410 100644
--- a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx
+++ b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx
@@ -8,39 +8,20 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
- EuiLink,
EuiPanel,
EuiSpacer,
EuiTitle,
IconType,
} from '@elastic/eui';
-import { i18n } from '@kbn/i18n';
import React, { Component, Fragment, ReactNode } from 'react';
interface Props {
iconType?: IconType;
title: string | ReactNode;
description: string;
- collapsible: boolean;
- initiallyCollapsed?: boolean;
}
-interface State {
- collapsed: boolean;
-}
-
-export class SectionPanel extends Component {
- public state = {
- collapsed: false,
- };
-
- constructor(props: Props) {
- super(props);
- this.state = {
- collapsed: props.initiallyCollapsed || false,
- };
- }
-
+export class SectionPanel extends Component {
public render() {
return (
@@ -51,30 +32,6 @@ export class SectionPanel extends Component {
}
public getTitle = () => {
- const showLinkText = i18n.translate('xpack.spaces.management.collapsiblePanel.showLinkText', {
- defaultMessage: 'show',
- });
-
- const hideLinkText = i18n.translate('xpack.spaces.management.collapsiblePanel.hideLinkText', {
- defaultMessage: 'hide',
- });
-
- const showLinkDescription = i18n.translate(
- 'xpack.spaces.management.collapsiblePanel.showLinkDescription',
- {
- defaultMessage: 'show {title}',
- values: { title: this.props.description },
- }
- );
-
- const hideLinkDescription = i18n.translate(
- 'xpack.spaces.management.collapsiblePanel.hideLinkDescription',
- {
- defaultMessage: 'hide {title}',
- values: { title: this.props.description },
- }
- );
-
return (
@@ -93,26 +50,11 @@ export class SectionPanel extends Component {
- {this.props.collapsible && (
-
-
- {this.state.collapsed ? showLinkText : hideLinkText}
-
-
- )}
);
};
public getForm = () => {
- if (this.state.collapsed) {
- return null;
- }
-
return (
@@ -120,10 +62,4 @@ export class SectionPanel extends Component {
);
};
-
- public toggleCollapsed = () => {
- this.setState({
- collapsed: !this.state.collapsed,
- });
- };
}
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index e344d18213ae59..f144cbd3873f76 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -14302,8 +14302,6 @@
"xpack.security.management.editRole.elasticSearchPrivileges.manageRoleActionsDescription": "このロールがクラスターに対して実行できる操作を管理します。 ",
"xpack.security.management.editRole.elasticSearchPrivileges.runAsPrivilegesTitle": "権限として実行",
"xpack.security.management.editRole.featureTable.customizeSubFeaturePrivilegesSwitchLabel": "サブ機能権限をカスタマイズする",
- "xpack.security.management.editRole.featureTable.enabledRoleFeaturesEnabledColumnTitle": "権限",
- "xpack.security.management.editRole.featureTable.enabledRoleFeaturesFeatureColumnTitle": "機能",
"xpack.security.management.editRole.featureTable.privilegeCustomizationTooltip": "機能でサブ機能の権限がカスタマイズされています。この行を展開すると詳細が表示されます。",
"xpack.security.management.editRole.indexPrivilegeForm.deleteSpacePrivilegeAriaLabel": "インデックスの権限を削除",
"xpack.security.management.editRole.indexPrivilegeForm.grantedDocumentsQueryFormRowLabel": "提供されたドキュメントのクエリ",
@@ -14345,35 +14343,24 @@
"xpack.security.management.editRole.spaceAwarePrivilegeForm.howToViewAllAvailableSpacesDescription": "利用可能なすべてのスペースを表示する権限がありません。",
"xpack.security.management.editRole.spaceAwarePrivilegeForm.insufficientPrivilegesDescription": "権限が不十分です",
"xpack.security.management.editRole.spaceAwarePrivilegeForm.kibanaAdminTitle": "kibana_admin",
- "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDetails": "選択されたスペースの全機能への完全アクセスを許可します。",
- "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDisplay": "すべて",
- "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDropdownDisplay": "すべて",
"xpack.security.management.editRole.spacePrivilegeForm.cancelButton": "キャンセル",
"xpack.security.management.editRole.spacePrivilegeForm.customizeFeaturePrivilegeDescription": "機能ごとに権限のレベルを上げます。機能によってはスペースごとに非表示になっているか、グローバルスペース権限による影響を受けているものもあります。",
"xpack.security.management.editRole.spacePrivilegeForm.customizeFeaturePrivileges": "機能ごとにカスタマイズ",
- "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDetails": "選択されたスペースの機能ごとにアクセスをカスタマイズします",
- "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDisplay": "カスタム",
- "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDropdownDisplay": "カスタム",
"xpack.security.management.editRole.spacePrivilegeForm.featurePrivilegeSummaryDescription": "機能によってはスペースごとに非表示になっているか、グローバルスペース権限による影響を受けているものもあります。",
"xpack.security.management.editRole.spacePrivilegeForm.globalPrivilegeNotice": "これらの権限はすべての現在および未来のスペースに適用されます。",
"xpack.security.management.editRole.spacePrivilegeForm.globalPrivilegeWarning": "グローバル権限の作成は他のスペース権限に影響を与える可能性があります。",
"xpack.security.management.editRole.spacePrivilegeForm.modalTitle": "スペース権限",
"xpack.security.management.editRole.spacePrivilegeForm.privilegeSelectorFormLabel": "権限",
- "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDetails": "選択されたスペースの全機能への読み込み専用アクセスを許可します。",
- "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDisplay": "読み込み",
- "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDropdownDisplay": "読み込み",
"xpack.security.management.editRole.spacePrivilegeForm.spaceSelectorFormLabel": "スペース",
"xpack.security.management.editRole.spacePrivilegeForm.summaryOfFeaturePrivileges": "機能権限のサマリー",
"xpack.security.management.editRole.spacePrivilegeForm.supersededWarning": "宣言された権限は、構成済みグローバル権限よりも許容度が低くなります。権限サマリーを表示すると有効な権限がわかります。",
"xpack.security.management.editRole.spacePrivilegeForm.supersededWarningTitle": "グローバル権限に置き換え",
"xpack.security.management.editRole.spacePrivilegeMatrix.globalSpaceName": "グローバル",
- "xpack.security.management.editRole.spacePrivilegeMatrix.showAllSpacesLink": "(すべてのスペース)",
"xpack.security.management.editRole.spacePrivilegeMatrix.showNMoreSpacesLink": "他 {count} 件",
"xpack.security.management.editRole.spacePrivilegeSection.addSpacePrivilegeButton": "スペース権限を追加",
"xpack.security.management.editRole.spacePrivilegeSection.noAccessToKibanaTitle": "このロールは Kibana へのアクセスを許可しません",
"xpack.security.management.editRole.spacePrivilegeTable.deletePrivilegesLabel": "次のスペースの権限を削除: {spaceNames}",
"xpack.security.management.editRole.spacePrivilegeTable.editPrivilegesLabel": "次のスペースの権限を編集: {spaceNames}",
- "xpack.security.management.editRole.spacePrivilegeTable.showAllSpacesLink": "スペースを表示",
"xpack.security.management.editRole.spacePrivilegeTable.showLessSpacesLink": "縮小表示",
"xpack.security.management.editRole.spacePrivilegeTable.showNMoreSpacesLink": "他 {count} 件",
"xpack.security.management.editRole.spacePrivilegeTable.supersededPrivilegeWarning": "権限は、構成されたグローバル権限に置き換わります。権限サマリーを表示すると有効な権限がわかります。",
@@ -17360,10 +17347,6 @@
"xpack.spaces.management.advancedSettingsSubtitle.applyingSettingsOnPageToSpaceDescription": "このページの設定は、別途指定されていない限り {spaceName}’スペースに適用されます。’",
"xpack.spaces.management.advancedSettingsTitle.settingsTitle": "設定",
"xpack.spaces.management.breadcrumb": "スペース",
- "xpack.spaces.management.collapsiblePanel.hideLinkDescription": "{title} を非表示",
- "xpack.spaces.management.collapsiblePanel.hideLinkText": "非表示",
- "xpack.spaces.management.collapsiblePanel.showLinkDescription": "{title} を表示",
- "xpack.spaces.management.collapsiblePanel.showLinkText": "表示",
"xpack.spaces.management.confirmAlterActiveSpaceModal.cancelButton": "キャンセル",
"xpack.spaces.management.confirmAlterActiveSpaceModal.reloadWarningMessage": "このスペースで表示される機能を更新しました。保存後にページが更新されます。",
"xpack.spaces.management.confirmAlterActiveSpaceModal.title": "スペースの更新の確認",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index ab7b558afbbf21..c4a599fa35e7fa 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -14311,8 +14311,6 @@
"xpack.security.management.editRole.elasticSearchPrivileges.manageRoleActionsDescription": "管理此角色可以对您的集群执行的操作。 ",
"xpack.security.management.editRole.elasticSearchPrivileges.runAsPrivilegesTitle": "运行身份权限",
"xpack.security.management.editRole.featureTable.customizeSubFeaturePrivilegesSwitchLabel": "定制子功能权限",
- "xpack.security.management.editRole.featureTable.enabledRoleFeaturesEnabledColumnTitle": "权限",
- "xpack.security.management.editRole.featureTable.enabledRoleFeaturesFeatureColumnTitle": "功能",
"xpack.security.management.editRole.featureTable.privilegeCustomizationTooltip": "功能已定制子功能权限。展开此行以了解更多信息。",
"xpack.security.management.editRole.indexPrivilegeForm.deleteSpacePrivilegeAriaLabel": "删除索引权限",
"xpack.security.management.editRole.indexPrivilegeForm.grantedDocumentsQueryFormRowLabel": "已授权文档查询",
@@ -14354,35 +14352,24 @@
"xpack.security.management.editRole.spaceAwarePrivilegeForm.howToViewAllAvailableSpacesDescription": "您无权查看所有可用工作区。",
"xpack.security.management.editRole.spaceAwarePrivilegeForm.insufficientPrivilegesDescription": "权限不足",
"xpack.security.management.editRole.spaceAwarePrivilegeForm.kibanaAdminTitle": "kibana_admin",
- "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDetails": "授予对选定工作区所有功能的完全访问权限。",
- "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDisplay": "全部",
- "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDropdownDisplay": "全部",
"xpack.security.management.editRole.spacePrivilegeForm.cancelButton": "取消",
"xpack.security.management.editRole.spacePrivilegeForm.customizeFeaturePrivilegeDescription": "按功能提高权限级别。某些功能可能被工作区隐藏或受全局工作区权限影响。",
"xpack.security.management.editRole.spacePrivilegeForm.customizeFeaturePrivileges": "按功能定制",
- "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDetails": "在选定工作区中按功能定制访问权限。",
- "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDisplay": "定制",
- "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDropdownDisplay": "定制",
"xpack.security.management.editRole.spacePrivilegeForm.featurePrivilegeSummaryDescription": "某些功能可能被工作区隐藏或受全局工作区权限影响。",
"xpack.security.management.editRole.spacePrivilegeForm.globalPrivilegeNotice": "这些权限将应用到所有当前和未来工作区。",
"xpack.security.management.editRole.spacePrivilegeForm.globalPrivilegeWarning": "创建全局权限可能会影响您的其他工作区权限。",
"xpack.security.management.editRole.spacePrivilegeForm.modalTitle": "工作区权限",
"xpack.security.management.editRole.spacePrivilegeForm.privilegeSelectorFormLabel": "权限",
- "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDetails": "授予对选定工作区所有功能的只读访问权限。",
- "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDisplay": "读取",
- "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDropdownDisplay": "读取",
"xpack.security.management.editRole.spacePrivilegeForm.spaceSelectorFormLabel": "工作区",
"xpack.security.management.editRole.spacePrivilegeForm.summaryOfFeaturePrivileges": "功能权限的摘要",
"xpack.security.management.editRole.spacePrivilegeForm.supersededWarning": "声明的权限相对配置的全局权限有较小的宽容度。查看权限摘要以查看有效的权限。",
"xpack.security.management.editRole.spacePrivilegeForm.supersededWarningTitle": "已由全局权限取代",
"xpack.security.management.editRole.spacePrivilegeMatrix.globalSpaceName": "全局",
- "xpack.security.management.editRole.spacePrivilegeMatrix.showAllSpacesLink": "(所有工作区)",
"xpack.security.management.editRole.spacePrivilegeMatrix.showNMoreSpacesLink": "另外 {count} 个",
"xpack.security.management.editRole.spacePrivilegeSection.addSpacePrivilegeButton": "添加工作区权限",
"xpack.security.management.editRole.spacePrivilegeSection.noAccessToKibanaTitle": "此角色未授予对 Kibana 的访问权限",
"xpack.security.management.editRole.spacePrivilegeTable.deletePrivilegesLabel": "删除以下工作区的权限:{spaceNames}。",
"xpack.security.management.editRole.spacePrivilegeTable.editPrivilegesLabel": "编辑以下工作区的权限:{spaceNames}。",
- "xpack.security.management.editRole.spacePrivilegeTable.showAllSpacesLink": "显示工作区",
"xpack.security.management.editRole.spacePrivilegeTable.showLessSpacesLink": "显示更少",
"xpack.security.management.editRole.spacePrivilegeTable.showNMoreSpacesLink": "另外 {count} 个",
"xpack.security.management.editRole.spacePrivilegeTable.supersededPrivilegeWarning": "权限已由配置的全局权限取代。查看权限摘要以查看有效的权限。",
@@ -17370,10 +17357,6 @@
"xpack.spaces.management.advancedSettingsSubtitle.applyingSettingsOnPageToSpaceDescription": "除非已指定,否则此页面上的设置适用于 {spaceName} 空间。",
"xpack.spaces.management.advancedSettingsTitle.settingsTitle": "设置",
"xpack.spaces.management.breadcrumb": "工作区",
- "xpack.spaces.management.collapsiblePanel.hideLinkDescription": "隐藏 {title}",
- "xpack.spaces.management.collapsiblePanel.hideLinkText": "隐藏",
- "xpack.spaces.management.collapsiblePanel.showLinkDescription": "显示 {title}",
- "xpack.spaces.management.collapsiblePanel.showLinkText": "显示",
"xpack.spaces.management.confirmAlterActiveSpaceModal.cancelButton": "取消",
"xpack.spaces.management.confirmAlterActiveSpaceModal.reloadWarningMessage": "您已更新此工作区中的可见功能。保存后,您的页面将重新加载。",
"xpack.spaces.management.confirmAlterActiveSpaceModal.title": "确认更新工作区",
diff --git a/x-pack/test/functional/page_objects/security_page.ts b/x-pack/test/functional/page_objects/security_page.ts
index 3ce8a0e681d69d..77457ad94cf88c 100644
--- a/x-pack/test/functional/page_objects/security_page.ts
+++ b/x-pack/test/functional/page_objects/security_page.ts
@@ -429,10 +429,7 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider
const globalSpaceOption = await find.byCssSelector(`#spaceOption_\\*`);
await globalSpaceOption.click();
- await testSubjects.click('basePrivilegeComboBox');
-
- const privilegeOption = await find.byCssSelector(`#basePrivilege_${privilegeName}`);
- await privilegeOption.click();
+ await testSubjects.click(`basePrivilege_${privilegeName}`);
await testSubjects.click('createSpacePrivilegeButton');
}
From 79eb9b7b7a47c4859020b0b84db911143fbffbb4 Mon Sep 17 00:00:00 2001
From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com>
Date: Fri, 2 Oct 2020 08:53:55 -0400
Subject: [PATCH 15/21] Use `process.executable` instead of `process.path`
(#79216)
---
.../common/endpoint/schema/trusted_apps.test.ts | 4 ++--
.../common/endpoint/schema/trusted_apps.ts | 2 +-
.../common/endpoint/types/trusted_apps.ts | 2 +-
.../components/condition_entry.tsx | 2 +-
.../components/trusted_app_card/index.stories.tsx | 4 ++--
.../routes/trusted_apps/trusted_apps.test.ts | 14 +++++++-------
6 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts
index 13a3fb96e10f7e..ef1d9a99b0aebe 100644
--- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts
@@ -76,7 +76,7 @@ describe('When invoking Trusted Apps Schema', () => {
os: 'windows',
entries: [
{
- field: 'process.path.text',
+ field: 'process.executable.text',
type: 'match',
operator: 'included',
value: 'c:/programs files/Anti-Virus',
@@ -204,7 +204,7 @@ describe('When invoking Trusted Apps Schema', () => {
field: 'process.hash.*',
value: 'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476',
},
- { field: 'process.path.text', value: '/tmp/dir1' },
+ { field: 'process.executable.text', value: '/tmp/dir1' },
].forEach((partialEntry) => {
const bodyMsg3 = {
...getCreateTrustedAppItem(),
diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts
index 912468b52adc04..25456115b37133 100644
--- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts
@@ -35,7 +35,7 @@ export const PostTrustedAppCreateRequestSchema = {
schema.object({
field: schema.oneOf([
schema.literal('process.hash.*'),
- schema.literal('process.path.text'),
+ schema.literal('process.executable.text'),
]),
type: schema.literal('match'),
operator: schema.literal('included'),
diff --git a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts
index c0afe3b612d826..75e0347b100780 100644
--- a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts
+++ b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts
@@ -33,7 +33,7 @@ export interface PostTrustedAppCreateResponse {
}
export interface MacosLinuxConditionEntry {
- field: 'process.hash.*' | 'process.path.text';
+ field: 'process.hash.*' | 'process.executable.text';
type: 'match';
operator: 'included';
value: string;
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx
index 7f7eae18b08163..7d30e81898cf22 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx
@@ -83,7 +83,7 @@ export const ConditionEntry = memo(
'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.path',
{ defaultMessage: 'Path' }
),
- value: 'process.path.text',
+ value: 'process.executable.text',
},
];
}, []);
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx
index 713e5e7095e12a..4b64030a702c55 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx
@@ -30,7 +30,7 @@ storiesOf('TrustedApps|TrustedAppCard', module)
trustedApp.created_at = '2020-09-17T14:52:33.899Z';
trustedApp.entries = [
{
- field: 'process.path.text',
+ field: 'process.executable.text',
operator: 'included',
type: 'match',
value: '/some/path/on/file/system',
@@ -44,7 +44,7 @@ storiesOf('TrustedApps|TrustedAppCard', module)
trustedApp.created_at = '2020-09-17T14:52:33.899Z';
trustedApp.entries = [
{
- field: 'process.path.text',
+ field: 'process.executable.text',
operator: 'included',
type: 'match',
value: '/some/path/on/file/system',
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts
index 98c9b79f32d6b3..9e9a35ea353185 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts
@@ -240,7 +240,7 @@ describe('when invoking endpoint trusted apps route handlers', () => {
os: 'windows',
entries: [
{
- field: 'process.path.text',
+ field: 'process.executable.text',
type: 'match',
operator: 'included',
value: 'c:/programs files/Anti-Virus',
@@ -293,7 +293,7 @@ describe('when invoking endpoint trusted apps route handlers', () => {
description: 'this one is ok',
entries: [
{
- field: 'process.path.text',
+ field: 'process.executable.text',
operator: 'included',
type: 'match',
value: 'c:/programs files/Anti-Virus',
@@ -320,7 +320,7 @@ describe('when invoking endpoint trusted apps route handlers', () => {
description: 'this one is ok',
entries: [
{
- field: 'process.path.text',
+ field: 'process.executable.text',
operator: 'included',
type: 'match',
value: 'c:/programs files/Anti-Virus',
@@ -357,7 +357,7 @@ describe('when invoking endpoint trusted apps route handlers', () => {
it('should trim condition entry values', async () => {
const newTrustedApp = createNewTrustedAppBody();
newTrustedApp.entries.push({
- field: 'process.path.text',
+ field: 'process.executable.text',
value: '\n some value \r\n ',
operator: 'included',
type: 'match',
@@ -366,13 +366,13 @@ describe('when invoking endpoint trusted apps route handlers', () => {
await routeHandler(context, request, response);
expect(exceptionsListClient.createExceptionListItem.mock.calls[0][0].entries).toEqual([
{
- field: 'process.path.text',
+ field: 'process.executable.text',
operator: 'included',
type: 'match',
value: 'c:/programs files/Anti-Virus',
},
{
- field: 'process.path.text',
+ field: 'process.executable.text',
value: 'some value',
operator: 'included',
type: 'match',
@@ -392,7 +392,7 @@ describe('when invoking endpoint trusted apps route handlers', () => {
await routeHandler(context, request, response);
expect(exceptionsListClient.createExceptionListItem.mock.calls[0][0].entries).toEqual([
{
- field: 'process.path.text',
+ field: 'process.executable.text',
operator: 'included',
type: 'match',
value: 'c:/programs files/Anti-Virus',
From 0628cfecf4ef2d0bab6525702d5a98404cd37509 Mon Sep 17 00:00:00 2001
From: Tiago Costa
Date: Fri, 2 Oct 2020 14:30:32 +0100
Subject: [PATCH 16/21] skip flaky suite (#79249)
---
.../spaces_only/tests/alerting/execution_status.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts
index ac63fe8faadc7c..1c2e51637fb41a 100644
--- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts
+++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts
@@ -19,7 +19,8 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context';
export default function executionStatusAlertTests({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
- describe('executionStatus', () => {
+ // FLAKY: https://github.com/elastic/kibana/issues/79249
+ describe.skip('executionStatus', () => {
const objectRemover = new ObjectRemover(supertest);
after(async () => await objectRemover.removeAll());
From a7d9e2f481c0d15b8fe6d8cedef881a310c23be7 Mon Sep 17 00:00:00 2001
From: Ryan Keairns
Date: Fri, 2 Oct 2020 08:39:37 -0500
Subject: [PATCH 17/21] Improved empty state for nav search (#79123)
* Improved empty state for nav search
* Updates tests to include required props
* Update empty state text
---
...tration_product_no_search_results_dark.svg | 1 +
...ration_product_no_search_results_light.svg | 1 +
.../public/components/search_bar.test.tsx | 21 +++++++-
.../public/components/search_bar.tsx | 51 ++++++++++++-------
.../global_search_bar/public/plugin.tsx | 20 ++++++--
5 files changed, 72 insertions(+), 22 deletions(-)
create mode 100644 x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_dark.svg
create mode 100644 x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_light.svg
diff --git a/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_dark.svg b/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_dark.svg
new file mode 100644
index 00000000000000..3a87f06b7bcc85
--- /dev/null
+++ b/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_light.svg b/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_light.svg
new file mode 100644
index 00000000000000..ac5298be17ccaf
--- /dev/null
+++ b/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx
index 11fbc7931e6201..6fad3335c5efce 100644
--- a/x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx
+++ b/x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx
@@ -8,6 +8,7 @@ import React from 'react';
import { wait } from '@testing-library/react';
import { of } from 'rxjs';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
+import { httpServiceMock, uiSettingsServiceMock } from '../../../../../src/core/public/mocks';
import {
GlobalSearchBatchedResults,
GlobalSearchPluginStart,
@@ -47,6 +48,10 @@ const getSearchProps: any = (component: any) => component.find('EuiFieldSearch')
describe('SearchBar', () => {
let searchService: GlobalSearchPluginStart;
let findSpy: jest.SpyInstance;
+ const http = httpServiceMock.createSetupContract({ basePath: '/test' });
+ const basePathUrl = http.basePath.prepend('/plugins/globalSearchBar/assets/');
+ const uiSettings = uiSettingsServiceMock.createStartContract();
+ const darkMode = uiSettings.get('theme:darkMode');
beforeEach(() => {
searchService = globalSearchPluginMock.createStartContract();
@@ -66,7 +71,12 @@ describe('SearchBar', () => {
.mockReturnValueOnce(of(createBatch('Discover', { id: 'My Dashboard', type: 'test' })));
const component = mountWithIntl(
-
+
);
expect(findSpy).toHaveBeenCalledTimes(0);
@@ -85,7 +95,14 @@ describe('SearchBar', () => {
});
it('supports keyboard shortcuts', () => {
- mountWithIntl( );
+ mountWithIntl(
+
+ );
const searchEvent = new KeyboardEvent('keydown', {
key: '/',
diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx
index 0dde28db0436d4..4ca0f8cf81b7bd 100644
--- a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx
+++ b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx
@@ -12,6 +12,7 @@ import {
EuiSelectableTemplateSitewideOption,
EuiText,
EuiIcon,
+ EuiImage,
EuiHeaderSectionItemButton,
EuiSelectableMessage,
} from '@elastic/eui';
@@ -27,6 +28,8 @@ import { GlobalSearchPluginStart, GlobalSearchResult } from '../../../global_sea
interface Props {
globalSearch: GlobalSearchPluginStart['find'];
navigateToUrl: ApplicationStart['navigateToUrl'];
+ basePathUrl: string;
+ darkMode: boolean;
}
const clearField = (field: HTMLInputElement) => {
@@ -42,7 +45,7 @@ const clearField = (field: HTMLInputElement) => {
const cleanMeta = (str: string) => (str.charAt(0).toUpperCase() + str.slice(1)).replace(/-/g, ' ');
const blurEvent = new FocusEvent('blur');
-export function SearchBar({ globalSearch, navigateToUrl }: Props) {
+export function SearchBar({ globalSearch, navigateToUrl, basePathUrl, darkMode }: Props) {
const isMounted = useMountedState();
const [searchValue, setSearchValue] = useState('');
const [searchRef, setSearchRef] = useState(null);
@@ -134,6 +137,34 @@ export function SearchBar({ globalSearch, navigateToUrl }: Props) {
}
};
+ const emptyMessage = (
+
+
+
+
+
+
+
+
+
+
+
+ );
+
useEvent('keydown', onKeyDown);
return (
@@ -164,22 +195,8 @@ export function SearchBar({ globalSearch, navigateToUrl }: Props) {
popoverProps={{
repositionOnScroll: true,
}}
- emptyMessage={
-
-
-
-
-
-
-
-
- }
+ emptyMessage={emptyMessage}
+ noMatchesMessage={emptyMessage}
popoverFooter={
{
public start(core: CoreStart, { globalSearch }: GlobalSearchBarPluginStartDeps) {
core.chrome.navControls.registerCenter({
order: 1000,
- mount: (target) => this.mount(target, globalSearch, core.application.navigateToUrl),
+ mount: (target) =>
+ this.mount(
+ target,
+ globalSearch,
+ core.application.navigateToUrl,
+ core.http.basePath.prepend('/plugins/globalSearchBar/assets/'),
+ core.uiSettings.get('theme:darkMode')
+ ),
});
return {};
}
@@ -32,11 +39,18 @@ export class GlobalSearchBarPlugin implements Plugin<{}, {}> {
private mount(
targetDomElement: HTMLElement,
globalSearch: GlobalSearchPluginStart,
- navigateToUrl: ApplicationStart['navigateToUrl']
+ navigateToUrl: ApplicationStart['navigateToUrl'],
+ basePathUrl: string,
+ darkMode: boolean
) {
ReactDOM.render(
-
+
,
targetDomElement
);
From 95bf8750cda2937afbc022163a1d28c0d1326e3e Mon Sep 17 00:00:00 2001
From: Bohdan Tsymbala
Date: Fri, 2 Oct 2020 16:00:09 +0200
Subject: [PATCH 18/21] Refactored store code to group properties related to
location so that would be easy to introduce a new view type parameter.
(#79083)
---
.../public/management/common/routing.test.ts | 22 +++----
.../public/management/common/routing.ts | 35 ++++++-----
.../state/trusted_apps_list_page_state.ts | 12 ++--
.../trusted_apps/store/middleware.test.ts | 58 ++++++++++++------
.../pages/trusted_apps/store/middleware.ts | 12 ++--
.../pages/trusted_apps/store/reducer.test.ts | 14 ++---
.../pages/trusted_apps/store/reducer.ts | 33 ++++------
.../trusted_apps/store/selectors.test.ts | 58 +++++++-----------
.../pages/trusted_apps/store/selectors.ts | 60 ++++++-------------
.../pages/trusted_apps/test_utils/index.ts | 43 ++-----------
.../trusted_apps/view/trusted_apps_list.tsx | 8 +--
.../trusted_apps/view/trusted_apps_page.tsx | 17 +++---
12 files changed, 159 insertions(+), 213 deletions(-)
diff --git a/x-pack/plugins/security_solution/public/management/common/routing.test.ts b/x-pack/plugins/security_solution/public/management/common/routing.test.ts
index 7a36654dcffc30..7082ab0ce5c4f4 100644
--- a/x-pack/plugins/security_solution/public/management/common/routing.test.ts
+++ b/x-pack/plugins/security_solution/public/management/common/routing.test.ts
@@ -4,57 +4,57 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { extractListPaginationParams, getTrustedAppsListPath } from './routing';
+import { extractTrustedAppsListPageLocation, getTrustedAppsListPath } from './routing';
import { MANAGEMENT_DEFAULT_PAGE, MANAGEMENT_DEFAULT_PAGE_SIZE } from './constants';
describe('routing', () => {
describe('extractListPaginationParams()', () => {
it('extracts default page index when not provided', () => {
- expect(extractListPaginationParams({}).page_index).toBe(MANAGEMENT_DEFAULT_PAGE);
+ expect(extractTrustedAppsListPageLocation({}).page_index).toBe(MANAGEMENT_DEFAULT_PAGE);
});
it('extracts default page index when too small value provided', () => {
- expect(extractListPaginationParams({ page_index: '-1' }).page_index).toBe(
+ expect(extractTrustedAppsListPageLocation({ page_index: '-1' }).page_index).toBe(
MANAGEMENT_DEFAULT_PAGE
);
});
it('extracts default page index when not a number provided', () => {
- expect(extractListPaginationParams({ page_index: 'a' }).page_index).toBe(
+ expect(extractTrustedAppsListPageLocation({ page_index: 'a' }).page_index).toBe(
MANAGEMENT_DEFAULT_PAGE
);
});
it('extracts only last page index when multiple values provided', () => {
- expect(extractListPaginationParams({ page_index: ['1', '2'] }).page_index).toBe(2);
+ expect(extractTrustedAppsListPageLocation({ page_index: ['1', '2'] }).page_index).toBe(2);
});
it('extracts proper page index when single valid value provided', () => {
- expect(extractListPaginationParams({ page_index: '2' }).page_index).toBe(2);
+ expect(extractTrustedAppsListPageLocation({ page_index: '2' }).page_index).toBe(2);
});
it('extracts default page size when not provided', () => {
- expect(extractListPaginationParams({}).page_size).toBe(MANAGEMENT_DEFAULT_PAGE_SIZE);
+ expect(extractTrustedAppsListPageLocation({}).page_size).toBe(MANAGEMENT_DEFAULT_PAGE_SIZE);
});
it('extracts default page size when invalid option provided', () => {
- expect(extractListPaginationParams({ page_size: '25' }).page_size).toBe(
+ expect(extractTrustedAppsListPageLocation({ page_size: '25' }).page_size).toBe(
MANAGEMENT_DEFAULT_PAGE_SIZE
);
});
it('extracts default page size when not a number provided', () => {
- expect(extractListPaginationParams({ page_size: 'a' }).page_size).toBe(
+ expect(extractTrustedAppsListPageLocation({ page_size: 'a' }).page_size).toBe(
MANAGEMENT_DEFAULT_PAGE_SIZE
);
});
it('extracts only last page size when multiple values provided', () => {
- expect(extractListPaginationParams({ page_size: ['10', '20'] }).page_size).toBe(20);
+ expect(extractTrustedAppsListPageLocation({ page_size: ['10', '20'] }).page_size).toBe(20);
});
it('extracts proper page size when single valid value provided', () => {
- expect(extractListPaginationParams({ page_size: '20' }).page_size).toBe(20);
+ expect(extractTrustedAppsListPageLocation({ page_size: '20' }).page_size).toBe(20);
});
});
diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts
index cb4ed9b098fce2..9acf4a1613c0b2 100644
--- a/x-pack/plugins/security_solution/public/management/common/routing.ts
+++ b/x-pack/plugins/security_solution/public/management/common/routing.ts
@@ -21,7 +21,7 @@ import {
import { AdministrationSubTab } from '../types';
import { appendSearch } from '../../common/components/link_to/helpers';
import { EndpointIndexUIQueryParams } from '../pages/endpoint_hosts/types';
-import { TrustedAppsUrlParams } from '../pages/trusted_apps/types';
+import { TrustedAppsListPageLocation } from '../pages/trusted_apps/state';
// Taken from: https://github.com/microsoft/TypeScript/issues/12936#issuecomment-559034150
type ExactKeys = Exclude extends never ? T1 : never;
@@ -94,18 +94,18 @@ const isDefaultOrMissing = (value: T | undefined, defaultValue: T) => {
return value === undefined || value === defaultValue;
};
-const normalizeListPaginationParams = (
- params?: Partial
-): Partial => {
- if (params) {
+const normalizeTrustedAppsPageLocation = (
+ location?: Partial
+): Partial => {
+ if (location) {
return {
- ...(!isDefaultOrMissing(params.page_index, MANAGEMENT_DEFAULT_PAGE)
- ? { page_index: params.page_index }
+ ...(!isDefaultOrMissing(location.page_index, MANAGEMENT_DEFAULT_PAGE)
+ ? { page_index: location.page_index }
: {}),
- ...(!isDefaultOrMissing(params.page_size, MANAGEMENT_DEFAULT_PAGE_SIZE)
- ? { page_size: params.page_size }
+ ...(!isDefaultOrMissing(location.page_size, MANAGEMENT_DEFAULT_PAGE_SIZE)
+ ? { page_size: location.page_size }
: {}),
- ...(!isDefaultOrMissing(params.show, undefined) ? { show: params.show } : {}),
+ ...(!isDefaultOrMissing(location.show, undefined) ? { show: location.show } : {}),
};
} else {
return {};
@@ -135,17 +135,22 @@ const extractPageSize = (query: querystring.ParsedUrlQuery): number => {
return MANAGEMENT_PAGE_SIZE_OPTIONS.includes(pageSize) ? pageSize : MANAGEMENT_DEFAULT_PAGE_SIZE;
};
-export const extractListPaginationParams = (
- query: querystring.ParsedUrlQuery
-): TrustedAppsUrlParams => ({
+export const extractListPaginationParams = (query: querystring.ParsedUrlQuery) => ({
page_index: extractPageIndex(query),
page_size: extractPageSize(query),
});
-export const getTrustedAppsListPath = (params?: Partial): string => {
+export const extractTrustedAppsListPageLocation = (
+ query: querystring.ParsedUrlQuery
+): TrustedAppsListPageLocation => ({
+ ...extractListPaginationParams(query),
+ show: extractFirstParamValue(query, 'show') === 'create' ? 'create' : undefined,
+});
+
+export const getTrustedAppsListPath = (params?: Partial): string => {
const path = generatePath(MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, {
tabName: AdministrationSubTab.trustedApps,
});
- return `${path}${appendSearch(querystring.stringify(normalizeListPaginationParams(params)))}`;
+ return `${path}${appendSearch(querystring.stringify(normalizeTrustedAppsPageLocation(params)))}`;
};
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts
index 4c38ac0c4239a0..a98ec03a006f53 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts
@@ -7,7 +7,6 @@
import { ServerApiError } from '../../../../common/types';
import { NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types/trusted_apps';
import { AsyncResourceState } from '.';
-import { TrustedAppsUrlParams } from '../types';
export interface PaginationInfo {
index: number;
@@ -39,12 +38,16 @@ export interface TrustedAppCreateFailure {
data: ServerApiError;
}
+export interface TrustedAppsListPageLocation {
+ page_index: number;
+ page_size: number;
+ show?: 'create';
+}
+
export interface TrustedAppsListPageState {
listView: {
- currentListResourceState: AsyncResourceState;
- currentPaginationInfo: PaginationInfo;
+ listResourceState: AsyncResourceState;
freshDataTimestamp: number;
- show: TrustedAppsUrlParams['show'] | undefined;
};
deletionDialog: {
entry?: TrustedApp;
@@ -56,5 +59,6 @@ export interface TrustedAppsListPageState {
| TrustedAppCreatePending
| TrustedAppCreateSuccess
| TrustedAppCreateFailure;
+ location: TrustedAppsListPageLocation;
active: boolean;
}
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts
index 19c2d3a62781f4..2143b5135c5753 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts
@@ -9,13 +9,12 @@ import { applyMiddleware, createStore } from 'redux';
import { createSpyMiddleware } from '../../../../common/store/test_utils';
import {
- createFailedListViewWithPagination,
createListLoadedResourceState,
createLoadedListViewWithPagination,
- createLoadingListViewWithPagination,
createSampleTrustedApp,
createSampleTrustedApps,
createServerApiError,
+ createUninitialisedResourceState,
createUserChangedUrlAction,
} from '../test_utils';
@@ -76,6 +75,7 @@ describe('middleware', () => {
describe('refreshing list resource state', () => {
it('refreshes the list when location changes and data gets outdated', async () => {
const pagination = { index: 2, size: 50 };
+ const location = { page_index: 2, page_size: 50, show: undefined };
const service = createTrustedAppsServiceMock();
const { store, spyMiddleware } = createStoreSetup(service);
@@ -87,21 +87,30 @@ describe('middleware', () => {
expect(store.getState()).toStrictEqual({
...initialState,
- listView: createLoadingListViewWithPagination(initialNow, pagination),
+ listView: {
+ listResourceState: {
+ type: 'LoadingResourceState',
+ previousState: createUninitialisedResourceState(),
+ },
+ freshDataTimestamp: initialNow,
+ },
active: true,
+ location,
});
await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged');
expect(store.getState()).toStrictEqual({
...initialState,
- listView: createLoadedListViewWithPagination(initialNow, pagination, pagination, 500),
+ listView: createLoadedListViewWithPagination(initialNow, pagination, 500),
active: true,
+ location,
});
});
it('does not refresh the list when location changes and data does not get outdated', async () => {
const pagination = { index: 2, size: 50 };
+ const location = { page_index: 2, page_size: 50, show: undefined };
const service = createTrustedAppsServiceMock();
const { store, spyMiddleware } = createStoreSetup(service);
@@ -118,14 +127,16 @@ describe('middleware', () => {
expect(service.getTrustedAppsList).toBeCalledTimes(1);
expect(store.getState()).toStrictEqual({
...initialState,
- listView: createLoadedListViewWithPagination(initialNow, pagination, pagination, 500),
+ listView: createLoadedListViewWithPagination(initialNow, pagination, 500),
active: true,
+ location,
});
});
it('refreshes the list when data gets outdated with and outdate action', async () => {
const newNow = 222222;
const pagination = { index: 0, size: 10 };
+ const location = { page_index: 0, page_size: 10, show: undefined };
const service = createTrustedAppsServiceMock();
const { store, spyMiddleware } = createStoreSetup(service);
@@ -143,20 +154,24 @@ describe('middleware', () => {
expect(store.getState()).toStrictEqual({
...initialState,
- listView: createLoadingListViewWithPagination(
- newNow,
- pagination,
- createListLoadedResourceState(pagination, 500, initialNow)
- ),
+ listView: {
+ listResourceState: {
+ type: 'LoadingResourceState',
+ previousState: createListLoadedResourceState(pagination, 500, initialNow),
+ },
+ freshDataTimestamp: newNow,
+ },
active: true,
+ location,
});
await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged');
expect(store.getState()).toStrictEqual({
...initialState,
- listView: createLoadedListViewWithPagination(newNow, pagination, pagination, 500),
+ listView: createLoadedListViewWithPagination(newNow, pagination, 500),
active: true,
+ location,
});
});
@@ -172,12 +187,16 @@ describe('middleware', () => {
expect(store.getState()).toStrictEqual({
...initialState,
- listView: createFailedListViewWithPagination(
- initialNow,
- { index: 2, size: 50 },
- createServerApiError('Internal Server Error')
- ),
+ listView: {
+ listResourceState: {
+ type: 'FailedResourceState',
+ error: createServerApiError('Internal Server Error'),
+ lastLoadedState: undefined,
+ },
+ freshDataTimestamp: initialNow,
+ },
active: true,
+ location: { page_index: 2, page_size: 50, show: undefined },
});
const infiniteLoopTest = async () => {
@@ -193,10 +212,11 @@ describe('middleware', () => {
const entry = createSampleTrustedApp(3);
const notFoundError = createServerApiError('Not Found');
const pagination = { index: 0, size: 10 };
+ const location = { page_index: 0, page_size: 10, show: undefined };
const getTrustedAppsListResponse = createGetTrustedListAppsResponse(pagination, 500);
- const listView = createLoadedListViewWithPagination(initialNow, pagination, pagination, 500);
- const listViewNew = createLoadedListViewWithPagination(newNow, pagination, pagination, 500);
- const testStartState = { ...initialState, listView, active: true };
+ const listView = createLoadedListViewWithPagination(initialNow, pagination, 500);
+ const listViewNew = createLoadedListViewWithPagination(newNow, pagination, 500);
+ const testStartState = { ...initialState, listView, active: true, location };
it('does not submit when entry is undefined', async () => {
const service = createTrustedAppsServiceMock();
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts
index dd96c8d8070481..9fa456dc5ffe23 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts
@@ -29,12 +29,12 @@ import {
} from './action';
import {
- getCurrentListResourceState,
+ getListResourceState,
getDeletionDialogEntry,
getDeletionSubmissionResourceState,
getLastLoadedListResourceState,
- getListCurrentPageIndex,
- getListCurrentPageSize,
+ getCurrentLocationPageIndex,
+ getCurrentLocationPageSize,
getTrustedAppCreateData,
isCreatePending,
needsRefreshOfListData,
@@ -56,15 +56,15 @@ const refreshListIfNeeded = async (
createTrustedAppsListResourceStateChangedAction({
type: 'LoadingResourceState',
// need to think on how to avoid the casting
- previousState: getCurrentListResourceState(store.getState()) as Immutable<
+ previousState: getListResourceState(store.getState()) as Immutable<
StaleResourceState
>,
})
);
try {
- const pageIndex = getListCurrentPageIndex(store.getState());
- const pageSize = getListCurrentPageSize(store.getState());
+ const pageIndex = getCurrentLocationPageIndex(store.getState());
+ const pageSize = getCurrentLocationPageSize(store.getState());
const response = await trustedAppsService.getTrustedAppsList({
page: pageIndex + 1,
per_page: pageSize,
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts
index 228f0932edd28a..94fcdb39bb1692 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts
@@ -32,17 +32,14 @@ describe('reducer', () => {
expect(result).toStrictEqual({
...initialState,
- listView: { ...initialState.listView, currentPaginationInfo: { index: 5, size: 50 } },
+ location: { page_index: 5, page_size: 50, show: undefined },
active: true,
});
});
it('extracts default pagination parameters when none provided', () => {
const result = trustedAppsPageReducer(
- {
- ...initialState,
- listView: { ...initialState.listView, currentPaginationInfo: { index: 5, size: 50 } },
- },
+ { ...initialState, location: { page_index: 5, page_size: 50 } },
createUserChangedUrlAction('/trusted_apps', '?page_index=b&page_size=60')
);
@@ -51,10 +48,7 @@ describe('reducer', () => {
it('extracts default pagination parameters when invalid provided', () => {
const result = trustedAppsPageReducer(
- {
- ...initialState,
- listView: { ...initialState.listView, currentPaginationInfo: { index: 5, size: 50 } },
- },
+ { ...initialState, location: { page_index: 5, page_size: 50 } },
createUserChangedUrlAction('/trusted_apps')
);
@@ -85,7 +79,7 @@ describe('reducer', () => {
expect(result).toStrictEqual({
...initialState,
- listView: { ...initialState.listView, currentListResourceState: listResourceState },
+ listView: { ...initialState.listView, listResourceState },
});
});
});
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts
index ec210254bf76fa..f4056f02a41404 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts
@@ -11,7 +11,8 @@ import { ImmutableReducer } from '../../../../common/store';
import { AppLocation, Immutable } from '../../../../../common/endpoint/types';
import { UserChangedUrl } from '../../../../common/store/routing/action';
import { AppAction } from '../../../../common/store/actions';
-import { extractFirstParamValue, extractListPaginationParams } from '../../../common/routing';
+import { extractTrustedAppsListPageLocation } from '../../../common/routing';
+
import {
MANAGEMENT_ROUTING_TRUSTED_APPS_PATH,
MANAGEMENT_DEFAULT_PAGE,
@@ -29,6 +30,7 @@ import {
ServerReturnedCreateTrustedAppSuccess,
UserClickedSaveNewTrustedAppButton,
} from './action';
+
import { TrustedAppsListPageState } from '../state';
type StateReducer = ImmutableReducer;
@@ -64,7 +66,7 @@ const trustedAppsListResourceStateChanged: CaseReducer = (state, action) => {
if (isTrustedAppsPageLocation(action.payload)) {
const parsedUrlsParams = parse(action.payload.search.slice(1));
- const paginationParams = extractListPaginationParams(parsedUrlsParams);
- const show =
- extractFirstParamValue(parsedUrlsParams, 'show') === 'create' ? 'create' : undefined;
+ const location = extractTrustedAppsListPageLocation(parsedUrlsParams);
return {
...state,
- listView: {
- ...state.listView,
- currentPaginationInfo: {
- index: paginationParams.page_index,
- size: paginationParams.page_size,
- },
- show,
- },
- createView: show ? state.createView : undefined,
+ createView: location.show ? state.createView : undefined,
active: true,
+ location,
};
} else {
return initialTrustedAppsPageState();
@@ -150,16 +143,16 @@ const initialDeletionDialogState = (): TrustedAppsListPageState['deletionDialog'
export const initialTrustedAppsPageState = (): TrustedAppsListPageState => ({
listView: {
- currentListResourceState: { type: 'UninitialisedResourceState' },
- currentPaginationInfo: {
- index: MANAGEMENT_DEFAULT_PAGE,
- size: MANAGEMENT_DEFAULT_PAGE_SIZE,
- },
+ listResourceState: { type: 'UninitialisedResourceState' },
freshDataTimestamp: Date.now(),
- show: undefined,
},
deletionDialog: initialDeletionDialogState(),
createView: undefined,
+ location: {
+ page_index: MANAGEMENT_DEFAULT_PAGE,
+ page_size: MANAGEMENT_DEFAULT_PAGE_SIZE,
+ show: undefined,
+ },
active: false,
});
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts
index 0be4d0b05acc44..01fe3e5bf202ea 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts
@@ -7,10 +7,10 @@
import { AsyncResourceState, TrustedAppsListPageState } from '../state';
import { initialTrustedAppsPageState } from './reducer';
import {
- getCurrentListResourceState,
+ getListResourceState,
getLastLoadedListResourceState,
- getListCurrentPageIndex,
- getListCurrentPageSize,
+ getCurrentLocationPageIndex,
+ getCurrentLocationPageSize,
getListErrorMessage,
getListItems,
getListTotalItemsCount,
@@ -25,7 +25,6 @@ import {
} from './selectors';
import {
- createDefaultListView,
createDefaultPaginationInfo,
createListComplexLoadingResourceState,
createListFailedResourceState,
@@ -85,16 +84,17 @@ describe('selectors', () => {
it('returns false when current loaded data is up to date', () => {
const listView = createLoadedListViewWithPagination(initialNow);
+ const location = { page_index: 0, page_size: 10 };
- expect(needsRefreshOfListData({ ...initialState, listView, active: true })).toBe(false);
+ expect(needsRefreshOfListData({ ...initialState, listView, active: true, location })).toBe(
+ false
+ );
});
});
- describe('getCurrentListResourceState()', () => {
+ describe('getListResourceState()', () => {
it('returns current list resource state', () => {
- const state = { ...initialState, listView: createDefaultListView(initialNow) };
-
- expect(getCurrentListResourceState(state)).toStrictEqual(createUninitialisedResourceState());
+ expect(getListResourceState(initialState)).toStrictEqual(createUninitialisedResourceState());
});
});
@@ -103,14 +103,12 @@ describe('selectors', () => {
const state = {
...initialState,
listView: {
- currentListResourceState: createListComplexLoadingResourceState(
+ listResourceState: createListComplexLoadingResourceState(
createDefaultPaginationInfo(),
200,
initialNow
),
- currentPaginationInfo: createDefaultPaginationInfo(),
freshDataTimestamp: initialNow,
- show: undefined,
},
};
@@ -122,23 +120,19 @@ describe('selectors', () => {
describe('getListItems()', () => {
it('returns empty list when no valid data loaded', () => {
- const state = { ...initialState, listView: createDefaultListView(initialNow) };
-
- expect(getListItems(state)).toStrictEqual([]);
+ expect(getListItems(initialState)).toStrictEqual([]);
});
it('returns last loaded list items', () => {
const state = {
...initialState,
listView: {
- currentListResourceState: createListComplexLoadingResourceState(
+ listResourceState: createListComplexLoadingResourceState(
createDefaultPaginationInfo(),
200,
initialNow
),
- currentPaginationInfo: createDefaultPaginationInfo(),
freshDataTimestamp: initialNow,
- show: undefined,
},
};
@@ -150,23 +144,19 @@ describe('selectors', () => {
describe('getListTotalItemsCount()', () => {
it('returns 0 when no valid data loaded', () => {
- const state = { ...initialState, listView: createDefaultListView(initialNow) };
-
- expect(getListTotalItemsCount(state)).toBe(0);
+ expect(getListTotalItemsCount(initialState)).toBe(0);
});
it('returns last loaded total items count', () => {
const state = {
...initialState,
listView: {
- currentListResourceState: createListComplexLoadingResourceState(
+ listResourceState: createListComplexLoadingResourceState(
createDefaultPaginationInfo(),
200,
initialNow
),
- currentPaginationInfo: createDefaultPaginationInfo(),
freshDataTimestamp: initialNow,
- show: undefined,
},
};
@@ -176,17 +166,17 @@ describe('selectors', () => {
describe('getListCurrentPageIndex()', () => {
it('returns page index', () => {
- const state = { ...initialState, listView: createDefaultListView(initialNow) };
+ const state = { ...initialState, location: { page_index: 3, page_size: 10 } };
- expect(getListCurrentPageIndex(state)).toBe(0);
+ expect(getCurrentLocationPageIndex(state)).toBe(3);
});
});
describe('getListCurrentPageSize()', () => {
it('returns page size', () => {
- const state = { ...initialState, listView: createDefaultListView(initialNow) };
+ const state = { ...initialState, location: { page_index: 0, page_size: 20 } };
- expect(getListCurrentPageSize(state)).toBe(20);
+ expect(getCurrentLocationPageSize(state)).toBe(20);
});
});
@@ -195,14 +185,12 @@ describe('selectors', () => {
const state = {
...initialState,
listView: {
- currentListResourceState: createListComplexLoadingResourceState(
+ listResourceState: createListComplexLoadingResourceState(
createDefaultPaginationInfo(),
200,
initialNow
),
- currentPaginationInfo: createDefaultPaginationInfo(),
freshDataTimestamp: initialNow,
- show: undefined,
},
};
@@ -213,10 +201,8 @@ describe('selectors', () => {
const state = {
...initialState,
listView: {
- currentListResourceState: createListFailedResourceState('Internal Server Error'),
- currentPaginationInfo: createDefaultPaginationInfo(),
+ listResourceState: createListFailedResourceState('Internal Server Error'),
freshDataTimestamp: initialNow,
- show: undefined,
},
};
@@ -233,14 +219,12 @@ describe('selectors', () => {
const state = {
...initialState,
listView: {
- currentListResourceState: createListComplexLoadingResourceState(
+ listResourceState: createListComplexLoadingResourceState(
createDefaultPaginationInfo(),
200,
initialNow
),
- currentPaginationInfo: createDefaultPaginationInfo(),
freshDataTimestamp: initialNow,
- show: undefined,
},
};
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts
index 6239b425efe2fc..62ffa364e4a6c8 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts
@@ -4,7 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { createSelector } from 'reselect';
import { ServerApiError } from '../../../../common/types';
import { Immutable, NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types';
@@ -17,97 +16,76 @@ import {
isLoadingResourceState,
isOutdatedResourceState,
LoadedResourceState,
- PaginationInfo,
TrustedAppCreateFailure,
TrustedAppsListData,
+ TrustedAppsListPageLocation,
TrustedAppsListPageState,
} from '../state';
-import { TrustedAppsUrlParams } from '../types';
import {
isTrustedAppCreateFailureState,
isTrustedAppCreatePendingState,
isTrustedAppCreateSuccessState,
} from '../state/type_guards';
-const pageInfosEqual = (pageInfo1: PaginationInfo, pageInfo2: PaginationInfo): boolean =>
- pageInfo1.index === pageInfo2.index && pageInfo1.size === pageInfo2.size;
-
export const needsRefreshOfListData = (state: Immutable): boolean => {
- const currentPageInfo = state.listView.currentPaginationInfo;
- const currentPage = state.listView.currentListResourceState;
const freshDataTimestamp = state.listView.freshDataTimestamp;
+ const currentPage = state.listView.listResourceState;
+ const location = state.location;
return (
state.active &&
isOutdatedResourceState(currentPage, (data) => {
return (
- pageInfosEqual(currentPageInfo, data.paginationInfo) && data.timestamp >= freshDataTimestamp
+ data.paginationInfo.index === location.page_index &&
+ data.paginationInfo.size === location.page_size &&
+ data.timestamp >= freshDataTimestamp
);
})
);
};
-export const getCurrentListResourceState = (
+export const getListResourceState = (
state: Immutable
): Immutable> | undefined => {
- return state.listView.currentListResourceState;
+ return state.listView.listResourceState;
};
export const getLastLoadedListResourceState = (
state: Immutable
): Immutable> | undefined => {
- return getLastLoadedResourceState(state.listView.currentListResourceState);
+ return getLastLoadedResourceState(state.listView.listResourceState);
};
export const getListItems = (
state: Immutable
): Immutable => {
- return getLastLoadedResourceState(state.listView.currentListResourceState)?.data.items || [];
+ return getLastLoadedResourceState(state.listView.listResourceState)?.data.items || [];
};
-export const getListCurrentPageIndex = (state: Immutable): number => {
- return state.listView.currentPaginationInfo.index;
+export const getCurrentLocationPageIndex = (state: Immutable): number => {
+ return state.location.page_index;
};
-export const getListCurrentPageSize = (state: Immutable): number => {
- return state.listView.currentPaginationInfo.size;
+export const getCurrentLocationPageSize = (state: Immutable): number => {
+ return state.location.page_size;
};
export const getListTotalItemsCount = (state: Immutable): number => {
- return (
- getLastLoadedResourceState(state.listView.currentListResourceState)?.data.totalItemsCount || 0
- );
-};
-
-export const getListCurrentShowValue: (
- state: Immutable
-) => TrustedAppsListPageState['listView']['show'] = (state) => {
- return state.listView.show;
+ return getLastLoadedResourceState(state.listView.listResourceState)?.data.totalItemsCount || 0;
};
-export const getListUrlSearchParams: (
+export const getCurrentLocation = (
state: Immutable
-) => TrustedAppsUrlParams = createSelector(
- getListCurrentPageIndex,
- getListCurrentPageSize,
- getListCurrentShowValue,
- (pageIndex, pageSize, showValue) => {
- return {
- page_index: pageIndex,
- page_size: pageSize,
- show: showValue,
- };
- }
-);
+): TrustedAppsListPageLocation => state.location;
export const getListErrorMessage = (
state: Immutable
): string | undefined => {
- return getCurrentResourceError(state.listView.currentListResourceState)?.message;
+ return getCurrentResourceError(state.listView.listResourceState)?.message;
};
export const isListLoading = (state: Immutable): boolean => {
- return isLoadingResourceState(state.listView.currentListResourceState);
+ return isLoadingResourceState(state.listView.listResourceState);
};
export const isDeletionDialogOpen = (state: Immutable): boolean => {
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts
index 020a87f526e52e..c23b6ceae7b078 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts
@@ -5,11 +5,12 @@
*/
import { combineReducers, createStore } from 'redux';
-import { ServerApiError } from '../../../../common/types';
import { TrustedApp } from '../../../../../common/endpoint/types';
import { RoutingAction } from '../../../../common/store/routing';
import {
+ MANAGEMENT_DEFAULT_PAGE,
+ MANAGEMENT_DEFAULT_PAGE_SIZE,
MANAGEMENT_STORE_GLOBAL_NAMESPACE,
MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE,
} from '../../../common/constants';
@@ -105,54 +106,22 @@ export const createListComplexLoadingResourceState = (
)
);
-export const createDefaultPaginationInfo = () => ({ index: 0, size: 20 });
-
-export const createDefaultListView = (
- freshDataTimestamp: number
-): TrustedAppsListPageState['listView'] => ({
- currentListResourceState: createUninitialisedResourceState(),
- currentPaginationInfo: createDefaultPaginationInfo(),
- freshDataTimestamp,
- show: undefined,
-});
-
-export const createLoadingListViewWithPagination = (
- freshDataTimestamp: number,
- currentPaginationInfo: PaginationInfo,
- previousState: StaleResourceState = createUninitialisedResourceState()
-): TrustedAppsListPageState['listView'] => ({
- currentListResourceState: { type: 'LoadingResourceState', previousState },
- currentPaginationInfo,
- freshDataTimestamp,
- show: undefined,
+export const createDefaultPaginationInfo = () => ({
+ index: MANAGEMENT_DEFAULT_PAGE,
+ size: MANAGEMENT_DEFAULT_PAGE_SIZE,
});
export const createLoadedListViewWithPagination = (
freshDataTimestamp: number,
paginationInfo: PaginationInfo = createDefaultPaginationInfo(),
- currentPaginationInfo: PaginationInfo = createDefaultPaginationInfo(),
totalItemsCount: number = 200
): TrustedAppsListPageState['listView'] => ({
- currentListResourceState: createListLoadedResourceState(
+ listResourceState: createListLoadedResourceState(
paginationInfo,
totalItemsCount,
freshDataTimestamp
),
- currentPaginationInfo,
- freshDataTimestamp,
- show: undefined,
-});
-
-export const createFailedListViewWithPagination = (
- freshDataTimestamp: number,
- currentPaginationInfo: PaginationInfo,
- error: ServerApiError,
- lastLoadedState?: LoadedResourceState
-): TrustedAppsListPageState['listView'] => ({
- currentListResourceState: { type: 'FailedResourceState', error, lastLoadedState },
- currentPaginationInfo,
freshDataTimestamp,
- show: undefined,
});
export const createUserChangedUrlAction = (path: string, search: string = ''): RoutingAction => {
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx
index d0c1fb477ea46e..ae1f314842aab1 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx
@@ -23,8 +23,8 @@ import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../../common/constants';
import { getTrustedAppsListPath } from '../../../common/routing';
import {
- getListCurrentPageIndex,
- getListCurrentPageSize,
+ getCurrentLocationPageIndex,
+ getCurrentLocationPageSize,
getListErrorMessage,
getListItems,
getListTotalItemsCount,
@@ -149,8 +149,8 @@ const getColumnDefinitions = (context: TrustedAppsListContext): ColumnsList => {
export const TrustedAppsList = memo(() => {
const [detailsMap, setDetailsMap] = useState({});
- const pageIndex = useTrustedAppsSelector(getListCurrentPageIndex);
- const pageSize = useTrustedAppsSelector(getListCurrentPageSize);
+ const pageIndex = useTrustedAppsSelector(getCurrentLocationPageIndex);
+ const pageSize = useTrustedAppsSelector(getCurrentLocationPageSize);
const totalItemCount = useTrustedAppsSelector(getListTotalItemsCount);
const listItems = useTrustedAppsSelector(getListItems);
const dispatch = useDispatch();
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx
index 878818d9b77fe6..d63cda5b513dc1 100644
--- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx
+++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx
@@ -15,27 +15,26 @@ import { TrustedAppsNotifications } from './trusted_apps_notifications';
import { CreateTrustedAppFlyout } from './components/create_trusted_app_flyout';
import { getTrustedAppsListPath } from '../../../common/routing';
import { useTrustedAppsSelector } from './hooks';
-import { getListCurrentShowValue, getListUrlSearchParams } from '../store/selectors';
+import { getCurrentLocation } from '../store/selectors';
import { TrustedAppsListPageRouteState } from '../../../../../common/endpoint/types';
import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/use_navigate_to_app_event_handler';
export const TrustedAppsPage = memo(() => {
const history = useHistory();
const { state: routeState } = useLocation();
- const urlParams = useTrustedAppsSelector(getListUrlSearchParams);
- const showAddFlout = useTrustedAppsSelector(getListCurrentShowValue) === 'create';
+ const location = useTrustedAppsSelector(getCurrentLocation);
const handleAddButtonClick = useCallback(() => {
history.push(
getTrustedAppsListPath({
- ...urlParams,
+ ...location,
show: 'create',
})
);
- }, [history, urlParams]);
+ }, [history, location]);
const handleAddFlyoutClose = useCallback(() => {
- const { show, ...paginationParamsOnly } = urlParams;
+ const { show, ...paginationParamsOnly } = location;
history.push(getTrustedAppsListPath(paginationParamsOnly));
- }, [history, urlParams]);
+ }, [history, location]);
const backButton = useMemo(() => {
if (routeState && routeState.onBackButtonNavigateTo) {
@@ -50,7 +49,7 @@ export const TrustedAppsPage = memo(() => {
@@ -82,7 +81,7 @@ export const TrustedAppsPage = memo(() => {
>
- {showAddFlout && (
+ {location.show === 'create' && (
Date: Fri, 2 Oct 2020 10:10:38 -0400
Subject: [PATCH 19/21] [Security Solution][Detections] Enrich shell signals
with fields common to all building blocks (#79130)
* Enrich shell signals with fields common to all building blocks
* PR comments + additional unit test
---
.../signals/build_bulk_body.test.ts | 448 +++++++++++++++++-
.../signals/build_bulk_body.ts | 53 +++
2 files changed, 498 insertions(+), 3 deletions(-)
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts
index 2f7dd22c0c78e7..75a7de8cd2c443 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.test.ts
@@ -11,8 +11,15 @@ import {
sampleIdGuid,
sampleDocWithAncestors,
sampleRuleSO,
+ sampleDocNoSortIdNoVersion,
} from './__mocks__/es_results';
-import { buildBulkBody, buildSignalFromSequence, buildSignalFromEvent } from './build_bulk_body';
+import {
+ buildBulkBody,
+ buildSignalFromSequence,
+ buildSignalFromEvent,
+ objectPairIntersection,
+ objectArrayIntersection,
+} from './build_bulk_body';
import { SignalHit } from './types';
import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock';
@@ -438,13 +445,20 @@ describe('buildBulkBody', () => {
describe('buildSignalFromSequence', () => {
test('builds a basic signal from a sequence of building blocks', () => {
- const blocks = [sampleDocWithAncestors().hits.hits[0], sampleDocWithAncestors().hits.hits[0]];
+ const block1 = sampleDocWithAncestors().hits.hits[0];
+ block1._source.new_key = 'new_key_value';
+ block1._source.new_key2 = 'new_key2_value';
+ const block2 = sampleDocWithAncestors().hits.hits[0];
+ block2._source.new_key = 'new_key_value';
+ const blocks = [block1, block2];
const ruleSO = sampleRuleSO();
const signal = buildSignalFromSequence(blocks, ruleSO);
// Timestamp will potentially always be different so remove it for the test
// @ts-expect-error
delete signal['@timestamp'];
- const expected: Omit = {
+ const expected: Omit & { someKey: string; new_key: string } = {
+ someKey: 'someValue',
+ new_key: 'new_key_value',
event: {
kind: 'signal',
},
@@ -539,6 +553,96 @@ describe('buildSignalFromSequence', () => {
};
expect(signal).toEqual(expected);
});
+
+ test('builds a basic signal if there is no overlap between source events', () => {
+ const block1 = sampleDocNoSortIdNoVersion();
+ const block2 = sampleDocNoSortIdNoVersion();
+ block2._source['@timestamp'] = '2021-05-20T22:28:46+0000';
+ block2._source.someKey = 'someOtherValue';
+ const ruleSO = sampleRuleSO();
+ const signal = buildSignalFromSequence([block1, block2], ruleSO);
+ // Timestamp will potentially always be different so remove it for the test
+ // @ts-expect-error
+ delete signal['@timestamp'];
+ const expected: Omit = {
+ event: {
+ kind: 'signal',
+ },
+ signal: {
+ parents: [
+ {
+ id: sampleIdGuid,
+ type: 'event',
+ index: 'myFakeSignalIndex',
+ depth: 0,
+ },
+ {
+ id: sampleIdGuid,
+ type: 'event',
+ index: 'myFakeSignalIndex',
+ depth: 0,
+ },
+ ],
+ ancestors: [
+ {
+ id: sampleIdGuid,
+ type: 'event',
+ index: 'myFakeSignalIndex',
+ depth: 0,
+ },
+ {
+ id: sampleIdGuid,
+ type: 'event',
+ index: 'myFakeSignalIndex',
+ depth: 0,
+ },
+ ],
+ status: 'open',
+ rule: {
+ actions: [],
+ author: ['Elastic'],
+ building_block_type: 'default',
+ id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd',
+ rule_id: 'rule-1',
+ false_positives: [],
+ max_signals: 10000,
+ risk_score: 50,
+ risk_score_mapping: [],
+ output_index: '.siem-signals',
+ description: 'Detecting root and admin users',
+ from: 'now-6m',
+ immutable: false,
+ index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'],
+ interval: '5m',
+ language: 'kuery',
+ license: 'Elastic License',
+ name: 'rule-name',
+ query: 'user.name: root or user.name: admin',
+ references: ['http://google.com'],
+ severity: 'high',
+ severity_mapping: [],
+ tags: ['some fake tag 1', 'some fake tag 2'],
+ threat: [],
+ type: 'query',
+ to: 'now',
+ note: '',
+ enabled: true,
+ created_by: 'sample user',
+ updated_by: 'sample user',
+ version: 1,
+ updated_at: ruleSO.updated_at ?? '',
+ created_at: ruleSO.attributes.createdAt,
+ throttle: 'no_actions',
+ exceptions_list: getListArrayMock(),
+ },
+ depth: 1,
+ group: {
+ id: '269c1f5754bff92fb8040283b687258e99b03e8b2ab1262cc20c82442e5de5ea',
+ },
+ },
+ };
+ expect(signal).toEqual(expected);
+ });
});
describe('buildSignalFromEvent', () => {
@@ -632,3 +736,341 @@ describe('buildSignalFromEvent', () => {
expect(signal).toEqual(expected);
});
});
+
+describe('recursive intersection between objects', () => {
+ test('should treat numbers and strings as unequal', () => {
+ const a = {
+ field1: 1,
+ field2: 1,
+ };
+ const b = {
+ field1: 1,
+ field2: '1',
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = {
+ field1: 1,
+ };
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should strip unequal numbers and strings', () => {
+ const a = {
+ field1: 1,
+ field2: 1,
+ field3: 'abcd',
+ field4: 'abcd',
+ };
+ const b = {
+ field1: 1,
+ field2: 100,
+ field3: 'abcd',
+ field4: 'wxyz',
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = {
+ field1: 1,
+ field3: 'abcd',
+ };
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should handle null values', () => {
+ const a = {
+ field1: 1,
+ field2: '1',
+ field3: null,
+ };
+ const b = {
+ field1: null,
+ field2: null,
+ field3: null,
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = {
+ field3: null,
+ };
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should handle explicit undefined values and return undefined if left with only undefined fields', () => {
+ const a = {
+ field1: 1,
+ field2: '1',
+ field3: undefined,
+ };
+ const b = {
+ field1: undefined,
+ field2: undefined,
+ field3: undefined,
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = undefined;
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should strip arrays out regardless of whether they are equal', () => {
+ const a = {
+ array_field1: [1, 2],
+ array_field2: [1, 2],
+ };
+ const b = {
+ array_field1: [1, 2],
+ array_field2: [3, 4],
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = undefined;
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should strip fields that are not in both objects', () => {
+ const a = {
+ field1: 1,
+ };
+ const b = {
+ field2: 1,
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = undefined;
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should work on objects within objects', () => {
+ const a = {
+ container_field: {
+ field1: 1,
+ field2: 1,
+ field3: 10,
+ field5: 1,
+ field6: null,
+ array_field: [1, 2],
+ nested_container_field: {
+ field1: 1,
+ field2: 1,
+ },
+ nested_container_field2: {
+ field1: undefined,
+ },
+ },
+ container_field_without_intersection: {
+ sub_field1: 1,
+ },
+ };
+ const b = {
+ container_field: {
+ field1: 1,
+ field2: 2,
+ field4: 10,
+ field5: '1',
+ field6: null,
+ array_field: [1, 2],
+ nested_container_field: {
+ field1: 1,
+ field2: 2,
+ },
+ nested_container_field2: {
+ field1: undefined,
+ },
+ },
+ container_field_without_intersection: {
+ sub_field2: 1,
+ },
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = {
+ container_field: {
+ field1: 1,
+ field6: null,
+ nested_container_field: {
+ field1: 1,
+ },
+ },
+ };
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should work on objects with a variety of fields', () => {
+ const a = {
+ field1: 1,
+ field2: 1,
+ field3: 10,
+ field5: 1,
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 1,
+ sub_field3: 10,
+ },
+ container_field_without_intersection: {
+ sub_field1: 1,
+ },
+ };
+ const b = {
+ field1: 1,
+ field2: 2,
+ field4: 10,
+ field5: '1',
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 2,
+ sub_field4: 10,
+ },
+ container_field_without_intersection: {
+ sub_field2: 1,
+ },
+ };
+ const intersection = objectPairIntersection(a, b);
+ const expected = {
+ field1: 1,
+ field6: null,
+ container_field: {
+ sub_field1: 1,
+ },
+ };
+ expect(intersection).toEqual(expected);
+ });
+});
+
+describe('objectArrayIntersection', () => {
+ test('should return undefined if the array is empty', () => {
+ const intersection = objectArrayIntersection([]);
+ const expected = undefined;
+ expect(intersection).toEqual(expected);
+ });
+ test('should return the initial object if there is only 1', () => {
+ const a = {
+ field1: 1,
+ field2: 1,
+ field3: 10,
+ field5: 1,
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 1,
+ sub_field3: 10,
+ },
+ container_field_without_intersection: {
+ sub_field1: 1,
+ },
+ };
+ const intersection = objectArrayIntersection([a]);
+ const expected = {
+ field1: 1,
+ field2: 1,
+ field3: 10,
+ field5: 1,
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 1,
+ sub_field3: 10,
+ },
+ container_field_without_intersection: {
+ sub_field1: 1,
+ },
+ };
+ expect(intersection).toEqual(expected);
+ });
+ test('should work with exactly 2 objects', () => {
+ const a = {
+ field1: 1,
+ field2: 1,
+ field3: 10,
+ field5: 1,
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 1,
+ sub_field3: 10,
+ },
+ container_field_without_intersection: {
+ sub_field1: 1,
+ },
+ };
+ const b = {
+ field1: 1,
+ field2: 2,
+ field4: 10,
+ field5: '1',
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 2,
+ sub_field4: 10,
+ },
+ container_field_without_intersection: {
+ sub_field2: 1,
+ },
+ };
+ const intersection = objectArrayIntersection([a, b]);
+ const expected = {
+ field1: 1,
+ field6: null,
+ container_field: {
+ sub_field1: 1,
+ },
+ };
+ expect(intersection).toEqual(expected);
+ });
+
+ test('should work with 3 or more objects', () => {
+ const a = {
+ field1: 1,
+ field2: 1,
+ field3: 10,
+ field5: 1,
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 1,
+ sub_field3: 10,
+ },
+ container_field_without_intersection: {
+ sub_field1: 1,
+ },
+ };
+ const b = {
+ field1: 1,
+ field2: 2,
+ field4: 10,
+ field5: '1',
+ field6: null,
+ array_field: [1, 2],
+ container_field: {
+ sub_field1: 1,
+ sub_field2: 2,
+ sub_field4: 10,
+ },
+ container_field_without_intersection: {
+ sub_field2: 1,
+ },
+ };
+ const c = {
+ field1: 1,
+ field2: 2,
+ field4: 10,
+ field5: '1',
+ array_field: [1, 2],
+ container_field: {
+ sub_field2: 2,
+ sub_field4: 10,
+ },
+ container_field_without_intersection: {
+ sub_field2: 1,
+ },
+ };
+ const intersection = objectArrayIntersection([a, b, c]);
+ const expected = {
+ field1: 1,
+ };
+ expect(intersection).toEqual(expected);
+ });
+});
diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts
index f8632a85c77e9e..8e9571fe8a445d 100644
--- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts
+++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_bulk_body.ts
@@ -129,7 +129,9 @@ export const buildSignalFromSequence = (
): SignalHit => {
const rule = buildRuleWithoutOverrides(ruleSO);
const signal: Signal = buildSignal(events, rule);
+ const mergedEvents = objectArrayIntersection(events.map((event) => event._source));
return {
+ ...mergedEvents,
'@timestamp': new Date().toISOString(),
event: {
kind: 'signal',
@@ -167,3 +169,54 @@ export const buildSignalFromEvent = (
};
return signalHit;
};
+
+export const objectArrayIntersection = (objects: object[]) => {
+ if (objects.length === 0) {
+ return undefined;
+ } else if (objects.length === 1) {
+ return objects[0];
+ } else {
+ return objects
+ .slice(1)
+ .reduce(
+ (acc: object | undefined, obj): object | undefined => objectPairIntersection(acc, obj),
+ objects[0]
+ );
+ }
+};
+
+export const objectPairIntersection = (a: object | undefined, b: object | undefined) => {
+ if (a === undefined || b === undefined) {
+ return undefined;
+ }
+ const intersection: Record = {};
+ Object.entries(a).forEach(([key, aVal]) => {
+ if (key in b) {
+ const bVal = (b as Record)[key];
+ if (
+ typeof aVal === 'object' &&
+ !(aVal instanceof Array) &&
+ aVal !== null &&
+ typeof bVal === 'object' &&
+ !(bVal instanceof Array) &&
+ bVal !== null
+ ) {
+ intersection[key] = objectPairIntersection(aVal, bVal);
+ } else if (aVal === bVal) {
+ intersection[key] = aVal;
+ }
+ }
+ });
+ // Count up the number of entries that are NOT undefined in the intersection
+ // If there are no keys OR all entries are undefined, return undefined
+ if (
+ Object.values(intersection).reduce(
+ (acc: number, value) => (value !== undefined ? acc + 1 : acc),
+ 0
+ ) === 0
+ ) {
+ return undefined;
+ } else {
+ return intersection;
+ }
+};
From f398b492002dd33c0fc4f764be7a1e6d0d81c6f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?=
Date: Fri, 2 Oct 2020 16:03:42 +0100
Subject: [PATCH 20/21] [Usage Collection] [schema] `actions` (#78832)
---
x-pack/.telemetryrc.json | 1 -
.../server/usage/actions_usage_collector.ts | 23 +++++-
.../schema/xpack_plugins.json | 78 +++++++++++++++++++
3 files changed, 100 insertions(+), 2 deletions(-)
diff --git a/x-pack/.telemetryrc.json b/x-pack/.telemetryrc.json
index c7430666c538f6..db50727c599a9d 100644
--- a/x-pack/.telemetryrc.json
+++ b/x-pack/.telemetryrc.json
@@ -2,7 +2,6 @@
"output": "plugins/telemetry_collection_xpack/schema/xpack_plugins.json",
"root": "plugins/",
"exclude": [
- "plugins/actions/server/usage/actions_usage_collector.ts",
"plugins/alerts/server/usage/alerts_usage_collector.ts",
"plugins/apm/server/lib/apm_telemetry/index.ts"
]
diff --git a/x-pack/plugins/actions/server/usage/actions_usage_collector.ts b/x-pack/plugins/actions/server/usage/actions_usage_collector.ts
index aa546e08ea1ba0..fac57b6282c445 100644
--- a/x-pack/plugins/actions/server/usage/actions_usage_collector.ts
+++ b/x-pack/plugins/actions/server/usage/actions_usage_collector.ts
@@ -4,11 +4,26 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
+import { MakeSchemaFrom, UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { get } from 'lodash';
import { TaskManagerStartContract } from '../../../task_manager/server';
import { ActionsUsage } from './types';
+const byTypeSchema: MakeSchemaFrom['count_by_type'] = {
+ // TODO: Find out an automated way to populate the keys or reformat these into an array (and change the Remote Telemetry indexer accordingly)
+ DYNAMIC_KEY: { type: 'long' },
+ // Known actions:
+ __email: { type: 'long' },
+ __index: { type: 'long' },
+ __pagerduty: { type: 'long' },
+ '__server-log': { type: 'long' },
+ __slack: { type: 'long' },
+ __webhook: { type: 'long' },
+ __servicenow: { type: 'long' },
+ __jira: { type: 'long' },
+ __resilient: { type: 'long' },
+};
+
export function createActionsUsageCollector(
usageCollection: UsageCollectionSetup,
taskManager: TaskManagerStartContract
@@ -16,6 +31,12 @@ export function createActionsUsageCollector(
return usageCollection.makeUsageCollector({
type: 'actions',
isReady: () => true,
+ schema: {
+ count_total: { type: 'long' },
+ count_active_total: { type: 'long' },
+ count_by_type: byTypeSchema,
+ count_active_by_type: byTypeSchema,
+ },
fetch: async () => {
try {
const doc = await getLatestTaskState(await taskManager);
diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json
index b08585066f100a..bdafbfd8ec967c 100644
--- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json
+++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json
@@ -1,5 +1,83 @@
{
"properties": {
+ "actions": {
+ "properties": {
+ "count_total": {
+ "type": "long"
+ },
+ "count_active_total": {
+ "type": "long"
+ },
+ "count_by_type": {
+ "properties": {
+ "DYNAMIC_KEY": {
+ "type": "long"
+ },
+ "__email": {
+ "type": "long"
+ },
+ "__index": {
+ "type": "long"
+ },
+ "__pagerduty": {
+ "type": "long"
+ },
+ "__server-log": {
+ "type": "long"
+ },
+ "__slack": {
+ "type": "long"
+ },
+ "__webhook": {
+ "type": "long"
+ },
+ "__servicenow": {
+ "type": "long"
+ },
+ "__jira": {
+ "type": "long"
+ },
+ "__resilient": {
+ "type": "long"
+ }
+ }
+ },
+ "count_active_by_type": {
+ "properties": {
+ "DYNAMIC_KEY": {
+ "type": "long"
+ },
+ "__email": {
+ "type": "long"
+ },
+ "__index": {
+ "type": "long"
+ },
+ "__pagerduty": {
+ "type": "long"
+ },
+ "__server-log": {
+ "type": "long"
+ },
+ "__slack": {
+ "type": "long"
+ },
+ "__webhook": {
+ "type": "long"
+ },
+ "__servicenow": {
+ "type": "long"
+ },
+ "__jira": {
+ "type": "long"
+ },
+ "__resilient": {
+ "type": "long"
+ }
+ }
+ }
+ }
+ },
"canvas": {
"properties": {
"workpads": {
From fccfad24cb7856f0a871381524e3e404dedfa839 Mon Sep 17 00:00:00 2001
From: Marta Bondyra
Date: Fri, 2 Oct 2020 17:18:20 +0200
Subject: [PATCH 21/21] [Lens] remove test warnings about improper HTML
structure (#79251)
* [Lens] remove test warnings about improper HTML structure
---
.../workspace_panel/workspace_panel.tsx | 32 ++++++++-----------
1 file changed, 13 insertions(+), 19 deletions(-)
diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx
index 2a5798ac6a70c0..3993b4ffc02b0c 100644
--- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx
+++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx
@@ -208,27 +208,22 @@ export function InnerWorkspacePanel({
>
- {expression === null ? (
-
- ) : (
-
- )}
+ {expression === null
+ ? i18n.translate('xpack.lens.editorFrame.emptyWorkspace', {
+ defaultMessage: 'Drop some fields here to start',
+ })
+ : i18n.translate('xpack.lens.editorFrame.emptyWorkspaceSimple', {
+ defaultMessage: 'Drop field here',
+ })}
{expression === null && (
<>
-
+ {i18n.translate('xpack.lens.editorFrame.emptyWorkspaceHeading', {
+ defaultMessage: 'Lens is a new tool for creating visualization',
+ })}
@@ -237,10 +232,9 @@ export function InnerWorkspacePanel({
target="_blank"
external
>
-
+ {i18n.translate('xpack.lens.editorFrame.goToForums', {
+ defaultMessage: 'Make requests and give feedback',
+ })}