From f0bbc4044ed8eb26a41dba6d133c081a7a2c5296 Mon Sep 17 00:00:00 2001 From: Tomasz Ciecierski Date: Tue, 12 Jul 2022 14:47:22 +0200 Subject: [PATCH 01/42] add additional \r\n check in multilines (#134640) --- .../osquery/common/utils/build_query/remove_multilines.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/osquery/common/utils/build_query/remove_multilines.ts b/x-pack/plugins/osquery/common/utils/build_query/remove_multilines.ts index 66208a0c7524d..bb86704b0d269 100644 --- a/x-pack/plugins/osquery/common/utils/build_query/remove_multilines.ts +++ b/x-pack/plugins/osquery/common/utils/build_query/remove_multilines.ts @@ -6,4 +6,4 @@ */ export const removeMultilines = (query: string): string => - query.replaceAll('\n', ' ').replaceAll(/ +/g, ' '); + query.replaceAll('\r\n', ' ').replaceAll('\n', ' ').replaceAll(/ +/g, ' '); From 6d3b05255b2517fb6b9592984855c383f9070103 Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Tue, 12 Jul 2022 15:47:55 +0300 Subject: [PATCH 02/42] [Cloud Posture] create rules by benchmark type (#135798) --- .../common/constants.ts | 11 ++++ .../common/schemas/csp_rule_metadata.ts | 6 ++- .../latest_findings_table.test.tsx | 1 + .../fleet_integration.test.ts | 50 +++++++++++++++++++ .../fleet_integration/fleet_integration.ts | 40 ++++++++++++--- .../server/saved_objects/mappings.ts | 17 ++++++- .../saved_objects/migrations/csp_rule.ts | 3 +- .../migrations/csp_rule_template.ts | 3 +- 8 files changed, 119 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index 6ffd8cc01709e..795e90042a545 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -38,3 +38,14 @@ export const INTERNAL_FEATURE_FLAGS = { export const CSP_RULE_SAVED_OBJECT_TYPE = 'csp_rule'; export const CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE = 'csp-rule-template'; + +export const CLOUDBEAT_VANILLA = 'cloudbeat/vanilla'; // Integration input +export const INTEGRATION_CIS_K8S = 'cis_k8s'; // rule template benchmark id + +export const CLOUDBEAT_EKS = 'cloudbeat/eks'; // Integration input +export const INTEGRATION_CIS_EKS = 'cis_eks'; // rule template benchmark id + +export const CIS_INTEGRATION_INPUTS_MAP = { + [CLOUDBEAT_VANILLA]: INTEGRATION_CIS_K8S, + [CLOUDBEAT_EKS]: INTEGRATION_CIS_EKS, +} as const; diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_metadata.ts b/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_metadata.ts index 752233e7a3246..4bda1af7e04a6 100644 --- a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_metadata.ts +++ b/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_metadata.ts @@ -8,7 +8,11 @@ import { schema as rt, TypeOf } from '@kbn/config-schema'; export const cspRuleMetadataSchema = rt.object({ audit: rt.string(), - benchmark: rt.object({ name: rt.string(), version: rt.string() }), + benchmark: rt.object({ + name: rt.string(), + id: rt.oneOf([rt.literal('cis_k8s'), rt.literal('cis_eks')]), + version: rt.string(), + }), default_value: rt.maybe(rt.string()), description: rt.string(), id: rt.string(), diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx index 252e6e123cc44..676d05950c227 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx @@ -32,6 +32,7 @@ const getFakeFindings = (name: string): CspFinding & { id: string } => ({ benchmark: { name: 'CIS Kubernetes', version: '1.6.0', + id: 'cis_k8s', }, default_value: chance.sentence(), description: chance.paragraph(), diff --git a/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.test.ts b/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.test.ts index 45b93e8279614..f7e26f62df647 100644 --- a/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.test.ts @@ -18,6 +18,7 @@ import { import { createPackagePolicyMock, deletePackagePolicyMock } from '@kbn/fleet-plugin/common/mocks'; import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME } from '../../common/constants'; import { + getBenchmarkInputType, onPackagePolicyPostCreateCallback, removeCspRulesInstancesCallback, } from './fleet_integration'; @@ -41,6 +42,7 @@ describe('create CSP rules with post package create callback', () => { benchmark: { name: 'CIS Kubernetes V1.20', version: 'v1.0.0', + id: 'cis_k8s', }, enabled: true, rego_rule_id: 'cis_1_2_2', @@ -117,4 +119,52 @@ describe('create CSP rules with post package create callback', () => { expect(savedObjectRepositoryMock.find.mock.calls[0][0]).toMatchObject({ perPage: 10000 }); }); + + it('get default integration type from inputs with multiple enabled types', () => { + const mockPackagePolicy = createPackagePolicyMock(); + + // Both enabled falls back to default + mockPackagePolicy.inputs = [ + { type: 'cloudbeat/vanilla', enabled: true, streams: [] }, + { type: 'cloudbeat/eks', enabled: true, streams: [] }, + ]; + const type = getBenchmarkInputType(mockPackagePolicy.inputs); + expect(type).toMatch('cis_k8s'); + }); + + it('get default integration type from inputs without any enabled types', () => { + const mockPackagePolicy = createPackagePolicyMock(); + + // None enabled falls back to default + mockPackagePolicy.inputs = [ + { type: 'cloudbeat/vanilla', enabled: false, streams: [] }, + { type: 'cloudbeat/eks', enabled: false, streams: [] }, + ]; + const type = getBenchmarkInputType(mockPackagePolicy.inputs); + expect(type).toMatch('cis_k8s'); + }); + + it('get EKS integration type', () => { + const mockPackagePolicy = createPackagePolicyMock(); + + // Single EKS selected + mockPackagePolicy.inputs = [ + { type: 'cloudbeat/eks', enabled: true, streams: [] }, + { type: 'cloudbeat/vanilla', enabled: false, streams: [] }, + ]; + const typeEks = getBenchmarkInputType(mockPackagePolicy.inputs); + expect(typeEks).toMatch('cis_eks'); + }); + + it('get Vanilla K8S integration type', () => { + const mockPackagePolicy = createPackagePolicyMock(); + + // Single k8s selected + mockPackagePolicy.inputs = [ + { type: 'cloudbeat/eks', enabled: false, streams: [] }, + { type: 'cloudbeat/vanilla', enabled: true, streams: [] }, + ]; + const typeK8s = getBenchmarkInputType(mockPackagePolicy.inputs); + expect(typeK8s).toMatch('cis_k8s'); + }); }); diff --git a/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.ts b/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.ts index f8c9b2768d0e2..be81e6dac27f9 100644 --- a/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.ts +++ b/x-pack/plugins/cloud_security_posture/server/fleet_integration/fleet_integration.ts @@ -12,19 +12,39 @@ import type { SavedObjectsClientContract, Logger, } from '@kbn/core/server'; -import { PackagePolicy, DeletePackagePoliciesResponse } from '@kbn/fleet-plugin/common'; +import { + PackagePolicy, + DeletePackagePoliciesResponse, + PackagePolicyInput, +} from '@kbn/fleet-plugin/common'; import { createCspRuleSearchFilterByPackagePolicy } from '../../common/utils/helpers'; import { + CLOUDBEAT_VANILLA, + CIS_INTEGRATION_INPUTS_MAP, CSP_RULE_SAVED_OBJECT_TYPE, CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE, } from '../../common/constants'; -import type { CspRule, CspRuleTemplate } from '../../common/schemas'; +import type { CspRule, CspRuleMetadata, CspRuleTemplate } from '../../common/schemas'; + +type CloudbeatInputType = keyof typeof CIS_INTEGRATION_INPUTS_MAP; +type BenchmarkId = CspRuleMetadata['benchmark']['id']; + +const getBenchmarkTypeFilter = (type: BenchmarkId): string => + `${CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE}.attributes.metadata.benchmark.id: "${type}"`; + +const isEnabledBenchmarkInputType = (input: PackagePolicyInput) => + input.type in CIS_INTEGRATION_INPUTS_MAP && !!input.enabled; -type ArrayElement = ArrayType extends ReadonlyArray< - infer ElementType -> - ? ElementType - : never; +export const getBenchmarkInputType = (inputs: PackagePolicy['inputs']): BenchmarkId => { + const enabledInputs = inputs.filter(isEnabledBenchmarkInputType); + + // Use the only enabled input + if (enabledInputs.length === 1) + return CIS_INTEGRATION_INPUTS_MAP[enabledInputs[0].type as CloudbeatInputType]; + + // Use the the default input for multiple/none selected + return CIS_INTEGRATION_INPUTS_MAP[CLOUDBEAT_VANILLA]; +}; /** * Callback to handle creation of PackagePolicies in Fleet @@ -34,14 +54,18 @@ export const onPackagePolicyPostCreateCallback = async ( packagePolicy: PackagePolicy, savedObjectsClient: SavedObjectsClientContract ): Promise => { + const benchmarkType = getBenchmarkInputType(packagePolicy.inputs); + // Create csp-rules from the generic asset const existingRuleTemplates: SavedObjectsFindResponse = await savedObjectsClient.find({ type: CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE, perPage: 10000, + filter: getBenchmarkTypeFilter(benchmarkType), }); if (existingRuleTemplates.total === 0) { + logger.warn(`expected CSP rule templates to exists for type: ${benchmarkType}`); return; } @@ -64,7 +88,7 @@ export const onPackagePolicyPostCreateCallback = async ( * Callback to handle deletion of PackagePolicies in Fleet */ export const removeCspRulesInstancesCallback = async ( - deletedPackagePolicy: ArrayElement, + deletedPackagePolicy: DeletePackagePoliciesResponse[number], soClient: ISavedObjectsRepository, logger: Logger ): Promise => { diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/mappings.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/mappings.ts index 80f7ae069d459..829392deaf4e9 100644 --- a/x-pack/plugins/cloud_security_posture/server/saved_objects/mappings.ts +++ b/x-pack/plugins/cloud_security_posture/server/saved_objects/mappings.ts @@ -42,5 +42,20 @@ export const cspRuleSavedObjectMapping: SavedObjectsTypeMappingDefinition = { export const cspRuleTemplateSavedObjectMapping: SavedObjectsTypeMappingDefinition = { dynamic: false, - properties: {}, + properties: { + metadata: { + type: 'object', + properties: { + benchmark: { + type: 'object', + properties: { + id: { + // Needed for filtering rule templates by benchmark.id + type: 'keyword', + }, + }, + }, + }, + }, + }, }; diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule.ts index 38df754b341ff..9e4dea8a1a78b 100644 --- a/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule.ts +++ b/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule.ts @@ -17,7 +17,7 @@ function migrateCspRuleMetadata( context: SavedObjectMigrationContext ): SavedObjectUnsanitizedDoc { // eslint-disable-next-line @typescript-eslint/naming-convention - const { enabled, muted, package_policy_id, policy_id, ...metadata } = doc.attributes; + const { enabled, muted, package_policy_id, policy_id, benchmark, ...metadata } = doc.attributes; return { ...doc, @@ -28,6 +28,7 @@ function migrateCspRuleMetadata( policy_id, metadata: { ...metadata, + benchmark: { ...benchmark, id: 'cis_k8s' }, impact: metadata.impact || undefined, default_value: metadata.default_value || undefined, references: metadata.references || undefined, diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule_template.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule_template.ts index 0e6fd63275562..514b235cac304 100644 --- a/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule_template.ts +++ b/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule_template.ts @@ -19,7 +19,7 @@ function migrateCspRuleMetadata( doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext ): SavedObjectUnsanitizedDoc { - const { enabled, muted, ...metadata } = doc.attributes; + const { enabled, muted, benchmark, ...metadata } = doc.attributes; return { ...doc, attributes: { @@ -27,6 +27,7 @@ function migrateCspRuleMetadata( muted, metadata: { ...metadata, + benchmark: { ...benchmark, id: 'cis_k8s' }, impact: metadata.impact || undefined, default_value: metadata.default_value || undefined, references: metadata.references || undefined, From cd1e67cd2c1217d68a05614525aaac086317e526 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Tue, 12 Jul 2022 09:57:26 -0400 Subject: [PATCH 03/42] [Maps] Move layer stats collector logic to common (#135754) --- x-pack/plugins/maps/common/telemetry/index.ts | 15 + .../telemetry/layer_stats_collector.test.ts | 111 +++++++ .../common/telemetry/layer_stats_collector.ts | 276 ++++++++++++++++++ .../sample_map_saved_objects.json | 0 x-pack/plugins/maps/common/telemetry/types.ts | 47 +++ .../server/maps_telemetry/find_maps.test.ts | 2 +- .../index_pattern_stats_collector.test.ts | 2 +- .../map_stats/map_stats_collector.test.ts | 2 +- .../map_stats/map_stats_collector.ts | 262 ++--------------- .../server/maps_telemetry/map_stats/types.ts | 49 +--- 10 files changed, 478 insertions(+), 288 deletions(-) create mode 100644 x-pack/plugins/maps/common/telemetry/index.ts create mode 100644 x-pack/plugins/maps/common/telemetry/layer_stats_collector.test.ts create mode 100644 x-pack/plugins/maps/common/telemetry/layer_stats_collector.ts rename x-pack/plugins/maps/{server/maps_telemetry => common/telemetry}/test_resources/sample_map_saved_objects.json (100%) create mode 100644 x-pack/plugins/maps/common/telemetry/types.ts diff --git a/x-pack/plugins/maps/common/telemetry/index.ts b/x-pack/plugins/maps/common/telemetry/index.ts new file mode 100644 index 0000000000000..c5ca2b05c6b52 --- /dev/null +++ b/x-pack/plugins/maps/common/telemetry/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { LayerStatsCollector } from './layer_stats_collector'; +export type { + EMS_BASEMAP_KEYS, + JOIN_KEYS, + LAYER_KEYS, + RESOLUTION_KEYS, + SCALING_KEYS, +} from './types'; diff --git a/x-pack/plugins/maps/common/telemetry/layer_stats_collector.test.ts b/x-pack/plugins/maps/common/telemetry/layer_stats_collector.test.ts new file mode 100644 index 0000000000000..fbdfad705a0d4 --- /dev/null +++ b/x-pack/plugins/maps/common/telemetry/layer_stats_collector.test.ts @@ -0,0 +1,111 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// @ts-ignore +import mapSavedObjects from './test_resources/sample_map_saved_objects.json'; +import { LayerStatsCollector } from './layer_stats_collector'; +import { MapSavedObjectAttributes } from '../map_saved_object_type'; + +const expecteds = [ + { + layerCount: 3, + basemapCounts: { roadmap: 1 }, + joinCounts: {}, + layerCounts: { ems_basemap: 1, ems_region: 1, es_agg_clusters: 1 }, + resolutionCounts: { coarse: 1 }, + scalingCounts: {}, + emsFileCounts: { italy_provinces: 1 }, + layerTypeCounts: { TILE: 1, GEOJSON_VECTOR: 2 }, + sourceCount: 3, + }, + { + layerCount: 3, + basemapCounts: { roadmap: 1 }, + joinCounts: { term: 1 }, + layerCounts: { ems_basemap: 1, ems_region: 1, es_docs: 1 }, + resolutionCounts: {}, + scalingCounts: { limit: 1 }, + emsFileCounts: { france_departments: 1 }, + layerTypeCounts: { TILE: 1, GEOJSON_VECTOR: 2 }, + sourceCount: 3, + }, + { + layerCount: 2, + basemapCounts: { roadmap: 1 }, + joinCounts: {}, + layerCounts: { ems_basemap: 1, ems_region: 1 }, + resolutionCounts: {}, + scalingCounts: {}, + emsFileCounts: { canada_provinces: 1 }, + layerTypeCounts: { TILE: 1, GEOJSON_VECTOR: 1 }, + sourceCount: 2, + }, + { + layerCount: 1, + basemapCounts: {}, + joinCounts: {}, + layerCounts: { es_agg_clusters: 1 }, + resolutionCounts: { coarse: 1 }, + scalingCounts: {}, + emsFileCounts: {}, + layerTypeCounts: { GEOJSON_VECTOR: 1 }, + sourceCount: 1, + }, + { + layerCount: 1, + basemapCounts: {}, + joinCounts: {}, + layerCounts: { es_agg_heatmap: 1 }, + resolutionCounts: { coarse: 1 }, + scalingCounts: {}, + emsFileCounts: {}, + layerTypeCounts: { HEATMAP: 1 }, + sourceCount: 1, + }, +]; + +const testsToRun = mapSavedObjects.map( + (savedObject: { attributes: MapSavedObjectAttributes }, index: number) => { + const { attributes } = savedObject; + return [attributes, expecteds[index]] as const; + } +); + +describe.each(testsToRun)('LayerStatsCollector %#', (attributes, expected) => { + const statsCollector = new LayerStatsCollector(attributes); + test('getLayerCount', () => { + expect(statsCollector.getLayerCount()).toBe(expected.layerCount); + }); + + test('getBasemapCounts', () => { + expect(statsCollector.getBasemapCounts()).toEqual(expected.basemapCounts); + }); + + test('getJoinCounts', () => { + expect(statsCollector.getJoinCounts()).toEqual(expected.joinCounts); + }); + + test('getLayerCounts', () => { + expect(statsCollector.getLayerCounts()).toEqual(expected.layerCounts); + }); + + test('getResolutionCounts', () => { + expect(statsCollector.getResolutionCounts()).toEqual(expected.resolutionCounts); + }); + + test('getScalingCounts', () => { + expect(statsCollector.getScalingCounts()).toEqual(expected.scalingCounts); + }); + + test('getEmsFileCounts', () => { + expect(statsCollector.getEmsFileCounts()).toEqual(expected.emsFileCounts); + }); + + test('getSourceCount', () => { + expect(statsCollector.getSourceCount()).toEqual(expected.sourceCount); + }); +}); diff --git a/x-pack/plugins/maps/common/telemetry/layer_stats_collector.ts b/x-pack/plugins/maps/common/telemetry/layer_stats_collector.ts new file mode 100644 index 0000000000000..634d11ca350a6 --- /dev/null +++ b/x-pack/plugins/maps/common/telemetry/layer_stats_collector.ts @@ -0,0 +1,276 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + DEFAULT_EMS_DARKMAP_ID, + DEFAULT_EMS_ROADMAP_DESATURATED_ID, + DEFAULT_EMS_ROADMAP_ID, +} from '@kbn/maps-ems-plugin/common'; +import { GRID_RESOLUTION, LAYER_TYPE, RENDER_AS, SCALING_TYPES, SOURCE_TYPES } from '../constants'; +import { + EMSTMSSourceDescriptor, + EMSFileSourceDescriptor, + ESGeoGridSourceDescriptor, + ESSearchSourceDescriptor, + LayerDescriptor, + VectorLayerDescriptor, +} from '../descriptor_types'; +import { MapSavedObjectAttributes } from '../map_saved_object_type'; +import { EMS_BASEMAP_KEYS, JOIN_KEYS, LAYER_KEYS, RESOLUTION_KEYS, SCALING_KEYS } from './types'; + +export class LayerStatsCollector { + private _layerCount = 0; + + private _basemapCounts: { [key in EMS_BASEMAP_KEYS]?: number } = {}; + private _joinCounts: { [key in JOIN_KEYS]?: number } = {}; + private _layerCounts: { [key in LAYER_KEYS]?: number } = {}; + private _resolutionCounts: { [key in RESOLUTION_KEYS]?: number } = {}; + private _scalingCounts: { [key in SCALING_KEYS]?: number } = {}; + private _emsFileCounts: { [key: string]: number } = {}; + private _layerTypeCounts: { [key: string]: number } = {}; + private _sourceIds: Set = new Set(); + + constructor(attributes: MapSavedObjectAttributes) { + if (!attributes || !attributes.layerListJSON) { + return; + } + + let layerList: LayerDescriptor[] = []; + try { + layerList = JSON.parse(attributes.layerListJSON); + } catch (e) { + return; + } + + this._layerCount = layerList.length; + layerList.forEach((layerDescriptor) => { + this._updateCounts(getBasemapKey(layerDescriptor), this._basemapCounts); + this._updateCounts(getJoinKey(layerDescriptor), this._joinCounts); + this._updateCounts(getLayerKey(layerDescriptor), this._layerCounts); + this._updateCounts(getResolutionKey(layerDescriptor), this._resolutionCounts); + this._updateCounts(getScalingKey(layerDescriptor), this._scalingCounts); + this._updateCounts(getEmsFileId(layerDescriptor), this._emsFileCounts); + if (layerDescriptor.type) { + this._updateCounts(layerDescriptor.type, this._layerTypeCounts); + } + if (layerDescriptor.sourceDescriptor?.id) { + this._sourceIds.add(layerDescriptor.sourceDescriptor.id); + } + }); + } + + getLayerCount() { + return this._layerCount; + } + + getBasemapCounts() { + return this._basemapCounts; + } + + getJoinCounts() { + return this._joinCounts; + } + + getLayerCounts() { + return this._layerCounts; + } + + getResolutionCounts() { + return this._resolutionCounts; + } + + getScalingCounts() { + return this._scalingCounts; + } + + getEmsFileCounts() { + return this._emsFileCounts; + } + + getLayerTypeCounts() { + return this._layerTypeCounts; + } + + getSourceCount() { + return this._sourceIds.size; + } + + _updateCounts(key: string | null, counts: { [key: string]: number }) { + if (key) { + if (key in counts) { + counts[key] += 1; + } else { + counts[key] = 1; + } + } + } +} + +function getEmsFileId(layerDescriptor: LayerDescriptor): string | null { + return layerDescriptor.sourceDescriptor !== null && + layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.EMS_FILE && + 'id' in layerDescriptor.sourceDescriptor + ? (layerDescriptor.sourceDescriptor as EMSFileSourceDescriptor).id + : null; +} + +function getBasemapKey(layerDescriptor: LayerDescriptor): EMS_BASEMAP_KEYS | null { + if ( + !layerDescriptor.sourceDescriptor || + layerDescriptor.sourceDescriptor.type !== SOURCE_TYPES.EMS_TMS + ) { + return null; + } + + const descriptor = layerDescriptor.sourceDescriptor as EMSTMSSourceDescriptor; + + if (descriptor.isAutoSelect) { + return EMS_BASEMAP_KEYS.AUTO; + } + + if (descriptor.id === DEFAULT_EMS_ROADMAP_ID) { + return EMS_BASEMAP_KEYS.ROADMAP; + } + + if (descriptor.id === DEFAULT_EMS_ROADMAP_DESATURATED_ID) { + return EMS_BASEMAP_KEYS.ROADMAP_DESATURATED; + } + + if (descriptor.id === DEFAULT_EMS_DARKMAP_ID) { + return EMS_BASEMAP_KEYS.DARK; + } + + return null; +} + +function getJoinKey(layerDescriptor: LayerDescriptor): JOIN_KEYS | null { + return layerDescriptor.type === LAYER_TYPE.GEOJSON_VECTOR && + (layerDescriptor as VectorLayerDescriptor)?.joins?.length + ? JOIN_KEYS.TERM + : null; +} + +function getLayerKey(layerDescriptor: LayerDescriptor): LAYER_KEYS | null { + if (!layerDescriptor.sourceDescriptor) { + return null; + } + + if (layerDescriptor.type === LAYER_TYPE.HEATMAP) { + return LAYER_KEYS.ES_AGG_HEATMAP; + } + + if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.EMS_FILE) { + return LAYER_KEYS.EMS_REGION; + } + + if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.EMS_TMS) { + return LAYER_KEYS.EMS_BASEMAP; + } + + if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.KIBANA_TILEMAP) { + return LAYER_KEYS.KBN_TMS_RASTER; + } + + if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.EMS_XYZ) { + return LAYER_KEYS.UX_TMS_RASTER; + } + + if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.WMS) { + return LAYER_KEYS.UX_WMS; + } + + if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.MVT_SINGLE_LAYER) { + return LAYER_KEYS.UX_TMS_MVT; + } + + if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.ES_GEO_LINE) { + return LAYER_KEYS.ES_TRACKS; + } + + if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.ES_PEW_PEW) { + return LAYER_KEYS.ES_POINT_TO_POINT; + } + + if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.ES_SEARCH) { + const sourceDescriptor = layerDescriptor.sourceDescriptor as ESSearchSourceDescriptor; + + if (sourceDescriptor.scalingType === SCALING_TYPES.TOP_HITS) { + return LAYER_KEYS.ES_TOP_HITS; + } else { + return LAYER_KEYS.ES_DOCS; + } + } + + if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.ES_GEO_GRID) { + const sourceDescriptor = layerDescriptor.sourceDescriptor as ESGeoGridSourceDescriptor; + if (sourceDescriptor.requestType === RENDER_AS.POINT) { + return LAYER_KEYS.ES_AGG_CLUSTERS; + } else if (sourceDescriptor.requestType === RENDER_AS.GRID) { + return LAYER_KEYS.ES_AGG_GRIDS; + } else if (sourceDescriptor.requestType === RENDER_AS.HEX) { + return LAYER_KEYS.ES_AGG_HEXAGONS; + } + } + + return null; +} + +function getResolutionKey(layerDescriptor: LayerDescriptor): RESOLUTION_KEYS | null { + if ( + !layerDescriptor.sourceDescriptor || + layerDescriptor.sourceDescriptor.type !== SOURCE_TYPES.ES_GEO_GRID || + !(layerDescriptor.sourceDescriptor as ESGeoGridSourceDescriptor).resolution + ) { + return null; + } + + const descriptor = layerDescriptor.sourceDescriptor as ESGeoGridSourceDescriptor; + + if (descriptor.resolution === GRID_RESOLUTION.COARSE) { + return RESOLUTION_KEYS.COARSE; + } + + if (descriptor.resolution === GRID_RESOLUTION.FINE) { + return RESOLUTION_KEYS.FINE; + } + + if (descriptor.resolution === GRID_RESOLUTION.MOST_FINE) { + return RESOLUTION_KEYS.MOST_FINE; + } + + if (descriptor.resolution === GRID_RESOLUTION.SUPER_FINE) { + return RESOLUTION_KEYS.SUPER_FINE; + } + + return null; +} + +function getScalingKey(layerDescriptor: LayerDescriptor): SCALING_KEYS | null { + if ( + !layerDescriptor.sourceDescriptor || + layerDescriptor.sourceDescriptor.type !== SOURCE_TYPES.ES_SEARCH || + !(layerDescriptor.sourceDescriptor as ESSearchSourceDescriptor).scalingType + ) { + return null; + } + + const descriptor = layerDescriptor.sourceDescriptor as ESSearchSourceDescriptor; + + if (descriptor.scalingType === SCALING_TYPES.CLUSTERS) { + return SCALING_KEYS.CLUSTERS; + } + + if (descriptor.scalingType === SCALING_TYPES.MVT) { + return SCALING_KEYS.MVT; + } + + if (descriptor.scalingType === SCALING_TYPES.LIMIT) { + return SCALING_KEYS.LIMIT; + } + + return null; +} diff --git a/x-pack/plugins/maps/server/maps_telemetry/test_resources/sample_map_saved_objects.json b/x-pack/plugins/maps/common/telemetry/test_resources/sample_map_saved_objects.json similarity index 100% rename from x-pack/plugins/maps/server/maps_telemetry/test_resources/sample_map_saved_objects.json rename to x-pack/plugins/maps/common/telemetry/test_resources/sample_map_saved_objects.json diff --git a/x-pack/plugins/maps/common/telemetry/types.ts b/x-pack/plugins/maps/common/telemetry/types.ts new file mode 100644 index 0000000000000..c48be2de13c49 --- /dev/null +++ b/x-pack/plugins/maps/common/telemetry/types.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export enum EMS_BASEMAP_KEYS { + ROADMAP_DESATURATED = 'roadmap_desaturated', + ROADMAP = 'roadmap', + AUTO = 'auto', + DARK = 'dark', +} + +export enum JOIN_KEYS { + TERM = 'term', +} + +export enum LAYER_KEYS { + ES_DOCS = 'es_docs', + ES_TOP_HITS = 'es_top_hits', + ES_TRACKS = 'es_tracks', + ES_POINT_TO_POINT = 'es_point_to_point', + ES_AGG_CLUSTERS = 'es_agg_clusters', + ES_AGG_GRIDS = 'es_agg_grids', + ES_AGG_HEXAGONS = 'es_agg_hexagons', + ES_AGG_HEATMAP = 'es_agg_heatmap', + EMS_REGION = 'ems_region', + EMS_BASEMAP = 'ems_basemap', + KBN_TMS_RASTER = 'kbn_tms_raster', + UX_TMS_RASTER = 'ux_tms_raster', // configured in the UX layer wizard of Maps + UX_TMS_MVT = 'ux_tms_mvt', // configured in the UX layer wizard of Maps + UX_WMS = 'ux_wms', // configured in the UX layer wizard of Maps +} + +export enum RESOLUTION_KEYS { + COARSE = 'coarse', + FINE = 'fine', + MOST_FINE = 'most_fine', + SUPER_FINE = 'super_fine', +} + +export enum SCALING_KEYS { + LIMIT = 'limit', + MVT = 'mvt', + CLUSTERS = 'clusters', +} diff --git a/x-pack/plugins/maps/server/maps_telemetry/find_maps.test.ts b/x-pack/plugins/maps/server/maps_telemetry/find_maps.test.ts index 85677ef13bf6b..acc9d11c66d4a 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/find_maps.test.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/find_maps.test.ts @@ -7,7 +7,7 @@ import { ISavedObjectsRepository } from '@kbn/core/server'; // @ts-ignore -import mapSavedObjects from './test_resources/sample_map_saved_objects.json'; +import mapSavedObjects from '../../common/telemetry/test_resources/sample_map_saved_objects.json'; import { findMaps } from './find_maps'; function getMockSavedObjectsClient(perPage: number) { diff --git a/x-pack/plugins/maps/server/maps_telemetry/index_pattern_stats/index_pattern_stats_collector.test.ts b/x-pack/plugins/maps/server/maps_telemetry/index_pattern_stats/index_pattern_stats_collector.test.ts index 02dd0718b3da7..9877c29bc5951 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/index_pattern_stats/index_pattern_stats_collector.test.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/index_pattern_stats/index_pattern_stats_collector.test.ts @@ -7,7 +7,7 @@ import { asyncForEach } from '@kbn/std'; // @ts-ignore -import mapSavedObjects from '../test_resources/sample_map_saved_objects.json'; +import mapSavedObjects from '../../../common/telemetry/test_resources/sample_map_saved_objects.json'; import { DataViewsService } from '@kbn/data-views-plugin/common'; import { IndexPatternStatsCollector } from './index_pattern_stats_collector'; diff --git a/x-pack/plugins/maps/server/maps_telemetry/map_stats/map_stats_collector.test.ts b/x-pack/plugins/maps/server/maps_telemetry/map_stats/map_stats_collector.test.ts index c6aeff35f98b1..f75b66a8a7f4d 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/map_stats/map_stats_collector.test.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/map_stats/map_stats_collector.test.ts @@ -6,7 +6,7 @@ */ // @ts-ignore -import mapSavedObjects from '../test_resources/sample_map_saved_objects.json'; +import mapSavedObjects from '../../../common/telemetry/test_resources/sample_map_saved_objects.json'; import { MapStatsCollector } from './map_stats_collector'; test('returns zeroed telemetry data when there are no saved objects', () => { diff --git a/x-pack/plugins/maps/server/maps_telemetry/map_stats/map_stats_collector.ts b/x-pack/plugins/maps/server/maps_telemetry/map_stats/map_stats_collector.ts index 9eeafaec54e22..c1bfb4791a5d5 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/map_stats/map_stats_collector.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/map_stats/map_stats_collector.ts @@ -5,37 +5,17 @@ * 2.0. */ -import _ from 'lodash'; -import { - DEFAULT_EMS_DARKMAP_ID, - DEFAULT_EMS_ROADMAP_DESATURATED_ID, - DEFAULT_EMS_ROADMAP_ID, -} from '@kbn/maps-ems-plugin/common'; -import { - GRID_RESOLUTION, - LAYER_TYPE, - RENDER_AS, - SCALING_TYPES, - SOURCE_TYPES, -} from '../../../common/constants'; -import { - EMSTMSSourceDescriptor, - EMSFileSourceDescriptor, - ESGeoGridSourceDescriptor, - ESSearchSourceDescriptor, - LayerDescriptor, - VectorLayerDescriptor, -} from '../../../common/descriptor_types'; import { MapSavedObjectAttributes } from '../../../common/map_saved_object_type'; import { - ClusterCountStats, EMS_BASEMAP_KEYS, JOIN_KEYS, LAYER_KEYS, - MapStats, RESOLUTION_KEYS, SCALING_KEYS, -} from './types'; +} from '../../../common/telemetry/types'; +import { LayerStatsCollector } from '../../../common/telemetry/layer_stats_collector'; + +import { ClusterCountStats, MapStats } from './types'; /* * Use MapStatsCollector instance to track map saved object stats. @@ -57,20 +37,13 @@ export class MapStatsCollector { private _sourceCountStats: ClusterCountStats | undefined; push(attributes: MapSavedObjectAttributes) { - if (!attributes || !attributes.layerListJSON) { - return; - } + const layerStatsCollector = new LayerStatsCollector(attributes); - let layerList: LayerDescriptor[] = []; - try { - layerList = JSON.parse(attributes.layerListJSON); - } catch (e) { - return; - } + if (!layerStatsCollector) return; this._mapCount++; - const layerCount = layerList.length; + const layerCount = layerStatsCollector.getLayerCount(); if (this._layerCountStats) { const layerCountTotal = this._layerCountStats.total + layerCount; this._layerCountStats = { @@ -88,16 +61,7 @@ export class MapStatsCollector { }; } - const sourceIdList = layerList - .map((layer: LayerDescriptor) => { - return layer.sourceDescriptor && 'id' in layer.sourceDescriptor - ? layer.sourceDescriptor.id - : null; - }) - .filter((id: string | null | undefined) => { - return id; - }); - const sourceCount = _.uniq(sourceIdList).length; + const sourceCount = layerStatsCollector.getSourceCount(); if (this._sourceCountStats) { const sourceCountTotal = this._sourceCountStats.total + sourceCount; this._sourceCountStats = { @@ -115,31 +79,16 @@ export class MapStatsCollector { }; } - const basemapCounts: { [key in EMS_BASEMAP_KEYS]?: number } = {}; - const joinCounts: { [key in JOIN_KEYS]?: number } = {}; - const layerCounts: { [key in LAYER_KEYS]?: number } = {}; - const resolutionCounts: { [key in RESOLUTION_KEYS]?: number } = {}; - const scalingCounts: { [key in SCALING_KEYS]?: number } = {}; - const emsFileCounts: { [key: string]: number } = {}; - const layerTypeCounts: { [key: string]: number } = {}; - layerList.forEach((layerDescriptor) => { - this._updateCounts(getBasemapKey(layerDescriptor), basemapCounts); - this._updateCounts(getJoinKey(layerDescriptor), joinCounts); - this._updateCounts(getLayerKey(layerDescriptor), layerCounts); - this._updateCounts(getResolutionKey(layerDescriptor), resolutionCounts); - this._updateCounts(getScalingKey(layerDescriptor), scalingCounts); - this._updateCounts(getEmsFileId(layerDescriptor), emsFileCounts); - if (layerDescriptor.type) { - this._updateCounts(layerDescriptor.type, layerTypeCounts); - } - }); - this._updateClusterStats(this._basemapClusterStats, basemapCounts); - this._updateClusterStats(this._joinClusterStats, joinCounts); - this._updateClusterStats(this._layerClusterStats, layerCounts); - this._updateClusterStats(this._resolutionClusterStats, resolutionCounts); - this._updateClusterStats(this._scalingClusterStats, scalingCounts); - this._updateClusterStats(this._emsFileClusterStats, emsFileCounts); - this._updateClusterStats(this._layerTypeClusterStats, layerTypeCounts); + this._updateClusterStats(this._basemapClusterStats, layerStatsCollector.getBasemapCounts()); + this._updateClusterStats(this._joinClusterStats, layerStatsCollector.getJoinCounts()); + this._updateClusterStats(this._layerClusterStats, layerStatsCollector.getLayerCounts()); + this._updateClusterStats( + this._resolutionClusterStats, + layerStatsCollector.getResolutionCounts() + ); + this._updateClusterStats(this._scalingClusterStats, layerStatsCollector.getScalingCounts()); + this._updateClusterStats(this._emsFileClusterStats, layerStatsCollector.getEmsFileCounts()); + this._updateClusterStats(this._layerTypeClusterStats, layerStatsCollector.getLayerTypeCounts()); } getStats(): MapStats { @@ -198,16 +147,6 @@ export class MapStatsCollector { } } - _updateCounts(key: string | null, counts: { [key: string]: number }) { - if (key) { - if (key in counts) { - counts[key] += 1; - } else { - counts[key] = 1; - } - } - } - // stats in attributesPerMap do not include 'total' key. Use this method to remove 'total' key from ClusterCountStats _excludeTotalFromKeyedStats(clusterStats: { [key: string]: ClusterCountStats }): { [key: string]: Omit; @@ -232,168 +171,3 @@ export class MapStatsCollector { return modifiedStats; } } - -function getEmsFileId(layerDescriptor: LayerDescriptor): string | null { - return layerDescriptor.sourceDescriptor !== null && - layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.EMS_FILE && - 'id' in layerDescriptor.sourceDescriptor - ? (layerDescriptor.sourceDescriptor as EMSFileSourceDescriptor).id - : null; -} - -function getBasemapKey(layerDescriptor: LayerDescriptor): EMS_BASEMAP_KEYS | null { - if ( - !layerDescriptor.sourceDescriptor || - layerDescriptor.sourceDescriptor.type !== SOURCE_TYPES.EMS_TMS - ) { - return null; - } - - const descriptor = layerDescriptor.sourceDescriptor as EMSTMSSourceDescriptor; - - if (descriptor.isAutoSelect) { - return EMS_BASEMAP_KEYS.AUTO; - } - - if (descriptor.id === DEFAULT_EMS_ROADMAP_ID) { - return EMS_BASEMAP_KEYS.ROADMAP; - } - - if (descriptor.id === DEFAULT_EMS_ROADMAP_DESATURATED_ID) { - return EMS_BASEMAP_KEYS.ROADMAP_DESATURATED; - } - - if (descriptor.id === DEFAULT_EMS_DARKMAP_ID) { - return EMS_BASEMAP_KEYS.DARK; - } - - return null; -} - -function getJoinKey(layerDescriptor: LayerDescriptor): JOIN_KEYS | null { - return layerDescriptor.type === LAYER_TYPE.GEOJSON_VECTOR && - (layerDescriptor as VectorLayerDescriptor)?.joins?.length - ? JOIN_KEYS.TERM - : null; -} - -function getLayerKey(layerDescriptor: LayerDescriptor): LAYER_KEYS | null { - if (!layerDescriptor.sourceDescriptor) { - return null; - } - - if (layerDescriptor.type === LAYER_TYPE.HEATMAP) { - return LAYER_KEYS.ES_AGG_HEATMAP; - } - - if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.EMS_FILE) { - return LAYER_KEYS.EMS_REGION; - } - - if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.EMS_TMS) { - return LAYER_KEYS.EMS_BASEMAP; - } - - if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.KIBANA_TILEMAP) { - return LAYER_KEYS.KBN_TMS_RASTER; - } - - if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.EMS_XYZ) { - return LAYER_KEYS.UX_TMS_RASTER; - } - - if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.WMS) { - return LAYER_KEYS.UX_WMS; - } - - if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.MVT_SINGLE_LAYER) { - return LAYER_KEYS.UX_TMS_MVT; - } - - if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.ES_GEO_LINE) { - return LAYER_KEYS.ES_TRACKS; - } - - if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.ES_PEW_PEW) { - return LAYER_KEYS.ES_POINT_TO_POINT; - } - - if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.ES_SEARCH) { - const sourceDescriptor = layerDescriptor.sourceDescriptor as ESSearchSourceDescriptor; - - if (sourceDescriptor.scalingType === SCALING_TYPES.TOP_HITS) { - return LAYER_KEYS.ES_TOP_HITS; - } else { - return LAYER_KEYS.ES_DOCS; - } - } - - if (layerDescriptor.sourceDescriptor.type === SOURCE_TYPES.ES_GEO_GRID) { - const sourceDescriptor = layerDescriptor.sourceDescriptor as ESGeoGridSourceDescriptor; - if (sourceDescriptor.requestType === RENDER_AS.POINT) { - return LAYER_KEYS.ES_AGG_CLUSTERS; - } else if (sourceDescriptor.requestType === RENDER_AS.GRID) { - return LAYER_KEYS.ES_AGG_GRIDS; - } else if (sourceDescriptor.requestType === RENDER_AS.HEX) { - return LAYER_KEYS.ES_AGG_HEXAGONS; - } - } - - return null; -} - -function getResolutionKey(layerDescriptor: LayerDescriptor): RESOLUTION_KEYS | null { - if ( - !layerDescriptor.sourceDescriptor || - layerDescriptor.sourceDescriptor.type !== SOURCE_TYPES.ES_GEO_GRID || - !(layerDescriptor.sourceDescriptor as ESGeoGridSourceDescriptor).resolution - ) { - return null; - } - - const descriptor = layerDescriptor.sourceDescriptor as ESGeoGridSourceDescriptor; - - if (descriptor.resolution === GRID_RESOLUTION.COARSE) { - return RESOLUTION_KEYS.COARSE; - } - - if (descriptor.resolution === GRID_RESOLUTION.FINE) { - return RESOLUTION_KEYS.FINE; - } - - if (descriptor.resolution === GRID_RESOLUTION.MOST_FINE) { - return RESOLUTION_KEYS.MOST_FINE; - } - - if (descriptor.resolution === GRID_RESOLUTION.SUPER_FINE) { - return RESOLUTION_KEYS.SUPER_FINE; - } - - return null; -} - -function getScalingKey(layerDescriptor: LayerDescriptor): SCALING_KEYS | null { - if ( - !layerDescriptor.sourceDescriptor || - layerDescriptor.sourceDescriptor.type !== SOURCE_TYPES.ES_SEARCH || - !(layerDescriptor.sourceDescriptor as ESSearchSourceDescriptor).scalingType - ) { - return null; - } - - const descriptor = layerDescriptor.sourceDescriptor as ESSearchSourceDescriptor; - - if (descriptor.scalingType === SCALING_TYPES.CLUSTERS) { - return SCALING_KEYS.CLUSTERS; - } - - if (descriptor.scalingType === SCALING_TYPES.MVT) { - return SCALING_KEYS.MVT; - } - - if (descriptor.scalingType === SCALING_TYPES.LIMIT) { - return SCALING_KEYS.LIMIT; - } - - return null; -} diff --git a/x-pack/plugins/maps/server/maps_telemetry/map_stats/types.ts b/x-pack/plugins/maps/server/maps_telemetry/map_stats/types.ts index 78190873a1768..8524c1d7c0de6 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/map_stats/types.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/map_stats/types.ts @@ -5,6 +5,14 @@ * 2.0. */ +import type { + LAYER_KEYS, + SCALING_KEYS, + JOIN_KEYS, + EMS_BASEMAP_KEYS, + RESOLUTION_KEYS, +} from '../../../common/telemetry/types'; + export interface ClusterCountStats { min: number; max: number; @@ -12,47 +20,6 @@ export interface ClusterCountStats { avg: number; } -export enum EMS_BASEMAP_KEYS { - ROADMAP_DESATURATED = 'roadmap_desaturated', - ROADMAP = 'roadmap', - AUTO = 'auto', - DARK = 'dark', -} - -export enum JOIN_KEYS { - TERM = 'term', -} - -export enum LAYER_KEYS { - ES_DOCS = 'es_docs', - ES_TOP_HITS = 'es_top_hits', - ES_TRACKS = 'es_tracks', - ES_POINT_TO_POINT = 'es_point_to_point', - ES_AGG_CLUSTERS = 'es_agg_clusters', - ES_AGG_GRIDS = 'es_agg_grids', - ES_AGG_HEXAGONS = 'es_agg_hexagons', - ES_AGG_HEATMAP = 'es_agg_heatmap', - EMS_REGION = 'ems_region', - EMS_BASEMAP = 'ems_basemap', - KBN_TMS_RASTER = 'kbn_tms_raster', - UX_TMS_RASTER = 'ux_tms_raster', // configured in the UX layer wizard of Maps - UX_TMS_MVT = 'ux_tms_mvt', // configured in the UX layer wizard of Maps - UX_WMS = 'ux_wms', // configured in the UX layer wizard of Maps -} - -export enum RESOLUTION_KEYS { - COARSE = 'coarse', - FINE = 'fine', - MOST_FINE = 'most_fine', - SUPER_FINE = 'super_fine', -} - -export enum SCALING_KEYS { - LIMIT = 'limit', - MVT = 'mvt', - CLUSTERS = 'clusters', -} - export interface MapStats { mapsTotalCount: number; timeCaptured: string; From 97c0588e517ad93b61face977d5bf7b6a815f400 Mon Sep 17 00:00:00 2001 From: Tre Date: Tue, 12 Jul 2022 15:11:42 +0100 Subject: [PATCH 04/42] [Archive Migration] x-pack spaces/enter_space (#135200) * [Archive Migration] x-pack spaces/enter_space * Replace the archive definitions with code. * Add the malformed url to the kbn archive, thanks to a tip from Oleg Zasypkin. * Drop exclusive. * Use Oleg's suggestion. * Whoops. --- .../functional/apps/spaces/enter_space.ts | 35 ++- .../es_archives/spaces/enter_space/data.json | 83 ------ .../spaces/enter_space/mappings.json | 248 ------------------ .../kbn_archiver/spaces/enter_space.json | 33 +++ 4 files changed, 62 insertions(+), 337 deletions(-) delete mode 100644 x-pack/test/functional/es_archives/spaces/enter_space/data.json delete mode 100644 x-pack/test/functional/es_archives/spaces/enter_space/mappings.json create mode 100644 x-pack/test/functional/fixtures/kbn_archiver/spaces/enter_space.json diff --git a/x-pack/test/functional/apps/spaces/enter_space.ts b/x-pack/test/functional/apps/spaces/enter_space.ts index d58a5c0f19f39..4e7655add8667 100644 --- a/x-pack/test/functional/apps/spaces/enter_space.ts +++ b/x-pack/test/functional/apps/spaces/enter_space.ts @@ -7,22 +7,45 @@ import { FtrProviderContext } from '../../ftr_provider_context'; -export default function enterSpaceFunctonalTests({ +export default function enterSpaceFunctionalTests({ getService, getPageObjects, }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); const PageObjects = getPageObjects(['security', 'spaceSelector']); + const spacesService = getService('spaces'); describe('Enter Space', function () { this.tags('includeFirefox'); before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/spaces/enter_space'); + await spacesService.create({ + id: 'another-space', + name: 'Another Space', + disabledFeatures: [], + }); + await kibanaServer.uiSettings.replace( + { + defaultRoute: '/app/canvas', + buildNum: 8467, + 'dateFormat:tz': 'UTC', + }, + { space: 'another-space' } + ); + const config = await kibanaServer.savedObjects.get({ + id: await kibanaServer.version.get(), + type: 'config', + }); + await kibanaServer.savedObjects.update({ + id: config.id, + type: config.type, + attributes: { defaultRoute: 'http://example.com/evil' }, + }); await PageObjects.security.forceLogout(); }); - after( - async () => await esArchiver.unload('x-pack/test/functional/es_archives/spaces/enter_space') - ); + after(async () => { + await spacesService.delete('another-space'); + await kibanaServer.savedObjects.cleanStandardList(); + }); afterEach(async () => { // NOTE: Logout needs to happen before anything else to avoid flaky behavior diff --git a/x-pack/test/functional/es_archives/spaces/enter_space/data.json b/x-pack/test/functional/es_archives/spaces/enter_space/data.json deleted file mode 100644 index 475fc14e96e6a..0000000000000 --- a/x-pack/test/functional/es_archives/spaces/enter_space/data.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "type": "doc", - "value": { - "id": "config:6.0.0", - "index": ".kibana", - "source": { - "config": { - "buildNum": 8467, - "dateFormat:tz": "UTC", - "defaultRoute": "http://example.com/evil" - }, - "type": "config" - } - } -} - -{ - "type": "doc", - "value": { - "id": "another-space:config:6.0.0", - "index": ".kibana", - "source": { - "namespace": "another-space", - "config": { - "buildNum": 8467, - "dateFormat:tz": "UTC", - "defaultRoute": "/app/canvas" - }, - "type": "config" - } - } -} - -{ - "type": "doc", - "value": { - "index": ".kibana", - "id": "another-space:index-pattern:logstash-*", - "source": { - "index-pattern": { - "title": "logstash-*", - "timeFieldName": "@timestamp", - "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]" - }, - "type": "index-pattern", - "namespace": "another-space", - "migrationVersion": { - "index-pattern": "6.5.0" - }, - "updated_at": "2018-12-21T00:43:07.096Z" - } - } -} - -{ - "type": "doc", - "value": { - "id": "space:default", - "index": ".kibana", - "source": { - "space": { - "description": "This is the default space!", - "name": "Default" - }, - "type": "space" - } - } -} - -{ - "type": "doc", - "value": { - "id": "space:another-space", - "index": ".kibana", - "source": { - "space": { - "description": "This is another space", - "name": "Another Space" - }, - "type": "space" - } - } -} \ No newline at end of file diff --git a/x-pack/test/functional/es_archives/spaces/enter_space/mappings.json b/x-pack/test/functional/es_archives/spaces/enter_space/mappings.json deleted file mode 100644 index f813fca64c328..0000000000000 --- a/x-pack/test/functional/es_archives/spaces/enter_space/mappings.json +++ /dev/null @@ -1,248 +0,0 @@ -{ - "type": "index", - "value": { - "aliases": { - ".kibana": {} - }, - "index": ".kibana_1", - "mappings": { - "properties": { - "config": { - "dynamic": "true", - "properties": { - "buildNum": { - "type": "keyword" - }, - "dateFormat:tz": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" - }, - "defaultRoute": { - "type": "keyword" - } - } - }, - "dashboard": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "optionsJSON": { - "type": "text" - }, - "panelsJSON": { - "type": "text" - }, - "refreshInterval": { - "properties": { - "display": { - "type": "keyword" - }, - "pause": { - "type": "boolean" - }, - "section": { - "type": "integer" - }, - "value": { - "type": "integer" - } - } - }, - "timeFrom": { - "type": "keyword" - }, - "timeRestore": { - "type": "boolean" - }, - "timeTo": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "index-pattern": { - "dynamic": "strict", - "properties": { - "fieldFormatMap": { - "type": "text" - }, - "fields": { - "type": "text" - }, - "intervalName": { - "type": "keyword" - }, - "notExpandable": { - "type": "boolean" - }, - "sourceFilters": { - "type": "text" - }, - "timeFieldName": { - "type": "keyword" - }, - "title": { - "type": "text" - } - } - }, - "search": { - "dynamic": "strict", - "properties": { - "columns": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "hits": { - "type": "integer" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "sort": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "version": { - "type": "integer" - } - } - }, - "server": { - "dynamic": "strict", - "properties": { - "uuid": { - "type": "keyword" - } - } - }, - "space": { - "properties": { - "_reserved": { - "type": "boolean" - }, - "color": { - "type": "keyword" - }, - "description": { - "type": "text" - }, - "disabledFeatures": { - "type": "keyword" - }, - "initials": { - "type": "keyword" - }, - "name": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "spaceId": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "url": { - "dynamic": "strict", - "properties": { - "accessCount": { - "type": "long" - }, - "accessDate": { - "type": "date" - }, - "createDate": { - "type": "date" - }, - "url": { - "fields": { - "keyword": { - "ignore_above": 2048, - "type": "keyword" - } - }, - "type": "text" - } - } - }, - "visualization": { - "dynamic": "strict", - "properties": { - "description": { - "type": "text" - }, - "kibanaSavedObjectMeta": { - "properties": { - "searchSourceJSON": { - "type": "text" - } - } - }, - "savedSearchId": { - "type": "keyword" - }, - "title": { - "type": "text" - }, - "uiStateJSON": { - "type": "text" - }, - "version": { - "type": "integer" - }, - "visState": { - "type": "text" - } - } - } - } - }, - "settings": { - "index": { - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } -} diff --git a/x-pack/test/functional/fixtures/kbn_archiver/spaces/enter_space.json b/x-pack/test/functional/fixtures/kbn_archiver/spaces/enter_space.json new file mode 100644 index 0000000000000..ba375c51d97fe --- /dev/null +++ b/x-pack/test/functional/fixtures/kbn_archiver/spaces/enter_space.json @@ -0,0 +1,33 @@ +{ + "attributes": { + "buildNum": 8467, + "dateFormat:tz": "UTC", + "defaultRoute": "http://example.com/evil" + }, + "coreMigrationVersion": "8.4.0", + "id": "6.0.0", + "migrationVersion": { + "config": "8.1.0" + }, + "references": [], + "type": "config", + "version": "WzYsMl0=" +} + +{ + "attributes": { + "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", + "timeFieldName": "@timestamp", + "title": "logstash-*" + }, + "coreMigrationVersion": "8.4.0", + "id": "d1bd6c84-d9d0-56fb-8a72-63fe60020920", + "migrationVersion": { + "index-pattern": "8.0.0" + }, + "originId": "logstash-*", + "references": [], + "type": "index-pattern", + "updated_at": "2018-12-21T00:43:07.096Z", + "version": "WzgsMl0=" +} From 3f06a69ab64464bc02782e4a676e426eebe2fe9d Mon Sep 17 00:00:00 2001 From: Sander Philipse <94373878+sphilipse@users.noreply.github.com> Date: Tue, 12 Jul 2022 16:44:28 +0200 Subject: [PATCH 05/42] [Enterprise Search] Add mappings for index creation (#136185) --- .../utils/is_not_nullish.ts | 0 .../connector_configuration_config.tsx | 3 +- .../plugins/enterprise_search/server/index.ts | 3 +- .../index_management/setup_indices.test.ts | 216 ++++++++++++++++ .../server/index_management/setup_indices.ts | 143 +++++++++++ .../lib/connectors/add_connector.test.ts | 17 +- .../server/lib/connectors/add_connector.ts | 12 +- .../lib/connectors/fetch_connectors.test.ts | 232 ++++++++++++++++++ .../server/lib/connectors/fetch_connectors.ts | 90 +++++++ .../server/lib/indices/fetch_index.test.ts | 78 +++--- .../server/lib/indices/fetch_index.ts | 14 +- .../lib/indices/generate_api_key.test.ts | 32 +-- .../server/lib/indices/generate_api_key.ts | 2 +- .../server/types/connector.ts | 4 + .../server/utils/identify_exceptions.test.ts | 81 ++++++ .../server/utils/identify_exceptions.ts | 24 ++ 16 files changed, 875 insertions(+), 76 deletions(-) rename x-pack/plugins/enterprise_search/{public/applications/enterprise_search_content => common}/utils/is_not_nullish.ts (100%) create mode 100644 x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts create mode 100644 x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts create mode 100644 x-pack/plugins/enterprise_search/server/lib/connectors/fetch_connectors.test.ts create mode 100644 x-pack/plugins/enterprise_search/server/lib/connectors/fetch_connectors.ts create mode 100644 x-pack/plugins/enterprise_search/server/utils/identify_exceptions.test.ts create mode 100644 x-pack/plugins/enterprise_search/server/utils/identify_exceptions.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/is_not_nullish.ts b/x-pack/plugins/enterprise_search/common/utils/is_not_nullish.ts similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/utils/is_not_nullish.ts rename to x-pack/plugins/enterprise_search/common/utils/is_not_nullish.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_config.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_config.tsx index 8fd3429003e37..233207d6cf427 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_config.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/connector_configuration_config.tsx @@ -24,11 +24,10 @@ import { import { i18n } from '@kbn/i18n'; import { Status } from '../../../../../../common/types/api'; +import { isNotNullish } from '../../../../../../common/utils/is_not_nullish'; import { ConnectorConfigurationApiLogic } from '../../../api/connector_package/update_connector_configuration_api_logic'; import { ConnectorConfiguration } from '../../../api/index/fetch_index_api_logic'; -import { isNotNullish } from '../../../utils/is_not_nullish'; - import { ConnectorConfigurationLogic } from './connector_configuration_logic'; interface ConnectorConfigurationConfigArgs { diff --git a/x-pack/plugins/enterprise_search/server/index.ts b/x-pack/plugins/enterprise_search/server/index.ts index 2641d6c44f18a..e2279fc35397a 100644 --- a/x-pack/plugins/enterprise_search/server/index.ts +++ b/x-pack/plugins/enterprise_search/server/index.ts @@ -38,5 +38,6 @@ export const config: PluginConfigDescriptor = { host: true, }, }; - export const CONNECTORS_INDEX = '.elastic-connectors'; +export const CONNECTORS_JOBS_INDEX = '.elastic-connectors-sync-jobs'; +export const CONNECTORS_VERSION = '1'; diff --git a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts new file mode 100644 index 0000000000000..ac709cdf07ff3 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.test.ts @@ -0,0 +1,216 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CONNECTORS_INDEX, CONNECTORS_JOBS_INDEX, CONNECTORS_VERSION } from '..'; + +import { setupConnectorsIndices } from './setup_indices'; + +describe('Setup Indices', () => { + const mockClient = { + asCurrentUser: { + indices: { + create: jest.fn(), + get: jest.fn(), + stats: jest.fn(), + updateAliases: jest.fn(), + }, + search: jest.fn(), + }, + asInternalUser: {}, + }; + + const connectorsIndexName = `${CONNECTORS_INDEX}-v${CONNECTORS_VERSION}`; + const jobsIndexName = `${CONNECTORS_JOBS_INDEX}-v${CONNECTORS_VERSION}`; + + const connectorsMappings = { + _meta: { + version: CONNECTORS_VERSION, + }, + properties: { + api_key_id: { + type: 'keyword', + }, + configuration: { + type: 'object', + }, + error: { type: 'keyword' }, + index_name: { type: 'text' }, + language: { type: 'keyword' }, + last_seen: { type: 'date' }, + last_sync_error: { type: 'keyword' }, + last_sync_status: { type: 'keyword' }, + last_synced: { type: 'date' }, + scheduling: { + properties: { + enabled: { type: 'boolean' }, + interval: { type: 'text' }, + }, + }, + service_type: { type: 'keyword' }, + status: { type: 'keyword' }, + sync_now: { type: 'boolean' }, + }, + }; + + const connectorsJobsMappings = { + _meta: { + version: CONNECTORS_VERSION, + }, + properties: { + api_key_id: { + type: 'keyword', + }, + configuration: { + type: 'object', + }, + error: { type: 'keyword' }, + index_name: { type: 'text' }, + language: { type: 'keyword' }, + last_seen: { type: 'date' }, + last_sync_error: { type: 'keyword' }, + last_sync_status: { type: 'keyword' }, + last_synced: { type: 'date' }, + scheduling: { + properties: { + enabled: { type: 'boolean' }, + interval: { type: 'text' }, + }, + }, + service_type: { type: 'keyword' }, + status: { type: 'keyword' }, + sync_now: { type: 'boolean' }, + }, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('setupConnectorsIndices', () => { + it('should do nothing if indices exist', async () => { + const result = { + [connectorsIndexName]: { + mappings: { + _meta: { + version: CONNECTORS_VERSION, + }, + }, + }, + [jobsIndexName]: { + mappings: { + _meta: { + version: CONNECTORS_VERSION, + }, + }, + }, + }; + mockClient.asCurrentUser.indices.get.mockImplementation(() => Promise.resolve(result)); + mockClient.asCurrentUser.indices.create.mockImplementation(() => Promise.resolve()); + mockClient.asCurrentUser.indices.updateAliases.mockImplementation(() => Promise.resolve()); + await expect(setupConnectorsIndices(mockClient.asCurrentUser as any)).resolves.toEqual( + undefined + ); + expect(mockClient.asCurrentUser.indices.create).not.toHaveBeenCalled(); + expect(mockClient.asCurrentUser.indices.updateAliases).not.toHaveBeenCalled(); + }); + it('should do nothing if it hits race condition exist', async () => { + const result = { + [connectorsIndexName]: { + mappings: { + _meta: { + version: CONNECTORS_VERSION, + }, + }, + }, + [jobsIndexName]: { + mappings: { + _meta: { + version: CONNECTORS_VERSION, + }, + }, + }, + }; + mockClient.asCurrentUser.indices.get.mockImplementation(() => Promise.resolve(result)); + mockClient.asCurrentUser.indices.create.mockImplementation(() => + Promise.reject({ meta: { body: { error: { type: 'resource_already_exists_exception' } } } }) + ); + mockClient.asCurrentUser.indices.updateAliases.mockImplementation(() => Promise.resolve()); + await expect(setupConnectorsIndices(mockClient.asCurrentUser as any)).resolves.toEqual( + undefined + ); + expect(mockClient.asCurrentUser.indices.create).not.toHaveBeenCalled(); + expect(mockClient.asCurrentUser.indices.updateAliases).not.toHaveBeenCalled(); + }); + it('should create new index and update alias if connectors index does not exist', async () => { + const result = { + [jobsIndexName]: { + mappings: { + _meta: { + version: CONNECTORS_VERSION, + }, + }, + }, + }; + mockClient.asCurrentUser.indices.get.mockImplementation(() => Promise.resolve(result)); + mockClient.asCurrentUser.indices.create.mockImplementation(() => Promise.resolve()); + mockClient.asCurrentUser.indices.updateAliases.mockImplementation(() => Promise.resolve()); + await expect(setupConnectorsIndices(mockClient.asCurrentUser as any)).resolves.toEqual( + undefined + ); + expect(mockClient.asCurrentUser.indices.create).toHaveBeenCalledWith({ + index: connectorsIndexName, + mappings: connectorsMappings, + settings: { hidden: true }, + }); + expect(mockClient.asCurrentUser.indices.updateAliases).toHaveBeenCalledWith({ + actions: [ + { + add: { + aliases: [CONNECTORS_INDEX], + index: `${CONNECTORS_INDEX}-v${CONNECTORS_VERSION}`, + is_hidden: true, + is_write_index: true, + }, + }, + ], + }); + }); + it('should create new jobs index and update alias if jobs index does not exist', async () => { + const result = { + [connectorsIndexName]: { + mappings: { + _meta: { + version: CONNECTORS_VERSION, + }, + }, + }, + }; + mockClient.asCurrentUser.indices.get.mockImplementation(() => Promise.resolve(result)); + mockClient.asCurrentUser.indices.create.mockImplementation(() => Promise.resolve()); + mockClient.asCurrentUser.indices.updateAliases.mockImplementation(() => Promise.resolve()); + await expect(setupConnectorsIndices(mockClient.asCurrentUser as any)).resolves.toEqual( + undefined + ); + expect(mockClient.asCurrentUser.indices.create).toHaveBeenCalledWith({ + index: jobsIndexName, + mappings: connectorsJobsMappings, + settings: { hidden: true }, + }); + expect(mockClient.asCurrentUser.indices.updateAliases).toHaveBeenCalledWith({ + actions: [ + { + add: { + aliases: [CONNECTORS_JOBS_INDEX], + index: `${CONNECTORS_JOBS_INDEX}-v${CONNECTORS_VERSION}`, + is_hidden: true, + is_write_index: true, + }, + }, + ], + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts new file mode 100644 index 0000000000000..52513a478bacc --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/index_management/setup_indices.ts @@ -0,0 +1,143 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IndicesIndexSettings, MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; +import { ElasticsearchClient } from '@kbn/core/server'; + +import { CONNECTORS_INDEX } from '..'; +import { isResourceAlreadyExistsException } from '../utils/identify_exceptions'; + +export enum SETUP_ERRORS { + 'insufficient_permissions', + 'index_already_exists', +} + +interface IndexDefinition { + aliases: string[]; + mappings: MappingTypeMapping; + name: string; + settings: IndicesIndexSettings; +} + +const indices: IndexDefinition[] = [ + { + aliases: ['.elastic-connectors'], + mappings: { + _meta: { + version: '1', + }, + properties: { + api_key_id: { + type: 'keyword', + }, + configuration: { + type: 'object', + }, + error: { type: 'keyword' }, + index_name: { type: 'text' }, + language: { type: 'keyword' }, + last_seen: { type: 'date' }, + last_sync_error: { type: 'keyword' }, + last_sync_status: { type: 'keyword' }, + last_synced: { type: 'date' }, + scheduling: { + properties: { + enabled: { type: 'boolean' }, + interval: { type: 'text' }, + }, + }, + service_type: { type: 'keyword' }, + status: { type: 'keyword' }, + sync_now: { type: 'boolean' }, + }, + }, + name: '.elastic-connectors-v1', + settings: { + hidden: true, + }, + }, + { + aliases: ['.elastic-connectors-sync-jobs'], + mappings: { + _meta: { + version: '1', + }, + properties: { + api_key_id: { + type: 'keyword', + }, + configuration: { + type: 'object', + }, + error: { type: 'keyword' }, + index_name: { type: 'text' }, + language: { type: 'keyword' }, + last_seen: { type: 'date' }, + last_sync_error: { type: 'keyword' }, + last_sync_status: { type: 'keyword' }, + last_synced: { type: 'date' }, + scheduling: { + properties: { + enabled: { type: 'boolean' }, + interval: { type: 'text' }, + }, + }, + service_type: { type: 'keyword' }, + status: { type: 'keyword' }, + sync_now: { type: 'boolean' }, + }, + }, + name: '.elastic-connectors-sync-jobs-v1', + settings: { + hidden: true, + }, + }, +]; + +const createConnectorsIndex = async ( + client: ElasticsearchClient, + indexDefinition: IndexDefinition +) => { + try { + const { aliases, mappings, name: index, settings } = indexDefinition; + await client.indices.create({ + index, + mappings, + settings, + }); + await client.indices.updateAliases({ + actions: [ + { + add: { + aliases, + index, + is_hidden: true, + is_write_index: true, + }, + }, + ], + }); + } catch (error) { + if (isResourceAlreadyExistsException(error)) { + // We hit a race condition, do nothing + return; + } + return error; + } +}; + +export const setupConnectorsIndices = async (client: ElasticsearchClient) => { + const connectorsIndexResponse = await client.indices.get({ + index: `${CONNECTORS_INDEX}*`, + }); + for (const indexDefinition of indices) { + if (!connectorsIndexResponse[indexDefinition.name]) { + await createConnectorsIndex(client, indexDefinition); + } + // TODO handle migrations once we start migrating stuff + } +}; diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts index ad0c847bee493..aa4290e241a2f 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.test.ts @@ -9,8 +9,14 @@ import { IScopedClusterClient } from '@kbn/core/server'; import { CONNECTORS_INDEX } from '../..'; +import { setupConnectorsIndices } from '../../index_management/setup_indices'; + import { addConnector } from './add_connector'; +jest.mock('../../index_management/setup_indices', () => ({ + setupConnectorsIndices: jest.fn(), +})); + describe('addConnector lib function', () => { const mockClient = { asCurrentUser: { @@ -54,14 +60,15 @@ describe('addConnector lib function', () => { it('should create index if no connectors index exists', async () => { mockClient.asCurrentUser.index.mockImplementationOnce(() => { - return Promise.reject({ statusCode: 404 }); + return Promise.reject({ + meta: { body: { error: { type: 'index_not_found_exception' } } }, + statusCode: 404, + }); }); await expect( addConnector(mockClient as unknown as IScopedClusterClient, { index_name: 'index_name' }) ).resolves.toEqual({ id: 'fakeId', index_name: 'index_name' }); - expect(mockClient.asCurrentUser.indices.create).toHaveBeenCalledWith({ - index: CONNECTORS_INDEX, - }); + expect(setupConnectorsIndices as jest.Mock).toHaveBeenCalledWith(mockClient.asCurrentUser); expect(mockClient.asCurrentUser.index).toHaveBeenCalledWith({ document: { api_key_id: null, @@ -88,7 +95,7 @@ describe('addConnector lib function', () => { await expect( addConnector(mockClient as unknown as IScopedClusterClient, { index_name: 'index_name' }) ).rejects.toEqual({ statusCode: 500 }); - expect(mockClient.asCurrentUser.indices.create).not.toHaveBeenCalled(); + expect(setupConnectorsIndices).not.toHaveBeenCalled(); expect(mockClient.asCurrentUser.index).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts index b35d0e6d32c1a..c416d698157da 100644 --- a/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/add_connector.ts @@ -8,12 +8,9 @@ import { IScopedClusterClient } from '@kbn/core/server'; import { CONNECTORS_INDEX } from '../..'; +import { setupConnectorsIndices } from '../../index_management/setup_indices'; import { Connector } from '../../types/connector'; - -export const createConnectorsIndex = async (client: IScopedClusterClient): Promise => { - const index = CONNECTORS_INDEX; - await client.asCurrentUser.indices.create({ index }); -}; +import { isIndexNotFoundException } from '../../utils/identify_exceptions'; const createConnector = async ( index: string, @@ -51,11 +48,10 @@ export const addConnector = async ( try { return await createConnector(index, document, client); } catch (error) { - if (error.statusCode === 404) { + if (isIndexNotFoundException(error)) { // This means .ent-search-connectors index doesn't exist yet // So we first have to create it, and then try inserting the document again - // TODO: Move index creation to Kibana startup instead - await createConnectorsIndex(client); + await setupConnectorsIndices(client.asCurrentUser); return await createConnector(index, document, client); } else { throw error; diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_connectors.test.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_connectors.test.ts new file mode 100644 index 0000000000000..296e425cf16a2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_connectors.test.ts @@ -0,0 +1,232 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CONNECTORS_INDEX } from '../..'; +import { setupConnectorsIndices } from '../../index_management/setup_indices'; + +import { fetchConnectorById, fetchConnectorByIndexName, fetchConnectors } from './fetch_connectors'; + +jest.mock('../../index_management/setup_indices', () => ({ + setupConnectorsIndices: jest.fn(), +})); + +describe('fetchConnectors lib', () => { + const mockClient = { + asCurrentUser: { + get: jest.fn(), + search: jest.fn(), + }, + asInternalUser: {}, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('fetch connector by id', () => { + it('should fetch connector by id', async () => { + mockClient.asCurrentUser.get.mockImplementationOnce(() => + Promise.resolve({ _id: 'connectorId', _source: { source: 'source' } }) + ); + await expect(fetchConnectorById(mockClient as any, 'id')).resolves.toEqual({ + id: 'connectorId', + source: 'source', + }); + expect(mockClient.asCurrentUser.get).toHaveBeenCalledWith({ + id: 'id', + index: CONNECTORS_INDEX, + }); + }); + it('should call setup connectors on index not found error', async () => { + mockClient.asCurrentUser.get.mockImplementationOnce(() => + Promise.reject({ + meta: { + body: { + error: { + type: 'index_not_found_exception', + }, + }, + }, + }) + ); + await expect(fetchConnectorById(mockClient as any, 'id')).resolves.toEqual(undefined); + expect(mockClient.asCurrentUser.get).toHaveBeenCalledWith({ + id: 'id', + index: CONNECTORS_INDEX, + }); + expect(setupConnectorsIndices as jest.Mock).toHaveBeenCalledWith(mockClient.asCurrentUser); + }); + it('should not call setup connectors on other errors', async () => { + mockClient.asCurrentUser.get.mockImplementationOnce(() => + Promise.reject({ + meta: { + body: { + error: { + type: 'other error', + }, + }, + }, + }) + ); + await expect(fetchConnectorById(mockClient as any, 'id')).resolves.toEqual(undefined); + expect(mockClient.asCurrentUser.get).toHaveBeenCalledWith({ + id: 'id', + index: CONNECTORS_INDEX, + }); + expect(setupConnectorsIndices as jest.Mock).not.toHaveBeenCalled(); + }); + }); + describe('fetch connector by name', () => { + it('should fetch connector by index name', async () => { + mockClient.asCurrentUser.search.mockImplementationOnce(() => + Promise.resolve({ hits: { hits: [{ _id: 'connectorId', _source: { source: 'source' } }] } }) + ); + await expect(fetchConnectorByIndexName(mockClient as any, 'id')).resolves.toEqual({ + id: 'connectorId', + source: 'source', + }); + expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + index: CONNECTORS_INDEX, + query: { + term: { + ['index_name.keyword']: 'id', + }, + }, + }); + }); + it('should call setup connectors on index not found error', async () => { + mockClient.asCurrentUser.search.mockImplementationOnce(() => + Promise.reject({ + meta: { + body: { + error: { type: 'index_not_found_exception' }, + }, + }, + }) + ); + await expect(fetchConnectorByIndexName(mockClient as any, 'id')).resolves.toEqual(undefined); + expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + index: CONNECTORS_INDEX, + query: { + term: { + ['index_name.keyword']: 'id', + }, + }, + }); + expect(setupConnectorsIndices as jest.Mock).toHaveBeenCalledWith(mockClient.asCurrentUser); + }); + it('should not call setup connectors on other errors', async () => { + mockClient.asCurrentUser.search.mockImplementationOnce(() => + Promise.reject({ + meta: { + body: { + error: { + type: 'other error', + }, + }, + }, + }) + ); + await expect(fetchConnectorByIndexName(mockClient as any, 'id')).resolves.toEqual(undefined); + expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + index: CONNECTORS_INDEX, + query: { + term: { + ['index_name.keyword']: 'id', + }, + }, + }); + expect(setupConnectorsIndices as jest.Mock).not.toHaveBeenCalled(); + }); + }); + describe('fetch connectors', () => { + it('should fetch connectors', async () => { + mockClient.asCurrentUser.search.mockImplementationOnce(() => + Promise.resolve({ hits: { hits: [{ _id: 'connectorId', _source: { source: 'source' } }] } }) + ); + await expect(fetchConnectors(mockClient as any)).resolves.toEqual([ + { + id: 'connectorId', + source: 'source', + }, + ]); + expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + from: 0, + index: CONNECTORS_INDEX, + query: { match_all: {} }, + size: 1000, + }); + }); + it('should fetch all connectors if there are more than 1000', async () => { + const hits = [...Array(1000).keys()].map((key) => ({ + _id: key, + _source: { source: 'source' }, + })); + const resultHits = hits.map((hit) => ({ ...hit._source, id: hit._id })); + + let count = 0; + + mockClient.asCurrentUser.search.mockImplementation(() => { + count += 1; + if (count === 3) { + return Promise.resolve({ hits: { hits: [] } }); + } + return Promise.resolve({ hits: { hits } }); + }); + await expect(fetchConnectors(mockClient as any)).resolves.toEqual([ + ...resultHits, + ...resultHits, + ]); + expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + from: 0, + index: CONNECTORS_INDEX, + query: { match_all: {} }, + size: 1000, + }); + expect(mockClient.asCurrentUser.search).toHaveBeenCalledTimes(3); + }); + it('should call setup connectors on index not found error', async () => { + mockClient.asCurrentUser.search.mockImplementationOnce(() => + Promise.reject({ + meta: { + body: { + error: { type: 'index_not_found_exception' }, + }, + }, + }) + ); + await expect(fetchConnectors(mockClient as any)).resolves.toEqual([]); + expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + from: 0, + index: CONNECTORS_INDEX, + query: { match_all: {} }, + size: 1000, + }); + expect(setupConnectorsIndices as jest.Mock).toHaveBeenCalledWith(mockClient.asCurrentUser); + }); + it('should not call setup connectors on other errors', async () => { + mockClient.asCurrentUser.search.mockImplementationOnce(() => + Promise.reject({ + meta: { + body: { + error: { + type: 'other error', + }, + }, + }, + }) + ); + await expect(fetchConnectors(mockClient as any)).resolves.toEqual([]); + expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + from: 0, + index: CONNECTORS_INDEX, + query: { match_all: {} }, + size: 1000, + }); + expect(setupConnectorsIndices as jest.Mock).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_connectors.ts b/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_connectors.ts new file mode 100644 index 0000000000000..6c33baa1064c2 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/lib/connectors/fetch_connectors.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IScopedClusterClient } from '@kbn/core/server'; + +import { CONNECTORS_INDEX } from '../..'; +import { isNotNullish } from '../../../common/utils/is_not_nullish'; +import { setupConnectorsIndices } from '../../index_management/setup_indices'; + +import { Connector, ConnectorWithId } from '../../types/connector'; +import { isIndexNotFoundException } from '../../utils/identify_exceptions'; + +export const fetchConnectorById = async ( + client: IScopedClusterClient, + connectorId: string +): Promise => { + try { + const connectorResult = await client.asCurrentUser.get({ + id: connectorId, + index: CONNECTORS_INDEX, + }); + return connectorResult._source + ? { ...connectorResult._source, id: connectorResult._id } + : undefined; + } catch (error) { + if (isIndexNotFoundException(error)) { + await setupConnectorsIndices(client.asCurrentUser); + } + return undefined; + } +}; + +export const fetchConnectorByIndexName = async ( + client: IScopedClusterClient, + indexName: string +): Promise => { + try { + const connectorResult = await client.asCurrentUser.search({ + index: CONNECTORS_INDEX, + query: { term: { 'index_name.keyword': indexName } }, + }); + const result = connectorResult.hits.hits[0]?._source + ? { + ...connectorResult.hits.hits[0]._source, + id: connectorResult.hits.hits[0]._id, + } + : undefined; + return result; + } catch (error) { + if (isIndexNotFoundException(error)) { + await setupConnectorsIndices(client.asCurrentUser); + } + return undefined; + } +}; + +export const fetchConnectors = async (client: IScopedClusterClient): Promise => { + try { + const connectorResult = await client.asCurrentUser.search({ + from: 0, + index: CONNECTORS_INDEX, + query: { match_all: {} }, + size: 1000, + }); + let connectors = connectorResult.hits.hits; + let length = connectors.length; + while (length >= 1000) { + const newConnectorResult = await client.asCurrentUser.search({ + from: 0, + index: CONNECTORS_INDEX, + query: { match_all: {} }, + size: 1000, + }); + connectors = connectors.concat(newConnectorResult.hits.hits); + length = newConnectorResult.hits.hits.length; + } + return connectors + .map(({ _source, _id }) => (_source ? { ..._source, id: _id } : undefined)) + .filter(isNotNullish); + } catch (error) { + if (isIndexNotFoundException(error)) { + await setupConnectorsIndices(client.asCurrentUser); + } + return []; + } +}; diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.test.ts b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.test.ts index 2739c1e85b076..15ccaa680cc92 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.test.ts @@ -8,16 +8,22 @@ import { ByteSizeValue } from '@kbn/config-schema'; import { IScopedClusterClient } from '@kbn/core/server'; +import { fetchConnectorByIndexName } from '../connectors/fetch_connectors'; + import { fetchIndex } from './fetch_index'; +jest.mock('../connectors/fetch_connectors', () => ({ + fetchConnectorByIndexName: jest.fn(), +})); + describe('fetchIndex lib function', () => { const mockClient = { asCurrentUser: { + index: jest.fn(), indices: { get: jest.fn(), stats: jest.fn(), }, - index: jest.fn(), search: jest.fn(), }, asInternalUser: {}, @@ -31,8 +37,8 @@ describe('fetchIndex lib function', () => { indices: { index_name: { health: 'green', + size: new ByteSizeValue(108000).toString(), status: 'open', - uuid: '83a81e7e-5955-4255-b008-5d6961203f57', total: { docs: { count: 100, @@ -42,7 +48,7 @@ describe('fetchIndex lib function', () => { size_in_bytes: 108000, }, }, - size: new ByteSizeValue(108000).toString(), + uuid: '83a81e7e-5955-4255-b008-5d6961203f57', }, }, }; @@ -66,18 +72,17 @@ describe('fetchIndex lib function', () => { }, }; - it('should return data and stats for index if no connector is present', async () => { + it('should return data and stats for index if no connector or crawler is present', async () => { mockClient.asCurrentUser.indices.get.mockImplementation(() => Promise.resolve({ - index_name: { data: 'full index', aliases: [] }, + index_name: { aliases: [], data: 'full index' }, }) ); mockClient.asCurrentUser.search.mockImplementation(() => - Promise.resolve({ - hits: { - hits: [], - }, - }) + Promise.resolve({ hits: { hits: [] } }) + ); + (fetchConnectorByIndexName as jest.Mock).mockImplementationOnce(() => + Promise.resolve(undefined) ); mockClient.asCurrentUser.indices.stats.mockImplementation(() => Promise.resolve(statsResponse)); @@ -85,33 +90,48 @@ describe('fetchIndex lib function', () => { fetchIndex(mockClient as unknown as IScopedClusterClient, 'index_name') ).resolves.toEqual(result); }); + it('should return data and stats for index and connector if connector is present', async () => { mockClient.asCurrentUser.indices.get.mockImplementation(() => Promise.resolve({ - index_name: { data: 'full index', aliases: [] }, + index_name: { aliases: [], data: 'full index' }, }) ); mockClient.asCurrentUser.search.mockImplementation(() => + Promise.resolve({ hits: { hits: [] } }) + ); + (fetchConnectorByIndexName as jest.Mock).mockImplementationOnce(() => + Promise.resolve({ doc: 'doc' }) + ); + mockClient.asCurrentUser.indices.stats.mockImplementation(() => Promise.resolve(statsResponse)); + + await expect( + fetchIndex(mockClient as unknown as IScopedClusterClient, 'index_name') + ).resolves.toEqual({ ...result, connector: { doc: 'doc' } }); + }); + + it('should return data and stats for index and crawler if crawler is present', async () => { + mockClient.asCurrentUser.indices.get.mockImplementation(() => Promise.resolve({ - hits: { - hits: [{ _source: { doc: 'doc' } }], - }, + index_name: { aliases: [], data: 'full index' }, }) ); + (fetchConnectorByIndexName as jest.Mock).mockImplementationOnce(() => + Promise.resolve(undefined) + ); + mockClient.asCurrentUser.search.mockImplementation(() => ({ + hits: { hits: [{ _source: 'source' }] }, + })); mockClient.asCurrentUser.indices.stats.mockImplementation(() => Promise.resolve(statsResponse)); await expect( fetchIndex(mockClient as unknown as IScopedClusterClient, 'index_name') - ).resolves.toEqual({ ...result, connector: { doc: 'doc' } }); + ).resolves.toEqual({ ...result, crawler: 'source' }); }); it('should throw a 404 error if the index cannot be fonud', async () => { mockClient.asCurrentUser.indices.get.mockImplementation(() => Promise.resolve({})); - mockClient.asCurrentUser.search.mockImplementation(() => - Promise.resolve({ - hits: { - hits: [], - }, - }) + (fetchConnectorByIndexName as jest.Mock).mockImplementationOnce(() => + Promise.resolve(undefined) ); mockClient.asCurrentUser.indices.stats.mockImplementation(() => Promise.resolve(statsResponse)); @@ -125,12 +145,8 @@ describe('fetchIndex lib function', () => { index_name: { aliases: [] }, }) ); - mockClient.asCurrentUser.search.mockImplementation(() => - Promise.resolve({ - hits: { - hits: [], - }, - }) + (fetchConnectorByIndexName as jest.Mock).mockImplementationOnce(() => + Promise.resolve(undefined) ); mockClient.asCurrentUser.indices.stats.mockImplementation(() => Promise.resolve({ indices: {} }) @@ -146,12 +162,8 @@ describe('fetchIndex lib function', () => { index_name: { aliases: [] }, }) ); - mockClient.asCurrentUser.search.mockImplementation(() => - Promise.resolve({ - hits: { - hits: [], - }, - }) + (fetchConnectorByIndexName as jest.Mock).mockImplementationOnce(() => + Promise.resolve(undefined) ); mockClient.asCurrentUser.indices.stats.mockImplementation(() => Promise.resolve({})); diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.ts b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.ts index 7d2551c0d747c..ad13cbb145413 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/fetch_index.ts @@ -7,9 +7,8 @@ import { IScopedClusterClient } from '@kbn/core/server'; -import { CONNECTORS_INDEX } from '../..'; -import { Connector } from '../../types/connector'; import { Crawler } from '../../types/crawler'; +import { fetchConnectorByIndexName } from '../connectors/fetch_connectors'; import { mapIndexStats } from './fetch_indices'; @@ -23,15 +22,10 @@ export const fetchIndex = async (client: IScopedClusterClient, index: string) => const indexStats = indices[index]; const indexResult = mapIndexStats(indexData, indexStats, index); - const connectorResult = await client.asCurrentUser.search({ - index: CONNECTORS_INDEX, - query: { term: { 'index_name.keyword': index } }, - }); - const connector = connectorResult.hits.hits[0]?._source ?? undefined; - + const connector = await fetchConnectorByIndexName(client, index); if (connector) { return { - connector: { ...connector, id: connectorResult.hits.hits[0]._id }, + connector, index: indexResult, }; } @@ -40,7 +34,7 @@ export const fetchIndex = async (client: IScopedClusterClient, index: string) => index: '.ent-search-actastic-crawler2_configurations', query: { term: { index_name: index } }, }); - const crawler = crawlerResult.hits.hits[0]?._source ?? undefined; + const crawler = crawlerResult.hits.hits[0]?._source; if (crawler) { return { diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.test.ts b/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.test.ts index 509c5de5108d4..e240f4c527874 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.test.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.test.ts @@ -14,10 +14,10 @@ import { generateApiKey } from './generate_api_key'; describe('generateApiKey lib function', () => { const mockClient = { asCurrentUser: { + index: jest.fn(), indices: { create: jest.fn(), }, - index: jest.fn(), search: jest.fn(), security: { createApiKey: jest.fn(), @@ -44,13 +44,13 @@ describe('generateApiKey lib function', () => { _source: 'Document', })); mockClient.asCurrentUser.security.createApiKey.mockImplementation(() => ({ - id: 'apiKeyId', encoded: 'encoded', + id: 'apiKeyId', })); await expect( generateApiKey(mockClient as unknown as IScopedClusterClient, 'index_name') - ).resolves.toEqual({ id: 'apiKeyId', encoded: 'encoded' }); + ).resolves.toEqual({ encoded: 'encoded', id: 'apiKeyId' }); expect(mockClient.asCurrentUser.index).not.toHaveBeenCalled(); expect(mockClient.asCurrentUser.security.createApiKey).toHaveBeenCalledWith({ name: 'index_name-connector', @@ -59,7 +59,7 @@ describe('generateApiKey lib function', () => { cluster: [], index: [ { - names: ['index_name', CONNECTORS_INDEX], + names: ['index_name', `${CONNECTORS_INDEX}*`], privileges: ['all'], }, ], @@ -71,7 +71,7 @@ describe('generateApiKey lib function', () => { mockClient.asCurrentUser.search.mockImplementation(() => Promise.resolve({ hits: { - hits: [{ _source: { doc: 'doc' }, _id: 'connectorId' }], + hits: [{ _id: 'connectorId', _source: { doc: 'doc' } }], }, }) ); @@ -80,13 +80,13 @@ describe('generateApiKey lib function', () => { _source: 'Document', })); mockClient.asCurrentUser.security.createApiKey.mockImplementation(() => ({ - id: 'apiKeyId', encoded: 'encoded', + id: 'apiKeyId', })); await expect( generateApiKey(mockClient as unknown as IScopedClusterClient, 'index_name') - ).resolves.toEqual({ id: 'apiKeyId', encoded: 'encoded' }); + ).resolves.toEqual({ encoded: 'encoded', id: 'apiKeyId' }); expect(mockClient.asCurrentUser.security.createApiKey).toHaveBeenCalledWith({ name: 'index_name-connector', role_descriptors: { @@ -94,7 +94,7 @@ describe('generateApiKey lib function', () => { cluster: [], index: [ { - names: ['index_name', CONNECTORS_INDEX], + names: ['index_name', `${CONNECTORS_INDEX}*`], privileges: ['all'], }, ], @@ -102,9 +102,9 @@ describe('generateApiKey lib function', () => { }, }); expect(mockClient.asCurrentUser.index).toHaveBeenCalledWith({ - index: CONNECTORS_INDEX, + document: { api_key_id: 'apiKeyId', doc: 'doc' }, id: 'connectorId', - document: { doc: 'doc', api_key_id: 'apiKeyId' }, + index: CONNECTORS_INDEX, }); expect(mockClient.asCurrentUser.security.invalidateApiKey).not.toHaveBeenCalled(); }); @@ -114,8 +114,8 @@ describe('generateApiKey lib function', () => { hits: { hits: [ { - _source: { doc: 'doc', api_key_id: '1' }, _id: 'connectorId', + _source: { api_key_id: '1', doc: 'doc' }, fields: { api_key_id: '1' }, }, ], @@ -127,13 +127,13 @@ describe('generateApiKey lib function', () => { _source: 'Document', })); mockClient.asCurrentUser.security.createApiKey.mockImplementation(() => ({ - id: 'apiKeyId', encoded: 'encoded', + id: 'apiKeyId', })); await expect( generateApiKey(mockClient as unknown as IScopedClusterClient, 'index_name') - ).resolves.toEqual({ id: 'apiKeyId', encoded: 'encoded' }); + ).resolves.toEqual({ encoded: 'encoded', id: 'apiKeyId' }); expect(mockClient.asCurrentUser.security.createApiKey).toHaveBeenCalledWith({ name: 'index_name-connector', role_descriptors: { @@ -141,7 +141,7 @@ describe('generateApiKey lib function', () => { cluster: [], index: [ { - names: ['index_name', CONNECTORS_INDEX], + names: ['index_name', `${CONNECTORS_INDEX}*`], privileges: ['all'], }, ], @@ -149,9 +149,9 @@ describe('generateApiKey lib function', () => { }, }); expect(mockClient.asCurrentUser.index).toHaveBeenCalledWith({ - index: CONNECTORS_INDEX, + document: { api_key_id: 'apiKeyId', doc: 'doc' }, id: 'connectorId', - document: { doc: 'doc', api_key_id: 'apiKeyId' }, + index: CONNECTORS_INDEX, }); expect(mockClient.asCurrentUser.security.invalidateApiKey).toHaveBeenCalledWith({ id: '1', diff --git a/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.ts b/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.ts index f664a8e8a1584..dfa2558449cdd 100644 --- a/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.ts +++ b/x-pack/plugins/enterprise_search/server/lib/indices/generate_api_key.ts @@ -18,7 +18,7 @@ export const generateApiKey = async (client: IScopedClusterClient, indexName: st cluster: [], index: [ { - names: [indexName, CONNECTORS_INDEX], + names: [indexName, `${CONNECTORS_INDEX}*`], privileges: ['all'], }, ], diff --git a/x-pack/plugins/enterprise_search/server/types/connector.ts b/x-pack/plugins/enterprise_search/server/types/connector.ts index 38685419938e2..95e1e6876e4a8 100644 --- a/x-pack/plugins/enterprise_search/server/types/connector.ts +++ b/x-pack/plugins/enterprise_search/server/types/connector.ts @@ -30,3 +30,7 @@ export interface Connector { sync_now: boolean; sync_status: string | null; } + +export interface ConnectorWithId extends Connector { + id: string; +} diff --git a/x-pack/plugins/enterprise_search/server/utils/identify_exceptions.test.ts b/x-pack/plugins/enterprise_search/server/utils/identify_exceptions.test.ts new file mode 100644 index 0000000000000..b88b047144f20 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/utils/identify_exceptions.test.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isIndexNotFoundException, isResourceAlreadyExistsException } from './identify_exceptions'; + +describe('IdentifyExceptions', () => { + describe('IsIndexNotFoundException', () => { + it('should return true for index not found exception', () => { + const error = { + meta: { + body: { + error: { + type: 'index_not_found_exception', + }, + status: 404, + }, + name: 'ResponseError', + statusCode: 404, + }, + }; + expect(isIndexNotFoundException(error as any)).toEqual(true); + }); + it('should return false for other exception', () => { + const error = { + meta: { + body: { + error: { + type: 'other_exception', + }, + status: 404, + }, + name: 'ResponseError', + statusCode: 404, + }, + }; + expect(isIndexNotFoundException(error as any)).toEqual(false); + }); + it('should return false for other object', () => { + expect(isIndexNotFoundException({} as any)).toEqual(false); + }); + }); + describe('isResourceAlreadyExistsError', () => { + it('should return true for resource already exists exception', () => { + const error = { + meta: { + body: { + error: { + type: 'resource_already_exists_exception', + }, + status: 400, + }, + name: 'ResponseError', + statusCode: 400, + }, + }; + expect(isResourceAlreadyExistsException(error as any)).toEqual(true); + }); + it('should return false for other exception', () => { + const error = { + meta: { + body: { + error: { + type: 'other_exception', + }, + status: 404, + }, + name: 'ResponseError', + statusCode: 404, + }, + }; + expect(isResourceAlreadyExistsException(error as any)).toEqual(false); + }); + it('should return false for other object', () => { + expect(isResourceAlreadyExistsException({} as any)).toEqual(false); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/utils/identify_exceptions.ts b/x-pack/plugins/enterprise_search/server/utils/identify_exceptions.ts new file mode 100644 index 0000000000000..ce0349b95c207 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/utils/identify_exceptions.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +interface ErrorResponse { + meta?: { + body?: { + error?: { + type: string; + }; + }; + }; + name: 'ResponseError'; + statusCode: string; +} + +export const isIndexNotFoundException = (error: ErrorResponse) => + error?.meta?.body?.error?.type === 'index_not_found_exception'; + +export const isResourceAlreadyExistsException = (error: ErrorResponse) => + error?.meta?.body?.error?.type === 'resource_already_exists_exception'; From b35f96166b9e9969eadbe79c7f28f3f7c0206466 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20S=C3=A1nchez?= Date: Tue, 12 Jul 2022 16:46:12 +0200 Subject: [PATCH 06/42] Enables policyResponseInFleetEnabled by default (#136186) --- .../plugins/security_solution/common/experimental_features.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index f22a0b4af5170..d1a6348204a5d 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -21,7 +21,7 @@ export const allowedExperimentalValues = Object.freeze({ riskyUsersEnabled: false, pendingActionResponsesWithAck: true, policyListEnabled: true, - policyResponseInFleetEnabled: false, + policyResponseInFleetEnabled: true, groupedNavigation: true, /** From cbe94c6db6b4bfa13f41e9505d1ef5d215b573bf Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 12 Jul 2022 10:00:10 -0500 Subject: [PATCH 07/42] Remove fast-glob (#136125) * Remove fast-glob This is not used. * [CI] Auto-commit changed files from 'yarn kbn run build -i @kbn/pm' Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 1 - packages/kbn-pm/dist/index.js | 13854 ++++++++++++++------------------ yarn.lock | 24 +- 3 files changed, 6108 insertions(+), 7771 deletions(-) diff --git a/package.json b/package.json index b31198a809324..e66145e4d50b1 100644 --- a/package.json +++ b/package.json @@ -1021,7 +1021,6 @@ "expose-loader": "^0.7.5", "faker": "^5.1.0", "fancy-log": "^1.3.2", - "fast-glob": "2.2.7", "fetch-mock": "^7.3.9", "file-loader": "^4.2.0", "form-data": "^4.0.0", diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 3b48937eb1098..647e8a8d4bbbe 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -23894,7209 +23894,6648 @@ module.exports = AggregateError; /***/ }), -/***/ "../../node_modules/del/node_modules/fast-glob/out/index.js": +/***/ "../../node_modules/del/node_modules/globby/gitignore.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; -const taskManager = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/managers/tasks.js"); -const async_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/providers/async.js"); -const stream_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/providers/stream.js"); -const sync_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/providers/sync.js"); -const settings_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/settings.js"); -const utils = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/index.js"); -async function FastGlob(source, options) { - assertPatternsInput(source); - const works = getWorks(source, async_1.default, options); - const result = await Promise.all(works); - return utils.array.flatten(result); -} -// https://github.com/typescript-eslint/typescript-eslint/issues/60 -// eslint-disable-next-line no-redeclare -(function (FastGlob) { - function sync(source, options) { - assertPatternsInput(source); - const works = getWorks(source, sync_1.default, options); - return utils.array.flatten(works); - } - FastGlob.sync = sync; - function stream(source, options) { - assertPatternsInput(source); - const works = getWorks(source, stream_1.default, options); - /** - * The stream returned by the provider cannot work with an asynchronous iterator. - * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. - * This affects performance (+25%). I don't see best solution right now. - */ - return utils.stream.merge(works); - } - FastGlob.stream = stream; - function generateTasks(source, options) { - assertPatternsInput(source); - const patterns = [].concat(source); - const settings = new settings_1.default(options); - return taskManager.generate(patterns, settings); - } - FastGlob.generateTasks = generateTasks; - function isDynamicPattern(source, options) { - assertPatternsInput(source); - const settings = new settings_1.default(options); - return utils.pattern.isDynamicPattern(source, settings); - } - FastGlob.isDynamicPattern = isDynamicPattern; - function escapePath(source) { - assertPatternsInput(source); - return utils.path.escape(source); - } - FastGlob.escapePath = escapePath; -})(FastGlob || (FastGlob = {})); -function getWorks(source, _Provider, options) { - const patterns = [].concat(source); - const settings = new settings_1.default(options); - const tasks = taskManager.generate(patterns, settings); - const provider = new _Provider(settings); - return tasks.map(provider.read, provider); -} -function assertPatternsInput(input) { - const source = [].concat(input); - const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); - if (!isValidSource) { - throw new TypeError('Patterns must be a string (non empty) or an array of strings'); - } -} -module.exports = FastGlob; +const {promisify} = __webpack_require__("util"); +const fs = __webpack_require__("fs"); +const path = __webpack_require__("path"); +const fastGlob = __webpack_require__("../../node_modules/fast-glob/out/index.js"); +const gitIgnore = __webpack_require__("../../node_modules/ignore/index.js"); +const slash = __webpack_require__("../../node_modules/slash/index.js"); +const DEFAULT_IGNORE = [ + '**/node_modules/**', + '**/flow-typed/**', + '**/coverage/**', + '**/.git' +]; -/***/ }), +const readFileP = promisify(fs.readFile); -/***/ "../../node_modules/del/node_modules/fast-glob/out/managers/tasks.js": -/***/ (function(module, exports, __webpack_require__) { +const mapGitIgnorePatternTo = base => ignore => { + if (ignore.startsWith('!')) { + return '!' + path.posix.join(base, ignore.slice(1)); + } -"use strict"; + return path.posix.join(base, ignore); +}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.convertPatternGroupToTask = exports.convertPatternGroupsToTasks = exports.groupPatternsByBaseDirectory = exports.getNegativePatternsAsPositive = exports.getPositivePatterns = exports.convertPatternsToTasks = exports.generate = void 0; -const utils = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/index.js"); -function generate(patterns, settings) { - const positivePatterns = getPositivePatterns(patterns); - const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); - const staticPatterns = positivePatterns.filter((pattern) => utils.pattern.isStaticPattern(pattern, settings)); - const dynamicPatterns = positivePatterns.filter((pattern) => utils.pattern.isDynamicPattern(pattern, settings)); - const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); - const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); - return staticTasks.concat(dynamicTasks); -} -exports.generate = generate; -/** - * Returns tasks grouped by basic pattern directories. - * - * Patterns that can be found inside (`./`) and outside (`../`) the current directory are handled separately. - * This is necessary because directory traversal starts at the base directory and goes deeper. - */ -function convertPatternsToTasks(positive, negative, dynamic) { - const tasks = []; - const patternsOutsideCurrentDirectory = utils.pattern.getPatternsOutsideCurrentDirectory(positive); - const patternsInsideCurrentDirectory = utils.pattern.getPatternsInsideCurrentDirectory(positive); - const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory); - const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory); - tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic)); - /* - * For the sake of reducing future accesses to the file system, we merge all tasks within the current directory - * into a global task, if at least one pattern refers to the root (`.`). In this case, the global task covers the rest. - */ - if ('.' in insideCurrentDirectoryGroup) { - tasks.push(convertPatternGroupToTask('.', patternsInsideCurrentDirectory, negative, dynamic)); - } - else { - tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic)); - } - return tasks; -} -exports.convertPatternsToTasks = convertPatternsToTasks; -function getPositivePatterns(patterns) { - return utils.pattern.getPositivePatterns(patterns); -} -exports.getPositivePatterns = getPositivePatterns; -function getNegativePatternsAsPositive(patterns, ignore) { - const negative = utils.pattern.getNegativePatterns(patterns).concat(ignore); - const positive = negative.map(utils.pattern.convertToPositivePattern); - return positive; -} -exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; -function groupPatternsByBaseDirectory(patterns) { - const group = {}; - return patterns.reduce((collection, pattern) => { - const base = utils.pattern.getBaseDirectory(pattern); - if (base in collection) { - collection[base].push(pattern); - } - else { - collection[base] = [pattern]; - } - return collection; - }, group); -} -exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; -function convertPatternGroupsToTasks(positive, negative, dynamic) { - return Object.keys(positive).map((base) => { - return convertPatternGroupToTask(base, positive[base], negative, dynamic); - }); -} -exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; -function convertPatternGroupToTask(base, positive, negative, dynamic) { - return { - dynamic, - positive, - negative, - base, - patterns: [].concat(positive, negative.map(utils.pattern.convertToNegativePattern)) - }; -} -exports.convertPatternGroupToTask = convertPatternGroupToTask; +const parseGitIgnore = (content, options) => { + const base = slash(path.relative(options.cwd, path.dirname(options.fileName))); + return content + .split(/\r?\n/) + .filter(Boolean) + .filter(line => !line.startsWith('#')) + .map(mapGitIgnorePatternTo(base)); +}; -/***/ }), +const reduceIgnore = files => { + return files.reduce((ignores, file) => { + ignores.add(parseGitIgnore(file.content, { + cwd: file.cwd, + fileName: file.filePath + })); + return ignores; + }, gitIgnore()); +}; -/***/ "../../node_modules/del/node_modules/fast-glob/out/providers/async.js": -/***/ (function(module, exports, __webpack_require__) { +const ensureAbsolutePathForCwd = (cwd, p) => { + if (path.isAbsolute(p)) { + if (p.startsWith(cwd)) { + return p; + } -"use strict"; + throw new Error(`Path ${p} is not in cwd ${cwd}`); + } -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/readers/stream.js"); -const provider_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/providers/provider.js"); -class ProviderAsync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = []; - return new Promise((resolve, reject) => { - const stream = this.api(root, task, options); - stream.once('error', reject); - stream.on('data', (entry) => entries.push(options.transform(entry))); - stream.once('end', () => resolve(entries)); - }); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderAsync; + return path.join(cwd, p); +}; +const getIsIgnoredPredecate = (ignores, cwd) => { + return p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p)))); +}; -/***/ }), +const getFile = async (file, cwd) => { + const filePath = path.join(cwd, file); + const content = await readFileP(filePath, 'utf8'); -/***/ "../../node_modules/del/node_modules/fast-glob/out/providers/filters/deep.js": -/***/ (function(module, exports, __webpack_require__) { + return { + cwd, + filePath, + content + }; +}; -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/index.js"); -const partial_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/providers/matchers/partial.js"); -class DeepFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - } - getFilter(basePath, positive, negative) { - const matcher = this._getMatcher(positive); - const negativeRe = this._getNegativePatternsRe(negative); - return (entry) => this._filter(basePath, entry, matcher, negativeRe); - } - _getMatcher(patterns) { - return new partial_1.default(patterns, this._settings, this._micromatchOptions); - } - _getNegativePatternsRe(patterns) { - const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); - return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); - } - _filter(basePath, entry, matcher, negativeRe) { - if (this._isSkippedByDeep(basePath, entry.path)) { - return false; - } - if (this._isSkippedSymbolicLink(entry)) { - return false; - } - const filepath = utils.path.removeLeadingDotSegment(entry.path); - if (this._isSkippedByPositivePatterns(filepath, matcher)) { - return false; - } - return this._isSkippedByNegativePatterns(filepath, negativeRe); - } - _isSkippedByDeep(basePath, entryPath) { - /** - * Avoid unnecessary depth calculations when it doesn't matter. - */ - if (this._settings.deep === Infinity) { - return false; - } - return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; - } - _getEntryLevel(basePath, entryPath) { - const entryPathDepth = entryPath.split('/').length; - if (basePath === '') { - return entryPathDepth; - } - const basePathDepth = basePath.split('/').length; - return entryPathDepth - basePathDepth; - } - _isSkippedSymbolicLink(entry) { - return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); - } - _isSkippedByPositivePatterns(entryPath, matcher) { - return !this._settings.baseNameMatch && !matcher.match(entryPath); - } - _isSkippedByNegativePatterns(entryPath, patternsRe) { - return !utils.pattern.matchAny(entryPath, patternsRe); - } -} -exports.default = DeepFilter; +const getFileSync = (file, cwd) => { + const filePath = path.join(cwd, file); + const content = fs.readFileSync(filePath, 'utf8'); + return { + cwd, + filePath, + content + }; +}; -/***/ }), +const normalizeOptions = ({ + ignore = [], + cwd = slash(process.cwd()) +} = {}) => { + return {ignore, cwd}; +}; -/***/ "../../node_modules/del/node_modules/fast-glob/out/providers/filters/entry.js": -/***/ (function(module, exports, __webpack_require__) { +module.exports = async options => { + options = normalizeOptions(options); -"use strict"; + const paths = await fastGlob('**/.gitignore', { + ignore: DEFAULT_IGNORE.concat(options.ignore), + cwd: options.cwd + }); -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/index.js"); -class EntryFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this.index = new Map(); - } - getFilter(positive, negative) { - const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions); - const negativeRe = utils.pattern.convertPatternsToRe(negative, this._micromatchOptions); - return (entry) => this._filter(entry, positiveRe, negativeRe); - } - _filter(entry, positiveRe, negativeRe) { - if (this._settings.unique && this._isDuplicateEntry(entry)) { - return false; - } - if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { - return false; - } - if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) { - return false; - } - const filepath = this._settings.baseNameMatch ? entry.name : entry.path; - const isMatched = this._isMatchToPatterns(filepath, positiveRe) && !this._isMatchToPatterns(entry.path, negativeRe); - if (this._settings.unique && isMatched) { - this._createIndexRecord(entry); - } - return isMatched; - } - _isDuplicateEntry(entry) { - return this.index.has(entry.path); - } - _createIndexRecord(entry) { - this.index.set(entry.path, undefined); - } - _onlyFileFilter(entry) { - return this._settings.onlyFiles && !entry.dirent.isFile(); - } - _onlyDirectoryFilter(entry) { - return this._settings.onlyDirectories && !entry.dirent.isDirectory(); - } - _isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) { - if (!this._settings.absolute) { - return false; - } - const fullpath = utils.path.makeAbsolute(this._settings.cwd, entryPath); - return utils.pattern.matchAny(fullpath, patternsRe); - } - _isMatchToPatterns(entryPath, patternsRe) { - const filepath = utils.path.removeLeadingDotSegment(entryPath); - return utils.pattern.matchAny(filepath, patternsRe); - } -} -exports.default = EntryFilter; + const files = await Promise.all(paths.map(file => getFile(file, options.cwd))); + const ignores = reduceIgnore(files); + return getIsIgnoredPredecate(ignores, options.cwd); +}; -/***/ }), +module.exports.sync = options => { + options = normalizeOptions(options); -/***/ "../../node_modules/del/node_modules/fast-glob/out/providers/filters/error.js": -/***/ (function(module, exports, __webpack_require__) { + const paths = fastGlob.sync('**/.gitignore', { + ignore: DEFAULT_IGNORE.concat(options.ignore), + cwd: options.cwd + }); -"use strict"; + const files = paths.map(file => getFileSync(file, options.cwd)); + const ignores = reduceIgnore(files); -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/index.js"); -class ErrorFilter { - constructor(_settings) { - this._settings = _settings; - } - getFilter() { - return (error) => this._isNonFatalError(error); - } - _isNonFatalError(error) { - return utils.errno.isEnoentCodeError(error) || this._settings.suppressErrors; - } -} -exports.default = ErrorFilter; + return getIsIgnoredPredecate(ignores, options.cwd); +}; /***/ }), -/***/ "../../node_modules/del/node_modules/fast-glob/out/providers/matchers/matcher.js": +/***/ "../../node_modules/del/node_modules/globby/index.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/index.js"); -class Matcher { - constructor(_patterns, _settings, _micromatchOptions) { - this._patterns = _patterns; - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this._storage = []; - this._fillStorage(); - } - _fillStorage() { - /** - * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). - * So, before expand patterns with brace expansion into separated patterns. - */ - const patterns = utils.pattern.expandPatternsWithBraceExpansion(this._patterns); - for (const pattern of patterns) { - const segments = this._getPatternSegments(pattern); - const sections = this._splitSegmentsIntoSections(segments); - this._storage.push({ - complete: sections.length <= 1, - pattern, - segments, - sections - }); - } - } - _getPatternSegments(pattern) { - const parts = utils.pattern.getPatternParts(pattern, this._micromatchOptions); - return parts.map((part) => { - const dynamic = utils.pattern.isDynamicPattern(part, this._settings); - if (!dynamic) { - return { - dynamic: false, - pattern: part - }; - } - return { - dynamic: true, - pattern: part, - patternRe: utils.pattern.makeRe(part, this._micromatchOptions) - }; - }); - } - _splitSegmentsIntoSections(segments) { - return utils.array.splitWhen(segments, (segment) => segment.dynamic && utils.pattern.hasGlobStar(segment.pattern)); - } -} -exports.default = Matcher; - - -/***/ }), +const fs = __webpack_require__("fs"); +const arrayUnion = __webpack_require__("../../node_modules/array-union/index.js"); +const merge2 = __webpack_require__("../../node_modules/merge2/index.js"); +const glob = __webpack_require__("../../node_modules/glob/glob.js"); +const fastGlob = __webpack_require__("../../node_modules/fast-glob/out/index.js"); +const dirGlob = __webpack_require__("../../node_modules/dir-glob/index.js"); +const gitignore = __webpack_require__("../../node_modules/del/node_modules/globby/gitignore.js"); +const {FilterStream, UniqueStream} = __webpack_require__("../../node_modules/del/node_modules/globby/stream-utils.js"); -/***/ "../../node_modules/del/node_modules/fast-glob/out/providers/matchers/partial.js": -/***/ (function(module, exports, __webpack_require__) { +const DEFAULT_FILTER = () => false; -"use strict"; +const isNegative = pattern => pattern[0] === '!'; -Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/providers/matchers/matcher.js"); -class PartialMatcher extends matcher_1.default { - match(filepath) { - const parts = filepath.split('/'); - const levels = parts.length; - const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); - for (const pattern of patterns) { - const section = pattern.sections[0]; - /** - * In this case, the pattern has a globstar and we must read all directories unconditionally, - * but only if the level has reached the end of the first group. - * - * fixtures/{a,b}/** - * ^ true/false ^ always true - */ - if (!pattern.complete && levels > section.length) { - return true; - } - const match = parts.every((part, index) => { - const segment = pattern.segments[index]; - if (segment.dynamic && segment.patternRe.test(part)) { - return true; - } - if (!segment.dynamic && segment.pattern === part) { - return true; - } - return false; - }); - if (match) { - return true; - } - } - return false; - } -} -exports.default = PartialMatcher; +const assertPatternsInput = patterns => { + if (!patterns.every(pattern => typeof pattern === 'string')) { + throw new TypeError('Patterns must be a string or an array of strings'); + } +}; +const checkCwdOption = (options = {}) => { + if (!options.cwd) { + return; + } -/***/ }), + let stat; + try { + stat = fs.statSync(options.cwd); + } catch (_) { + return; + } -/***/ "../../node_modules/del/node_modules/fast-glob/out/providers/provider.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; + if (!stat.isDirectory()) { + throw new Error('The `cwd` option must be a path to a directory'); + } +}; -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__("path"); -const deep_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/providers/filters/deep.js"); -const entry_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/providers/filters/entry.js"); -const error_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/providers/filters/error.js"); -const entry_2 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/providers/transformers/entry.js"); -class Provider { - constructor(_settings) { - this._settings = _settings; - this.errorFilter = new error_1.default(this._settings); - this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); - this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); - this.entryTransformer = new entry_2.default(this._settings); - } - _getRootDirectory(task) { - return path.resolve(this._settings.cwd, task.base); - } - _getReaderOptions(task) { - const basePath = task.base === '.' ? '' : task.base; - return { - basePath, - pathSegmentSeparator: '/', - concurrency: this._settings.concurrency, - deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), - entryFilter: this.entryFilter.getFilter(task.positive, task.negative), - errorFilter: this.errorFilter.getFilter(), - followSymbolicLinks: this._settings.followSymbolicLinks, - fs: this._settings.fs, - stats: this._settings.stats, - throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, - transform: this.entryTransformer.getTransformer() - }; - } - _getMicromatchOptions() { - return { - dot: this._settings.dot, - matchBase: this._settings.baseNameMatch, - nobrace: !this._settings.braceExpansion, - nocase: !this._settings.caseSensitiveMatch, - noext: !this._settings.extglob, - noglobstar: !this._settings.globstar, - posix: true, - strictSlashes: false - }; - } -} -exports.default = Provider; +const getPathString = p => p.stats instanceof fs.Stats ? p.path : p; +const generateGlobTasks = (patterns, taskOptions) => { + patterns = arrayUnion([].concat(patterns)); + assertPatternsInput(patterns); + checkCwdOption(taskOptions); -/***/ }), + const globTasks = []; -/***/ "../../node_modules/del/node_modules/fast-glob/out/providers/stream.js": -/***/ (function(module, exports, __webpack_require__) { + taskOptions = { + ignore: [], + expandDirectories: true, + ...taskOptions + }; -"use strict"; + for (const [index, pattern] of patterns.entries()) { + if (isNegative(pattern)) { + continue; + } -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__("stream"); -const stream_2 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/readers/stream.js"); -const provider_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/providers/provider.js"); -class ProviderStream extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_2.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const source = this.api(root, task, options); - const destination = new stream_1.Readable({ objectMode: true, read: () => { } }); - source - .once('error', (error) => destination.emit('error', error)) - .on('data', (entry) => destination.emit('data', options.transform(entry))) - .once('end', () => destination.emit('end')); - destination - .once('close', () => source.destroy()); - return destination; - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderStream; + const ignore = patterns + .slice(index) + .filter(isNegative) + .map(pattern => pattern.slice(1)); + const options = { + ...taskOptions, + ignore: taskOptions.ignore.concat(ignore) + }; -/***/ }), + globTasks.push({pattern, options}); + } -/***/ "../../node_modules/del/node_modules/fast-glob/out/providers/sync.js": -/***/ (function(module, exports, __webpack_require__) { + return globTasks; +}; -"use strict"; +const globDirs = (task, fn) => { + let options = {}; + if (task.options.cwd) { + options.cwd = task.options.cwd; + } -Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/readers/sync.js"); -const provider_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/providers/provider.js"); -class ProviderSync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new sync_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = this.api(root, task, options); - return entries.map(options.transform); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderSync; + if (Array.isArray(task.options.expandDirectories)) { + options = { + ...options, + files: task.options.expandDirectories + }; + } else if (typeof task.options.expandDirectories === 'object') { + options = { + ...options, + ...task.options.expandDirectories + }; + } + return fn(task.pattern, options); +}; -/***/ }), +const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; -/***/ "../../node_modules/del/node_modules/fast-glob/out/providers/transformers/entry.js": -/***/ (function(module, exports, __webpack_require__) { +const getFilterSync = options => { + return options && options.gitignore ? + gitignore.sync({cwd: options.cwd, ignore: options.ignore}) : + DEFAULT_FILTER; +}; -"use strict"; +const globToTask = task => glob => { + const {options} = task; + if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { + options.ignore = dirGlob.sync(options.ignore); + } -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/index.js"); -class EntryTransformer { - constructor(_settings) { - this._settings = _settings; - } - getTransformer() { - return (entry) => this._transform(entry); - } - _transform(entry) { - let filepath = entry.path; - if (this._settings.absolute) { - filepath = utils.path.makeAbsolute(this._settings.cwd, filepath); - filepath = utils.path.unixify(filepath); - } - if (this._settings.markDirectories && entry.dirent.isDirectory()) { - filepath += '/'; - } - if (!this._settings.objectMode) { - return filepath; - } - return Object.assign(Object.assign({}, entry), { path: filepath }); - } -} -exports.default = EntryTransformer; + return { + pattern: glob, + options + }; +}; +module.exports = async (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); -/***/ }), + const getFilter = async () => { + return options && options.gitignore ? + gitignore({cwd: options.cwd, ignore: options.ignore}) : + DEFAULT_FILTER; + }; -/***/ "../../node_modules/del/node_modules/fast-glob/out/readers/reader.js": -/***/ (function(module, exports, __webpack_require__) { + const getTasks = async () => { + const tasks = await Promise.all(globTasks.map(async task => { + const globs = await getPattern(task, dirGlob); + return Promise.all(globs.map(globToTask(task))); + })); -"use strict"; + return arrayUnion(...tasks); + }; -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__("path"); -const fsStat = __webpack_require__("../../node_modules/@nodelib/fs.stat/out/index.js"); -const utils = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/index.js"); -class Reader { - constructor(_settings) { - this._settings = _settings; - this._fsStatSettings = new fsStat.Settings({ - followSymbolicLink: this._settings.followSymbolicLinks, - fs: this._settings.fs, - throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks - }); - } - _getFullEntryPath(filepath) { - return path.resolve(this._settings.cwd, filepath); - } - _makeEntry(stats, pattern) { - const entry = { - name: pattern, - path: pattern, - dirent: utils.fs.createDirentFromStats(pattern, stats) - }; - if (this._settings.stats) { - entry.stats = stats; - } - return entry; - } - _isFatalError(error) { - return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; - } -} -exports.default = Reader; + const [filter, tasks] = await Promise.all([getFilter(), getTasks()]); + const paths = await Promise.all(tasks.map(task => fastGlob(task.pattern, task.options))); + return arrayUnion(...paths).filter(path_ => !filter(getPathString(path_))); +}; -/***/ }), +module.exports.sync = (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); -/***/ "../../node_modules/del/node_modules/fast-glob/out/readers/stream.js": -/***/ (function(module, exports, __webpack_require__) { + const tasks = globTasks.reduce((tasks, task) => { + const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); + return tasks.concat(newTask); + }, []); -"use strict"; + const filter = getFilterSync(options); -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__("stream"); -const fsStat = __webpack_require__("../../node_modules/@nodelib/fs.stat/out/index.js"); -const fsWalk = __webpack_require__("../../node_modules/@nodelib/fs.walk/out/index.js"); -const reader_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/readers/reader.js"); -class ReaderStream extends reader_1.default { - constructor() { - super(...arguments); - this._walkStream = fsWalk.walkStream; - this._stat = fsStat.stat; - } - dynamic(root, options) { - return this._walkStream(root, options); - } - static(patterns, options) { - const filepaths = patterns.map(this._getFullEntryPath, this); - const stream = new stream_1.PassThrough({ objectMode: true }); - stream._write = (index, _enc, done) => { - return this._getEntry(filepaths[index], patterns[index], options) - .then((entry) => { - if (entry !== null && options.entryFilter(entry)) { - stream.push(entry); - } - if (index === filepaths.length - 1) { - stream.end(); - } - done(); - }) - .catch(done); - }; - for (let i = 0; i < filepaths.length; i++) { - stream.write(i); - } - return stream; - } - _getEntry(filepath, pattern, options) { - return this._getStat(filepath) - .then((stats) => this._makeEntry(stats, pattern)) - .catch((error) => { - if (options.errorFilter(error)) { - return null; - } - throw error; - }); - } - _getStat(filepath) { - return new Promise((resolve, reject) => { - this._stat(filepath, this._fsStatSettings, (error, stats) => { - return error === null ? resolve(stats) : reject(error); - }); - }); - } -} -exports.default = ReaderStream; + return tasks.reduce( + (matches, task) => arrayUnion(matches, fastGlob.sync(task.pattern, task.options)), + [] + ).filter(path_ => !filter(path_)); +}; +module.exports.stream = (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); -/***/ }), + const tasks = globTasks.reduce((tasks, task) => { + const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); + return tasks.concat(newTask); + }, []); -/***/ "../../node_modules/del/node_modules/fast-glob/out/readers/sync.js": -/***/ (function(module, exports, __webpack_require__) { + const filter = getFilterSync(options); + const filterStream = new FilterStream(p => !filter(p)); + const uniqueStream = new UniqueStream(); -"use strict"; + return merge2(tasks.map(task => fastGlob.stream(task.pattern, task.options))) + .pipe(filterStream) + .pipe(uniqueStream); +}; -Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__("../../node_modules/@nodelib/fs.stat/out/index.js"); -const fsWalk = __webpack_require__("../../node_modules/@nodelib/fs.walk/out/index.js"); -const reader_1 = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/readers/reader.js"); -class ReaderSync extends reader_1.default { - constructor() { - super(...arguments); - this._walkSync = fsWalk.walkSync; - this._statSync = fsStat.statSync; - } - dynamic(root, options) { - return this._walkSync(root, options); - } - static(patterns, options) { - const entries = []; - for (const pattern of patterns) { - const filepath = this._getFullEntryPath(pattern); - const entry = this._getEntry(filepath, pattern, options); - if (entry === null || !options.entryFilter(entry)) { - continue; - } - entries.push(entry); - } - return entries; - } - _getEntry(filepath, pattern, options) { - try { - const stats = this._getStat(filepath); - return this._makeEntry(stats, pattern); - } - catch (error) { - if (options.errorFilter(error)) { - return null; - } - throw error; - } - } - _getStat(filepath) { - return this._statSync(filepath, this._fsStatSettings); - } -} -exports.default = ReaderSync; +module.exports.generateGlobTasks = generateGlobTasks; + +module.exports.hasMagic = (patterns, options) => [] + .concat(patterns) + .some(pattern => glob.hasMagic(pattern, options)); + +module.exports.gitignore = gitignore; /***/ }), -/***/ "../../node_modules/del/node_modules/fast-glob/out/settings.js": +/***/ "../../node_modules/del/node_modules/globby/stream-utils.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0; -const fs = __webpack_require__("fs"); -const os = __webpack_require__("os"); -/** - * The `os.cpus` method can return zero. We expect the number of cores to be greater than zero. - * https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107 - */ -const CPU_COUNT = Math.max(os.cpus().length, 1); -exports.DEFAULT_FILE_SYSTEM_ADAPTER = { - lstat: fs.lstat, - lstatSync: fs.lstatSync, - stat: fs.stat, - statSync: fs.statSync, - readdir: fs.readdir, - readdirSync: fs.readdirSync -}; -class Settings { - constructor(_options = {}) { - this._options = _options; - this.absolute = this._getValue(this._options.absolute, false); - this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); - this.braceExpansion = this._getValue(this._options.braceExpansion, true); - this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); - this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); - this.cwd = this._getValue(this._options.cwd, process.cwd()); - this.deep = this._getValue(this._options.deep, Infinity); - this.dot = this._getValue(this._options.dot, false); - this.extglob = this._getValue(this._options.extglob, true); - this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); - this.fs = this._getFileSystemMethods(this._options.fs); - this.globstar = this._getValue(this._options.globstar, true); - this.ignore = this._getValue(this._options.ignore, []); - this.markDirectories = this._getValue(this._options.markDirectories, false); - this.objectMode = this._getValue(this._options.objectMode, false); - this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); - this.onlyFiles = this._getValue(this._options.onlyFiles, true); - this.stats = this._getValue(this._options.stats, false); - this.suppressErrors = this._getValue(this._options.suppressErrors, false); - this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); - this.unique = this._getValue(this._options.unique, true); - if (this.onlyDirectories) { - this.onlyFiles = false; - } - if (this.stats) { - this.objectMode = true; - } - } - _getValue(option, value) { - return option === undefined ? value : option; - } - _getFileSystemMethods(methods = {}) { - return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); - } +const {Transform} = __webpack_require__("stream"); + +class ObjectTransform extends Transform { + constructor() { + super({ + objectMode: true + }); + } } -exports.default = Settings; +class FilterStream extends ObjectTransform { + constructor(filter) { + super(); + this._filter = filter; + } -/***/ }), + _transform(data, encoding, callback) { + if (this._filter(data)) { + this.push(data); + } -/***/ "../../node_modules/del/node_modules/fast-glob/out/utils/array.js": -/***/ (function(module, exports, __webpack_require__) { + callback(); + } +} -"use strict"; +class UniqueStream extends ObjectTransform { + constructor() { + super(); + this._pushed = new Set(); + } -Object.defineProperty(exports, "__esModule", { value: true }); -exports.splitWhen = exports.flatten = void 0; -function flatten(items) { - return items.reduce((collection, item) => [].concat(collection, item), []); -} -exports.flatten = flatten; -function splitWhen(items, predicate) { - const result = [[]]; - let groupIndex = 0; - for (const item of items) { - if (predicate(item)) { - groupIndex++; - result[groupIndex] = []; - } - else { - result[groupIndex].push(item); - } - } - return result; + _transform(data, encoding, callback) { + if (!this._pushed.has(data)) { + this.push(data); + this._pushed.add(data); + } + + callback(); + } } -exports.splitWhen = splitWhen; + +module.exports = { + FilterStream, + UniqueStream +}; /***/ }), -/***/ "../../node_modules/del/node_modules/fast-glob/out/utils/errno.js": +/***/ "../../node_modules/del/node_modules/indent-string/index.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isEnoentCodeError = void 0; -function isEnoentCodeError(error) { - return error.code === 'ENOENT'; -} -exports.isEnoentCodeError = isEnoentCodeError; +module.exports = (string, count = 1, options) => { + options = { + indent: ' ', + includeEmptyLines: false, + ...options + }; -/***/ }), + if (typeof string !== 'string') { + throw new TypeError( + `Expected \`input\` to be a \`string\`, got \`${typeof string}\`` + ); + } -/***/ "../../node_modules/del/node_modules/fast-glob/out/utils/fs.js": -/***/ (function(module, exports, __webpack_require__) { + if (typeof count !== 'number') { + throw new TypeError( + `Expected \`count\` to be a \`number\`, got \`${typeof count}\`` + ); + } -"use strict"; + if (typeof options.indent !== 'string') { + throw new TypeError( + `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\`` + ); + } -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createDirentFromStats = void 0; -class DirentFromStats { - constructor(name, stats) { - this.name = name; - this.isBlockDevice = stats.isBlockDevice.bind(stats); - this.isCharacterDevice = stats.isCharacterDevice.bind(stats); - this.isDirectory = stats.isDirectory.bind(stats); - this.isFIFO = stats.isFIFO.bind(stats); - this.isFile = stats.isFile.bind(stats); - this.isSocket = stats.isSocket.bind(stats); - this.isSymbolicLink = stats.isSymbolicLink.bind(stats); - } -} -function createDirentFromStats(name, stats) { - return new DirentFromStats(name, stats); -} -exports.createDirentFromStats = createDirentFromStats; + if (count === 0) { + return string; + } + + const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; + + return string.replace(regex, options.indent.repeat(count)); +}; /***/ }), -/***/ "../../node_modules/del/node_modules/fast-glob/out/utils/index.js": +/***/ "../../node_modules/del/node_modules/p-map/index.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.string = exports.stream = exports.pattern = exports.path = exports.fs = exports.errno = exports.array = void 0; -const array = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/array.js"); -exports.array = array; -const errno = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/errno.js"); -exports.errno = errno; -const fs = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/fs.js"); -exports.fs = fs; -const path = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/path.js"); -exports.path = path; -const pattern = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/pattern.js"); -exports.pattern = pattern; -const stream = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/stream.js"); -exports.stream = stream; -const string = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/utils/string.js"); -exports.string = string; +const AggregateError = __webpack_require__("../../node_modules/del/node_modules/aggregate-error/index.js"); +module.exports = async ( + iterable, + mapper, + { + concurrency = Infinity, + stopOnError = true + } = {} +) => { + return new Promise((resolve, reject) => { + if (typeof mapper !== 'function') { + throw new TypeError('Mapper function is required'); + } -/***/ }), + if (!(typeof concurrency === 'number' && concurrency >= 1)) { + throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); + } -/***/ "../../node_modules/del/node_modules/fast-glob/out/utils/path.js": -/***/ (function(module, exports, __webpack_require__) { + const ret = []; + const errors = []; + const iterator = iterable[Symbol.iterator](); + let isRejected = false; + let isIterableDone = false; + let resolvingCount = 0; + let currentIndex = 0; -"use strict"; + const next = () => { + if (isRejected) { + return; + } -Object.defineProperty(exports, "__esModule", { value: true }); -exports.removeLeadingDotSegment = exports.escape = exports.makeAbsolute = exports.unixify = void 0; -const path = __webpack_require__("path"); -const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ -const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; -/** - * Designed to work only with simple paths: `dir\\file`. - */ -function unixify(filepath) { - return filepath.replace(/\\/g, '/'); -} -exports.unixify = unixify; -function makeAbsolute(cwd, filepath) { - return path.resolve(cwd, filepath); -} -exports.makeAbsolute = makeAbsolute; -function escape(pattern) { - return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); -} -exports.escape = escape; -function removeLeadingDotSegment(entry) { - // We do not use `startsWith` because this is 10x slower than current implementation for some cases. - // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with - if (entry.charAt(0) === '.') { - const secondCharactery = entry.charAt(1); - if (secondCharactery === '/' || secondCharactery === '\\') { - return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); - } - } - return entry; -} -exports.removeLeadingDotSegment = removeLeadingDotSegment; + const nextItem = iterator.next(); + const i = currentIndex; + currentIndex++; + + if (nextItem.done) { + isIterableDone = true; + + if (resolvingCount === 0) { + if (!stopOnError && errors.length !== 0) { + reject(new AggregateError(errors)); + } else { + resolve(ret); + } + } + + return; + } + + resolvingCount++; + + (async () => { + try { + const element = await nextItem.value; + ret[i] = await mapper(element, i); + resolvingCount--; + next(); + } catch (error) { + if (stopOnError) { + isRejected = true; + reject(error); + } else { + errors.push(error); + resolvingCount--; + next(); + } + } + })(); + }; + + for (let i = 0; i < concurrency; i++) { + next(); + + if (isIterableDone) { + break; + } + } + }); +}; /***/ }), -/***/ "../../node_modules/del/node_modules/fast-glob/out/utils/pattern.js": +/***/ "../../node_modules/delayed-stream/lib/delayed_stream.js": /***/ (function(module, exports, __webpack_require__) { -"use strict"; +var Stream = __webpack_require__("stream").Stream; +var util = __webpack_require__("util"); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; -const path = __webpack_require__("path"); -const globParent = __webpack_require__("../../node_modules/glob-parent/index.js"); -const micromatch = __webpack_require__("../../node_modules/del/node_modules/micromatch/index.js"); -const GLOBSTAR = '**'; -const ESCAPE_SYMBOL = '\\'; -const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; -const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[.*]/; -const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\(.*\|.*\)/; -const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\(.*\)/; -const BRACE_EXPANSIONS_SYMBOLS_RE = /{.*(?:,|\.\.).*}/; -function isStaticPattern(pattern, options = {}) { - return !isDynamicPattern(pattern, options); -} -exports.isStaticPattern = isStaticPattern; -function isDynamicPattern(pattern, options = {}) { - /** - * A special case with an empty string is necessary for matching patterns that start with a forward slash. - * An empty string cannot be a dynamic pattern. - * For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'. - */ - if (pattern === '') { - return false; - } - /** - * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check - * filepath directly (without read directory). - */ - if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { - return true; - } - if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.braceExpansion !== false && BRACE_EXPANSIONS_SYMBOLS_RE.test(pattern)) { - return true; - } - return false; -} -exports.isDynamicPattern = isDynamicPattern; -function convertToPositivePattern(pattern) { - return isNegativePattern(pattern) ? pattern.slice(1) : pattern; -} -exports.convertToPositivePattern = convertToPositivePattern; -function convertToNegativePattern(pattern) { - return '!' + pattern; -} -exports.convertToNegativePattern = convertToNegativePattern; -function isNegativePattern(pattern) { - return pattern.startsWith('!') && pattern[1] !== '('; -} -exports.isNegativePattern = isNegativePattern; -function isPositivePattern(pattern) { - return !isNegativePattern(pattern); -} -exports.isPositivePattern = isPositivePattern; -function getNegativePatterns(patterns) { - return patterns.filter(isNegativePattern); -} -exports.getNegativePatterns = getNegativePatterns; -function getPositivePatterns(patterns) { - return patterns.filter(isPositivePattern); -} -exports.getPositivePatterns = getPositivePatterns; -/** - * Returns patterns that can be applied inside the current directory. - * - * @example - * // ['./*', '*', 'a/*'] - * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) - */ -function getPatternsInsideCurrentDirectory(patterns) { - return patterns.filter((pattern) => !isPatternRelatedToParentDirectory(pattern)); +module.exports = DelayedStream; +function DelayedStream() { + this.source = null; + this.dataSize = 0; + this.maxDataSize = 1024 * 1024; + this.pauseStream = true; + + this._maxDataSizeExceeded = false; + this._released = false; + this._bufferedEvents = []; } -exports.getPatternsInsideCurrentDirectory = getPatternsInsideCurrentDirectory; -/** - * Returns patterns to be expanded relative to (outside) the current directory. - * - * @example - * // ['../*', './../*'] - * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) - */ -function getPatternsOutsideCurrentDirectory(patterns) { - return patterns.filter(isPatternRelatedToParentDirectory); -} -exports.getPatternsOutsideCurrentDirectory = getPatternsOutsideCurrentDirectory; -function isPatternRelatedToParentDirectory(pattern) { - return pattern.startsWith('..') || pattern.startsWith('./..'); -} -exports.isPatternRelatedToParentDirectory = isPatternRelatedToParentDirectory; -function getBaseDirectory(pattern) { - return globParent(pattern, { flipBackslashes: false }); -} -exports.getBaseDirectory = getBaseDirectory; -function hasGlobStar(pattern) { - return pattern.includes(GLOBSTAR); -} -exports.hasGlobStar = hasGlobStar; -function endsWithSlashGlobStar(pattern) { - return pattern.endsWith('/' + GLOBSTAR); -} -exports.endsWithSlashGlobStar = endsWithSlashGlobStar; -function isAffectDepthOfReadingPattern(pattern) { - const basename = path.basename(pattern); - return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); -} -exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; -function expandPatternsWithBraceExpansion(patterns) { - return patterns.reduce((collection, pattern) => { - return collection.concat(expandBraceExpansion(pattern)); - }, []); -} -exports.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; -function expandBraceExpansion(pattern) { - return micromatch.braces(pattern, { - expand: true, - nodupes: true - }); -} -exports.expandBraceExpansion = expandBraceExpansion; -function getPatternParts(pattern, options) { - let { parts } = micromatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); - /** - * The scan method returns an empty array in some cases. - * See micromatch/picomatch#58 for more details. - */ - if (parts.length === 0) { - parts = [pattern]; - } - /** - * The scan method does not return an empty part for the pattern with a forward slash. - * This is another part of micromatch/picomatch#58. - */ - if (parts[0].startsWith('/')) { - parts[0] = parts[0].slice(1); - parts.unshift(''); - } - return parts; -} -exports.getPatternParts = getPatternParts; -function makeRe(pattern, options) { - return micromatch.makeRe(pattern, options); -} -exports.makeRe = makeRe; -function convertPatternsToRe(patterns, options) { - return patterns.map((pattern) => makeRe(pattern, options)); -} -exports.convertPatternsToRe = convertPatternsToRe; -function matchAny(entry, patternsRe) { - return patternsRe.some((patternRe) => patternRe.test(entry)); -} -exports.matchAny = matchAny; - - -/***/ }), - -/***/ "../../node_modules/del/node_modules/fast-glob/out/utils/stream.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.merge = void 0; -const merge2 = __webpack_require__("../../node_modules/merge2/index.js"); -function merge(streams) { - const mergedStream = merge2(streams); - streams.forEach((stream) => { - stream.once('error', (error) => mergedStream.emit('error', error)); - }); - mergedStream.once('close', () => propagateCloseEventToSources(streams)); - mergedStream.once('end', () => propagateCloseEventToSources(streams)); - return mergedStream; -} -exports.merge = merge; -function propagateCloseEventToSources(streams) { - streams.forEach((stream) => stream.emit('close')); -} - - -/***/ }), - -/***/ "../../node_modules/del/node_modules/fast-glob/out/utils/string.js": -/***/ (function(module, exports, __webpack_require__) { +util.inherits(DelayedStream, Stream); -"use strict"; +DelayedStream.create = function(source, options) { + var delayedStream = new this(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isEmpty = exports.isString = void 0; -function isString(input) { - return typeof input === 'string'; -} -exports.isString = isString; -function isEmpty(input) { - return input === ''; -} -exports.isEmpty = isEmpty; + options = options || {}; + for (var option in options) { + delayedStream[option] = options[option]; + } + delayedStream.source = source; -/***/ }), + var realEmit = source.emit; + source.emit = function() { + delayedStream._handleEmit(arguments); + return realEmit.apply(source, arguments); + }; -/***/ "../../node_modules/del/node_modules/globby/gitignore.js": -/***/ (function(module, exports, __webpack_require__) { + source.on('error', function() {}); + if (delayedStream.pauseStream) { + source.pause(); + } -"use strict"; + return delayedStream; +}; -const {promisify} = __webpack_require__("util"); -const fs = __webpack_require__("fs"); -const path = __webpack_require__("path"); -const fastGlob = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/index.js"); -const gitIgnore = __webpack_require__("../../node_modules/ignore/index.js"); -const slash = __webpack_require__("../../node_modules/slash/index.js"); +Object.defineProperty(DelayedStream.prototype, 'readable', { + configurable: true, + enumerable: true, + get: function() { + return this.source.readable; + } +}); -const DEFAULT_IGNORE = [ - '**/node_modules/**', - '**/flow-typed/**', - '**/coverage/**', - '**/.git' -]; +DelayedStream.prototype.setEncoding = function() { + return this.source.setEncoding.apply(this.source, arguments); +}; -const readFileP = promisify(fs.readFile); +DelayedStream.prototype.resume = function() { + if (!this._released) { + this.release(); + } -const mapGitIgnorePatternTo = base => ignore => { - if (ignore.startsWith('!')) { - return '!' + path.posix.join(base, ignore.slice(1)); - } + this.source.resume(); +}; - return path.posix.join(base, ignore); +DelayedStream.prototype.pause = function() { + this.source.pause(); }; -const parseGitIgnore = (content, options) => { - const base = slash(path.relative(options.cwd, path.dirname(options.fileName))); +DelayedStream.prototype.release = function() { + this._released = true; - return content - .split(/\r?\n/) - .filter(Boolean) - .filter(line => !line.startsWith('#')) - .map(mapGitIgnorePatternTo(base)); + this._bufferedEvents.forEach(function(args) { + this.emit.apply(this, args); + }.bind(this)); + this._bufferedEvents = []; }; -const reduceIgnore = files => { - return files.reduce((ignores, file) => { - ignores.add(parseGitIgnore(file.content, { - cwd: file.cwd, - fileName: file.filePath - })); - return ignores; - }, gitIgnore()); +DelayedStream.prototype.pipe = function() { + var r = Stream.prototype.pipe.apply(this, arguments); + this.resume(); + return r; }; -const ensureAbsolutePathForCwd = (cwd, p) => { - if (path.isAbsolute(p)) { - if (p.startsWith(cwd)) { - return p; - } +DelayedStream.prototype._handleEmit = function(args) { + if (this._released) { + this.emit.apply(this, args); + return; + } - throw new Error(`Path ${p} is not in cwd ${cwd}`); - } + if (args[0] === 'data') { + this.dataSize += args[1].length; + this._checkIfMaxDataSizeExceeded(); + } - return path.join(cwd, p); + this._bufferedEvents.push(args); }; -const getIsIgnoredPredecate = (ignores, cwd) => { - return p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p)))); -}; +DelayedStream.prototype._checkIfMaxDataSizeExceeded = function() { + if (this._maxDataSizeExceeded) { + return; + } -const getFile = async (file, cwd) => { - const filePath = path.join(cwd, file); - const content = await readFileP(filePath, 'utf8'); + if (this.dataSize <= this.maxDataSize) { + return; + } - return { - cwd, - filePath, - content - }; + this._maxDataSizeExceeded = true; + var message = + 'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.' + this.emit('error', new Error(message)); }; -const getFileSync = (file, cwd) => { - const filePath = path.join(cwd, file); - const content = fs.readFileSync(filePath, 'utf8'); - return { - cwd, - filePath, - content - }; -}; +/***/ }), -const normalizeOptions = ({ - ignore = [], - cwd = slash(process.cwd()) -} = {}) => { - return {ignore, cwd}; -}; +/***/ "../../node_modules/detect-indent/index.js": +/***/ (function(module, exports, __webpack_require__) { -module.exports = async options => { - options = normalizeOptions(options); +"use strict"; - const paths = await fastGlob('**/.gitignore', { - ignore: DEFAULT_IGNORE.concat(options.ignore), - cwd: options.cwd - }); - const files = await Promise.all(paths.map(file => getFile(file, options.cwd))); - const ignores = reduceIgnore(files); +// Detect either spaces or tabs but not both to properly handle tabs for indentation and spaces for alignment +const INDENT_REGEX = /^(?:( )+|\t+)/; - return getIsIgnoredPredecate(ignores, options.cwd); -}; +const INDENT_TYPE_SPACE = 'space'; +const INDENT_TYPE_TAB = 'tab'; -module.exports.sync = options => { - options = normalizeOptions(options); +// Make a Map that counts how many indents/unindents have occurred for a given size and how many lines follow a given indentation. +// The key is a concatenation of the indentation type (s = space and t = tab) and the size of the indents/unindents. +// +// indents = { +// t3: [1, 0], +// t4: [1, 5], +// s5: [1, 0], +// s12: [1, 0], +// } +function makeIndentsMap(string, ignoreSingleSpaces) { + const indents = new Map(); - const paths = fastGlob.sync('**/.gitignore', { - ignore: DEFAULT_IGNORE.concat(options.ignore), - cwd: options.cwd - }); + // Remember the size of previous line's indentation + let previousSize = 0; + let previousIndentType; - const files = paths.map(file => getFileSync(file, options.cwd)); - const ignores = reduceIgnore(files); + // Indents key (ident type + size of the indents/unindents) + let key; - return getIsIgnoredPredecate(ignores, options.cwd); -}; + for (const line of string.split(/\n/g)) { + if (!line) { + // Ignore empty lines + continue; + } + let indent; + let indentType; + let weight; + let entry; + const matches = line.match(INDENT_REGEX); -/***/ }), + if (matches === null) { + previousSize = 0; + previousIndentType = ''; + } else { + indent = matches[0].length; -/***/ "../../node_modules/del/node_modules/globby/index.js": -/***/ (function(module, exports, __webpack_require__) { + if (matches[1]) { + indentType = INDENT_TYPE_SPACE; + } else { + indentType = INDENT_TYPE_TAB; + } -"use strict"; + // Ignore single space unless it's the only indent detected to prevent common false positives + if (ignoreSingleSpaces && indentType === INDENT_TYPE_SPACE && indent === 1) { + continue; + } -const fs = __webpack_require__("fs"); -const arrayUnion = __webpack_require__("../../node_modules/array-union/index.js"); -const merge2 = __webpack_require__("../../node_modules/merge2/index.js"); -const glob = __webpack_require__("../../node_modules/glob/glob.js"); -const fastGlob = __webpack_require__("../../node_modules/del/node_modules/fast-glob/out/index.js"); -const dirGlob = __webpack_require__("../../node_modules/dir-glob/index.js"); -const gitignore = __webpack_require__("../../node_modules/del/node_modules/globby/gitignore.js"); -const {FilterStream, UniqueStream} = __webpack_require__("../../node_modules/del/node_modules/globby/stream-utils.js"); + if (indentType !== previousIndentType) { + previousSize = 0; + } -const DEFAULT_FILTER = () => false; + previousIndentType = indentType; -const isNegative = pattern => pattern[0] === '!'; + weight = 0; -const assertPatternsInput = patterns => { - if (!patterns.every(pattern => typeof pattern === 'string')) { - throw new TypeError('Patterns must be a string or an array of strings'); - } -}; + const indentDifference = indent - previousSize; + previousSize = indent; -const checkCwdOption = (options = {}) => { - if (!options.cwd) { - return; - } + // Previous line have same indent? + if (indentDifference === 0) { + weight++; + // We use the key from previous loop + } else { + const absoluteIndentDifference = indentDifference > 0 ? indentDifference : -indentDifference; + key = encodeIndentsKey(indentType, absoluteIndentDifference); + } - let stat; - try { - stat = fs.statSync(options.cwd); - } catch (_) { - return; - } + // Update the stats + entry = indents.get(key); - if (!stat.isDirectory()) { - throw new Error('The `cwd` option must be a path to a directory'); - } -}; + if (entry === undefined) { + entry = [1, 0]; // Init + } else { + entry = [++entry[0], entry[1] + weight]; + } -const getPathString = p => p.stats instanceof fs.Stats ? p.path : p; + indents.set(key, entry); + } + } -const generateGlobTasks = (patterns, taskOptions) => { - patterns = arrayUnion([].concat(patterns)); - assertPatternsInput(patterns); - checkCwdOption(taskOptions); + return indents; +} - const globTasks = []; +// Encode the indent type and amount as a string (e.g. 's4') for use as a compound key in the indents Map. +function encodeIndentsKey(indentType, indentAmount) { + const typeCharacter = indentType === INDENT_TYPE_SPACE ? 's' : 't'; + return typeCharacter + String(indentAmount); +} - taskOptions = { - ignore: [], - expandDirectories: true, - ...taskOptions - }; +// Extract the indent type and amount from a key of the indents Map. +function decodeIndentsKey(indentsKey) { + const keyHasTypeSpace = indentsKey[0] === 's'; + const type = keyHasTypeSpace ? INDENT_TYPE_SPACE : INDENT_TYPE_TAB; - for (const [index, pattern] of patterns.entries()) { - if (isNegative(pattern)) { - continue; - } + const amount = Number(indentsKey.slice(1)); - const ignore = patterns - .slice(index) - .filter(isNegative) - .map(pattern => pattern.slice(1)); + return {type, amount}; +} - const options = { - ...taskOptions, - ignore: taskOptions.ignore.concat(ignore) - }; +// Return the key (e.g. 's4') from the indents Map that represents the most common indent, +// or return undefined if there are no indents. +function getMostUsedKey(indents) { + let result; + let maxUsed = 0; + let maxWeight = 0; - globTasks.push({pattern, options}); + for (const [key, [usedCount, weight]] of indents) { + if (usedCount > maxUsed || (usedCount === maxUsed && weight > maxWeight)) { + maxUsed = usedCount; + maxWeight = weight; + result = key; + } } - return globTasks; -}; + return result; +} -const globDirs = (task, fn) => { - let options = {}; - if (task.options.cwd) { - options.cwd = task.options.cwd; - } +function makeIndentString(type, amount) { + const indentCharacter = type === INDENT_TYPE_SPACE ? ' ' : '\t'; + return indentCharacter.repeat(amount); +} - if (Array.isArray(task.options.expandDirectories)) { - options = { - ...options, - files: task.options.expandDirectories - }; - } else if (typeof task.options.expandDirectories === 'object') { - options = { - ...options, - ...task.options.expandDirectories - }; +module.exports = string => { + if (typeof string !== 'string') { + throw new TypeError('Expected a string'); } - return fn(task.pattern, options); -}; + // Identify indents while skipping single space indents to avoid common edge cases (e.g. code comments) + // If no indents are identified, run again and include all indents for comprehensive detection + let indents = makeIndentsMap(string, true); + if (indents.size === 0) { + indents = makeIndentsMap(string, false); + } -const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; + const keyOfMostUsedIndent = getMostUsedKey(indents); -const getFilterSync = options => { - return options && options.gitignore ? - gitignore.sync({cwd: options.cwd, ignore: options.ignore}) : - DEFAULT_FILTER; -}; + let type; + let amount = 0; + let indent = ''; -const globToTask = task => glob => { - const {options} = task; - if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { - options.ignore = dirGlob.sync(options.ignore); + if (keyOfMostUsedIndent !== undefined) { + ({type, amount} = decodeIndentsKey(keyOfMostUsedIndent)); + indent = makeIndentString(type, amount); } return { - pattern: glob, - options + amount, + type, + indent }; }; -module.exports = async (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); - const getFilter = async () => { - return options && options.gitignore ? - gitignore({cwd: options.cwd, ignore: options.ignore}) : - DEFAULT_FILTER; - }; +/***/ }), - const getTasks = async () => { - const tasks = await Promise.all(globTasks.map(async task => { - const globs = await getPattern(task, dirGlob); - return Promise.all(globs.map(globToTask(task))); - })); +/***/ "../../node_modules/detect-newline/index.js": +/***/ (function(module, exports, __webpack_require__) { - return arrayUnion(...tasks); - }; +"use strict"; - const [filter, tasks] = await Promise.all([getFilter(), getTasks()]); - const paths = await Promise.all(tasks.map(task => fastGlob(task.pattern, task.options))); - return arrayUnion(...paths).filter(path_ => !filter(getPathString(path_))); -}; +const detectNewline = string => { + if (typeof string !== 'string') { + throw new TypeError('Expected a string'); + } -module.exports.sync = (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); + const newlines = string.match(/(?:\r?\n)/g) || []; - const tasks = globTasks.reduce((tasks, task) => { - const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); - return tasks.concat(newTask); - }, []); - - const filter = getFilterSync(options); - - return tasks.reduce( - (matches, task) => arrayUnion(matches, fastGlob.sync(task.pattern, task.options)), - [] - ).filter(path_ => !filter(path_)); -}; - -module.exports.stream = (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); - - const tasks = globTasks.reduce((tasks, task) => { - const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); - return tasks.concat(newTask); - }, []); + if (newlines.length === 0) { + return; + } - const filter = getFilterSync(options); - const filterStream = new FilterStream(p => !filter(p)); - const uniqueStream = new UniqueStream(); + const crlf = newlines.filter(newline => newline === '\r\n').length; + const lf = newlines.length - crlf; - return merge2(tasks.map(task => fastGlob.stream(task.pattern, task.options))) - .pipe(filterStream) - .pipe(uniqueStream); + return crlf > lf ? '\r\n' : '\n'; }; -module.exports.generateGlobTasks = generateGlobTasks; - -module.exports.hasMagic = (patterns, options) => [] - .concat(patterns) - .some(pattern => glob.hasMagic(pattern, options)); - -module.exports.gitignore = gitignore; +module.exports = detectNewline; +module.exports.graceful = string => (typeof string === 'string' && detectNewline(string)) || '\n'; /***/ }), -/***/ "../../node_modules/del/node_modules/globby/stream-utils.js": +/***/ "../../node_modules/dir-glob/index.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {Transform} = __webpack_require__("stream"); +const path = __webpack_require__("path"); +const pathType = __webpack_require__("../../node_modules/path-type/index.js"); -class ObjectTransform extends Transform { - constructor() { - super({ - objectMode: true - }); - } -} +const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; -class FilterStream extends ObjectTransform { - constructor(filter) { - super(); - this._filter = filter; +const getPath = (filepath, cwd) => { + const pth = filepath[0] === '!' ? filepath.slice(1) : filepath; + return path.isAbsolute(pth) ? pth : path.join(cwd, pth); +}; + +const addExtensions = (file, extensions) => { + if (path.extname(file)) { + return `**/${file}`; } - _transform(data, encoding, callback) { - if (this._filter(data)) { - this.push(data); - } + return `**/${file}.${getExtensions(extensions)}`; +}; - callback(); +const getGlob = (directory, options) => { + if (options.files && !Array.isArray(options.files)) { + throw new TypeError(`Expected \`files\` to be of type \`Array\` but received type \`${typeof options.files}\``); } -} -class UniqueStream extends ObjectTransform { - constructor() { - super(); - this._pushed = new Set(); + if (options.extensions && !Array.isArray(options.extensions)) { + throw new TypeError(`Expected \`extensions\` to be of type \`Array\` but received type \`${typeof options.extensions}\``); } - _transform(data, encoding, callback) { - if (!this._pushed.has(data)) { - this.push(data); - this._pushed.add(data); - } - - callback(); + if (options.files && options.extensions) { + return options.files.map(x => path.posix.join(directory, addExtensions(x, options.extensions))); } -} - -module.exports = { - FilterStream, - UniqueStream -}; - - -/***/ }), -/***/ "../../node_modules/del/node_modules/indent-string/index.js": -/***/ (function(module, exports, __webpack_require__) { + if (options.files) { + return options.files.map(x => path.posix.join(directory, `**/${x}`)); + } -"use strict"; + if (options.extensions) { + return [path.posix.join(directory, `**/*.${getExtensions(options.extensions)}`)]; + } + return [path.posix.join(directory, '**')]; +}; -module.exports = (string, count = 1, options) => { +module.exports = async (input, options) => { options = { - indent: ' ', - includeEmptyLines: false, + cwd: process.cwd(), ...options }; - if (typeof string !== 'string') { - throw new TypeError( - `Expected \`input\` to be a \`string\`, got \`${typeof string}\`` - ); + if (typeof options.cwd !== 'string') { + throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof options.cwd}\``); } - if (typeof count !== 'number') { - throw new TypeError( - `Expected \`count\` to be a \`number\`, got \`${typeof count}\`` - ); - } + const globs = await Promise.all([].concat(input).map(async x => { + const isDirectory = await pathType.isDirectory(getPath(x, options.cwd)); + return isDirectory ? getGlob(x, options) : x; + })); - if (typeof options.indent !== 'string') { - throw new TypeError( - `Expected \`options.indent\` to be a \`string\`, got \`${typeof options.indent}\`` - ); - } + return [].concat.apply([], globs); // eslint-disable-line prefer-spread +}; - if (count === 0) { - return string; +module.exports.sync = (input, options) => { + options = { + cwd: process.cwd(), + ...options + }; + + if (typeof options.cwd !== 'string') { + throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof options.cwd}\``); } - const regex = options.includeEmptyLines ? /^/gm : /^(?!\s*$)/gm; + const globs = [].concat(input).map(x => pathType.isDirectorySync(getPath(x, options.cwd)) ? getGlob(x, options) : x); - return string.replace(regex, options.indent.repeat(count)); + return [].concat.apply([], globs); // eslint-disable-line prefer-spread }; /***/ }), -/***/ "../../node_modules/del/node_modules/micromatch/index.js": +/***/ "../../node_modules/duplexer/index.js": /***/ (function(module, exports, __webpack_require__) { -"use strict"; - +var Stream = __webpack_require__("stream") +var writeMethods = ["write", "end", "destroy"] +var readMethods = ["resume", "pause"] +var readEvents = ["data", "close"] +var slice = Array.prototype.slice -const util = __webpack_require__("util"); -const braces = __webpack_require__("../../node_modules/braces/index.js"); -const picomatch = __webpack_require__("../../node_modules/picomatch/index.js"); -const utils = __webpack_require__("../../node_modules/picomatch/lib/utils.js"); -const isEmptyString = val => val === '' || val === './'; +module.exports = duplex -/** - * Returns an array of strings that match one or more glob patterns. - * - * ```js - * const mm = require('micromatch'); - * // mm(list, patterns[, options]); - * - * console.log(mm(['a.js', 'a.txt'], ['*.js'])); - * //=> [ 'a.js' ] - * ``` - * @param {String|Array} `list` List of strings to match. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) - * @return {Array} Returns an array of matches - * @summary false - * @api public - */ +function forEach (arr, fn) { + if (arr.forEach) { + return arr.forEach(fn) + } -const micromatch = (list, patterns, options) => { - patterns = [].concat(patterns); - list = [].concat(list); + for (var i = 0; i < arr.length; i++) { + fn(arr[i], i) + } +} - let omit = new Set(); - let keep = new Set(); - let items = new Set(); - let negatives = 0; +function duplex(writer, reader) { + var stream = new Stream() + var ended = false - let onResult = state => { - items.add(state.output); - if (options && options.onResult) { - options.onResult(state); - } - }; + forEach(writeMethods, proxyWriter) - for (let i = 0; i < patterns.length; i++) { - let isMatch = picomatch(String(patterns[i]), { ...options, onResult }, true); - let negated = isMatch.state.negated || isMatch.state.negatedExtglob; - if (negated) negatives++; + forEach(readMethods, proxyReader) - for (let item of list) { - let matched = isMatch(item, true); + forEach(readEvents, proxyStream) - let match = negated ? !matched.isMatch : matched.isMatch; - if (!match) continue; + reader.on("end", handleEnd) - if (negated) { - omit.add(matched.output); - } else { - omit.delete(matched.output); - keep.add(matched.output); - } - } - } + writer.on("drain", function() { + stream.emit("drain") + }) - let result = negatives === patterns.length ? [...items] : [...keep]; - let matches = result.filter(item => !omit.has(item)); + writer.on("error", reemit) + reader.on("error", reemit) - if (options && matches.length === 0) { - if (options.failglob === true) { - throw new Error(`No matches found for "${patterns.join(', ')}"`); - } + stream.writable = writer.writable + stream.readable = reader.readable - if (options.nonull === true || options.nullglob === true) { - return options.unescape ? patterns.map(p => p.replace(/\\/g, '')) : patterns; - } - } + return stream - return matches; -}; + function proxyWriter(methodName) { + stream[methodName] = method -/** - * Backwards compatibility - */ + function method() { + return writer[methodName].apply(writer, arguments) + } + } -micromatch.match = micromatch; + function proxyReader(methodName) { + stream[methodName] = method -/** - * Returns a matcher function from the given glob `pattern` and `options`. - * The returned function takes a string to match as its only argument and returns - * true if the string is a match. - * - * ```js - * const mm = require('micromatch'); - * // mm.matcher(pattern[, options]); - * - * const isMatch = mm.matcher('*.!(*a)'); - * console.log(isMatch('a.a')); //=> false - * console.log(isMatch('a.b')); //=> true - * ``` - * @param {String} `pattern` Glob pattern - * @param {Object} `options` - * @return {Function} Returns a matcher function. - * @api public - */ + function method() { + stream.emit(methodName) + var func = reader[methodName] + if (func) { + return func.apply(reader, arguments) + } + reader.emit(methodName) + } + } -micromatch.matcher = (pattern, options) => picomatch(pattern, options); + function proxyStream(methodName) { + reader.on(methodName, reemit) -/** - * Returns true if **any** of the given glob `patterns` match the specified `string`. - * - * ```js - * const mm = require('micromatch'); - * // mm.isMatch(string, patterns[, options]); - * - * console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true - * console.log(mm.isMatch('a.a', 'b.*')); //=> false - * ``` - * @param {String} `str` The string to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `[options]` See available [options](#options). - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ + function reemit() { + var args = slice.call(arguments) + args.unshift(methodName) + stream.emit.apply(stream, args) + } + } -micromatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); + function handleEnd() { + if (ended) { + return + } + ended = true + var args = slice.call(arguments) + args.unshift("end") + stream.emit.apply(stream, args) + } -/** - * Backwards compatibility - */ + function reemit(err) { + stream.emit("error", err) + } +} -micromatch.any = micromatch.isMatch; -/** - * Returns a list of strings that _**do not match any**_ of the given `patterns`. - * - * ```js - * const mm = require('micromatch'); - * // mm.not(list, patterns[, options]); - * - * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a')); - * //=> ['b.b', 'c.c'] - * ``` - * @param {Array} `list` Array of strings to match. - * @param {String|Array} `patterns` One or more glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Array} Returns an array of strings that **do not match** the given patterns. - * @api public - */ +/***/ }), -micromatch.not = (list, patterns, options = {}) => { - patterns = [].concat(patterns).map(String); - let result = new Set(); - let items = []; +/***/ "../../node_modules/end-of-stream/index.js": +/***/ (function(module, exports, __webpack_require__) { - let onResult = state => { - if (options.onResult) options.onResult(state); - items.push(state.output); - }; +var once = __webpack_require__("../../node_modules/once/once.js"); - let matches = micromatch(list, patterns, { ...options, onResult }); +var noop = function() {}; - for (let item of items) { - if (!matches.includes(item)) { - result.add(item); - } - } - return [...result]; +var isRequest = function(stream) { + return stream.setHeader && typeof stream.abort === 'function'; }; -/** - * Returns true if the given `string` contains the given pattern. Similar - * to [.isMatch](#isMatch) but the pattern can match any part of the string. - * - * ```js - * var mm = require('micromatch'); - * // mm.contains(string, pattern[, options]); - * - * console.log(mm.contains('aa/bb/cc', '*b')); - * //=> true - * console.log(mm.contains('aa/bb/cc', '*d')); - * //=> false - * ``` - * @param {String} `str` The string to match. - * @param {String|Array} `patterns` Glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any of the patterns matches any part of `str`. - * @api public - */ +var isChildProcess = function(stream) { + return stream.stdio && Array.isArray(stream.stdio) && stream.stdio.length === 3 +}; -micromatch.contains = (str, pattern, options) => { - if (typeof str !== 'string') { - throw new TypeError(`Expected a string: "${util.inspect(str)}"`); - } +var eos = function(stream, opts, callback) { + if (typeof opts === 'function') return eos(stream, null, opts); + if (!opts) opts = {}; - if (Array.isArray(pattern)) { - return pattern.some(p => micromatch.contains(str, p, options)); - } + callback = once(callback || noop); - if (typeof pattern === 'string') { - if (isEmptyString(str) || isEmptyString(pattern)) { - return false; - } + var ws = stream._writableState; + var rs = stream._readableState; + var readable = opts.readable || (opts.readable !== false && stream.readable); + var writable = opts.writable || (opts.writable !== false && stream.writable); + var cancelled = false; - if (str.includes(pattern) || (str.startsWith('./') && str.slice(2).includes(pattern))) { - return true; - } - } + var onlegacyfinish = function() { + if (!stream.writable) onfinish(); + }; - return micromatch.isMatch(str, pattern, { ...options, contains: true }); -}; + var onfinish = function() { + writable = false; + if (!readable) callback.call(stream); + }; -/** - * Filter the keys of the given object with the given `glob` pattern - * and `options`. Does not attempt to match nested keys. If you need this feature, - * use [glob-object][] instead. - * - * ```js - * const mm = require('micromatch'); - * // mm.matchKeys(object, patterns[, options]); - * - * const obj = { aa: 'a', ab: 'b', ac: 'c' }; - * console.log(mm.matchKeys(obj, '*b')); - * //=> { ab: 'b' } - * ``` - * @param {Object} `object` The object with keys to filter. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Object} Returns an object with only keys that match the given patterns. - * @api public - */ + var onend = function() { + readable = false; + if (!writable) callback.call(stream); + }; -micromatch.matchKeys = (obj, patterns, options) => { - if (!utils.isObject(obj)) { - throw new TypeError('Expected the first argument to be an object'); - } - let keys = micromatch(Object.keys(obj), patterns, options); - let res = {}; - for (let key of keys) res[key] = obj[key]; - return res; -}; + var onexit = function(exitCode) { + callback.call(stream, exitCode ? new Error('exited with error code: ' + exitCode) : null); + }; -/** - * Returns true if some of the strings in the given `list` match any of the given glob `patterns`. - * - * ```js - * const mm = require('micromatch'); - * // mm.some(list, patterns[, options]); - * - * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); - * // true - * console.log(mm.some(['foo.js'], ['*.js', '!foo.js'])); - * // false - * ``` - * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any `patterns` matches any of the strings in `list` - * @api public - */ + var onerror = function(err) { + callback.call(stream, err); + }; -micromatch.some = (list, patterns, options) => { - let items = [].concat(list); + var onclose = function() { + process.nextTick(onclosenexttick); + }; - for (let pattern of [].concat(patterns)) { - let isMatch = picomatch(String(pattern), options); - if (items.some(item => isMatch(item))) { - return true; - } - } - return false; -}; + var onclosenexttick = function() { + if (cancelled) return; + if (readable && !(rs && (rs.ended && !rs.destroyed))) return callback.call(stream, new Error('premature close')); + if (writable && !(ws && (ws.ended && !ws.destroyed))) return callback.call(stream, new Error('premature close')); + }; -/** - * Returns true if every string in the given `list` matches - * any of the given glob `patterns`. - * - * ```js - * const mm = require('micromatch'); - * // mm.every(list, patterns[, options]); - * - * console.log(mm.every('foo.js', ['foo.js'])); - * // true - * console.log(mm.every(['foo.js', 'bar.js'], ['*.js'])); - * // true - * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); - * // false - * console.log(mm.every(['foo.js'], ['*.js', '!foo.js'])); - * // false - * ``` - * @param {String|Array} `list` The string or array of strings to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if all `patterns` matches all of the strings in `list` - * @api public - */ - -micromatch.every = (list, patterns, options) => { - let items = [].concat(list); + var onrequest = function() { + stream.req.on('finish', onfinish); + }; - for (let pattern of [].concat(patterns)) { - let isMatch = picomatch(String(pattern), options); - if (!items.every(item => isMatch(item))) { - return false; - } - } - return true; -}; + if (isRequest(stream)) { + stream.on('complete', onfinish); + stream.on('abort', onclose); + if (stream.req) onrequest(); + else stream.on('request', onrequest); + } else if (writable && !ws) { // legacy streams + stream.on('end', onlegacyfinish); + stream.on('close', onlegacyfinish); + } -/** - * Returns true if **all** of the given `patterns` match - * the specified string. - * - * ```js - * const mm = require('micromatch'); - * // mm.all(string, patterns[, options]); - * - * console.log(mm.all('foo.js', ['foo.js'])); - * // true - * - * console.log(mm.all('foo.js', ['*.js', '!foo.js'])); - * // false - * - * console.log(mm.all('foo.js', ['*.js', 'foo.js'])); - * // true - * - * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); - * // true - * ``` - * @param {String|Array} `str` The string to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ + if (isChildProcess(stream)) stream.on('exit', onexit); -micromatch.all = (str, patterns, options) => { - if (typeof str !== 'string') { - throw new TypeError(`Expected a string: "${util.inspect(str)}"`); - } + stream.on('end', onend); + stream.on('finish', onfinish); + if (opts.error !== false) stream.on('error', onerror); + stream.on('close', onclose); - return [].concat(patterns).every(p => picomatch(p, options)(str)); + return function() { + cancelled = true; + stream.removeListener('complete', onfinish); + stream.removeListener('abort', onclose); + stream.removeListener('request', onrequest); + if (stream.req) stream.req.removeListener('finish', onfinish); + stream.removeListener('end', onlegacyfinish); + stream.removeListener('close', onlegacyfinish); + stream.removeListener('finish', onfinish); + stream.removeListener('exit', onexit); + stream.removeListener('end', onend); + stream.removeListener('error', onerror); + stream.removeListener('close', onclose); + }; }; -/** - * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match. - * - * ```js - * const mm = require('micromatch'); - * // mm.capture(pattern, string[, options]); - * - * console.log(mm.capture('test/*.js', 'test/foo.js')); - * //=> ['foo'] - * console.log(mm.capture('test/*.js', 'foo/bar.css')); - * //=> null - * ``` - * @param {String} `glob` Glob pattern to use for matching. - * @param {String} `input` String to match - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Array|null} Returns an array of captures if the input matches the glob pattern, otherwise `null`. - * @api public - */ +module.exports = eos; -micromatch.capture = (glob, input, options) => { - let posix = utils.isWindows(options); - let regex = picomatch.makeRe(String(glob), { ...options, capture: true }); - let match = regex.exec(posix ? utils.toPosixSlashes(input) : input); - if (match) { - return match.slice(1).map(v => v === void 0 ? '' : v); - } -}; +/***/ }), -/** - * Create a regular expression from the given glob `pattern`. - * - * ```js - * const mm = require('micromatch'); - * // mm.makeRe(pattern[, options]); - * - * console.log(mm.makeRe('*.js')); - * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ - * ``` - * @param {String} `pattern` A glob pattern to convert to regex. - * @param {Object} `options` - * @return {RegExp} Returns a regex created from the given pattern. - * @api public - */ +/***/ "../../node_modules/error-ex/index.js": +/***/ (function(module, exports, __webpack_require__) { -micromatch.makeRe = (...args) => picomatch.makeRe(...args); +"use strict"; -/** - * Scan a glob pattern to separate the pattern into segments. Used - * by the [split](#split) method. - * - * ```js - * const mm = require('micromatch'); - * const state = mm.scan(pattern[, options]); - * ``` - * @param {String} `pattern` - * @param {Object} `options` - * @return {Object} Returns an object with - * @api public - */ -micromatch.scan = (...args) => picomatch.scan(...args); +var util = __webpack_require__("util"); +var isArrayish = __webpack_require__("../../node_modules/is-arrayish/index.js"); -/** - * Parse a glob pattern to create the source string for a regular - * expression. - * - * ```js - * const mm = require('micromatch'); - * const state = mm(pattern[, options]); - * ``` - * @param {String} `glob` - * @param {Object} `options` - * @return {Object} Returns an object with useful properties and output to be used as regex source string. - * @api public - */ +var errorEx = function errorEx(name, properties) { + if (!name || name.constructor !== String) { + properties = name || {}; + name = Error.name; + } -micromatch.parse = (patterns, options) => { - let res = []; - for (let pattern of [].concat(patterns || [])) { - for (let str of braces(String(pattern), options)) { - res.push(picomatch.parse(str, options)); - } - } - return res; -}; + var errorExError = function ErrorEXError(message) { + if (!this) { + return new ErrorEXError(message); + } -/** - * Process the given brace `pattern`. - * - * ```js - * const { braces } = require('micromatch'); - * console.log(braces('foo/{a,b,c}/bar')); - * //=> [ 'foo/(a|b|c)/bar' ] - * - * console.log(braces('foo/{a,b,c}/bar', { expand: true })); - * //=> [ 'foo/a/bar', 'foo/b/bar', 'foo/c/bar' ] - * ``` - * @param {String} `pattern` String with brace pattern to process. - * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options. - * @return {Array} - * @api public - */ + message = message instanceof Error + ? message.message + : (message || this.message); -micromatch.braces = (pattern, options) => { - if (typeof pattern !== 'string') throw new TypeError('Expected a string'); - if ((options && options.nobrace === true) || !/\{.*\}/.test(pattern)) { - return [pattern]; - } - return braces(pattern, options); -}; + Error.call(this, message); + Error.captureStackTrace(this, errorExError); -/** - * Expand braces - */ + this.name = name; -micromatch.braceExpand = (pattern, options) => { - if (typeof pattern !== 'string') throw new TypeError('Expected a string'); - return micromatch.braces(pattern, { ...options, expand: true }); -}; + Object.defineProperty(this, 'message', { + configurable: true, + enumerable: false, + get: function () { + var newMessage = message.split(/\r?\n/g); -/** - * Expose micromatch - */ + for (var key in properties) { + if (!properties.hasOwnProperty(key)) { + continue; + } -module.exports = micromatch; + var modifier = properties[key]; + if ('message' in modifier) { + newMessage = modifier.message(this[key], newMessage) || newMessage; + if (!isArrayish(newMessage)) { + newMessage = [newMessage]; + } + } + } -/***/ }), + return newMessage.join('\n'); + }, + set: function (v) { + message = v; + } + }); -/***/ "../../node_modules/del/node_modules/p-map/index.js": -/***/ (function(module, exports, __webpack_require__) { + var stackDescriptor = Object.getOwnPropertyDescriptor(this, 'stack'); + var stackGetter = stackDescriptor.get; + var stackValue = stackDescriptor.value; + delete stackDescriptor.value; + delete stackDescriptor.writable; -"use strict"; + stackDescriptor.get = function () { + var stack = (stackGetter) + ? stackGetter.call(this).split(/\r?\n+/g) + : stackValue.split(/\r?\n+/g); -const AggregateError = __webpack_require__("../../node_modules/del/node_modules/aggregate-error/index.js"); + // starting in Node 7, the stack builder caches the message. + // just replace it. + stack[0] = this.name + ': ' + this.message; -module.exports = async ( - iterable, - mapper, - { - concurrency = Infinity, - stopOnError = true - } = {} -) => { - return new Promise((resolve, reject) => { - if (typeof mapper !== 'function') { - throw new TypeError('Mapper function is required'); - } + var lineCount = 1; + for (var key in properties) { + if (!properties.hasOwnProperty(key)) { + continue; + } - if (!(typeof concurrency === 'number' && concurrency >= 1)) { - throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); - } + var modifier = properties[key]; - const ret = []; - const errors = []; - const iterator = iterable[Symbol.iterator](); - let isRejected = false; - let isIterableDone = false; - let resolvingCount = 0; - let currentIndex = 0; + if ('line' in modifier) { + var line = modifier.line(this[key]); + if (line) { + stack.splice(lineCount++, 0, ' ' + line); + } + } - const next = () => { - if (isRejected) { - return; + if ('stack' in modifier) { + modifier.stack(this[key], stack); + } } - const nextItem = iterator.next(); - const i = currentIndex; - currentIndex++; + return stack.join('\n'); + }; - if (nextItem.done) { - isIterableDone = true; + Object.defineProperty(this, 'stack', stackDescriptor); + }; - if (resolvingCount === 0) { - if (!stopOnError && errors.length !== 0) { - reject(new AggregateError(errors)); - } else { - resolve(ret); - } - } + if (Object.setPrototypeOf) { + Object.setPrototypeOf(errorExError.prototype, Error.prototype); + Object.setPrototypeOf(errorExError, Error); + } else { + util.inherits(errorExError, Error); + } - return; - } + return errorExError; +}; - resolvingCount++; +errorEx.append = function (str, def) { + return { + message: function (v, message) { + v = v || def; - (async () => { - try { - const element = await nextItem.value; - ret[i] = await mapper(element, i); - resolvingCount--; - next(); - } catch (error) { - if (stopOnError) { - isRejected = true; - reject(error); - } else { - errors.push(error); - resolvingCount--; - next(); - } - } - })(); - }; + if (v) { + message[0] += ' ' + str.replace('%s', v.toString()); + } - for (let i = 0; i < concurrency; i++) { - next(); + return message; + } + }; +}; - if (isIterableDone) { - break; +errorEx.line = function (str, def) { + return { + line: function (v) { + v = v || def; + + if (v) { + return str.replace('%s', v.toString()); } + + return null; } - }); + }; }; +module.exports = errorEx; + /***/ }), -/***/ "../../node_modules/delayed-stream/lib/delayed_stream.js": +/***/ "../../node_modules/escape-string-regexp/index.js": /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__("stream").Stream; -var util = __webpack_require__("util"); +"use strict"; -module.exports = DelayedStream; -function DelayedStream() { - this.source = null; - this.dataSize = 0; - this.maxDataSize = 1024 * 1024; - this.pauseStream = true; - this._maxDataSizeExceeded = false; - this._released = false; - this._bufferedEvents = []; -} -util.inherits(DelayedStream, Stream); +var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; -DelayedStream.create = function(source, options) { - var delayedStream = new this(); +module.exports = function (str) { + if (typeof str !== 'string') { + throw new TypeError('Expected a string'); + } - options = options || {}; - for (var option in options) { - delayedStream[option] = options[option]; - } + return str.replace(matchOperatorsRe, '\\$&'); +}; - delayedStream.source = source; - var realEmit = source.emit; - source.emit = function() { - delayedStream._handleEmit(arguments); - return realEmit.apply(source, arguments); - }; +/***/ }), - source.on('error', function() {}); - if (delayedStream.pauseStream) { - source.pause(); - } +/***/ "../../node_modules/execa/index.js": +/***/ (function(module, exports, __webpack_require__) { - return delayedStream; -}; +"use strict"; -Object.defineProperty(DelayedStream.prototype, 'readable', { - configurable: true, - enumerable: true, - get: function() { - return this.source.readable; - } -}); +const path = __webpack_require__("path"); +const childProcess = __webpack_require__("child_process"); +const crossSpawn = __webpack_require__("../../node_modules/cross-spawn/index.js"); +const stripFinalNewline = __webpack_require__("../../node_modules/strip-final-newline/index.js"); +const npmRunPath = __webpack_require__("../../node_modules/npm-run-path/index.js"); +const onetime = __webpack_require__("../../node_modules/onetime/index.js"); +const makeError = __webpack_require__("../../node_modules/execa/lib/error.js"); +const normalizeStdio = __webpack_require__("../../node_modules/execa/lib/stdio.js"); +const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = __webpack_require__("../../node_modules/execa/lib/kill.js"); +const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = __webpack_require__("../../node_modules/execa/lib/stream.js"); +const {mergePromise, getSpawnedPromise} = __webpack_require__("../../node_modules/execa/lib/promise.js"); +const {joinCommand, parseCommand} = __webpack_require__("../../node_modules/execa/lib/command.js"); -DelayedStream.prototype.setEncoding = function() { - return this.source.setEncoding.apply(this.source, arguments); -}; +const DEFAULT_MAX_BUFFER = 1000 * 1000 * 100; -DelayedStream.prototype.resume = function() { - if (!this._released) { - this.release(); - } +const getEnv = ({env: envOption, extendEnv, preferLocal, localDir, execPath}) => { + const env = extendEnv ? {...process.env, ...envOption} : envOption; - this.source.resume(); -}; + if (preferLocal) { + return npmRunPath.env({env, cwd: localDir, execPath}); + } -DelayedStream.prototype.pause = function() { - this.source.pause(); + return env; }; -DelayedStream.prototype.release = function() { - this._released = true; +const handleArguments = (file, args, options = {}) => { + const parsed = crossSpawn._parse(file, args, options); + file = parsed.command; + args = parsed.args; + options = parsed.options; - this._bufferedEvents.forEach(function(args) { - this.emit.apply(this, args); - }.bind(this)); - this._bufferedEvents = []; -}; + options = { + maxBuffer: DEFAULT_MAX_BUFFER, + buffer: true, + stripFinalNewline: true, + extendEnv: true, + preferLocal: false, + localDir: options.cwd || process.cwd(), + execPath: process.execPath, + encoding: 'utf8', + reject: true, + cleanup: true, + all: false, + windowsHide: true, + ...options + }; -DelayedStream.prototype.pipe = function() { - var r = Stream.prototype.pipe.apply(this, arguments); - this.resume(); - return r; -}; + options.env = getEnv(options); -DelayedStream.prototype._handleEmit = function(args) { - if (this._released) { - this.emit.apply(this, args); - return; - } + options.stdio = normalizeStdio(options); - if (args[0] === 'data') { - this.dataSize += args[1].length; - this._checkIfMaxDataSizeExceeded(); - } + if (process.platform === 'win32' && path.basename(file, '.exe') === 'cmd') { + // #116 + args.unshift('/q'); + } - this._bufferedEvents.push(args); + return {file, args, options, parsed}; }; -DelayedStream.prototype._checkIfMaxDataSizeExceeded = function() { - if (this._maxDataSizeExceeded) { - return; - } +const handleOutput = (options, value, error) => { + if (typeof value !== 'string' && !Buffer.isBuffer(value)) { + // When `execa.sync()` errors, we normalize it to '' to mimic `execa()` + return error === undefined ? undefined : ''; + } - if (this.dataSize <= this.maxDataSize) { - return; - } + if (options.stripFinalNewline) { + return stripFinalNewline(value); + } - this._maxDataSizeExceeded = true; - var message = - 'DelayedStream#maxDataSize of ' + this.maxDataSize + ' bytes exceeded.' - this.emit('error', new Error(message)); + return value; }; +const execa = (file, args, options) => { + const parsed = handleArguments(file, args, options); + const command = joinCommand(file, args); -/***/ }), - -/***/ "../../node_modules/detect-indent/index.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; + let spawned; + try { + spawned = childProcess.spawn(parsed.file, parsed.args, parsed.options); + } catch (error) { + // Ensure the returned error is always both a promise and a child process + const dummySpawned = new childProcess.ChildProcess(); + const errorPromise = Promise.reject(makeError({ + error, + stdout: '', + stderr: '', + all: '', + command, + parsed, + timedOut: false, + isCanceled: false, + killed: false + })); + return mergePromise(dummySpawned, errorPromise); + } + const spawnedPromise = getSpawnedPromise(spawned); + const timedPromise = setupTimeout(spawned, parsed.options, spawnedPromise); + const processDone = setExitHandler(spawned, parsed.options, timedPromise); -// Detect either spaces or tabs but not both to properly handle tabs for indentation and spaces for alignment -const INDENT_REGEX = /^(?:( )+|\t+)/; + const context = {isCanceled: false}; -const INDENT_TYPE_SPACE = 'space'; -const INDENT_TYPE_TAB = 'tab'; + spawned.kill = spawnedKill.bind(null, spawned.kill.bind(spawned)); + spawned.cancel = spawnedCancel.bind(null, spawned, context); -// Make a Map that counts how many indents/unindents have occurred for a given size and how many lines follow a given indentation. -// The key is a concatenation of the indentation type (s = space and t = tab) and the size of the indents/unindents. -// -// indents = { -// t3: [1, 0], -// t4: [1, 5], -// s5: [1, 0], -// s12: [1, 0], -// } -function makeIndentsMap(string, ignoreSingleSpaces) { - const indents = new Map(); + const handlePromise = async () => { + const [{error, exitCode, signal, timedOut}, stdoutResult, stderrResult, allResult] = await getSpawnedResult(spawned, parsed.options, processDone); + const stdout = handleOutput(parsed.options, stdoutResult); + const stderr = handleOutput(parsed.options, stderrResult); + const all = handleOutput(parsed.options, allResult); - // Remember the size of previous line's indentation - let previousSize = 0; - let previousIndentType; + if (error || exitCode !== 0 || signal !== null) { + const returnedError = makeError({ + error, + exitCode, + signal, + stdout, + stderr, + all, + command, + parsed, + timedOut, + isCanceled: context.isCanceled, + killed: spawned.killed + }); - // Indents key (ident type + size of the indents/unindents) - let key; + if (!parsed.options.reject) { + return returnedError; + } - for (const line of string.split(/\n/g)) { - if (!line) { - // Ignore empty lines - continue; + throw returnedError; } - let indent; - let indentType; - let weight; - let entry; - const matches = line.match(INDENT_REGEX); + return { + command, + exitCode: 0, + stdout, + stderr, + all, + failed: false, + timedOut: false, + isCanceled: false, + killed: false + }; + }; - if (matches === null) { - previousSize = 0; - previousIndentType = ''; - } else { - indent = matches[0].length; + const handlePromiseOnce = onetime(handlePromise); - if (matches[1]) { - indentType = INDENT_TYPE_SPACE; - } else { - indentType = INDENT_TYPE_TAB; - } + crossSpawn._enoent.hookChildProcess(spawned, parsed.parsed); - // Ignore single space unless it's the only indent detected to prevent common false positives - if (ignoreSingleSpaces && indentType === INDENT_TYPE_SPACE && indent === 1) { - continue; - } + handleInput(spawned, parsed.options.input); - if (indentType !== previousIndentType) { - previousSize = 0; - } + spawned.all = makeAllStream(spawned, parsed.options); - previousIndentType = indentType; + return mergePromise(spawned, handlePromiseOnce); +}; - weight = 0; +module.exports = execa; - const indentDifference = indent - previousSize; - previousSize = indent; +module.exports.sync = (file, args, options) => { + const parsed = handleArguments(file, args, options); + const command = joinCommand(file, args); - // Previous line have same indent? - if (indentDifference === 0) { - weight++; - // We use the key from previous loop - } else { - const absoluteIndentDifference = indentDifference > 0 ? indentDifference : -indentDifference; - key = encodeIndentsKey(indentType, absoluteIndentDifference); - } + validateInputSync(parsed.options); - // Update the stats - entry = indents.get(key); + let result; + try { + result = childProcess.spawnSync(parsed.file, parsed.args, parsed.options); + } catch (error) { + throw makeError({ + error, + stdout: '', + stderr: '', + all: '', + command, + parsed, + timedOut: false, + isCanceled: false, + killed: false + }); + } - if (entry === undefined) { - entry = [1, 0]; // Init - } else { - entry = [++entry[0], entry[1] + weight]; - } + const stdout = handleOutput(parsed.options, result.stdout, result.error); + const stderr = handleOutput(parsed.options, result.stderr, result.error); - indents.set(key, entry); + if (result.error || result.status !== 0 || result.signal !== null) { + const error = makeError({ + stdout, + stderr, + error: result.error, + signal: result.signal, + exitCode: result.status, + command, + parsed, + timedOut: result.error && result.error.code === 'ETIMEDOUT', + isCanceled: false, + killed: result.signal !== null + }); + + if (!parsed.options.reject) { + return error; } + + throw error; } - return indents; -} + return { + command, + exitCode: 0, + stdout, + stderr, + failed: false, + timedOut: false, + isCanceled: false, + killed: false + }; +}; -// Encode the indent type and amount as a string (e.g. 's4') for use as a compound key in the indents Map. -function encodeIndentsKey(indentType, indentAmount) { - const typeCharacter = indentType === INDENT_TYPE_SPACE ? 's' : 't'; - return typeCharacter + String(indentAmount); -} +module.exports.command = (command, options) => { + const [file, ...args] = parseCommand(command); + return execa(file, args, options); +}; -// Extract the indent type and amount from a key of the indents Map. -function decodeIndentsKey(indentsKey) { - const keyHasTypeSpace = indentsKey[0] === 's'; - const type = keyHasTypeSpace ? INDENT_TYPE_SPACE : INDENT_TYPE_TAB; +module.exports.commandSync = (command, options) => { + const [file, ...args] = parseCommand(command); + return execa.sync(file, args, options); +}; - const amount = Number(indentsKey.slice(1)); +module.exports.node = (scriptPath, args, options = {}) => { + if (args && !Array.isArray(args) && typeof args === 'object') { + options = args; + args = []; + } - return {type, amount}; -} + const stdio = normalizeStdio.node(options); -// Return the key (e.g. 's4') from the indents Map that represents the most common indent, -// or return undefined if there are no indents. -function getMostUsedKey(indents) { - let result; - let maxUsed = 0; - let maxWeight = 0; + const {nodePath = process.execPath, nodeOptions = process.execArgv} = options; - for (const [key, [usedCount, weight]] of indents) { - if (usedCount > maxUsed || (usedCount === maxUsed && weight > maxWeight)) { - maxUsed = usedCount; - maxWeight = weight; - result = key; + return execa( + nodePath, + [ + ...nodeOptions, + scriptPath, + ...(Array.isArray(args) ? args : []) + ], + { + ...options, + stdin: undefined, + stdout: undefined, + stderr: undefined, + stdio, + shell: false } - } + ); +}; - return result; -} -function makeIndentString(type, amount) { - const indentCharacter = type === INDENT_TYPE_SPACE ? ' ' : '\t'; - return indentCharacter.repeat(amount); -} - -module.exports = string => { - if (typeof string !== 'string') { - throw new TypeError('Expected a string'); - } +/***/ }), - // Identify indents while skipping single space indents to avoid common edge cases (e.g. code comments) - // If no indents are identified, run again and include all indents for comprehensive detection - let indents = makeIndentsMap(string, true); - if (indents.size === 0) { - indents = makeIndentsMap(string, false); - } +/***/ "../../node_modules/execa/lib/command.js": +/***/ (function(module, exports, __webpack_require__) { - const keyOfMostUsedIndent = getMostUsedKey(indents); +"use strict"; - let type; - let amount = 0; - let indent = ''; +const SPACES_REGEXP = / +/g; - if (keyOfMostUsedIndent !== undefined) { - ({type, amount} = decodeIndentsKey(keyOfMostUsedIndent)); - indent = makeIndentString(type, amount); +const joinCommand = (file, args = []) => { + if (!Array.isArray(args)) { + return file; } - return { - amount, - type, - indent - }; + return [file, ...args].join(' '); }; - -/***/ }), - -/***/ "../../node_modules/detect-newline/index.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const detectNewline = string => { - if (typeof string !== 'string') { - throw new TypeError('Expected a string'); +// Allow spaces to be escaped by a backslash if not meant as a delimiter +const handleEscaping = (tokens, token, index) => { + if (index === 0) { + return [token]; } - const newlines = string.match(/(?:\r?\n)/g) || []; + const previousToken = tokens[tokens.length - 1]; - if (newlines.length === 0) { - return; + if (previousToken.endsWith('\\')) { + return [...tokens.slice(0, -1), `${previousToken.slice(0, -1)} ${token}`]; } - const crlf = newlines.filter(newline => newline === '\r\n').length; - const lf = newlines.length - crlf; + return [...tokens, token]; +}; - return crlf > lf ? '\r\n' : '\n'; +// Handle `execa.command()` +const parseCommand = command => { + return command + .trim() + .split(SPACES_REGEXP) + .reduce(handleEscaping, []); }; -module.exports = detectNewline; -module.exports.graceful = string => (typeof string === 'string' && detectNewline(string)) || '\n'; +module.exports = { + joinCommand, + parseCommand +}; /***/ }), -/***/ "../../node_modules/dir-glob/index.js": +/***/ "../../node_modules/execa/lib/error.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; -const path = __webpack_require__("path"); -const pathType = __webpack_require__("../../node_modules/path-type/index.js"); - -const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; - -const getPath = (filepath, cwd) => { - const pth = filepath[0] === '!' ? filepath.slice(1) : filepath; - return path.isAbsolute(pth) ? pth : path.join(cwd, pth); -}; - -const addExtensions = (file, extensions) => { - if (path.extname(file)) { - return `**/${file}`; - } - - return `**/${file}.${getExtensions(extensions)}`; -}; +const {signalsByName} = __webpack_require__("../../node_modules/human-signals/build/src/main.js"); -const getGlob = (directory, options) => { - if (options.files && !Array.isArray(options.files)) { - throw new TypeError(`Expected \`files\` to be of type \`Array\` but received type \`${typeof options.files}\``); +const getErrorPrefix = ({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}) => { + if (timedOut) { + return `timed out after ${timeout} milliseconds`; } - if (options.extensions && !Array.isArray(options.extensions)) { - throw new TypeError(`Expected \`extensions\` to be of type \`Array\` but received type \`${typeof options.extensions}\``); + if (isCanceled) { + return 'was canceled'; } - if (options.files && options.extensions) { - return options.files.map(x => path.posix.join(directory, addExtensions(x, options.extensions))); + if (errorCode !== undefined) { + return `failed with ${errorCode}`; } - if (options.files) { - return options.files.map(x => path.posix.join(directory, `**/${x}`)); + if (signal !== undefined) { + return `was killed with ${signal} (${signalDescription})`; } - if (options.extensions) { - return [path.posix.join(directory, `**/*.${getExtensions(options.extensions)}`)]; + if (exitCode !== undefined) { + return `failed with exit code ${exitCode}`; } - return [path.posix.join(directory, '**')]; + return 'failed'; }; -module.exports = async (input, options) => { - options = { - cwd: process.cwd(), - ...options - }; +const makeError = ({ + stdout, + stderr, + all, + error, + signal, + exitCode, + command, + timedOut, + isCanceled, + killed, + parsed: {options: {timeout}} +}) => { + // `signal` and `exitCode` emitted on `spawned.on('exit')` event can be `null`. + // We normalize them to `undefined` + exitCode = exitCode === null ? undefined : exitCode; + signal = signal === null ? undefined : signal; + const signalDescription = signal === undefined ? undefined : signalsByName[signal].description; - if (typeof options.cwd !== 'string') { - throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof options.cwd}\``); - } + const errorCode = error && error.code; - const globs = await Promise.all([].concat(input).map(async x => { - const isDirectory = await pathType.isDirectory(getPath(x, options.cwd)); - return isDirectory ? getGlob(x, options) : x; - })); + const prefix = getErrorPrefix({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}); + const execaMessage = `Command ${prefix}: ${command}`; + const isError = Object.prototype.toString.call(error) === '[object Error]'; + const shortMessage = isError ? `${execaMessage}\n${error.message}` : execaMessage; + const message = [shortMessage, stderr, stdout].filter(Boolean).join('\n'); - return [].concat.apply([], globs); // eslint-disable-line prefer-spread -}; + if (isError) { + error.originalMessage = error.message; + error.message = message; + } else { + error = new Error(message); + } -module.exports.sync = (input, options) => { - options = { - cwd: process.cwd(), - ...options - }; + error.shortMessage = shortMessage; + error.command = command; + error.exitCode = exitCode; + error.signal = signal; + error.signalDescription = signalDescription; + error.stdout = stdout; + error.stderr = stderr; - if (typeof options.cwd !== 'string') { - throw new TypeError(`Expected \`cwd\` to be of type \`string\` but received type \`${typeof options.cwd}\``); + if (all !== undefined) { + error.all = all; } - const globs = [].concat(input).map(x => pathType.isDirectorySync(getPath(x, options.cwd)) ? getGlob(x, options) : x); + if ('bufferedData' in error) { + delete error.bufferedData; + } - return [].concat.apply([], globs); // eslint-disable-line prefer-spread + error.failed = true; + error.timedOut = Boolean(timedOut); + error.isCanceled = isCanceled; + error.killed = killed && !timedOut; + + return error; }; +module.exports = makeError; + /***/ }), -/***/ "../../node_modules/duplexer/index.js": +/***/ "../../node_modules/execa/lib/kill.js": /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__("stream") -var writeMethods = ["write", "end", "destroy"] -var readMethods = ["resume", "pause"] -var readEvents = ["data", "close"] -var slice = Array.prototype.slice +"use strict"; -module.exports = duplex +const os = __webpack_require__("os"); +const onExit = __webpack_require__("../../node_modules/signal-exit/index.js"); -function forEach (arr, fn) { - if (arr.forEach) { - return arr.forEach(fn) - } +const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5; - for (var i = 0; i < arr.length; i++) { - fn(arr[i], i) - } -} +// Monkey-patches `childProcess.kill()` to add `forceKillAfterTimeout` behavior +const spawnedKill = (kill, signal = 'SIGTERM', options = {}) => { + const killResult = kill(signal); + setKillTimeout(kill, signal, options, killResult); + return killResult; +}; -function duplex(writer, reader) { - var stream = new Stream() - var ended = false +const setKillTimeout = (kill, signal, options, killResult) => { + if (!shouldForceKill(signal, options, killResult)) { + return; + } - forEach(writeMethods, proxyWriter) + const timeout = getForceKillAfterTimeout(options); + const t = setTimeout(() => { + kill('SIGKILL'); + }, timeout); - forEach(readMethods, proxyReader) + // Guarded because there's no `.unref()` when `execa` is used in the renderer + // process in Electron. This cannot be tested since we don't run tests in + // Electron. + // istanbul ignore else + if (t.unref) { + t.unref(); + } +}; - forEach(readEvents, proxyStream) +const shouldForceKill = (signal, {forceKillAfterTimeout}, killResult) => { + return isSigterm(signal) && forceKillAfterTimeout !== false && killResult; +}; - reader.on("end", handleEnd) +const isSigterm = signal => { + return signal === os.constants.signals.SIGTERM || + (typeof signal === 'string' && signal.toUpperCase() === 'SIGTERM'); +}; - writer.on("drain", function() { - stream.emit("drain") - }) +const getForceKillAfterTimeout = ({forceKillAfterTimeout = true}) => { + if (forceKillAfterTimeout === true) { + return DEFAULT_FORCE_KILL_TIMEOUT; + } - writer.on("error", reemit) - reader.on("error", reemit) - - stream.writable = writer.writable - stream.readable = reader.readable - - return stream - - function proxyWriter(methodName) { - stream[methodName] = method - - function method() { - return writer[methodName].apply(writer, arguments) - } - } + if (!Number.isFinite(forceKillAfterTimeout) || forceKillAfterTimeout < 0) { + throw new TypeError(`Expected the \`forceKillAfterTimeout\` option to be a non-negative integer, got \`${forceKillAfterTimeout}\` (${typeof forceKillAfterTimeout})`); + } - function proxyReader(methodName) { - stream[methodName] = method + return forceKillAfterTimeout; +}; - function method() { - stream.emit(methodName) - var func = reader[methodName] - if (func) { - return func.apply(reader, arguments) - } - reader.emit(methodName) - } - } +// `childProcess.cancel()` +const spawnedCancel = (spawned, context) => { + const killResult = spawned.kill(); - function proxyStream(methodName) { - reader.on(methodName, reemit) + if (killResult) { + context.isCanceled = true; + } +}; - function reemit() { - var args = slice.call(arguments) - args.unshift(methodName) - stream.emit.apply(stream, args) - } - } +const timeoutKill = (spawned, signal, reject) => { + spawned.kill(signal); + reject(Object.assign(new Error('Timed out'), {timedOut: true, signal})); +}; - function handleEnd() { - if (ended) { - return - } - ended = true - var args = slice.call(arguments) - args.unshift("end") - stream.emit.apply(stream, args) - } +// `timeout` option handling +const setupTimeout = (spawned, {timeout, killSignal = 'SIGTERM'}, spawnedPromise) => { + if (timeout === 0 || timeout === undefined) { + return spawnedPromise; + } - function reemit(err) { - stream.emit("error", err) - } -} + if (!Number.isFinite(timeout) || timeout < 0) { + throw new TypeError(`Expected the \`timeout\` option to be a non-negative integer, got \`${timeout}\` (${typeof timeout})`); + } + let timeoutId; + const timeoutPromise = new Promise((resolve, reject) => { + timeoutId = setTimeout(() => { + timeoutKill(spawned, killSignal, reject); + }, timeout); + }); -/***/ }), + const safeSpawnedPromise = spawnedPromise.finally(() => { + clearTimeout(timeoutId); + }); -/***/ "../../node_modules/end-of-stream/index.js": -/***/ (function(module, exports, __webpack_require__) { + return Promise.race([timeoutPromise, safeSpawnedPromise]); +}; -var once = __webpack_require__("../../node_modules/once/once.js"); +// `cleanup` option handling +const setExitHandler = async (spawned, {cleanup, detached}, timedPromise) => { + if (!cleanup || detached) { + return timedPromise; + } -var noop = function() {}; + const removeExitHandler = onExit(() => { + spawned.kill(); + }); -var isRequest = function(stream) { - return stream.setHeader && typeof stream.abort === 'function'; + return timedPromise.finally(() => { + removeExitHandler(); + }); }; -var isChildProcess = function(stream) { - return stream.stdio && Array.isArray(stream.stdio) && stream.stdio.length === 3 +module.exports = { + spawnedKill, + spawnedCancel, + setupTimeout, + setExitHandler }; -var eos = function(stream, opts, callback) { - if (typeof opts === 'function') return eos(stream, null, opts); - if (!opts) opts = {}; - - callback = once(callback || noop); - - var ws = stream._writableState; - var rs = stream._readableState; - var readable = opts.readable || (opts.readable !== false && stream.readable); - var writable = opts.writable || (opts.writable !== false && stream.writable); - var cancelled = false; - var onlegacyfinish = function() { - if (!stream.writable) onfinish(); - }; +/***/ }), - var onfinish = function() { - writable = false; - if (!readable) callback.call(stream); - }; +/***/ "../../node_modules/execa/lib/promise.js": +/***/ (function(module, exports, __webpack_require__) { - var onend = function() { - readable = false; - if (!writable) callback.call(stream); - }; +"use strict"; - var onexit = function(exitCode) { - callback.call(stream, exitCode ? new Error('exited with error code: ' + exitCode) : null); - }; - var onerror = function(err) { - callback.call(stream, err); - }; +const nativePromisePrototype = (async () => {})().constructor.prototype; +const descriptors = ['then', 'catch', 'finally'].map(property => [ + property, + Reflect.getOwnPropertyDescriptor(nativePromisePrototype, property) +]); - var onclose = function() { - process.nextTick(onclosenexttick); - }; +// The return value is a mixin of `childProcess` and `Promise` +const mergePromise = (spawned, promise) => { + for (const [property, descriptor] of descriptors) { + // Starting the main `promise` is deferred to avoid consuming streams + const value = typeof promise === 'function' ? + (...args) => Reflect.apply(descriptor.value, promise(), args) : + descriptor.value.bind(promise); - var onclosenexttick = function() { - if (cancelled) return; - if (readable && !(rs && (rs.ended && !rs.destroyed))) return callback.call(stream, new Error('premature close')); - if (writable && !(ws && (ws.ended && !ws.destroyed))) return callback.call(stream, new Error('premature close')); - }; + Reflect.defineProperty(spawned, property, {...descriptor, value}); + } - var onrequest = function() { - stream.req.on('finish', onfinish); - }; + return spawned; +}; - if (isRequest(stream)) { - stream.on('complete', onfinish); - stream.on('abort', onclose); - if (stream.req) onrequest(); - else stream.on('request', onrequest); - } else if (writable && !ws) { // legacy streams - stream.on('end', onlegacyfinish); - stream.on('close', onlegacyfinish); - } +// Use promises instead of `child_process` events +const getSpawnedPromise = spawned => { + return new Promise((resolve, reject) => { + spawned.on('exit', (exitCode, signal) => { + resolve({exitCode, signal}); + }); - if (isChildProcess(stream)) stream.on('exit', onexit); + spawned.on('error', error => { + reject(error); + }); - stream.on('end', onend); - stream.on('finish', onfinish); - if (opts.error !== false) stream.on('error', onerror); - stream.on('close', onclose); + if (spawned.stdin) { + spawned.stdin.on('error', error => { + reject(error); + }); + } + }); +}; - return function() { - cancelled = true; - stream.removeListener('complete', onfinish); - stream.removeListener('abort', onclose); - stream.removeListener('request', onrequest); - if (stream.req) stream.req.removeListener('finish', onfinish); - stream.removeListener('end', onlegacyfinish); - stream.removeListener('close', onlegacyfinish); - stream.removeListener('finish', onfinish); - stream.removeListener('exit', onexit); - stream.removeListener('end', onend); - stream.removeListener('error', onerror); - stream.removeListener('close', onclose); - }; +module.exports = { + mergePromise, + getSpawnedPromise }; -module.exports = eos; /***/ }), -/***/ "../../node_modules/error-ex/index.js": +/***/ "../../node_modules/execa/lib/stdio.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; +const aliases = ['stdin', 'stdout', 'stderr']; -var util = __webpack_require__("util"); -var isArrayish = __webpack_require__("../../node_modules/is-arrayish/index.js"); +const hasAlias = opts => aliases.some(alias => opts[alias] !== undefined); -var errorEx = function errorEx(name, properties) { - if (!name || name.constructor !== String) { - properties = name || {}; - name = Error.name; +const normalizeStdio = opts => { + if (!opts) { + return; } - var errorExError = function ErrorEXError(message) { - if (!this) { - return new ErrorEXError(message); - } + const {stdio} = opts; - message = message instanceof Error - ? message.message - : (message || this.message); + if (stdio === undefined) { + return aliases.map(alias => opts[alias]); + } - Error.call(this, message); - Error.captureStackTrace(this, errorExError); + if (hasAlias(opts)) { + throw new Error(`It's not possible to provide \`stdio\` in combination with one of ${aliases.map(alias => `\`${alias}\``).join(', ')}`); + } - this.name = name; + if (typeof stdio === 'string') { + return stdio; + } - Object.defineProperty(this, 'message', { - configurable: true, - enumerable: false, - get: function () { - var newMessage = message.split(/\r?\n/g); + if (!Array.isArray(stdio)) { + throw new TypeError(`Expected \`stdio\` to be of type \`string\` or \`Array\`, got \`${typeof stdio}\``); + } - for (var key in properties) { - if (!properties.hasOwnProperty(key)) { - continue; - } + const length = Math.max(stdio.length, aliases.length); + return Array.from({length}, (value, index) => stdio[index]); +}; - var modifier = properties[key]; +module.exports = normalizeStdio; - if ('message' in modifier) { - newMessage = modifier.message(this[key], newMessage) || newMessage; - if (!isArrayish(newMessage)) { - newMessage = [newMessage]; - } - } - } +// `ipc` is pushed unless it is already present +module.exports.node = opts => { + const stdio = normalizeStdio(opts); - return newMessage.join('\n'); - }, - set: function (v) { - message = v; - } - }); + if (stdio === 'ipc') { + return 'ipc'; + } - var stackDescriptor = Object.getOwnPropertyDescriptor(this, 'stack'); - var stackGetter = stackDescriptor.get; - var stackValue = stackDescriptor.value; - delete stackDescriptor.value; - delete stackDescriptor.writable; + if (stdio === undefined || typeof stdio === 'string') { + return [stdio, stdio, stdio, 'ipc']; + } - stackDescriptor.get = function () { - var stack = (stackGetter) - ? stackGetter.call(this).split(/\r?\n+/g) - : stackValue.split(/\r?\n+/g); + if (stdio.includes('ipc')) { + return stdio; + } - // starting in Node 7, the stack builder caches the message. - // just replace it. - stack[0] = this.name + ': ' + this.message; + return [...stdio, 'ipc']; +}; - var lineCount = 1; - for (var key in properties) { - if (!properties.hasOwnProperty(key)) { - continue; - } - var modifier = properties[key]; +/***/ }), - if ('line' in modifier) { - var line = modifier.line(this[key]); - if (line) { - stack.splice(lineCount++, 0, ' ' + line); - } - } +/***/ "../../node_modules/execa/lib/stream.js": +/***/ (function(module, exports, __webpack_require__) { - if ('stack' in modifier) { - modifier.stack(this[key], stack); - } - } +"use strict"; - return stack.join('\n'); - }; +const isStream = __webpack_require__("../../node_modules/is-stream/index.js"); +const getStream = __webpack_require__("../../node_modules/get-stream/index.js"); +const mergeStream = __webpack_require__("../../node_modules/merge-stream/index.js"); - Object.defineProperty(this, 'stack', stackDescriptor); - }; +// `input` option +const handleInput = (spawned, input) => { + // Checking for stdin is workaround for https://github.com/nodejs/node/issues/26852 + // TODO: Remove `|| spawned.stdin === undefined` once we drop support for Node.js <=12.2.0 + if (input === undefined || spawned.stdin === undefined) { + return; + } - if (Object.setPrototypeOf) { - Object.setPrototypeOf(errorExError.prototype, Error.prototype); - Object.setPrototypeOf(errorExError, Error); + if (isStream(input)) { + input.pipe(spawned.stdin); } else { - util.inherits(errorExError, Error); + spawned.stdin.end(input); } - - return errorExError; }; -errorEx.append = function (str, def) { - return { - message: function (v, message) { - v = v || def; - - if (v) { - message[0] += ' ' + str.replace('%s', v.toString()); - } +// `all` interleaves `stdout` and `stderr` +const makeAllStream = (spawned, {all}) => { + if (!all || (!spawned.stdout && !spawned.stderr)) { + return; + } - return message; - } - }; -}; + const mixed = mergeStream(); -errorEx.line = function (str, def) { - return { - line: function (v) { - v = v || def; + if (spawned.stdout) { + mixed.add(spawned.stdout); + } - if (v) { - return str.replace('%s', v.toString()); - } + if (spawned.stderr) { + mixed.add(spawned.stderr); + } - return null; - } - }; + return mixed; }; -module.exports = errorEx; +// On failure, `result.stdout|stderr|all` should contain the currently buffered stream +const getBufferedData = async (stream, streamPromise) => { + if (!stream) { + return; + } + stream.destroy(); -/***/ }), + try { + return await streamPromise; + } catch (error) { + return error.bufferedData; + } +}; -/***/ "../../node_modules/escape-string-regexp/index.js": -/***/ (function(module, exports, __webpack_require__) { +const getStreamPromise = (stream, {encoding, buffer, maxBuffer}) => { + if (!stream || !buffer) { + return; + } -"use strict"; + if (encoding) { + return getStream(stream, {encoding, maxBuffer}); + } + return getStream.buffer(stream, {maxBuffer}); +}; -var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; +// Retrieve result of child process: exit code, signal, error, streams (stdout/stderr/all) +const getSpawnedResult = async ({stdout, stderr, all}, {encoding, buffer, maxBuffer}, processDone) => { + const stdoutPromise = getStreamPromise(stdout, {encoding, buffer, maxBuffer}); + const stderrPromise = getStreamPromise(stderr, {encoding, buffer, maxBuffer}); + const allPromise = getStreamPromise(all, {encoding, buffer, maxBuffer: maxBuffer * 2}); -module.exports = function (str) { - if (typeof str !== 'string') { - throw new TypeError('Expected a string'); + try { + return await Promise.all([processDone, stdoutPromise, stderrPromise, allPromise]); + } catch (error) { + return Promise.all([ + {error, signal: error.signal, timedOut: error.timedOut}, + getBufferedData(stdout, stdoutPromise), + getBufferedData(stderr, stderrPromise), + getBufferedData(all, allPromise) + ]); + } +}; + +const validateInputSync = ({input}) => { + if (isStream(input)) { + throw new TypeError('The `input` option cannot be a stream in sync mode'); } +}; - return str.replace(matchOperatorsRe, '\\$&'); +module.exports = { + handleInput, + makeAllStream, + getSpawnedResult, + validateInputSync }; + /***/ }), -/***/ "../../node_modules/execa/index.js": +/***/ "../../node_modules/fast-glob/node_modules/micromatch/index.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; -const path = __webpack_require__("path"); -const childProcess = __webpack_require__("child_process"); -const crossSpawn = __webpack_require__("../../node_modules/cross-spawn/index.js"); -const stripFinalNewline = __webpack_require__("../../node_modules/strip-final-newline/index.js"); -const npmRunPath = __webpack_require__("../../node_modules/npm-run-path/index.js"); -const onetime = __webpack_require__("../../node_modules/onetime/index.js"); -const makeError = __webpack_require__("../../node_modules/execa/lib/error.js"); -const normalizeStdio = __webpack_require__("../../node_modules/execa/lib/stdio.js"); -const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = __webpack_require__("../../node_modules/execa/lib/kill.js"); -const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = __webpack_require__("../../node_modules/execa/lib/stream.js"); -const {mergePromise, getSpawnedPromise} = __webpack_require__("../../node_modules/execa/lib/promise.js"); -const {joinCommand, parseCommand} = __webpack_require__("../../node_modules/execa/lib/command.js"); -const DEFAULT_MAX_BUFFER = 1000 * 1000 * 100; +const util = __webpack_require__("util"); +const braces = __webpack_require__("../../node_modules/braces/index.js"); +const picomatch = __webpack_require__("../../node_modules/picomatch/index.js"); +const utils = __webpack_require__("../../node_modules/picomatch/lib/utils.js"); +const isEmptyString = val => val === '' || val === './'; -const getEnv = ({env: envOption, extendEnv, preferLocal, localDir, execPath}) => { - const env = extendEnv ? {...process.env, ...envOption} : envOption; +/** + * Returns an array of strings that match one or more glob patterns. + * + * ```js + * const mm = require('micromatch'); + * // mm(list, patterns[, options]); + * + * console.log(mm(['a.js', 'a.txt'], ['*.js'])); + * //=> [ 'a.js' ] + * ``` + * @param {String|Array} `list` List of strings to match. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) + * @return {Array} Returns an array of matches + * @summary false + * @api public + */ - if (preferLocal) { - return npmRunPath.env({env, cwd: localDir, execPath}); - } +const micromatch = (list, patterns, options) => { + patterns = [].concat(patterns); + list = [].concat(list); - return env; -}; + let omit = new Set(); + let keep = new Set(); + let items = new Set(); + let negatives = 0; -const handleArguments = (file, args, options = {}) => { - const parsed = crossSpawn._parse(file, args, options); - file = parsed.command; - args = parsed.args; - options = parsed.options; + let onResult = state => { + items.add(state.output); + if (options && options.onResult) { + options.onResult(state); + } + }; - options = { - maxBuffer: DEFAULT_MAX_BUFFER, - buffer: true, - stripFinalNewline: true, - extendEnv: true, - preferLocal: false, - localDir: options.cwd || process.cwd(), - execPath: process.execPath, - encoding: 'utf8', - reject: true, - cleanup: true, - all: false, - windowsHide: true, - ...options - }; + for (let i = 0; i < patterns.length; i++) { + let isMatch = picomatch(String(patterns[i]), { ...options, onResult }, true); + let negated = isMatch.state.negated || isMatch.state.negatedExtglob; + if (negated) negatives++; - options.env = getEnv(options); + for (let item of list) { + let matched = isMatch(item, true); - options.stdio = normalizeStdio(options); + let match = negated ? !matched.isMatch : matched.isMatch; + if (!match) continue; - if (process.platform === 'win32' && path.basename(file, '.exe') === 'cmd') { - // #116 - args.unshift('/q'); - } + if (negated) { + omit.add(matched.output); + } else { + omit.delete(matched.output); + keep.add(matched.output); + } + } + } - return {file, args, options, parsed}; -}; + let result = negatives === patterns.length ? [...items] : [...keep]; + let matches = result.filter(item => !omit.has(item)); -const handleOutput = (options, value, error) => { - if (typeof value !== 'string' && !Buffer.isBuffer(value)) { - // When `execa.sync()` errors, we normalize it to '' to mimic `execa()` - return error === undefined ? undefined : ''; - } + if (options && matches.length === 0) { + if (options.failglob === true) { + throw new Error(`No matches found for "${patterns.join(', ')}"`); + } - if (options.stripFinalNewline) { - return stripFinalNewline(value); - } + if (options.nonull === true || options.nullglob === true) { + return options.unescape ? patterns.map(p => p.replace(/\\/g, '')) : patterns; + } + } - return value; + return matches; }; -const execa = (file, args, options) => { - const parsed = handleArguments(file, args, options); - const command = joinCommand(file, args); - - let spawned; - try { - spawned = childProcess.spawn(parsed.file, parsed.args, parsed.options); - } catch (error) { - // Ensure the returned error is always both a promise and a child process - const dummySpawned = new childProcess.ChildProcess(); - const errorPromise = Promise.reject(makeError({ - error, - stdout: '', - stderr: '', - all: '', - command, - parsed, - timedOut: false, - isCanceled: false, - killed: false - })); - return mergePromise(dummySpawned, errorPromise); - } - - const spawnedPromise = getSpawnedPromise(spawned); - const timedPromise = setupTimeout(spawned, parsed.options, spawnedPromise); - const processDone = setExitHandler(spawned, parsed.options, timedPromise); +/** + * Backwards compatibility + */ - const context = {isCanceled: false}; +micromatch.match = micromatch; - spawned.kill = spawnedKill.bind(null, spawned.kill.bind(spawned)); - spawned.cancel = spawnedCancel.bind(null, spawned, context); +/** + * Returns a matcher function from the given glob `pattern` and `options`. + * The returned function takes a string to match as its only argument and returns + * true if the string is a match. + * + * ```js + * const mm = require('micromatch'); + * // mm.matcher(pattern[, options]); + * + * const isMatch = mm.matcher('*.!(*a)'); + * console.log(isMatch('a.a')); //=> false + * console.log(isMatch('a.b')); //=> true + * ``` + * @param {String} `pattern` Glob pattern + * @param {Object} `options` + * @return {Function} Returns a matcher function. + * @api public + */ - const handlePromise = async () => { - const [{error, exitCode, signal, timedOut}, stdoutResult, stderrResult, allResult] = await getSpawnedResult(spawned, parsed.options, processDone); - const stdout = handleOutput(parsed.options, stdoutResult); - const stderr = handleOutput(parsed.options, stderrResult); - const all = handleOutput(parsed.options, allResult); +micromatch.matcher = (pattern, options) => picomatch(pattern, options); - if (error || exitCode !== 0 || signal !== null) { - const returnedError = makeError({ - error, - exitCode, - signal, - stdout, - stderr, - all, - command, - parsed, - timedOut, - isCanceled: context.isCanceled, - killed: spawned.killed - }); +/** + * Returns true if **any** of the given glob `patterns` match the specified `string`. + * + * ```js + * const mm = require('micromatch'); + * // mm.isMatch(string, patterns[, options]); + * + * console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true + * console.log(mm.isMatch('a.a', 'b.*')); //=> false + * ``` + * @param {String} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `[options]` See available [options](#options). + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ - if (!parsed.options.reject) { - return returnedError; - } +micromatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); - throw returnedError; - } +/** + * Backwards compatibility + */ - return { - command, - exitCode: 0, - stdout, - stderr, - all, - failed: false, - timedOut: false, - isCanceled: false, - killed: false - }; - }; +micromatch.any = micromatch.isMatch; - const handlePromiseOnce = onetime(handlePromise); +/** + * Returns a list of strings that _**do not match any**_ of the given `patterns`. + * + * ```js + * const mm = require('micromatch'); + * // mm.not(list, patterns[, options]); + * + * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a')); + * //=> ['b.b', 'c.c'] + * ``` + * @param {Array} `list` Array of strings to match. + * @param {String|Array} `patterns` One or more glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of strings that **do not match** the given patterns. + * @api public + */ - crossSpawn._enoent.hookChildProcess(spawned, parsed.parsed); +micromatch.not = (list, patterns, options = {}) => { + patterns = [].concat(patterns).map(String); + let result = new Set(); + let items = []; - handleInput(spawned, parsed.options.input); + let onResult = state => { + if (options.onResult) options.onResult(state); + items.push(state.output); + }; - spawned.all = makeAllStream(spawned, parsed.options); + let matches = micromatch(list, patterns, { ...options, onResult }); - return mergePromise(spawned, handlePromiseOnce); + for (let item of items) { + if (!matches.includes(item)) { + result.add(item); + } + } + return [...result]; }; -module.exports = execa; +/** + * Returns true if the given `string` contains the given pattern. Similar + * to [.isMatch](#isMatch) but the pattern can match any part of the string. + * + * ```js + * var mm = require('micromatch'); + * // mm.contains(string, pattern[, options]); + * + * console.log(mm.contains('aa/bb/cc', '*b')); + * //=> true + * console.log(mm.contains('aa/bb/cc', '*d')); + * //=> false + * ``` + * @param {String} `str` The string to match. + * @param {String|Array} `patterns` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any of the patterns matches any part of `str`. + * @api public + */ -module.exports.sync = (file, args, options) => { - const parsed = handleArguments(file, args, options); - const command = joinCommand(file, args); +micromatch.contains = (str, pattern, options) => { + if (typeof str !== 'string') { + throw new TypeError(`Expected a string: "${util.inspect(str)}"`); + } - validateInputSync(parsed.options); + if (Array.isArray(pattern)) { + return pattern.some(p => micromatch.contains(str, p, options)); + } - let result; - try { - result = childProcess.spawnSync(parsed.file, parsed.args, parsed.options); - } catch (error) { - throw makeError({ - error, - stdout: '', - stderr: '', - all: '', - command, - parsed, - timedOut: false, - isCanceled: false, - killed: false - }); - } + if (typeof pattern === 'string') { + if (isEmptyString(str) || isEmptyString(pattern)) { + return false; + } - const stdout = handleOutput(parsed.options, result.stdout, result.error); - const stderr = handleOutput(parsed.options, result.stderr, result.error); + if (str.includes(pattern) || (str.startsWith('./') && str.slice(2).includes(pattern))) { + return true; + } + } - if (result.error || result.status !== 0 || result.signal !== null) { - const error = makeError({ - stdout, - stderr, - error: result.error, - signal: result.signal, - exitCode: result.status, - command, - parsed, - timedOut: result.error && result.error.code === 'ETIMEDOUT', - isCanceled: false, - killed: result.signal !== null - }); - - if (!parsed.options.reject) { - return error; - } - - throw error; - } - - return { - command, - exitCode: 0, - stdout, - stderr, - failed: false, - timedOut: false, - isCanceled: false, - killed: false - }; + return micromatch.isMatch(str, pattern, { ...options, contains: true }); }; -module.exports.command = (command, options) => { - const [file, ...args] = parseCommand(command); - return execa(file, args, options); -}; +/** + * Filter the keys of the given object with the given `glob` pattern + * and `options`. Does not attempt to match nested keys. If you need this feature, + * use [glob-object][] instead. + * + * ```js + * const mm = require('micromatch'); + * // mm.matchKeys(object, patterns[, options]); + * + * const obj = { aa: 'a', ab: 'b', ac: 'c' }; + * console.log(mm.matchKeys(obj, '*b')); + * //=> { ab: 'b' } + * ``` + * @param {Object} `object` The object with keys to filter. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Object} Returns an object with only keys that match the given patterns. + * @api public + */ -module.exports.commandSync = (command, options) => { - const [file, ...args] = parseCommand(command); - return execa.sync(file, args, options); +micromatch.matchKeys = (obj, patterns, options) => { + if (!utils.isObject(obj)) { + throw new TypeError('Expected the first argument to be an object'); + } + let keys = micromatch(Object.keys(obj), patterns, options); + let res = {}; + for (let key of keys) res[key] = obj[key]; + return res; }; -module.exports.node = (scriptPath, args, options = {}) => { - if (args && !Array.isArray(args) && typeof args === 'object') { - options = args; - args = []; - } - - const stdio = normalizeStdio.node(options); +/** + * Returns true if some of the strings in the given `list` match any of the given glob `patterns`. + * + * ```js + * const mm = require('micromatch'); + * // mm.some(list, patterns[, options]); + * + * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // true + * console.log(mm.some(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any `patterns` matches any of the strings in `list` + * @api public + */ - const {nodePath = process.execPath, nodeOptions = process.execArgv} = options; +micromatch.some = (list, patterns, options) => { + let items = [].concat(list); - return execa( - nodePath, - [ - ...nodeOptions, - scriptPath, - ...(Array.isArray(args) ? args : []) - ], - { - ...options, - stdin: undefined, - stdout: undefined, - stderr: undefined, - stdio, - shell: false - } - ); + for (let pattern of [].concat(patterns)) { + let isMatch = picomatch(String(pattern), options); + if (items.some(item => isMatch(item))) { + return true; + } + } + return false; }; +/** + * Returns true if every string in the given `list` matches + * any of the given glob `patterns`. + * + * ```js + * const mm = require('micromatch'); + * // mm.every(list, patterns[, options]); + * + * console.log(mm.every('foo.js', ['foo.js'])); + * // true + * console.log(mm.every(['foo.js', 'bar.js'], ['*.js'])); + * // true + * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // false + * console.log(mm.every(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if all `patterns` matches all of the strings in `list` + * @api public + */ -/***/ }), - -/***/ "../../node_modules/execa/lib/command.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -const SPACES_REGEXP = / +/g; - -const joinCommand = (file, args = []) => { - if (!Array.isArray(args)) { - return file; - } +micromatch.every = (list, patterns, options) => { + let items = [].concat(list); - return [file, ...args].join(' '); + for (let pattern of [].concat(patterns)) { + let isMatch = picomatch(String(pattern), options); + if (!items.every(item => isMatch(item))) { + return false; + } + } + return true; }; -// Allow spaces to be escaped by a backslash if not meant as a delimiter -const handleEscaping = (tokens, token, index) => { - if (index === 0) { - return [token]; - } - - const previousToken = tokens[tokens.length - 1]; - - if (previousToken.endsWith('\\')) { - return [...tokens.slice(0, -1), `${previousToken.slice(0, -1)} ${token}`]; - } +/** + * Returns true if **all** of the given `patterns` match + * the specified string. + * + * ```js + * const mm = require('micromatch'); + * // mm.all(string, patterns[, options]); + * + * console.log(mm.all('foo.js', ['foo.js'])); + * // true + * + * console.log(mm.all('foo.js', ['*.js', '!foo.js'])); + * // false + * + * console.log(mm.all('foo.js', ['*.js', 'foo.js'])); + * // true + * + * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); + * // true + * ``` + * @param {String|Array} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ - return [...tokens, token]; -}; +micromatch.all = (str, patterns, options) => { + if (typeof str !== 'string') { + throw new TypeError(`Expected a string: "${util.inspect(str)}"`); + } -// Handle `execa.command()` -const parseCommand = command => { - return command - .trim() - .split(SPACES_REGEXP) - .reduce(handleEscaping, []); + return [].concat(patterns).every(p => picomatch(p, options)(str)); }; -module.exports = { - joinCommand, - parseCommand -}; +/** + * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match. + * + * ```js + * const mm = require('micromatch'); + * // mm.capture(pattern, string[, options]); + * + * console.log(mm.capture('test/*.js', 'test/foo.js')); + * //=> ['foo'] + * console.log(mm.capture('test/*.js', 'foo/bar.css')); + * //=> null + * ``` + * @param {String} `glob` Glob pattern to use for matching. + * @param {String} `input` String to match + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array|null} Returns an array of captures if the input matches the glob pattern, otherwise `null`. + * @api public + */ +micromatch.capture = (glob, input, options) => { + let posix = utils.isWindows(options); + let regex = picomatch.makeRe(String(glob), { ...options, capture: true }); + let match = regex.exec(posix ? utils.toPosixSlashes(input) : input); -/***/ }), + if (match) { + return match.slice(1).map(v => v === void 0 ? '' : v); + } +}; -/***/ "../../node_modules/execa/lib/error.js": -/***/ (function(module, exports, __webpack_require__) { +/** + * Create a regular expression from the given glob `pattern`. + * + * ```js + * const mm = require('micromatch'); + * // mm.makeRe(pattern[, options]); + * + * console.log(mm.makeRe('*.js')); + * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ + * ``` + * @param {String} `pattern` A glob pattern to convert to regex. + * @param {Object} `options` + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ -"use strict"; +micromatch.makeRe = (...args) => picomatch.makeRe(...args); -const {signalsByName} = __webpack_require__("../../node_modules/human-signals/build/src/main.js"); - -const getErrorPrefix = ({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}) => { - if (timedOut) { - return `timed out after ${timeout} milliseconds`; - } - - if (isCanceled) { - return 'was canceled'; - } - - if (errorCode !== undefined) { - return `failed with ${errorCode}`; - } +/** + * Scan a glob pattern to separate the pattern into segments. Used + * by the [split](#split) method. + * + * ```js + * const mm = require('micromatch'); + * const state = mm.scan(pattern[, options]); + * ``` + * @param {String} `pattern` + * @param {Object} `options` + * @return {Object} Returns an object with + * @api public + */ - if (signal !== undefined) { - return `was killed with ${signal} (${signalDescription})`; - } +micromatch.scan = (...args) => picomatch.scan(...args); - if (exitCode !== undefined) { - return `failed with exit code ${exitCode}`; - } +/** + * Parse a glob pattern to create the source string for a regular + * expression. + * + * ```js + * const mm = require('micromatch'); + * const state = mm(pattern[, options]); + * ``` + * @param {String} `glob` + * @param {Object} `options` + * @return {Object} Returns an object with useful properties and output to be used as regex source string. + * @api public + */ - return 'failed'; +micromatch.parse = (patterns, options) => { + let res = []; + for (let pattern of [].concat(patterns || [])) { + for (let str of braces(String(pattern), options)) { + res.push(picomatch.parse(str, options)); + } + } + return res; }; -const makeError = ({ - stdout, - stderr, - all, - error, - signal, - exitCode, - command, - timedOut, - isCanceled, - killed, - parsed: {options: {timeout}} -}) => { - // `signal` and `exitCode` emitted on `spawned.on('exit')` event can be `null`. - // We normalize them to `undefined` - exitCode = exitCode === null ? undefined : exitCode; - signal = signal === null ? undefined : signal; - const signalDescription = signal === undefined ? undefined : signalsByName[signal].description; - - const errorCode = error && error.code; - - const prefix = getErrorPrefix({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}); - const execaMessage = `Command ${prefix}: ${command}`; - const isError = Object.prototype.toString.call(error) === '[object Error]'; - const shortMessage = isError ? `${execaMessage}\n${error.message}` : execaMessage; - const message = [shortMessage, stderr, stdout].filter(Boolean).join('\n'); - - if (isError) { - error.originalMessage = error.message; - error.message = message; - } else { - error = new Error(message); - } - - error.shortMessage = shortMessage; - error.command = command; - error.exitCode = exitCode; - error.signal = signal; - error.signalDescription = signalDescription; - error.stdout = stdout; - error.stderr = stderr; - - if (all !== undefined) { - error.all = all; - } +/** + * Process the given brace `pattern`. + * + * ```js + * const { braces } = require('micromatch'); + * console.log(braces('foo/{a,b,c}/bar')); + * //=> [ 'foo/(a|b|c)/bar' ] + * + * console.log(braces('foo/{a,b,c}/bar', { expand: true })); + * //=> [ 'foo/a/bar', 'foo/b/bar', 'foo/c/bar' ] + * ``` + * @param {String} `pattern` String with brace pattern to process. + * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options. + * @return {Array} + * @api public + */ - if ('bufferedData' in error) { - delete error.bufferedData; - } +micromatch.braces = (pattern, options) => { + if (typeof pattern !== 'string') throw new TypeError('Expected a string'); + if ((options && options.nobrace === true) || !/\{.*\}/.test(pattern)) { + return [pattern]; + } + return braces(pattern, options); +}; - error.failed = true; - error.timedOut = Boolean(timedOut); - error.isCanceled = isCanceled; - error.killed = killed && !timedOut; +/** + * Expand braces + */ - return error; +micromatch.braceExpand = (pattern, options) => { + if (typeof pattern !== 'string') throw new TypeError('Expected a string'); + return micromatch.braces(pattern, { ...options, expand: true }); }; -module.exports = makeError; +/** + * Expose micromatch + */ + +module.exports = micromatch; /***/ }), -/***/ "../../node_modules/execa/lib/kill.js": +/***/ "../../node_modules/fast-glob/out/index.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; -const os = __webpack_require__("os"); -const onExit = __webpack_require__("../../node_modules/signal-exit/index.js"); - -const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5; - -// Monkey-patches `childProcess.kill()` to add `forceKillAfterTimeout` behavior -const spawnedKill = (kill, signal = 'SIGTERM', options = {}) => { - const killResult = kill(signal); - setKillTimeout(kill, signal, options, killResult); - return killResult; -}; - -const setKillTimeout = (kill, signal, options, killResult) => { - if (!shouldForceKill(signal, options, killResult)) { - return; - } - - const timeout = getForceKillAfterTimeout(options); - const t = setTimeout(() => { - kill('SIGKILL'); - }, timeout); - - // Guarded because there's no `.unref()` when `execa` is used in the renderer - // process in Electron. This cannot be tested since we don't run tests in - // Electron. - // istanbul ignore else - if (t.unref) { - t.unref(); - } -}; - -const shouldForceKill = (signal, {forceKillAfterTimeout}, killResult) => { - return isSigterm(signal) && forceKillAfterTimeout !== false && killResult; -}; +const taskManager = __webpack_require__("../../node_modules/fast-glob/out/managers/tasks.js"); +const async_1 = __webpack_require__("../../node_modules/fast-glob/out/providers/async.js"); +const stream_1 = __webpack_require__("../../node_modules/fast-glob/out/providers/stream.js"); +const sync_1 = __webpack_require__("../../node_modules/fast-glob/out/providers/sync.js"); +const settings_1 = __webpack_require__("../../node_modules/fast-glob/out/settings.js"); +const utils = __webpack_require__("../../node_modules/fast-glob/out/utils/index.js"); +async function FastGlob(source, options) { + assertPatternsInput(source); + const works = getWorks(source, async_1.default, options); + const result = await Promise.all(works); + return utils.array.flatten(result); +} +// https://github.com/typescript-eslint/typescript-eslint/issues/60 +// eslint-disable-next-line no-redeclare +(function (FastGlob) { + function sync(source, options) { + assertPatternsInput(source); + const works = getWorks(source, sync_1.default, options); + return utils.array.flatten(works); + } + FastGlob.sync = sync; + function stream(source, options) { + assertPatternsInput(source); + const works = getWorks(source, stream_1.default, options); + /** + * The stream returned by the provider cannot work with an asynchronous iterator. + * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. + * This affects performance (+25%). I don't see best solution right now. + */ + return utils.stream.merge(works); + } + FastGlob.stream = stream; + function generateTasks(source, options) { + assertPatternsInput(source); + const patterns = [].concat(source); + const settings = new settings_1.default(options); + return taskManager.generate(patterns, settings); + } + FastGlob.generateTasks = generateTasks; + function isDynamicPattern(source, options) { + assertPatternsInput(source); + const settings = new settings_1.default(options); + return utils.pattern.isDynamicPattern(source, settings); + } + FastGlob.isDynamicPattern = isDynamicPattern; + function escapePath(source) { + assertPatternsInput(source); + return utils.path.escape(source); + } + FastGlob.escapePath = escapePath; +})(FastGlob || (FastGlob = {})); +function getWorks(source, _Provider, options) { + const patterns = [].concat(source); + const settings = new settings_1.default(options); + const tasks = taskManager.generate(patterns, settings); + const provider = new _Provider(settings); + return tasks.map(provider.read, provider); +} +function assertPatternsInput(input) { + const source = [].concat(input); + const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); + if (!isValidSource) { + throw new TypeError('Patterns must be a string (non empty) or an array of strings'); + } +} +module.exports = FastGlob; -const isSigterm = signal => { - return signal === os.constants.signals.SIGTERM || - (typeof signal === 'string' && signal.toUpperCase() === 'SIGTERM'); -}; -const getForceKillAfterTimeout = ({forceKillAfterTimeout = true}) => { - if (forceKillAfterTimeout === true) { - return DEFAULT_FORCE_KILL_TIMEOUT; - } +/***/ }), - if (!Number.isFinite(forceKillAfterTimeout) || forceKillAfterTimeout < 0) { - throw new TypeError(`Expected the \`forceKillAfterTimeout\` option to be a non-negative integer, got \`${forceKillAfterTimeout}\` (${typeof forceKillAfterTimeout})`); - } +/***/ "../../node_modules/fast-glob/out/managers/tasks.js": +/***/ (function(module, exports, __webpack_require__) { - return forceKillAfterTimeout; -}; +"use strict"; -// `childProcess.cancel()` -const spawnedCancel = (spawned, context) => { - const killResult = spawned.kill(); - - if (killResult) { - context.isCanceled = true; - } -}; - -const timeoutKill = (spawned, signal, reject) => { - spawned.kill(signal); - reject(Object.assign(new Error('Timed out'), {timedOut: true, signal})); -}; - -// `timeout` option handling -const setupTimeout = (spawned, {timeout, killSignal = 'SIGTERM'}, spawnedPromise) => { - if (timeout === 0 || timeout === undefined) { - return spawnedPromise; - } - - if (!Number.isFinite(timeout) || timeout < 0) { - throw new TypeError(`Expected the \`timeout\` option to be a non-negative integer, got \`${timeout}\` (${typeof timeout})`); - } - - let timeoutId; - const timeoutPromise = new Promise((resolve, reject) => { - timeoutId = setTimeout(() => { - timeoutKill(spawned, killSignal, reject); - }, timeout); - }); - - const safeSpawnedPromise = spawnedPromise.finally(() => { - clearTimeout(timeoutId); - }); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.convertPatternGroupToTask = exports.convertPatternGroupsToTasks = exports.groupPatternsByBaseDirectory = exports.getNegativePatternsAsPositive = exports.getPositivePatterns = exports.convertPatternsToTasks = exports.generate = void 0; +const utils = __webpack_require__("../../node_modules/fast-glob/out/utils/index.js"); +function generate(patterns, settings) { + const positivePatterns = getPositivePatterns(patterns); + const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); + const staticPatterns = positivePatterns.filter((pattern) => utils.pattern.isStaticPattern(pattern, settings)); + const dynamicPatterns = positivePatterns.filter((pattern) => utils.pattern.isDynamicPattern(pattern, settings)); + const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); + const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); + return staticTasks.concat(dynamicTasks); +} +exports.generate = generate; +/** + * Returns tasks grouped by basic pattern directories. + * + * Patterns that can be found inside (`./`) and outside (`../`) the current directory are handled separately. + * This is necessary because directory traversal starts at the base directory and goes deeper. + */ +function convertPatternsToTasks(positive, negative, dynamic) { + const tasks = []; + const patternsOutsideCurrentDirectory = utils.pattern.getPatternsOutsideCurrentDirectory(positive); + const patternsInsideCurrentDirectory = utils.pattern.getPatternsInsideCurrentDirectory(positive); + const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory); + const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory); + tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic)); + /* + * For the sake of reducing future accesses to the file system, we merge all tasks within the current directory + * into a global task, if at least one pattern refers to the root (`.`). In this case, the global task covers the rest. + */ + if ('.' in insideCurrentDirectoryGroup) { + tasks.push(convertPatternGroupToTask('.', patternsInsideCurrentDirectory, negative, dynamic)); + } + else { + tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic)); + } + return tasks; +} +exports.convertPatternsToTasks = convertPatternsToTasks; +function getPositivePatterns(patterns) { + return utils.pattern.getPositivePatterns(patterns); +} +exports.getPositivePatterns = getPositivePatterns; +function getNegativePatternsAsPositive(patterns, ignore) { + const negative = utils.pattern.getNegativePatterns(patterns).concat(ignore); + const positive = negative.map(utils.pattern.convertToPositivePattern); + return positive; +} +exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; +function groupPatternsByBaseDirectory(patterns) { + const group = {}; + return patterns.reduce((collection, pattern) => { + const base = utils.pattern.getBaseDirectory(pattern); + if (base in collection) { + collection[base].push(pattern); + } + else { + collection[base] = [pattern]; + } + return collection; + }, group); +} +exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; +function convertPatternGroupsToTasks(positive, negative, dynamic) { + return Object.keys(positive).map((base) => { + return convertPatternGroupToTask(base, positive[base], negative, dynamic); + }); +} +exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; +function convertPatternGroupToTask(base, positive, negative, dynamic) { + return { + dynamic, + positive, + negative, + base, + patterns: [].concat(positive, negative.map(utils.pattern.convertToNegativePattern)) + }; +} +exports.convertPatternGroupToTask = convertPatternGroupToTask; - return Promise.race([timeoutPromise, safeSpawnedPromise]); -}; -// `cleanup` option handling -const setExitHandler = async (spawned, {cleanup, detached}, timedPromise) => { - if (!cleanup || detached) { - return timedPromise; - } +/***/ }), - const removeExitHandler = onExit(() => { - spawned.kill(); - }); +/***/ "../../node_modules/fast-glob/out/providers/async.js": +/***/ (function(module, exports, __webpack_require__) { - return timedPromise.finally(() => { - removeExitHandler(); - }); -}; +"use strict"; -module.exports = { - spawnedKill, - spawnedCancel, - setupTimeout, - setExitHandler -}; +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__("../../node_modules/fast-glob/out/readers/stream.js"); +const provider_1 = __webpack_require__("../../node_modules/fast-glob/out/providers/provider.js"); +class ProviderAsync extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new stream_1.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = []; + return new Promise((resolve, reject) => { + const stream = this.api(root, task, options); + stream.once('error', reject); + stream.on('data', (entry) => entries.push(options.transform(entry))); + stream.once('end', () => resolve(entries)); + }); + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderAsync; /***/ }), -/***/ "../../node_modules/execa/lib/promise.js": +/***/ "../../node_modules/fast-glob/out/providers/filters/deep.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__("../../node_modules/fast-glob/out/utils/index.js"); +const partial_1 = __webpack_require__("../../node_modules/fast-glob/out/providers/matchers/partial.js"); +class DeepFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + } + getFilter(basePath, positive, negative) { + const matcher = this._getMatcher(positive); + const negativeRe = this._getNegativePatternsRe(negative); + return (entry) => this._filter(basePath, entry, matcher, negativeRe); + } + _getMatcher(patterns) { + return new partial_1.default(patterns, this._settings, this._micromatchOptions); + } + _getNegativePatternsRe(patterns) { + const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); + return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); + } + _filter(basePath, entry, matcher, negativeRe) { + if (this._isSkippedByDeep(basePath, entry.path)) { + return false; + } + if (this._isSkippedSymbolicLink(entry)) { + return false; + } + const filepath = utils.path.removeLeadingDotSegment(entry.path); + if (this._isSkippedByPositivePatterns(filepath, matcher)) { + return false; + } + return this._isSkippedByNegativePatterns(filepath, negativeRe); + } + _isSkippedByDeep(basePath, entryPath) { + /** + * Avoid unnecessary depth calculations when it doesn't matter. + */ + if (this._settings.deep === Infinity) { + return false; + } + return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; + } + _getEntryLevel(basePath, entryPath) { + const entryPathDepth = entryPath.split('/').length; + if (basePath === '') { + return entryPathDepth; + } + const basePathDepth = basePath.split('/').length; + return entryPathDepth - basePathDepth; + } + _isSkippedSymbolicLink(entry) { + return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); + } + _isSkippedByPositivePatterns(entryPath, matcher) { + return !this._settings.baseNameMatch && !matcher.match(entryPath); + } + _isSkippedByNegativePatterns(entryPath, patternsRe) { + return !utils.pattern.matchAny(entryPath, patternsRe); + } +} +exports.default = DeepFilter; -const nativePromisePrototype = (async () => {})().constructor.prototype; -const descriptors = ['then', 'catch', 'finally'].map(property => [ - property, - Reflect.getOwnPropertyDescriptor(nativePromisePrototype, property) -]); - -// The return value is a mixin of `childProcess` and `Promise` -const mergePromise = (spawned, promise) => { - for (const [property, descriptor] of descriptors) { - // Starting the main `promise` is deferred to avoid consuming streams - const value = typeof promise === 'function' ? - (...args) => Reflect.apply(descriptor.value, promise(), args) : - descriptor.value.bind(promise); - - Reflect.defineProperty(spawned, property, {...descriptor, value}); - } - - return spawned; -}; -// Use promises instead of `child_process` events -const getSpawnedPromise = spawned => { - return new Promise((resolve, reject) => { - spawned.on('exit', (exitCode, signal) => { - resolve({exitCode, signal}); - }); +/***/ }), - spawned.on('error', error => { - reject(error); - }); +/***/ "../../node_modules/fast-glob/out/providers/filters/entry.js": +/***/ (function(module, exports, __webpack_require__) { - if (spawned.stdin) { - spawned.stdin.on('error', error => { - reject(error); - }); - } - }); -}; - -module.exports = { - mergePromise, - getSpawnedPromise -}; +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__("../../node_modules/fast-glob/out/utils/index.js"); +class EntryFilter { + constructor(_settings, _micromatchOptions) { + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + this.index = new Map(); + } + getFilter(positive, negative) { + const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions); + const negativeRe = utils.pattern.convertPatternsToRe(negative, this._micromatchOptions); + return (entry) => this._filter(entry, positiveRe, negativeRe); + } + _filter(entry, positiveRe, negativeRe) { + if (this._settings.unique && this._isDuplicateEntry(entry)) { + return false; + } + if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { + return false; + } + if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) { + return false; + } + const filepath = this._settings.baseNameMatch ? entry.name : entry.path; + const isMatched = this._isMatchToPatterns(filepath, positiveRe) && !this._isMatchToPatterns(entry.path, negativeRe); + if (this._settings.unique && isMatched) { + this._createIndexRecord(entry); + } + return isMatched; + } + _isDuplicateEntry(entry) { + return this.index.has(entry.path); + } + _createIndexRecord(entry) { + this.index.set(entry.path, undefined); + } + _onlyFileFilter(entry) { + return this._settings.onlyFiles && !entry.dirent.isFile(); + } + _onlyDirectoryFilter(entry) { + return this._settings.onlyDirectories && !entry.dirent.isDirectory(); + } + _isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) { + if (!this._settings.absolute) { + return false; + } + const fullpath = utils.path.makeAbsolute(this._settings.cwd, entryPath); + return utils.pattern.matchAny(fullpath, patternsRe); + } + _isMatchToPatterns(entryPath, patternsRe) { + const filepath = utils.path.removeLeadingDotSegment(entryPath); + return utils.pattern.matchAny(filepath, patternsRe); + } +} +exports.default = EntryFilter; /***/ }), -/***/ "../../node_modules/execa/lib/stdio.js": +/***/ "../../node_modules/fast-glob/out/providers/filters/error.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; -const aliases = ['stdin', 'stdout', 'stderr']; +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__("../../node_modules/fast-glob/out/utils/index.js"); +class ErrorFilter { + constructor(_settings) { + this._settings = _settings; + } + getFilter() { + return (error) => this._isNonFatalError(error); + } + _isNonFatalError(error) { + return utils.errno.isEnoentCodeError(error) || this._settings.suppressErrors; + } +} +exports.default = ErrorFilter; -const hasAlias = opts => aliases.some(alias => opts[alias] !== undefined); -const normalizeStdio = opts => { - if (!opts) { - return; - } +/***/ }), - const {stdio} = opts; +/***/ "../../node_modules/fast-glob/out/providers/matchers/matcher.js": +/***/ (function(module, exports, __webpack_require__) { - if (stdio === undefined) { - return aliases.map(alias => opts[alias]); - } +"use strict"; - if (hasAlias(opts)) { - throw new Error(`It's not possible to provide \`stdio\` in combination with one of ${aliases.map(alias => `\`${alias}\``).join(', ')}`); - } +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__("../../node_modules/fast-glob/out/utils/index.js"); +class Matcher { + constructor(_patterns, _settings, _micromatchOptions) { + this._patterns = _patterns; + this._settings = _settings; + this._micromatchOptions = _micromatchOptions; + this._storage = []; + this._fillStorage(); + } + _fillStorage() { + /** + * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). + * So, before expand patterns with brace expansion into separated patterns. + */ + const patterns = utils.pattern.expandPatternsWithBraceExpansion(this._patterns); + for (const pattern of patterns) { + const segments = this._getPatternSegments(pattern); + const sections = this._splitSegmentsIntoSections(segments); + this._storage.push({ + complete: sections.length <= 1, + pattern, + segments, + sections + }); + } + } + _getPatternSegments(pattern) { + const parts = utils.pattern.getPatternParts(pattern, this._micromatchOptions); + return parts.map((part) => { + const dynamic = utils.pattern.isDynamicPattern(part, this._settings); + if (!dynamic) { + return { + dynamic: false, + pattern: part + }; + } + return { + dynamic: true, + pattern: part, + patternRe: utils.pattern.makeRe(part, this._micromatchOptions) + }; + }); + } + _splitSegmentsIntoSections(segments) { + return utils.array.splitWhen(segments, (segment) => segment.dynamic && utils.pattern.hasGlobStar(segment.pattern)); + } +} +exports.default = Matcher; - if (typeof stdio === 'string') { - return stdio; - } - if (!Array.isArray(stdio)) { - throw new TypeError(`Expected \`stdio\` to be of type \`string\` or \`Array\`, got \`${typeof stdio}\``); - } +/***/ }), - const length = Math.max(stdio.length, aliases.length); - return Array.from({length}, (value, index) => stdio[index]); -}; +/***/ "../../node_modules/fast-glob/out/providers/matchers/partial.js": +/***/ (function(module, exports, __webpack_require__) { -module.exports = normalizeStdio; +"use strict"; -// `ipc` is pushed unless it is already present -module.exports.node = opts => { - const stdio = normalizeStdio(opts); +Object.defineProperty(exports, "__esModule", { value: true }); +const matcher_1 = __webpack_require__("../../node_modules/fast-glob/out/providers/matchers/matcher.js"); +class PartialMatcher extends matcher_1.default { + match(filepath) { + const parts = filepath.split('/'); + const levels = parts.length; + const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); + for (const pattern of patterns) { + const section = pattern.sections[0]; + /** + * In this case, the pattern has a globstar and we must read all directories unconditionally, + * but only if the level has reached the end of the first group. + * + * fixtures/{a,b}/** + * ^ true/false ^ always true + */ + if (!pattern.complete && levels > section.length) { + return true; + } + const match = parts.every((part, index) => { + const segment = pattern.segments[index]; + if (segment.dynamic && segment.patternRe.test(part)) { + return true; + } + if (!segment.dynamic && segment.pattern === part) { + return true; + } + return false; + }); + if (match) { + return true; + } + } + return false; + } +} +exports.default = PartialMatcher; - if (stdio === 'ipc') { - return 'ipc'; - } - if (stdio === undefined || typeof stdio === 'string') { - return [stdio, stdio, stdio, 'ipc']; - } +/***/ }), - if (stdio.includes('ipc')) { - return stdio; - } +/***/ "../../node_modules/fast-glob/out/providers/provider.js": +/***/ (function(module, exports, __webpack_require__) { - return [...stdio, 'ipc']; -}; +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__("path"); +const deep_1 = __webpack_require__("../../node_modules/fast-glob/out/providers/filters/deep.js"); +const entry_1 = __webpack_require__("../../node_modules/fast-glob/out/providers/filters/entry.js"); +const error_1 = __webpack_require__("../../node_modules/fast-glob/out/providers/filters/error.js"); +const entry_2 = __webpack_require__("../../node_modules/fast-glob/out/providers/transformers/entry.js"); +class Provider { + constructor(_settings) { + this._settings = _settings; + this.errorFilter = new error_1.default(this._settings); + this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); + this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); + this.entryTransformer = new entry_2.default(this._settings); + } + _getRootDirectory(task) { + return path.resolve(this._settings.cwd, task.base); + } + _getReaderOptions(task) { + const basePath = task.base === '.' ? '' : task.base; + return { + basePath, + pathSegmentSeparator: '/', + concurrency: this._settings.concurrency, + deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), + entryFilter: this.entryFilter.getFilter(task.positive, task.negative), + errorFilter: this.errorFilter.getFilter(), + followSymbolicLinks: this._settings.followSymbolicLinks, + fs: this._settings.fs, + stats: this._settings.stats, + throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, + transform: this.entryTransformer.getTransformer() + }; + } + _getMicromatchOptions() { + return { + dot: this._settings.dot, + matchBase: this._settings.baseNameMatch, + nobrace: !this._settings.braceExpansion, + nocase: !this._settings.caseSensitiveMatch, + noext: !this._settings.extglob, + noglobstar: !this._settings.globstar, + posix: true, + strictSlashes: false + }; + } +} +exports.default = Provider; /***/ }), -/***/ "../../node_modules/execa/lib/stream.js": +/***/ "../../node_modules/fast-glob/out/providers/stream.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; -const isStream = __webpack_require__("../../node_modules/is-stream/index.js"); -const getStream = __webpack_require__("../../node_modules/get-stream/index.js"); -const mergeStream = __webpack_require__("../../node_modules/merge-stream/index.js"); - -// `input` option -const handleInput = (spawned, input) => { - // Checking for stdin is workaround for https://github.com/nodejs/node/issues/26852 - // TODO: Remove `|| spawned.stdin === undefined` once we drop support for Node.js <=12.2.0 - if (input === undefined || spawned.stdin === undefined) { - return; - } - - if (isStream(input)) { - input.pipe(spawned.stdin); - } else { - spawned.stdin.end(input); - } -}; - -// `all` interleaves `stdout` and `stderr` -const makeAllStream = (spawned, {all}) => { - if (!all || (!spawned.stdout && !spawned.stderr)) { - return; - } +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__("stream"); +const stream_2 = __webpack_require__("../../node_modules/fast-glob/out/readers/stream.js"); +const provider_1 = __webpack_require__("../../node_modules/fast-glob/out/providers/provider.js"); +class ProviderStream extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new stream_2.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const source = this.api(root, task, options); + const destination = new stream_1.Readable({ objectMode: true, read: () => { } }); + source + .once('error', (error) => destination.emit('error', error)) + .on('data', (entry) => destination.emit('data', options.transform(entry))) + .once('end', () => destination.emit('end')); + destination + .once('close', () => source.destroy()); + return destination; + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderStream; - const mixed = mergeStream(); - if (spawned.stdout) { - mixed.add(spawned.stdout); - } +/***/ }), - if (spawned.stderr) { - mixed.add(spawned.stderr); - } +/***/ "../../node_modules/fast-glob/out/providers/sync.js": +/***/ (function(module, exports, __webpack_require__) { - return mixed; -}; +"use strict"; -// On failure, `result.stdout|stderr|all` should contain the currently buffered stream -const getBufferedData = async (stream, streamPromise) => { - if (!stream) { - return; - } +Object.defineProperty(exports, "__esModule", { value: true }); +const sync_1 = __webpack_require__("../../node_modules/fast-glob/out/readers/sync.js"); +const provider_1 = __webpack_require__("../../node_modules/fast-glob/out/providers/provider.js"); +class ProviderSync extends provider_1.default { + constructor() { + super(...arguments); + this._reader = new sync_1.default(this._settings); + } + read(task) { + const root = this._getRootDirectory(task); + const options = this._getReaderOptions(task); + const entries = this.api(root, task, options); + return entries.map(options.transform); + } + api(root, task, options) { + if (task.dynamic) { + return this._reader.dynamic(root, options); + } + return this._reader.static(task.patterns, options); + } +} +exports.default = ProviderSync; - stream.destroy(); - try { - return await streamPromise; - } catch (error) { - return error.bufferedData; - } -}; +/***/ }), -const getStreamPromise = (stream, {encoding, buffer, maxBuffer}) => { - if (!stream || !buffer) { - return; - } +/***/ "../../node_modules/fast-glob/out/providers/transformers/entry.js": +/***/ (function(module, exports, __webpack_require__) { - if (encoding) { - return getStream(stream, {encoding, maxBuffer}); - } +"use strict"; - return getStream.buffer(stream, {maxBuffer}); -}; +Object.defineProperty(exports, "__esModule", { value: true }); +const utils = __webpack_require__("../../node_modules/fast-glob/out/utils/index.js"); +class EntryTransformer { + constructor(_settings) { + this._settings = _settings; + } + getTransformer() { + return (entry) => this._transform(entry); + } + _transform(entry) { + let filepath = entry.path; + if (this._settings.absolute) { + filepath = utils.path.makeAbsolute(this._settings.cwd, filepath); + filepath = utils.path.unixify(filepath); + } + if (this._settings.markDirectories && entry.dirent.isDirectory()) { + filepath += '/'; + } + if (!this._settings.objectMode) { + return filepath; + } + return Object.assign(Object.assign({}, entry), { path: filepath }); + } +} +exports.default = EntryTransformer; -// Retrieve result of child process: exit code, signal, error, streams (stdout/stderr/all) -const getSpawnedResult = async ({stdout, stderr, all}, {encoding, buffer, maxBuffer}, processDone) => { - const stdoutPromise = getStreamPromise(stdout, {encoding, buffer, maxBuffer}); - const stderrPromise = getStreamPromise(stderr, {encoding, buffer, maxBuffer}); - const allPromise = getStreamPromise(all, {encoding, buffer, maxBuffer: maxBuffer * 2}); - try { - return await Promise.all([processDone, stdoutPromise, stderrPromise, allPromise]); - } catch (error) { - return Promise.all([ - {error, signal: error.signal, timedOut: error.timedOut}, - getBufferedData(stdout, stdoutPromise), - getBufferedData(stderr, stderrPromise), - getBufferedData(all, allPromise) - ]); - } -}; +/***/ }), -const validateInputSync = ({input}) => { - if (isStream(input)) { - throw new TypeError('The `input` option cannot be a stream in sync mode'); - } -}; +/***/ "../../node_modules/fast-glob/out/readers/reader.js": +/***/ (function(module, exports, __webpack_require__) { -module.exports = { - handleInput, - makeAllStream, - getSpawnedResult, - validateInputSync -}; +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __webpack_require__("path"); +const fsStat = __webpack_require__("../../node_modules/@nodelib/fs.stat/out/index.js"); +const utils = __webpack_require__("../../node_modules/fast-glob/out/utils/index.js"); +class Reader { + constructor(_settings) { + this._settings = _settings; + this._fsStatSettings = new fsStat.Settings({ + followSymbolicLink: this._settings.followSymbolicLinks, + fs: this._settings.fs, + throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks + }); + } + _getFullEntryPath(filepath) { + return path.resolve(this._settings.cwd, filepath); + } + _makeEntry(stats, pattern) { + const entry = { + name: pattern, + path: pattern, + dirent: utils.fs.createDirentFromStats(pattern, stats) + }; + if (this._settings.stats) { + entry.stats = stats; + } + return entry; + } + _isFatalError(error) { + return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; + } +} +exports.default = Reader; /***/ }), -/***/ "../../node_modules/fastq/queue.js": +/***/ "../../node_modules/fast-glob/out/readers/stream.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const stream_1 = __webpack_require__("stream"); +const fsStat = __webpack_require__("../../node_modules/@nodelib/fs.stat/out/index.js"); +const fsWalk = __webpack_require__("../../node_modules/@nodelib/fs.walk/out/index.js"); +const reader_1 = __webpack_require__("../../node_modules/fast-glob/out/readers/reader.js"); +class ReaderStream extends reader_1.default { + constructor() { + super(...arguments); + this._walkStream = fsWalk.walkStream; + this._stat = fsStat.stat; + } + dynamic(root, options) { + return this._walkStream(root, options); + } + static(patterns, options) { + const filepaths = patterns.map(this._getFullEntryPath, this); + const stream = new stream_1.PassThrough({ objectMode: true }); + stream._write = (index, _enc, done) => { + return this._getEntry(filepaths[index], patterns[index], options) + .then((entry) => { + if (entry !== null && options.entryFilter(entry)) { + stream.push(entry); + } + if (index === filepaths.length - 1) { + stream.end(); + } + done(); + }) + .catch(done); + }; + for (let i = 0; i < filepaths.length; i++) { + stream.write(i); + } + return stream; + } + _getEntry(filepath, pattern, options) { + return this._getStat(filepath) + .then((stats) => this._makeEntry(stats, pattern)) + .catch((error) => { + if (options.errorFilter(error)) { + return null; + } + throw error; + }); + } + _getStat(filepath) { + return new Promise((resolve, reject) => { + this._stat(filepath, this._fsStatSettings, (error, stats) => { + return error === null ? resolve(stats) : reject(error); + }); + }); + } +} +exports.default = ReaderStream; -var reusify = __webpack_require__("../../node_modules/reusify/reusify.js") -function fastqueue (context, worker, concurrency) { - if (typeof context === 'function') { - concurrency = worker - worker = context - context = null - } +/***/ }), - var cache = reusify(Task) - var queueHead = null - var queueTail = null - var _running = 0 +/***/ "../../node_modules/fast-glob/out/readers/sync.js": +/***/ (function(module, exports, __webpack_require__) { - var self = { - push: push, - drain: noop, - saturated: noop, - pause: pause, - paused: false, - concurrency: concurrency, - running: running, - resume: resume, - idle: idle, - length: length, - unshift: unshift, - empty: noop, - kill: kill, - killAndDrain: killAndDrain - } - - return self - - function running () { - return _running - } - - function pause () { - self.paused = true - } - - function length () { - var current = queueHead - var counter = 0 +"use strict"; - while (current) { - current = current.next - counter++ +Object.defineProperty(exports, "__esModule", { value: true }); +const fsStat = __webpack_require__("../../node_modules/@nodelib/fs.stat/out/index.js"); +const fsWalk = __webpack_require__("../../node_modules/@nodelib/fs.walk/out/index.js"); +const reader_1 = __webpack_require__("../../node_modules/fast-glob/out/readers/reader.js"); +class ReaderSync extends reader_1.default { + constructor() { + super(...arguments); + this._walkSync = fsWalk.walkSync; + this._statSync = fsStat.statSync; } - - return counter - } - - function resume () { - if (!self.paused) return - self.paused = false - for (var i = 0; i < self.concurrency; i++) { - _running++ - release() + dynamic(root, options) { + return this._walkSync(root, options); } - } - - function idle () { - return _running === 0 && self.length() === 0 - } - - function push (value, done) { - var current = cache.get() - - current.context = context - current.release = release - current.value = value - current.callback = done || noop - - if (_running === self.concurrency || self.paused) { - if (queueTail) { - queueTail.next = current - queueTail = current - } else { - queueHead = current - queueTail = current - self.saturated() - } - } else { - _running++ - worker.call(context, current.value, current.worked) + static(patterns, options) { + const entries = []; + for (const pattern of patterns) { + const filepath = this._getFullEntryPath(pattern); + const entry = this._getEntry(filepath, pattern, options); + if (entry === null || !options.entryFilter(entry)) { + continue; + } + entries.push(entry); + } + return entries; } - } + _getEntry(filepath, pattern, options) { + try { + const stats = this._getStat(filepath); + return this._makeEntry(stats, pattern); + } + catch (error) { + if (options.errorFilter(error)) { + return null; + } + throw error; + } + } + _getStat(filepath) { + return this._statSync(filepath, this._fsStatSettings); + } +} +exports.default = ReaderSync; - function unshift (value, done) { - var current = cache.get() - current.context = context - current.release = release - current.value = value - current.callback = done || noop +/***/ }), - if (_running === self.concurrency || self.paused) { - if (queueHead) { - current.next = queueHead - queueHead = current - } else { - queueHead = current - queueTail = current - self.saturated() - } - } else { - _running++ - worker.call(context, current.value, current.worked) - } - } +/***/ "../../node_modules/fast-glob/out/settings.js": +/***/ (function(module, exports, __webpack_require__) { - function release (holder) { - if (holder) { - cache.release(holder) - } - var next = queueHead - if (next) { - if (!self.paused) { - if (queueTail === queueHead) { - queueTail = null +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0; +const fs = __webpack_require__("fs"); +const os = __webpack_require__("os"); +/** + * The `os.cpus` method can return zero. We expect the number of cores to be greater than zero. + * https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107 + */ +const CPU_COUNT = Math.max(os.cpus().length, 1); +exports.DEFAULT_FILE_SYSTEM_ADAPTER = { + lstat: fs.lstat, + lstatSync: fs.lstatSync, + stat: fs.stat, + statSync: fs.statSync, + readdir: fs.readdir, + readdirSync: fs.readdirSync +}; +class Settings { + constructor(_options = {}) { + this._options = _options; + this.absolute = this._getValue(this._options.absolute, false); + this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); + this.braceExpansion = this._getValue(this._options.braceExpansion, true); + this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); + this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); + this.cwd = this._getValue(this._options.cwd, process.cwd()); + this.deep = this._getValue(this._options.deep, Infinity); + this.dot = this._getValue(this._options.dot, false); + this.extglob = this._getValue(this._options.extglob, true); + this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); + this.fs = this._getFileSystemMethods(this._options.fs); + this.globstar = this._getValue(this._options.globstar, true); + this.ignore = this._getValue(this._options.ignore, []); + this.markDirectories = this._getValue(this._options.markDirectories, false); + this.objectMode = this._getValue(this._options.objectMode, false); + this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); + this.onlyFiles = this._getValue(this._options.onlyFiles, true); + this.stats = this._getValue(this._options.stats, false); + this.suppressErrors = this._getValue(this._options.suppressErrors, false); + this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); + this.unique = this._getValue(this._options.unique, true); + if (this.onlyDirectories) { + this.onlyFiles = false; } - queueHead = next.next - next.next = null - worker.call(context, next.value, next.worked) - if (queueTail === null) { - self.empty() + if (this.stats) { + this.objectMode = true; } - } else { - _running-- - } - } else if (--_running === 0) { - self.drain() } - } - - function kill () { - queueHead = null - queueTail = null - self.drain = noop - } - - function killAndDrain () { - queueHead = null - queueTail = null - self.drain() - self.drain = noop - } + _getValue(option, value) { + return option === undefined ? value : option; + } + _getFileSystemMethods(methods = {}) { + return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); + } } +exports.default = Settings; -function noop () {} -function Task () { - this.value = null - this.callback = noop - this.next = null - this.release = noop - this.context = null +/***/ }), - var self = this +/***/ "../../node_modules/fast-glob/out/utils/array.js": +/***/ (function(module, exports, __webpack_require__) { - this.worked = function worked (err, result) { - var callback = self.callback - self.value = null - self.callback = noop - callback.call(self.context, err, result) - self.release(self) - } -} +"use strict"; -module.exports = fastqueue +Object.defineProperty(exports, "__esModule", { value: true }); +exports.splitWhen = exports.flatten = void 0; +function flatten(items) { + return items.reduce((collection, item) => [].concat(collection, item), []); +} +exports.flatten = flatten; +function splitWhen(items, predicate) { + const result = [[]]; + let groupIndex = 0; + for (const item of items) { + if (predicate(item)) { + groupIndex++; + result[groupIndex] = []; + } + else { + result[groupIndex].push(item); + } + } + return result; +} +exports.splitWhen = splitWhen; /***/ }), -/***/ "../../node_modules/fill-range/index.js": +/***/ "../../node_modules/fast-glob/out/utils/errno.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * fill-range - * - * Copyright (c) 2014-present, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -const util = __webpack_require__("util"); -const toRegexRange = __webpack_require__("../../node_modules/to-regex-range/index.js"); - -const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); - -const transform = toNumber => { - return value => toNumber === true ? Number(value) : String(value); -}; - -const isValidValue = value => { - return typeof value === 'number' || (typeof value === 'string' && value !== ''); -}; - -const isNumber = num => Number.isInteger(+num); - -const zeros = input => { - let value = `${input}`; - let index = -1; - if (value[0] === '-') value = value.slice(1); - if (value === '0') return false; - while (value[++index] === '0'); - return index > 0; -}; - -const stringify = (start, end, options) => { - if (typeof start === 'string' || typeof end === 'string') { - return true; - } - return options.stringify === true; -}; - -const pad = (input, maxLength, toNumber) => { - if (maxLength > 0) { - let dash = input[0] === '-' ? '-' : ''; - if (dash) input = input.slice(1); - input = (dash + input.padStart(dash ? maxLength - 1 : maxLength, '0')); - } - if (toNumber === false) { - return String(input); - } - return input; -}; - -const toMaxLen = (input, maxLength) => { - let negative = input[0] === '-' ? '-' : ''; - if (negative) { - input = input.slice(1); - maxLength--; - } - while (input.length < maxLength) input = '0' + input; - return negative ? ('-' + input) : input; -}; - -const toSequence = (parts, options) => { - parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); - parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); - - let prefix = options.capture ? '' : '?:'; - let positives = ''; - let negatives = ''; - let result; - - if (parts.positives.length) { - positives = parts.positives.join('|'); - } - - if (parts.negatives.length) { - negatives = `-(${prefix}${parts.negatives.join('|')})`; - } - - if (positives && negatives) { - result = `${positives}|${negatives}`; - } else { - result = positives || negatives; - } - - if (options.wrap) { - return `(${prefix}${result})`; - } - return result; -}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isEnoentCodeError = void 0; +function isEnoentCodeError(error) { + return error.code === 'ENOENT'; +} +exports.isEnoentCodeError = isEnoentCodeError; -const toRange = (a, b, isNumbers, options) => { - if (isNumbers) { - return toRegexRange(a, b, { wrap: false, ...options }); - } - let start = String.fromCharCode(a); - if (a === b) return start; +/***/ }), - let stop = String.fromCharCode(b); - return `[${start}-${stop}]`; -}; +/***/ "../../node_modules/fast-glob/out/utils/fs.js": +/***/ (function(module, exports, __webpack_require__) { -const toRegex = (start, end, options) => { - if (Array.isArray(start)) { - let wrap = options.wrap === true; - let prefix = options.capture ? '' : '?:'; - return wrap ? `(${prefix}${start.join('|')})` : start.join('|'); - } - return toRegexRange(start, end, options); -}; +"use strict"; -const rangeError = (...args) => { - return new RangeError('Invalid range arguments: ' + util.inspect(...args)); -}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createDirentFromStats = void 0; +class DirentFromStats { + constructor(name, stats) { + this.name = name; + this.isBlockDevice = stats.isBlockDevice.bind(stats); + this.isCharacterDevice = stats.isCharacterDevice.bind(stats); + this.isDirectory = stats.isDirectory.bind(stats); + this.isFIFO = stats.isFIFO.bind(stats); + this.isFile = stats.isFile.bind(stats); + this.isSocket = stats.isSocket.bind(stats); + this.isSymbolicLink = stats.isSymbolicLink.bind(stats); + } +} +function createDirentFromStats(name, stats) { + return new DirentFromStats(name, stats); +} +exports.createDirentFromStats = createDirentFromStats; -const invalidRange = (start, end, options) => { - if (options.strictRanges === true) throw rangeError([start, end]); - return []; -}; -const invalidStep = (step, options) => { - if (options.strictRanges === true) { - throw new TypeError(`Expected step "${step}" to be a number`); - } - return []; -}; +/***/ }), -const fillNumbers = (start, end, step = 1, options = {}) => { - let a = Number(start); - let b = Number(end); +/***/ "../../node_modules/fast-glob/out/utils/index.js": +/***/ (function(module, exports, __webpack_require__) { - if (!Number.isInteger(a) || !Number.isInteger(b)) { - if (options.strictRanges === true) throw rangeError([start, end]); - return []; - } +"use strict"; - // fix negative zero - if (a === 0) a = 0; - if (b === 0) b = 0; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.string = exports.stream = exports.pattern = exports.path = exports.fs = exports.errno = exports.array = void 0; +const array = __webpack_require__("../../node_modules/fast-glob/out/utils/array.js"); +exports.array = array; +const errno = __webpack_require__("../../node_modules/fast-glob/out/utils/errno.js"); +exports.errno = errno; +const fs = __webpack_require__("../../node_modules/fast-glob/out/utils/fs.js"); +exports.fs = fs; +const path = __webpack_require__("../../node_modules/fast-glob/out/utils/path.js"); +exports.path = path; +const pattern = __webpack_require__("../../node_modules/fast-glob/out/utils/pattern.js"); +exports.pattern = pattern; +const stream = __webpack_require__("../../node_modules/fast-glob/out/utils/stream.js"); +exports.stream = stream; +const string = __webpack_require__("../../node_modules/fast-glob/out/utils/string.js"); +exports.string = string; - let descending = a > b; - let startString = String(start); - let endString = String(end); - let stepString = String(step); - step = Math.max(Math.abs(step), 1); - let padded = zeros(startString) || zeros(endString) || zeros(stepString); - let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0; - let toNumber = padded === false && stringify(start, end, options) === false; - let format = options.transform || transform(toNumber); +/***/ }), - if (options.toRegex && step === 1) { - return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options); - } +/***/ "../../node_modules/fast-glob/out/utils/path.js": +/***/ (function(module, exports, __webpack_require__) { - let parts = { negatives: [], positives: [] }; - let push = num => parts[num < 0 ? 'negatives' : 'positives'].push(Math.abs(num)); - let range = []; - let index = 0; +"use strict"; - while (descending ? a >= b : a <= b) { - if (options.toRegex === true && step > 1) { - push(a); - } else { - range.push(pad(format(a, index), maxLen, toNumber)); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.removeLeadingDotSegment = exports.escape = exports.makeAbsolute = exports.unixify = void 0; +const path = __webpack_require__("path"); +const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ +const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; +/** + * Designed to work only with simple paths: `dir\\file`. + */ +function unixify(filepath) { + return filepath.replace(/\\/g, '/'); +} +exports.unixify = unixify; +function makeAbsolute(cwd, filepath) { + return path.resolve(cwd, filepath); +} +exports.makeAbsolute = makeAbsolute; +function escape(pattern) { + return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); +} +exports.escape = escape; +function removeLeadingDotSegment(entry) { + // We do not use `startsWith` because this is 10x slower than current implementation for some cases. + // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with + if (entry.charAt(0) === '.') { + const secondCharactery = entry.charAt(1); + if (secondCharactery === '/' || secondCharactery === '\\') { + return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); + } } - a = descending ? a - step : a + step; - index++; - } - - if (options.toRegex === true) { - return step > 1 - ? toSequence(parts, options) - : toRegex(range, null, { wrap: false, ...options }); - } - - return range; -}; - -const fillLetters = (start, end, step = 1, options = {}) => { - if ((!isNumber(start) && start.length > 1) || (!isNumber(end) && end.length > 1)) { - return invalidRange(start, end, options); - } - + return entry; +} +exports.removeLeadingDotSegment = removeLeadingDotSegment; - let format = options.transform || (val => String.fromCharCode(val)); - let a = `${start}`.charCodeAt(0); - let b = `${end}`.charCodeAt(0); - let descending = a > b; - let min = Math.min(a, b); - let max = Math.max(a, b); +/***/ }), - if (options.toRegex && step === 1) { - return toRange(min, max, false, options); - } +/***/ "../../node_modules/fast-glob/out/utils/pattern.js": +/***/ (function(module, exports, __webpack_require__) { - let range = []; - let index = 0; +"use strict"; - while (descending ? a >= b : a <= b) { - range.push(format(a, index)); - a = descending ? a - step : a + step; - index++; - } - - if (options.toRegex === true) { - return toRegex(range, null, { wrap: false, options }); - } +Object.defineProperty(exports, "__esModule", { value: true }); +exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; +const path = __webpack_require__("path"); +const globParent = __webpack_require__("../../node_modules/glob-parent/index.js"); +const micromatch = __webpack_require__("../../node_modules/fast-glob/node_modules/micromatch/index.js"); +const GLOBSTAR = '**'; +const ESCAPE_SYMBOL = '\\'; +const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; +const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[.*]/; +const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\(.*\|.*\)/; +const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\(.*\)/; +const BRACE_EXPANSIONS_SYMBOLS_RE = /{.*(?:,|\.\.).*}/; +function isStaticPattern(pattern, options = {}) { + return !isDynamicPattern(pattern, options); +} +exports.isStaticPattern = isStaticPattern; +function isDynamicPattern(pattern, options = {}) { + /** + * A special case with an empty string is necessary for matching patterns that start with a forward slash. + * An empty string cannot be a dynamic pattern. + * For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'. + */ + if (pattern === '') { + return false; + } + /** + * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check + * filepath directly (without read directory). + */ + if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { + return true; + } + if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { + return true; + } + if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { + return true; + } + if (options.braceExpansion !== false && BRACE_EXPANSIONS_SYMBOLS_RE.test(pattern)) { + return true; + } + return false; +} +exports.isDynamicPattern = isDynamicPattern; +function convertToPositivePattern(pattern) { + return isNegativePattern(pattern) ? pattern.slice(1) : pattern; +} +exports.convertToPositivePattern = convertToPositivePattern; +function convertToNegativePattern(pattern) { + return '!' + pattern; +} +exports.convertToNegativePattern = convertToNegativePattern; +function isNegativePattern(pattern) { + return pattern.startsWith('!') && pattern[1] !== '('; +} +exports.isNegativePattern = isNegativePattern; +function isPositivePattern(pattern) { + return !isNegativePattern(pattern); +} +exports.isPositivePattern = isPositivePattern; +function getNegativePatterns(patterns) { + return patterns.filter(isNegativePattern); +} +exports.getNegativePatterns = getNegativePatterns; +function getPositivePatterns(patterns) { + return patterns.filter(isPositivePattern); +} +exports.getPositivePatterns = getPositivePatterns; +/** + * Returns patterns that can be applied inside the current directory. + * + * @example + * // ['./*', '*', 'a/*'] + * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) + */ +function getPatternsInsideCurrentDirectory(patterns) { + return patterns.filter((pattern) => !isPatternRelatedToParentDirectory(pattern)); +} +exports.getPatternsInsideCurrentDirectory = getPatternsInsideCurrentDirectory; +/** + * Returns patterns to be expanded relative to (outside) the current directory. + * + * @example + * // ['../*', './../*'] + * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) + */ +function getPatternsOutsideCurrentDirectory(patterns) { + return patterns.filter(isPatternRelatedToParentDirectory); +} +exports.getPatternsOutsideCurrentDirectory = getPatternsOutsideCurrentDirectory; +function isPatternRelatedToParentDirectory(pattern) { + return pattern.startsWith('..') || pattern.startsWith('./..'); +} +exports.isPatternRelatedToParentDirectory = isPatternRelatedToParentDirectory; +function getBaseDirectory(pattern) { + return globParent(pattern, { flipBackslashes: false }); +} +exports.getBaseDirectory = getBaseDirectory; +function hasGlobStar(pattern) { + return pattern.includes(GLOBSTAR); +} +exports.hasGlobStar = hasGlobStar; +function endsWithSlashGlobStar(pattern) { + return pattern.endsWith('/' + GLOBSTAR); +} +exports.endsWithSlashGlobStar = endsWithSlashGlobStar; +function isAffectDepthOfReadingPattern(pattern) { + const basename = path.basename(pattern); + return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); +} +exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; +function expandPatternsWithBraceExpansion(patterns) { + return patterns.reduce((collection, pattern) => { + return collection.concat(expandBraceExpansion(pattern)); + }, []); +} +exports.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; +function expandBraceExpansion(pattern) { + return micromatch.braces(pattern, { + expand: true, + nodupes: true + }); +} +exports.expandBraceExpansion = expandBraceExpansion; +function getPatternParts(pattern, options) { + let { parts } = micromatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); + /** + * The scan method returns an empty array in some cases. + * See micromatch/picomatch#58 for more details. + */ + if (parts.length === 0) { + parts = [pattern]; + } + /** + * The scan method does not return an empty part for the pattern with a forward slash. + * This is another part of micromatch/picomatch#58. + */ + if (parts[0].startsWith('/')) { + parts[0] = parts[0].slice(1); + parts.unshift(''); + } + return parts; +} +exports.getPatternParts = getPatternParts; +function makeRe(pattern, options) { + return micromatch.makeRe(pattern, options); +} +exports.makeRe = makeRe; +function convertPatternsToRe(patterns, options) { + return patterns.map((pattern) => makeRe(pattern, options)); +} +exports.convertPatternsToRe = convertPatternsToRe; +function matchAny(entry, patternsRe) { + return patternsRe.some((patternRe) => patternRe.test(entry)); +} +exports.matchAny = matchAny; - return range; -}; -const fill = (start, end, step, options = {}) => { - if (end == null && isValidValue(start)) { - return [start]; - } +/***/ }), - if (!isValidValue(start) || !isValidValue(end)) { - return invalidRange(start, end, options); - } +/***/ "../../node_modules/fast-glob/out/utils/stream.js": +/***/ (function(module, exports, __webpack_require__) { - if (typeof step === 'function') { - return fill(start, end, 1, { transform: step }); - } +"use strict"; - if (isObject(step)) { - return fill(start, end, 0, step); - } +Object.defineProperty(exports, "__esModule", { value: true }); +exports.merge = void 0; +const merge2 = __webpack_require__("../../node_modules/merge2/index.js"); +function merge(streams) { + const mergedStream = merge2(streams); + streams.forEach((stream) => { + stream.once('error', (error) => mergedStream.emit('error', error)); + }); + mergedStream.once('close', () => propagateCloseEventToSources(streams)); + mergedStream.once('end', () => propagateCloseEventToSources(streams)); + return mergedStream; +} +exports.merge = merge; +function propagateCloseEventToSources(streams) { + streams.forEach((stream) => stream.emit('close')); +} - let opts = { ...options }; - if (opts.capture === true) opts.wrap = true; - step = step || opts.step || 1; - if (!isNumber(step)) { - if (step != null && !isObject(step)) return invalidStep(step, opts); - return fill(start, end, 1, step); - } +/***/ }), - if (isNumber(start) && isNumber(end)) { - return fillNumbers(start, end, step, opts); - } +/***/ "../../node_modules/fast-glob/out/utils/string.js": +/***/ (function(module, exports, __webpack_require__) { - return fillLetters(start, end, Math.max(Math.abs(step), 1), opts); -}; +"use strict"; -module.exports = fill; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isEmpty = exports.isString = void 0; +function isString(input) { + return typeof input === 'string'; +} +exports.isString = isString; +function isEmpty(input) { + return input === ''; +} +exports.isEmpty = isEmpty; /***/ }), -/***/ "../../node_modules/follow-redirects/debug.js": +/***/ "../../node_modules/fastq/queue.js": /***/ (function(module, exports, __webpack_require__) { -var debug; +"use strict"; -module.exports = function () { - if (!debug) { - try { - /* eslint global-require: off */ - debug = __webpack_require__("../../node_modules/debug/src/index.js")("follow-redirects"); - } - catch (error) { /* */ } - if (typeof debug !== "function") { - debug = function () { /* */ }; - } - } - debug.apply(null, arguments); -}; +var reusify = __webpack_require__("../../node_modules/reusify/reusify.js") -/***/ }), +function fastqueue (context, worker, concurrency) { + if (typeof context === 'function') { + concurrency = worker + worker = context + context = null + } -/***/ "../../node_modules/follow-redirects/index.js": -/***/ (function(module, exports, __webpack_require__) { + var cache = reusify(Task) + var queueHead = null + var queueTail = null + var _running = 0 -var url = __webpack_require__("url"); -var URL = url.URL; -var http = __webpack_require__("http"); -var https = __webpack_require__("https"); -var Writable = __webpack_require__("stream").Writable; -var assert = __webpack_require__("assert"); -var debug = __webpack_require__("../../node_modules/follow-redirects/debug.js"); - -// Create handlers that pass events from native requests -var events = ["abort", "aborted", "connect", "error", "socket", "timeout"]; -var eventHandlers = Object.create(null); -events.forEach(function (event) { - eventHandlers[event] = function (arg1, arg2, arg3) { - this._redirectable.emit(event, arg1, arg2, arg3); - }; -}); - -// Error types with codes -var RedirectionError = createErrorType( - "ERR_FR_REDIRECTION_FAILURE", - "Redirected request failed" -); -var TooManyRedirectsError = createErrorType( - "ERR_FR_TOO_MANY_REDIRECTS", - "Maximum number of redirects exceeded" -); -var MaxBodyLengthExceededError = createErrorType( - "ERR_FR_MAX_BODY_LENGTH_EXCEEDED", - "Request body larger than maxBodyLength limit" -); -var WriteAfterEndError = createErrorType( - "ERR_STREAM_WRITE_AFTER_END", - "write after end" -); + var self = { + push: push, + drain: noop, + saturated: noop, + pause: pause, + paused: false, + concurrency: concurrency, + running: running, + resume: resume, + idle: idle, + length: length, + unshift: unshift, + empty: noop, + kill: kill, + killAndDrain: killAndDrain + } -// An HTTP(S) request that can be redirected -function RedirectableRequest(options, responseCallback) { - // Initialize the request - Writable.call(this); - this._sanitizeOptions(options); - this._options = options; - this._ended = false; - this._ending = false; - this._redirectCount = 0; - this._redirects = []; - this._requestBodyLength = 0; - this._requestBodyBuffers = []; + return self - // Attach a callback if passed - if (responseCallback) { - this.on("response", responseCallback); + function running () { + return _running } - // React to responses of native requests - var self = this; - this._onNativeResponse = function (response) { - self._processResponse(response); - }; - - // Perform the first request - this._performRequest(); -} -RedirectableRequest.prototype = Object.create(Writable.prototype); + function pause () { + self.paused = true + } -RedirectableRequest.prototype.abort = function () { - abortRequest(this._currentRequest); - this.emit("abort"); -}; + function length () { + var current = queueHead + var counter = 0 -// Writes buffered data to the current native request -RedirectableRequest.prototype.write = function (data, encoding, callback) { - // Writing is not allowed if end has been called - if (this._ending) { - throw new WriteAfterEndError(); - } + while (current) { + current = current.next + counter++ + } - // Validate input and shift parameters if necessary - if (!(typeof data === "string" || typeof data === "object" && ("length" in data))) { - throw new TypeError("data should be a string, Buffer or Uint8Array"); - } - if (typeof encoding === "function") { - callback = encoding; - encoding = null; + return counter } - // Ignore empty buffers, since writing them doesn't invoke the callback - // https://github.com/nodejs/node/issues/22066 - if (data.length === 0) { - if (callback) { - callback(); + function resume () { + if (!self.paused) return + self.paused = false + for (var i = 0; i < self.concurrency; i++) { + _running++ + release() } - return; - } - // Only write when we don't exceed the maximum body length - if (this._requestBodyLength + data.length <= this._options.maxBodyLength) { - this._requestBodyLength += data.length; - this._requestBodyBuffers.push({ data: data, encoding: encoding }); - this._currentRequest.write(data, encoding, callback); - } - // Error when we exceed the maximum body length - else { - this.emit("error", new MaxBodyLengthExceededError()); - this.abort(); } -}; -// Ends the current native request -RedirectableRequest.prototype.end = function (data, encoding, callback) { - // Shift parameters if necessary - if (typeof data === "function") { - callback = data; - data = encoding = null; - } - else if (typeof encoding === "function") { - callback = encoding; - encoding = null; + function idle () { + return _running === 0 && self.length() === 0 } - // Write data if needed and end - if (!data) { - this._ended = this._ending = true; - this._currentRequest.end(null, null, callback); - } - else { - var self = this; - var currentRequest = this._currentRequest; - this.write(data, encoding, function () { - self._ended = true; - currentRequest.end(null, null, callback); - }); - this._ending = true; - } -}; + function push (value, done) { + var current = cache.get() -// Sets a header value on the current native request -RedirectableRequest.prototype.setHeader = function (name, value) { - this._options.headers[name] = value; - this._currentRequest.setHeader(name, value); -}; + current.context = context + current.release = release + current.value = value + current.callback = done || noop -// Clears a header value on the current native request -RedirectableRequest.prototype.removeHeader = function (name) { - delete this._options.headers[name]; - this._currentRequest.removeHeader(name); -}; + if (_running === self.concurrency || self.paused) { + if (queueTail) { + queueTail.next = current + queueTail = current + } else { + queueHead = current + queueTail = current + self.saturated() + } + } else { + _running++ + worker.call(context, current.value, current.worked) + } + } -// Global timeout for all underlying requests -RedirectableRequest.prototype.setTimeout = function (msecs, callback) { - var self = this; + function unshift (value, done) { + var current = cache.get() - // Destroys the socket on timeout - function destroyOnTimeout(socket) { - socket.setTimeout(msecs); - socket.removeListener("timeout", socket.destroy); - socket.addListener("timeout", socket.destroy); - } + current.context = context + current.release = release + current.value = value + current.callback = done || noop - // Sets up a timer to trigger a timeout event - function startTimer(socket) { - if (self._timeout) { - clearTimeout(self._timeout); + if (_running === self.concurrency || self.paused) { + if (queueHead) { + current.next = queueHead + queueHead = current + } else { + queueHead = current + queueTail = current + self.saturated() + } + } else { + _running++ + worker.call(context, current.value, current.worked) } - self._timeout = setTimeout(function () { - self.emit("timeout"); - clearTimer(); - }, msecs); - destroyOnTimeout(socket); } - // Stops a timeout from triggering - function clearTimer() { - // Clear the timeout - if (self._timeout) { - clearTimeout(self._timeout); - self._timeout = null; + function release (holder) { + if (holder) { + cache.release(holder) } - - // Clean up all attached listeners - self.removeListener("abort", clearTimer); - self.removeListener("error", clearTimer); - self.removeListener("response", clearTimer); - if (callback) { - self.removeListener("timeout", callback); - } - if (!self.socket) { - self._currentRequest.removeListener("socket", startTimer); + var next = queueHead + if (next) { + if (!self.paused) { + if (queueTail === queueHead) { + queueTail = null + } + queueHead = next.next + next.next = null + worker.call(context, next.value, next.worked) + if (queueTail === null) { + self.empty() + } + } else { + _running-- + } + } else if (--_running === 0) { + self.drain() } } - // Attach callback if passed - if (callback) { - this.on("timeout", callback); + function kill () { + queueHead = null + queueTail = null + self.drain = noop } - // Start the timer if or when the socket is opened - if (this.socket) { - startTimer(this.socket); + function killAndDrain () { + queueHead = null + queueTail = null + self.drain() + self.drain = noop } - else { - this._currentRequest.once("socket", startTimer); +} + +function noop () {} + +function Task () { + this.value = null + this.callback = noop + this.next = null + this.release = noop + this.context = null + + var self = this + + this.worked = function worked (err, result) { + var callback = self.callback + self.value = null + self.callback = noop + callback.call(self.context, err, result) + self.release(self) } +} - // Clean up on events - this.on("socket", destroyOnTimeout); - this.on("abort", clearTimer); - this.on("error", clearTimer); - this.on("response", clearTimer); +module.exports = fastqueue - return this; + +/***/ }), + +/***/ "../../node_modules/fill-range/index.js": +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * fill-range + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Licensed under the MIT License. + */ + + + +const util = __webpack_require__("util"); +const toRegexRange = __webpack_require__("../../node_modules/to-regex-range/index.js"); + +const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); + +const transform = toNumber => { + return value => toNumber === true ? Number(value) : String(value); }; -// Proxy all other public ClientRequest methods -[ - "flushHeaders", "getHeader", - "setNoDelay", "setSocketKeepAlive", -].forEach(function (method) { - RedirectableRequest.prototype[method] = function (a, b) { - return this._currentRequest[method](a, b); - }; -}); +const isValidValue = value => { + return typeof value === 'number' || (typeof value === 'string' && value !== ''); +}; -// Proxy all public ClientRequest properties -["aborted", "connection", "socket"].forEach(function (property) { - Object.defineProperty(RedirectableRequest.prototype, property, { - get: function () { return this._currentRequest[property]; }, - }); -}); +const isNumber = num => Number.isInteger(+num); -RedirectableRequest.prototype._sanitizeOptions = function (options) { - // Ensure headers are always present - if (!options.headers) { - options.headers = {}; +const zeros = input => { + let value = `${input}`; + let index = -1; + if (value[0] === '-') value = value.slice(1); + if (value === '0') return false; + while (value[++index] === '0'); + return index > 0; +}; + +const stringify = (start, end, options) => { + if (typeof start === 'string' || typeof end === 'string') { + return true; } + return options.stringify === true; +}; - // Since http.request treats host as an alias of hostname, - // but the url module interprets host as hostname plus port, - // eliminate the host property to avoid confusion. - if (options.host) { - // Use hostname if set, because it has precedence - if (!options.hostname) { - options.hostname = options.host; - } - delete options.host; +const pad = (input, maxLength, toNumber) => { + if (maxLength > 0) { + let dash = input[0] === '-' ? '-' : ''; + if (dash) input = input.slice(1); + input = (dash + input.padStart(dash ? maxLength - 1 : maxLength, '0')); } + if (toNumber === false) { + return String(input); + } + return input; +}; - // Complete the URL object when necessary - if (!options.pathname && options.path) { - var searchPos = options.path.indexOf("?"); - if (searchPos < 0) { - options.pathname = options.path; - } - else { - options.pathname = options.path.substring(0, searchPos); - options.search = options.path.substring(searchPos); - } +const toMaxLen = (input, maxLength) => { + let negative = input[0] === '-' ? '-' : ''; + if (negative) { + input = input.slice(1); + maxLength--; } + while (input.length < maxLength) input = '0' + input; + return negative ? ('-' + input) : input; }; +const toSequence = (parts, options) => { + parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); + parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); -// Executes the next native request (initial or redirect) -RedirectableRequest.prototype._performRequest = function () { - // Load the native protocol - var protocol = this._options.protocol; - var nativeProtocol = this._options.nativeProtocols[protocol]; - if (!nativeProtocol) { - this.emit("error", new TypeError("Unsupported protocol " + protocol)); - return; - } + let prefix = options.capture ? '' : '?:'; + let positives = ''; + let negatives = ''; + let result; - // If specified, use the agent corresponding to the protocol - // (HTTP and HTTPS use different types of agents) - if (this._options.agents) { - var scheme = protocol.slice(0, -1); - this._options.agent = this._options.agents[scheme]; + if (parts.positives.length) { + positives = parts.positives.join('|'); } - // Create the native request - var request = this._currentRequest = - nativeProtocol.request(this._options, this._onNativeResponse); - this._currentUrl = url.format(this._options); + if (parts.negatives.length) { + negatives = `-(${prefix}${parts.negatives.join('|')})`; + } - // Set up event handlers - request._redirectable = this; - for (var e = 0; e < events.length; e++) { - request.on(events[e], eventHandlers[events[e]]); + if (positives && negatives) { + result = `${positives}|${negatives}`; + } else { + result = positives || negatives; } - // End a redirected request - // (The first request must be ended explicitly with RedirectableRequest#end) - if (this._isRedirect) { - // Write the request entity and end. - var i = 0; - var self = this; - var buffers = this._requestBodyBuffers; - (function writeNext(error) { - // Only write if this request has not been redirected yet - /* istanbul ignore else */ - if (request === self._currentRequest) { - // Report any write errors - /* istanbul ignore if */ - if (error) { - self.emit("error", error); - } - // Write the next buffer if there are still left - else if (i < buffers.length) { - var buffer = buffers[i++]; - /* istanbul ignore else */ - if (!request.finished) { - request.write(buffer.data, buffer.encoding, writeNext); - } - } - // End the request if `end` has been called on us - else if (self._ended) { - request.end(); - } - } - }()); + if (options.wrap) { + return `(${prefix}${result})`; } + + return result; }; -// Processes a response from the current native request -RedirectableRequest.prototype._processResponse = function (response) { - // Store the redirected response - var statusCode = response.statusCode; - if (this._options.trackRedirects) { - this._redirects.push({ - url: this._currentUrl, - headers: response.headers, - statusCode: statusCode, - }); +const toRange = (a, b, isNumbers, options) => { + if (isNumbers) { + return toRegexRange(a, b, { wrap: false, ...options }); } - // RFC7231§6.4: The 3xx (Redirection) class of status code indicates - // that further action needs to be taken by the user agent in order to - // fulfill the request. If a Location header field is provided, - // the user agent MAY automatically redirect its request to the URI - // referenced by the Location field value, - // even if the specific status code is not understood. + let start = String.fromCharCode(a); + if (a === b) return start; - // If the response is not a redirect; return it as-is - var location = response.headers.location; - if (!location || this._options.followRedirects === false || - statusCode < 300 || statusCode >= 400) { - response.responseUrl = this._currentUrl; - response.redirects = this._redirects; - this.emit("response", response); + let stop = String.fromCharCode(b); + return `[${start}-${stop}]`; +}; - // Clean up - this._requestBodyBuffers = []; - return; +const toRegex = (start, end, options) => { + if (Array.isArray(start)) { + let wrap = options.wrap === true; + let prefix = options.capture ? '' : '?:'; + return wrap ? `(${prefix}${start.join('|')})` : start.join('|'); } + return toRegexRange(start, end, options); +}; - // The response is a redirect, so abort the current request - abortRequest(this._currentRequest); - // Discard the remainder of the response to avoid waiting for data - response.destroy(); +const rangeError = (...args) => { + return new RangeError('Invalid range arguments: ' + util.inspect(...args)); +}; - // RFC7231§6.4: A client SHOULD detect and intervene - // in cyclical redirections (i.e., "infinite" redirection loops). - if (++this._redirectCount > this._options.maxRedirects) { - this.emit("error", new TooManyRedirectsError()); - return; - } +const invalidRange = (start, end, options) => { + if (options.strictRanges === true) throw rangeError([start, end]); + return []; +}; - // Store the request headers if applicable - var requestHeaders; - var beforeRedirect = this._options.beforeRedirect; - if (beforeRedirect) { - requestHeaders = Object.assign({ - // The Host header was set by nativeProtocol.request - Host: response.req.getHeader("host"), - }, this._options.headers); +const invalidStep = (step, options) => { + if (options.strictRanges === true) { + throw new TypeError(`Expected step "${step}" to be a number`); } + return []; +}; - // RFC7231§6.4: Automatic redirection needs to done with - // care for methods not known to be safe, […] - // RFC7231§6.4.2–3: For historical reasons, a user agent MAY change - // the request method from POST to GET for the subsequent request. - var method = this._options.method; - if ((statusCode === 301 || statusCode === 302) && this._options.method === "POST" || - // RFC7231§6.4.4: The 303 (See Other) status code indicates that - // the server is redirecting the user agent to a different resource […] - // A user agent can perform a retrieval request targeting that URI - // (a GET or HEAD request if using HTTP) […] - (statusCode === 303) && !/^(?:GET|HEAD)$/.test(this._options.method)) { - this._options.method = "GET"; - // Drop a possible entity and headers related to it - this._requestBodyBuffers = []; - removeMatchingHeaders(/^content-/i, this._options.headers); - } +const fillNumbers = (start, end, step = 1, options = {}) => { + let a = Number(start); + let b = Number(end); - // Drop the Host header, as the redirect might lead to a different host - var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers); + if (!Number.isInteger(a) || !Number.isInteger(b)) { + if (options.strictRanges === true) throw rangeError([start, end]); + return []; + } - // If the redirect is relative, carry over the host of the last request - var currentUrlParts = url.parse(this._currentUrl); - var currentHost = currentHostHeader || currentUrlParts.host; - var currentUrl = /^\w+:/.test(location) ? this._currentUrl : - url.format(Object.assign(currentUrlParts, { host: currentHost })); + // fix negative zero + if (a === 0) a = 0; + if (b === 0) b = 0; - // Determine the URL of the redirection - var redirectUrl; - try { - redirectUrl = url.resolve(currentUrl, location); - } - catch (cause) { - this.emit("error", new RedirectionError(cause)); - return; - } + let descending = a > b; + let startString = String(start); + let endString = String(end); + let stepString = String(step); + step = Math.max(Math.abs(step), 1); - // Create the redirected request - debug("redirecting to", redirectUrl); - this._isRedirect = true; - var redirectUrlParts = url.parse(redirectUrl); - Object.assign(this._options, redirectUrlParts); + let padded = zeros(startString) || zeros(endString) || zeros(stepString); + let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0; + let toNumber = padded === false && stringify(start, end, options) === false; + let format = options.transform || transform(toNumber); - // Drop confidential headers when redirecting to a less secure protocol - // or to a different domain that is not a superdomain - if (redirectUrlParts.protocol !== currentUrlParts.protocol && - redirectUrlParts.protocol !== "https:" || - redirectUrlParts.host !== currentHost && - !isSubdomain(redirectUrlParts.host, currentHost)) { - removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers); + if (options.toRegex && step === 1) { + return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options); } - // Evaluate the beforeRedirect callback - if (typeof beforeRedirect === "function") { - var responseDetails = { - headers: response.headers, - statusCode: statusCode, - }; - var requestDetails = { - url: currentUrl, - method: method, - headers: requestHeaders, - }; - try { - beforeRedirect(this._options, responseDetails, requestDetails); - } - catch (err) { - this.emit("error", err); - return; + let parts = { negatives: [], positives: [] }; + let push = num => parts[num < 0 ? 'negatives' : 'positives'].push(Math.abs(num)); + let range = []; + let index = 0; + + while (descending ? a >= b : a <= b) { + if (options.toRegex === true && step > 1) { + push(a); + } else { + range.push(pad(format(a, index), maxLen, toNumber)); } - this._sanitizeOptions(this._options); + a = descending ? a - step : a + step; + index++; } - // Perform the redirected request - try { - this._performRequest(); - } - catch (cause) { - this.emit("error", new RedirectionError(cause)); + if (options.toRegex === true) { + return step > 1 + ? toSequence(parts, options) + : toRegex(range, null, { wrap: false, ...options }); } + + return range; }; -// Wraps the key/value object of protocols with redirect functionality -function wrap(protocols) { - // Default settings - var exports = { - maxRedirects: 21, - maxBodyLength: 10 * 1024 * 1024, - }; +const fillLetters = (start, end, step = 1, options = {}) => { + if ((!isNumber(start) && start.length > 1) || (!isNumber(end) && end.length > 1)) { + return invalidRange(start, end, options); + } - // Wrap each protocol - var nativeProtocols = {}; - Object.keys(protocols).forEach(function (scheme) { - var protocol = scheme + ":"; - var nativeProtocol = nativeProtocols[protocol] = protocols[scheme]; - var wrappedProtocol = exports[scheme] = Object.create(nativeProtocol); - // Executes a request, following redirects - function request(input, options, callback) { - // Parse parameters - if (typeof input === "string") { - var urlStr = input; - try { - input = urlToOptions(new URL(urlStr)); - } - catch (err) { - /* istanbul ignore next */ - input = url.parse(urlStr); - } - } - else if (URL && (input instanceof URL)) { - input = urlToOptions(input); - } - else { - callback = options; - options = input; - input = { protocol: protocol }; - } - if (typeof options === "function") { - callback = options; - options = null; - } + let format = options.transform || (val => String.fromCharCode(val)); + let a = `${start}`.charCodeAt(0); + let b = `${end}`.charCodeAt(0); - // Set defaults - options = Object.assign({ - maxRedirects: exports.maxRedirects, - maxBodyLength: exports.maxBodyLength, - }, input, options); - options.nativeProtocols = nativeProtocols; + let descending = a > b; + let min = Math.min(a, b); + let max = Math.max(a, b); - assert.equal(options.protocol, protocol, "protocol mismatch"); - debug("options", options); - return new RedirectableRequest(options, callback); - } + if (options.toRegex && step === 1) { + return toRange(min, max, false, options); + } - // Executes a GET request, following redirects - function get(input, options, callback) { - var wrappedRequest = wrappedProtocol.request(input, options, callback); - wrappedRequest.end(); - return wrappedRequest; - } + let range = []; + let index = 0; - // Expose the properties on the wrapped protocol - Object.defineProperties(wrappedProtocol, { - request: { value: request, configurable: true, enumerable: true, writable: true }, - get: { value: get, configurable: true, enumerable: true, writable: true }, - }); - }); - return exports; -} + while (descending ? a >= b : a <= b) { + range.push(format(a, index)); + a = descending ? a - step : a + step; + index++; + } -/* istanbul ignore next */ -function noop() { /* empty */ } + if (options.toRegex === true) { + return toRegex(range, null, { wrap: false, options }); + } -// from https://github.com/nodejs/node/blob/master/lib/internal/url.js -function urlToOptions(urlObject) { - var options = { - protocol: urlObject.protocol, - hostname: urlObject.hostname.startsWith("[") ? - /* istanbul ignore next */ - urlObject.hostname.slice(1, -1) : - urlObject.hostname, - hash: urlObject.hash, - search: urlObject.search, - pathname: urlObject.pathname, - path: urlObject.pathname + urlObject.search, - href: urlObject.href, - }; - if (urlObject.port !== "") { - options.port = Number(urlObject.port); + return range; +}; + +const fill = (start, end, step, options = {}) => { + if (end == null && isValidValue(start)) { + return [start]; } - return options; -} -function removeMatchingHeaders(regex, headers) { - var lastValue; - for (var header in headers) { - if (regex.test(header)) { - lastValue = headers[header]; - delete headers[header]; - } + if (!isValidValue(start) || !isValidValue(end)) { + return invalidRange(start, end, options); } - return (lastValue === null || typeof lastValue === "undefined") ? - undefined : String(lastValue).trim(); -} -function createErrorType(code, defaultMessage) { - function CustomError(cause) { - Error.captureStackTrace(this, this.constructor); - if (!cause) { - this.message = defaultMessage; - } - else { - this.message = defaultMessage + ": " + cause.message; - this.cause = cause; - } + if (typeof step === 'function') { + return fill(start, end, 1, { transform: step }); } - CustomError.prototype = new Error(); - CustomError.prototype.constructor = CustomError; - CustomError.prototype.name = "Error [" + code + "]"; - CustomError.prototype.code = code; - return CustomError; -} -function abortRequest(request) { - for (var e = 0; e < events.length; e++) { - request.removeListener(events[e], eventHandlers[events[e]]); + if (isObject(step)) { + return fill(start, end, 0, step); } - request.on("error", noop); - request.abort(); -} - -function isSubdomain(subdomain, domain) { - const dot = subdomain.length - domain.length - 1; - return dot > 0 && subdomain[dot] === "." && subdomain.endsWith(domain); -} - -// Exports -module.exports = wrap({ http: http, https: https }); -module.exports.wrap = wrap; + let opts = { ...options }; + if (opts.capture === true) opts.wrap = true; + step = step || opts.step || 1; -/***/ }), + if (!isNumber(step)) { + if (step != null && !isObject(step)) return invalidStep(step, opts); + return fill(start, end, 1, step); + } -/***/ "../../node_modules/form-data/lib/form_data.js": -/***/ (function(module, exports, __webpack_require__) { + if (isNumber(start) && isNumber(end)) { + return fillNumbers(start, end, step, opts); + } -var CombinedStream = __webpack_require__("../../node_modules/combined-stream/lib/combined_stream.js"); -var util = __webpack_require__("util"); -var path = __webpack_require__("path"); -var http = __webpack_require__("http"); -var https = __webpack_require__("https"); -var parseUrl = __webpack_require__("url").parse; -var fs = __webpack_require__("fs"); -var Stream = __webpack_require__("stream").Stream; -var mime = __webpack_require__("../../node_modules/mime-types/index.js"); -var asynckit = __webpack_require__("../../node_modules/asynckit/index.js"); -var populate = __webpack_require__("../../node_modules/form-data/lib/populate.js"); + return fillLetters(start, end, Math.max(Math.abs(step), 1), opts); +}; -// Public API -module.exports = FormData; +module.exports = fill; -// make it a Stream -util.inherits(FormData, CombinedStream); -/** - * Create readable "multipart/form-data" streams. - * Can be used to submit forms - * and file uploads to other web applications. - * - * @constructor - * @param {Object} options - Properties to be added/overriden for FormData and CombinedStream - */ -function FormData(options) { - if (!(this instanceof FormData)) { - return new FormData(options); - } +/***/ }), - this._overheadLength = 0; - this._valueLength = 0; - this._valuesToMeasure = []; +/***/ "../../node_modules/follow-redirects/debug.js": +/***/ (function(module, exports, __webpack_require__) { - CombinedStream.call(this); +var debug; - options = options || {}; - for (var option in options) { - this[option] = options[option]; +module.exports = function () { + if (!debug) { + try { + /* eslint global-require: off */ + debug = __webpack_require__("../../node_modules/debug/src/index.js")("follow-redirects"); + } + catch (error) { /* */ } + if (typeof debug !== "function") { + debug = function () { /* */ }; + } } -} + debug.apply(null, arguments); +}; -FormData.LINE_BREAK = '\r\n'; -FormData.DEFAULT_CONTENT_TYPE = 'application/octet-stream'; -FormData.prototype.append = function(field, value, options) { +/***/ }), - options = options || {}; +/***/ "../../node_modules/follow-redirects/index.js": +/***/ (function(module, exports, __webpack_require__) { - // allow filename as single option - if (typeof options == 'string') { - options = {filename: options}; - } +var url = __webpack_require__("url"); +var URL = url.URL; +var http = __webpack_require__("http"); +var https = __webpack_require__("https"); +var Writable = __webpack_require__("stream").Writable; +var assert = __webpack_require__("assert"); +var debug = __webpack_require__("../../node_modules/follow-redirects/debug.js"); - var append = CombinedStream.prototype.append.bind(this); +// Create handlers that pass events from native requests +var events = ["abort", "aborted", "connect", "error", "socket", "timeout"]; +var eventHandlers = Object.create(null); +events.forEach(function (event) { + eventHandlers[event] = function (arg1, arg2, arg3) { + this._redirectable.emit(event, arg1, arg2, arg3); + }; +}); - // all that streamy business can't handle numbers - if (typeof value == 'number') { - value = '' + value; - } +// Error types with codes +var RedirectionError = createErrorType( + "ERR_FR_REDIRECTION_FAILURE", + "Redirected request failed" +); +var TooManyRedirectsError = createErrorType( + "ERR_FR_TOO_MANY_REDIRECTS", + "Maximum number of redirects exceeded" +); +var MaxBodyLengthExceededError = createErrorType( + "ERR_FR_MAX_BODY_LENGTH_EXCEEDED", + "Request body larger than maxBodyLength limit" +); +var WriteAfterEndError = createErrorType( + "ERR_STREAM_WRITE_AFTER_END", + "write after end" +); - // https://github.com/felixge/node-form-data/issues/38 - if (util.isArray(value)) { - // Please convert your array into string - // the way web server expects it - this._error(new Error('Arrays are not supported.')); - return; +// An HTTP(S) request that can be redirected +function RedirectableRequest(options, responseCallback) { + // Initialize the request + Writable.call(this); + this._sanitizeOptions(options); + this._options = options; + this._ended = false; + this._ending = false; + this._redirectCount = 0; + this._redirects = []; + this._requestBodyLength = 0; + this._requestBodyBuffers = []; + + // Attach a callback if passed + if (responseCallback) { + this.on("response", responseCallback); } - var header = this._multiPartHeader(field, value, options); - var footer = this._multiPartFooter(); + // React to responses of native requests + var self = this; + this._onNativeResponse = function (response) { + self._processResponse(response); + }; - append(header); - append(value); - append(footer); + // Perform the first request + this._performRequest(); +} +RedirectableRequest.prototype = Object.create(Writable.prototype); - // pass along options.knownLength - this._trackLength(header, value, options); +RedirectableRequest.prototype.abort = function () { + abortRequest(this._currentRequest); + this.emit("abort"); }; -FormData.prototype._trackLength = function(header, value, options) { - var valueLength = 0; - - // used w/ getLengthSync(), when length is known. - // e.g. for streaming directly from a remote server, - // w/ a known file a size, and not wanting to wait for - // incoming file to finish to get its size. - if (options.knownLength != null) { - valueLength += +options.knownLength; - } else if (Buffer.isBuffer(value)) { - valueLength = value.length; - } else if (typeof value === 'string') { - valueLength = Buffer.byteLength(value); +// Writes buffered data to the current native request +RedirectableRequest.prototype.write = function (data, encoding, callback) { + // Writing is not allowed if end has been called + if (this._ending) { + throw new WriteAfterEndError(); } - this._valueLength += valueLength; - - // @check why add CRLF? does this account for custom/multiple CRLFs? - this._overheadLength += - Buffer.byteLength(header) + - FormData.LINE_BREAK.length; + // Validate input and shift parameters if necessary + if (!(typeof data === "string" || typeof data === "object" && ("length" in data))) { + throw new TypeError("data should be a string, Buffer or Uint8Array"); + } + if (typeof encoding === "function") { + callback = encoding; + encoding = null; + } - // empty or either doesn't have path or not an http response or not a stream - if (!value || ( !value.path && !(value.readable && value.hasOwnProperty('httpVersion')) && !(value instanceof Stream))) { + // Ignore empty buffers, since writing them doesn't invoke the callback + // https://github.com/nodejs/node/issues/22066 + if (data.length === 0) { + if (callback) { + callback(); + } return; } - - // no need to bother with the length - if (!options.knownLength) { - this._valuesToMeasure.push(value); + // Only write when we don't exceed the maximum body length + if (this._requestBodyLength + data.length <= this._options.maxBodyLength) { + this._requestBodyLength += data.length; + this._requestBodyBuffers.push({ data: data, encoding: encoding }); + this._currentRequest.write(data, encoding, callback); + } + // Error when we exceed the maximum body length + else { + this.emit("error", new MaxBodyLengthExceededError()); + this.abort(); } }; -FormData.prototype._lengthRetriever = function(value, callback) { - - if (value.hasOwnProperty('fd')) { - - // take read range into a account - // `end` = Infinity –> read file till the end - // - // TODO: Looks like there is bug in Node fs.createReadStream - // it doesn't respect `end` options without `start` options - // Fix it when node fixes it. - // https://github.com/joyent/node/issues/7819 - if (value.end != undefined && value.end != Infinity && value.start != undefined) { - - // when end specified - // no need to calculate range - // inclusive, starts with 0 - callback(null, value.end + 1 - (value.start ? value.start : 0)); - - // not that fast snoopy - } else { - // still need to fetch file size from fs - fs.stat(value.path, function(err, stat) { - - var fileSize; - - if (err) { - callback(err); - return; - } - - // update final size based on the range options - fileSize = stat.size - (value.start ? value.start : 0); - callback(null, fileSize); - }); - } - - // or http response - } else if (value.hasOwnProperty('httpVersion')) { - callback(null, +value.headers['content-length']); +// Ends the current native request +RedirectableRequest.prototype.end = function (data, encoding, callback) { + // Shift parameters if necessary + if (typeof data === "function") { + callback = data; + data = encoding = null; + } + else if (typeof encoding === "function") { + callback = encoding; + encoding = null; + } - // or request stream http://github.com/mikeal/request - } else if (value.hasOwnProperty('httpModule')) { - // wait till response come back - value.on('response', function(response) { - value.pause(); - callback(null, +response.headers['content-length']); + // Write data if needed and end + if (!data) { + this._ended = this._ending = true; + this._currentRequest.end(null, null, callback); + } + else { + var self = this; + var currentRequest = this._currentRequest; + this.write(data, encoding, function () { + self._ended = true; + currentRequest.end(null, null, callback); }); - value.resume(); - - // something else - } else { - callback('Unknown stream'); + this._ending = true; } }; -FormData.prototype._multiPartHeader = function(field, value, options) { - // custom header specified (as string)? - // it becomes responsible for boundary - // (e.g. to handle extra CRLFs on .NET servers) - if (typeof options.header == 'string') { - return options.header; - } +// Sets a header value on the current native request +RedirectableRequest.prototype.setHeader = function (name, value) { + this._options.headers[name] = value; + this._currentRequest.setHeader(name, value); +}; - var contentDisposition = this._getContentDisposition(value, options); - var contentType = this._getContentType(value, options); +// Clears a header value on the current native request +RedirectableRequest.prototype.removeHeader = function (name) { + delete this._options.headers[name]; + this._currentRequest.removeHeader(name); +}; - var contents = ''; - var headers = { - // add custom disposition as third element or keep it two elements if not - 'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []), - // if no content type. allow it to be empty array - 'Content-Type': [].concat(contentType || []) - }; +// Global timeout for all underlying requests +RedirectableRequest.prototype.setTimeout = function (msecs, callback) { + var self = this; - // allow custom headers. - if (typeof options.header == 'object') { - populate(headers, options.header); + // Destroys the socket on timeout + function destroyOnTimeout(socket) { + socket.setTimeout(msecs); + socket.removeListener("timeout", socket.destroy); + socket.addListener("timeout", socket.destroy); } - var header; - for (var prop in headers) { - if (!headers.hasOwnProperty(prop)) continue; - header = headers[prop]; - - // skip nullish headers. - if (header == null) { - continue; + // Sets up a timer to trigger a timeout event + function startTimer(socket) { + if (self._timeout) { + clearTimeout(self._timeout); } + self._timeout = setTimeout(function () { + self.emit("timeout"); + clearTimer(); + }, msecs); + destroyOnTimeout(socket); + } - // convert all headers to arrays. - if (!Array.isArray(header)) { - header = [header]; + // Stops a timeout from triggering + function clearTimer() { + // Clear the timeout + if (self._timeout) { + clearTimeout(self._timeout); + self._timeout = null; } - // add non-empty headers. - if (header.length) { - contents += prop + ': ' + header.join('; ') + FormData.LINE_BREAK; + // Clean up all attached listeners + self.removeListener("abort", clearTimer); + self.removeListener("error", clearTimer); + self.removeListener("response", clearTimer); + if (callback) { + self.removeListener("timeout", callback); + } + if (!self.socket) { + self._currentRequest.removeListener("socket", startTimer); } } - return '--' + this.getBoundary() + FormData.LINE_BREAK + contents + FormData.LINE_BREAK; -}; - -FormData.prototype._getContentDisposition = function(value, options) { - - var filename - , contentDisposition - ; - - if (typeof options.filepath === 'string') { - // custom filepath for relative paths - filename = path.normalize(options.filepath).replace(/\\/g, '/'); - } else if (options.filename || value.name || value.path) { - // custom filename take precedence - // formidable and the browser add a name property - // fs- and request- streams have path property - filename = path.basename(options.filename || value.name || value.path); - } else if (value.readable && value.hasOwnProperty('httpVersion')) { - // or try http response - filename = path.basename(value.client._httpMessage.path || ''); + // Attach callback if passed + if (callback) { + this.on("timeout", callback); } - if (filename) { - contentDisposition = 'filename="' + filename + '"'; + // Start the timer if or when the socket is opened + if (this.socket) { + startTimer(this.socket); + } + else { + this._currentRequest.once("socket", startTimer); } - return contentDisposition; + // Clean up on events + this.on("socket", destroyOnTimeout); + this.on("abort", clearTimer); + this.on("error", clearTimer); + this.on("response", clearTimer); + + return this; }; -FormData.prototype._getContentType = function(value, options) { +// Proxy all other public ClientRequest methods +[ + "flushHeaders", "getHeader", + "setNoDelay", "setSocketKeepAlive", +].forEach(function (method) { + RedirectableRequest.prototype[method] = function (a, b) { + return this._currentRequest[method](a, b); + }; +}); - // use custom content-type above all - var contentType = options.contentType; +// Proxy all public ClientRequest properties +["aborted", "connection", "socket"].forEach(function (property) { + Object.defineProperty(RedirectableRequest.prototype, property, { + get: function () { return this._currentRequest[property]; }, + }); +}); - // or try `name` from formidable, browser - if (!contentType && value.name) { - contentType = mime.lookup(value.name); +RedirectableRequest.prototype._sanitizeOptions = function (options) { + // Ensure headers are always present + if (!options.headers) { + options.headers = {}; } - // or try `path` from fs-, request- streams - if (!contentType && value.path) { - contentType = mime.lookup(value.path); + // Since http.request treats host as an alias of hostname, + // but the url module interprets host as hostname plus port, + // eliminate the host property to avoid confusion. + if (options.host) { + // Use hostname if set, because it has precedence + if (!options.hostname) { + options.hostname = options.host; + } + delete options.host; } - // or if it's http-reponse - if (!contentType && value.readable && value.hasOwnProperty('httpVersion')) { - contentType = value.headers['content-type']; + // Complete the URL object when necessary + if (!options.pathname && options.path) { + var searchPos = options.path.indexOf("?"); + if (searchPos < 0) { + options.pathname = options.path; + } + else { + options.pathname = options.path.substring(0, searchPos); + options.search = options.path.substring(searchPos); + } } +}; - // or guess it from the filepath or filename - if (!contentType && (options.filepath || options.filename)) { - contentType = mime.lookup(options.filepath || options.filename); - } - // fallback to the default content type if `value` is not simple value - if (!contentType && typeof value == 'object') { - contentType = FormData.DEFAULT_CONTENT_TYPE; +// Executes the next native request (initial or redirect) +RedirectableRequest.prototype._performRequest = function () { + // Load the native protocol + var protocol = this._options.protocol; + var nativeProtocol = this._options.nativeProtocols[protocol]; + if (!nativeProtocol) { + this.emit("error", new TypeError("Unsupported protocol " + protocol)); + return; } - return contentType; -}; + // If specified, use the agent corresponding to the protocol + // (HTTP and HTTPS use different types of agents) + if (this._options.agents) { + var scheme = protocol.slice(0, -1); + this._options.agent = this._options.agents[scheme]; + } -FormData.prototype._multiPartFooter = function() { - return function(next) { - var footer = FormData.LINE_BREAK; - - var lastPart = (this._streams.length === 0); - if (lastPart) { - footer += this._lastBoundary(); - } - - next(footer); - }.bind(this); -}; - -FormData.prototype._lastBoundary = function() { - return '--' + this.getBoundary() + '--' + FormData.LINE_BREAK; -}; - -FormData.prototype.getHeaders = function(userHeaders) { - var header; - var formHeaders = { - 'content-type': 'multipart/form-data; boundary=' + this.getBoundary() - }; + // Create the native request + var request = this._currentRequest = + nativeProtocol.request(this._options, this._onNativeResponse); + this._currentUrl = url.format(this._options); - for (header in userHeaders) { - if (userHeaders.hasOwnProperty(header)) { - formHeaders[header.toLowerCase()] = userHeaders[header]; - } + // Set up event handlers + request._redirectable = this; + for (var e = 0; e < events.length; e++) { + request.on(events[e], eventHandlers[events[e]]); } - return formHeaders; -}; - -FormData.prototype.setBoundary = function(boundary) { - this._boundary = boundary; -}; - -FormData.prototype.getBoundary = function() { - if (!this._boundary) { - this._generateBoundary(); + // End a redirected request + // (The first request must be ended explicitly with RedirectableRequest#end) + if (this._isRedirect) { + // Write the request entity and end. + var i = 0; + var self = this; + var buffers = this._requestBodyBuffers; + (function writeNext(error) { + // Only write if this request has not been redirected yet + /* istanbul ignore else */ + if (request === self._currentRequest) { + // Report any write errors + /* istanbul ignore if */ + if (error) { + self.emit("error", error); + } + // Write the next buffer if there are still left + else if (i < buffers.length) { + var buffer = buffers[i++]; + /* istanbul ignore else */ + if (!request.finished) { + request.write(buffer.data, buffer.encoding, writeNext); + } + } + // End the request if `end` has been called on us + else if (self._ended) { + request.end(); + } + } + }()); } - - return this._boundary; }; -FormData.prototype.getBuffer = function() { - var dataBuffer = new Buffer.alloc( 0 ); - var boundary = this.getBoundary(); +// Processes a response from the current native request +RedirectableRequest.prototype._processResponse = function (response) { + // Store the redirected response + var statusCode = response.statusCode; + if (this._options.trackRedirects) { + this._redirects.push({ + url: this._currentUrl, + headers: response.headers, + statusCode: statusCode, + }); + } - // Create the form content. Add Line breaks to the end of data. - for (var i = 0, len = this._streams.length; i < len; i++) { - if (typeof this._streams[i] !== 'function') { + // RFC7231§6.4: The 3xx (Redirection) class of status code indicates + // that further action needs to be taken by the user agent in order to + // fulfill the request. If a Location header field is provided, + // the user agent MAY automatically redirect its request to the URI + // referenced by the Location field value, + // even if the specific status code is not understood. - // Add content to the buffer. - if(Buffer.isBuffer(this._streams[i])) { - dataBuffer = Buffer.concat( [dataBuffer, this._streams[i]]); - }else { - dataBuffer = Buffer.concat( [dataBuffer, Buffer.from(this._streams[i])]); - } + // If the response is not a redirect; return it as-is + var location = response.headers.location; + if (!location || this._options.followRedirects === false || + statusCode < 300 || statusCode >= 400) { + response.responseUrl = this._currentUrl; + response.redirects = this._redirects; + this.emit("response", response); - // Add break after content. - if (typeof this._streams[i] !== 'string' || this._streams[i].substring( 2, boundary.length + 2 ) !== boundary) { - dataBuffer = Buffer.concat( [dataBuffer, Buffer.from(FormData.LINE_BREAK)] ); - } - } + // Clean up + this._requestBodyBuffers = []; + return; } - // Add the footer and return the Buffer object. - return Buffer.concat( [dataBuffer, Buffer.from(this._lastBoundary())] ); -}; + // The response is a redirect, so abort the current request + abortRequest(this._currentRequest); + // Discard the remainder of the response to avoid waiting for data + response.destroy(); -FormData.prototype._generateBoundary = function() { - // This generates a 50 character boundary similar to those used by Firefox. - // They are optimized for boyer-moore parsing. - var boundary = '--------------------------'; - for (var i = 0; i < 24; i++) { - boundary += Math.floor(Math.random() * 10).toString(16); + // RFC7231§6.4: A client SHOULD detect and intervene + // in cyclical redirections (i.e., "infinite" redirection loops). + if (++this._redirectCount > this._options.maxRedirects) { + this.emit("error", new TooManyRedirectsError()); + return; } - this._boundary = boundary; -}; - -// Note: getLengthSync DOESN'T calculate streams length -// As workaround one can calculate file size manually -// and add it as knownLength option -FormData.prototype.getLengthSync = function() { - var knownLength = this._overheadLength + this._valueLength; - - // Don't get confused, there are 3 "internal" streams for each keyval pair - // so it basically checks if there is any value added to the form - if (this._streams.length) { - knownLength += this._lastBoundary().length; + // Store the request headers if applicable + var requestHeaders; + var beforeRedirect = this._options.beforeRedirect; + if (beforeRedirect) { + requestHeaders = Object.assign({ + // The Host header was set by nativeProtocol.request + Host: response.req.getHeader("host"), + }, this._options.headers); } - // https://github.com/form-data/form-data/issues/40 - if (!this.hasKnownLength()) { - // Some async length retrievers are present - // therefore synchronous length calculation is false. - // Please use getLength(callback) to get proper length - this._error(new Error('Cannot calculate proper length in synchronous way.')); + // RFC7231§6.4: Automatic redirection needs to done with + // care for methods not known to be safe, […] + // RFC7231§6.4.2–3: For historical reasons, a user agent MAY change + // the request method from POST to GET for the subsequent request. + var method = this._options.method; + if ((statusCode === 301 || statusCode === 302) && this._options.method === "POST" || + // RFC7231§6.4.4: The 303 (See Other) status code indicates that + // the server is redirecting the user agent to a different resource […] + // A user agent can perform a retrieval request targeting that URI + // (a GET or HEAD request if using HTTP) […] + (statusCode === 303) && !/^(?:GET|HEAD)$/.test(this._options.method)) { + this._options.method = "GET"; + // Drop a possible entity and headers related to it + this._requestBodyBuffers = []; + removeMatchingHeaders(/^content-/i, this._options.headers); } - return knownLength; -}; + // Drop the Host header, as the redirect might lead to a different host + var currentHostHeader = removeMatchingHeaders(/^host$/i, this._options.headers); -// Public API to check if length of added values is known -// https://github.com/form-data/form-data/issues/196 -// https://github.com/form-data/form-data/issues/262 -FormData.prototype.hasKnownLength = function() { - var hasKnownLength = true; + // If the redirect is relative, carry over the host of the last request + var currentUrlParts = url.parse(this._currentUrl); + var currentHost = currentHostHeader || currentUrlParts.host; + var currentUrl = /^\w+:/.test(location) ? this._currentUrl : + url.format(Object.assign(currentUrlParts, { host: currentHost })); - if (this._valuesToMeasure.length) { - hasKnownLength = false; + // Determine the URL of the redirection + var redirectUrl; + try { + redirectUrl = url.resolve(currentUrl, location); } - - return hasKnownLength; -}; - -FormData.prototype.getLength = function(cb) { - var knownLength = this._overheadLength + this._valueLength; - - if (this._streams.length) { - knownLength += this._lastBoundary().length; + catch (cause) { + this.emit("error", new RedirectionError(cause)); + return; } - if (!this._valuesToMeasure.length) { - process.nextTick(cb.bind(this, null, knownLength)); - return; + // Create the redirected request + debug("redirecting to", redirectUrl); + this._isRedirect = true; + var redirectUrlParts = url.parse(redirectUrl); + Object.assign(this._options, redirectUrlParts); + + // Drop confidential headers when redirecting to a less secure protocol + // or to a different domain that is not a superdomain + if (redirectUrlParts.protocol !== currentUrlParts.protocol && + redirectUrlParts.protocol !== "https:" || + redirectUrlParts.host !== currentHost && + !isSubdomain(redirectUrlParts.host, currentHost)) { + removeMatchingHeaders(/^(?:authorization|cookie)$/i, this._options.headers); } - asynckit.parallel(this._valuesToMeasure, this._lengthRetriever, function(err, values) { - if (err) { - cb(err); + // Evaluate the beforeRedirect callback + if (typeof beforeRedirect === "function") { + var responseDetails = { + headers: response.headers, + statusCode: statusCode, + }; + var requestDetails = { + url: currentUrl, + method: method, + headers: requestHeaders, + }; + try { + beforeRedirect(this._options, responseDetails, requestDetails); + } + catch (err) { + this.emit("error", err); return; } + this._sanitizeOptions(this._options); + } - values.forEach(function(length) { - knownLength += length; - }); - - cb(null, knownLength); - }); -}; - -FormData.prototype.submit = function(params, cb) { - var request - , options - , defaults = {method: 'post'} - ; - - // parse provided url if it's string - // or treat it as options object - if (typeof params == 'string') { - - params = parseUrl(params); - options = populate({ - port: params.port, - path: params.pathname, - host: params.hostname, - protocol: params.protocol - }, defaults); + // Perform the redirected request + try { + this._performRequest(); + } + catch (cause) { + this.emit("error", new RedirectionError(cause)); + } +}; - // use custom params - } else { +// Wraps the key/value object of protocols with redirect functionality +function wrap(protocols) { + // Default settings + var exports = { + maxRedirects: 21, + maxBodyLength: 10 * 1024 * 1024, + }; - options = populate(params, defaults); - // if no port provided use default one - if (!options.port) { - options.port = options.protocol == 'https:' ? 443 : 80; - } - } + // Wrap each protocol + var nativeProtocols = {}; + Object.keys(protocols).forEach(function (scheme) { + var protocol = scheme + ":"; + var nativeProtocol = nativeProtocols[protocol] = protocols[scheme]; + var wrappedProtocol = exports[scheme] = Object.create(nativeProtocol); - // put that good code in getHeaders to some use - options.headers = this.getHeaders(params.headers); + // Executes a request, following redirects + function request(input, options, callback) { + // Parse parameters + if (typeof input === "string") { + var urlStr = input; + try { + input = urlToOptions(new URL(urlStr)); + } + catch (err) { + /* istanbul ignore next */ + input = url.parse(urlStr); + } + } + else if (URL && (input instanceof URL)) { + input = urlToOptions(input); + } + else { + callback = options; + options = input; + input = { protocol: protocol }; + } + if (typeof options === "function") { + callback = options; + options = null; + } - // https if specified, fallback to http in any other case - if (options.protocol == 'https:') { - request = https.request(options); - } else { - request = http.request(options); - } + // Set defaults + options = Object.assign({ + maxRedirects: exports.maxRedirects, + maxBodyLength: exports.maxBodyLength, + }, input, options); + options.nativeProtocols = nativeProtocols; - // get content length and fire away - this.getLength(function(err, length) { - if (err && err !== 'Unknown stream') { - this._error(err); - return; + assert.equal(options.protocol, protocol, "protocol mismatch"); + debug("options", options); + return new RedirectableRequest(options, callback); } - // add content length - if (length) { - request.setHeader('Content-Length', length); + // Executes a GET request, following redirects + function get(input, options, callback) { + var wrappedRequest = wrappedProtocol.request(input, options, callback); + wrappedRequest.end(); + return wrappedRequest; } - this.pipe(request); - if (cb) { - var onResponse; - - var callback = function (error, responce) { - request.removeListener('error', callback); - request.removeListener('response', onResponse); + // Expose the properties on the wrapped protocol + Object.defineProperties(wrappedProtocol, { + request: { value: request, configurable: true, enumerable: true, writable: true }, + get: { value: get, configurable: true, enumerable: true, writable: true }, + }); + }); + return exports; +} - return cb.call(this, error, responce); - }; +/* istanbul ignore next */ +function noop() { /* empty */ } - onResponse = callback.bind(this, null); +// from https://github.com/nodejs/node/blob/master/lib/internal/url.js +function urlToOptions(urlObject) { + var options = { + protocol: urlObject.protocol, + hostname: urlObject.hostname.startsWith("[") ? + /* istanbul ignore next */ + urlObject.hostname.slice(1, -1) : + urlObject.hostname, + hash: urlObject.hash, + search: urlObject.search, + pathname: urlObject.pathname, + path: urlObject.pathname + urlObject.search, + href: urlObject.href, + }; + if (urlObject.port !== "") { + options.port = Number(urlObject.port); + } + return options; +} - request.on('error', callback); - request.on('response', onResponse); +function removeMatchingHeaders(regex, headers) { + var lastValue; + for (var header in headers) { + if (regex.test(header)) { + lastValue = headers[header]; + delete headers[header]; } - }.bind(this)); + } + return (lastValue === null || typeof lastValue === "undefined") ? + undefined : String(lastValue).trim(); +} - return request; -}; +function createErrorType(code, defaultMessage) { + function CustomError(cause) { + Error.captureStackTrace(this, this.constructor); + if (!cause) { + this.message = defaultMessage; + } + else { + this.message = defaultMessage + ": " + cause.message; + this.cause = cause; + } + } + CustomError.prototype = new Error(); + CustomError.prototype.constructor = CustomError; + CustomError.prototype.name = "Error [" + code + "]"; + CustomError.prototype.code = code; + return CustomError; +} -FormData.prototype._error = function(err) { - if (!this.error) { - this.error = err; - this.pause(); - this.emit('error', err); +function abortRequest(request) { + for (var e = 0; e < events.length; e++) { + request.removeListener(events[e], eventHandlers[events[e]]); } -}; + request.on("error", noop); + request.abort(); +} -FormData.prototype.toString = function () { - return '[object FormData]'; -}; +function isSubdomain(subdomain, domain) { + const dot = subdomain.length - domain.length - 1; + return dot > 0 && subdomain[dot] === "." && subdomain.endsWith(domain); +} + +// Exports +module.exports = wrap({ http: http, https: https }); +module.exports.wrap = wrap; /***/ }), -/***/ "../../node_modules/form-data/lib/populate.js": -/***/ (function(module, exports) { +/***/ "../../node_modules/form-data/lib/form_data.js": +/***/ (function(module, exports, __webpack_require__) { -// populates missing values -module.exports = function(dst, src) { +var CombinedStream = __webpack_require__("../../node_modules/combined-stream/lib/combined_stream.js"); +var util = __webpack_require__("util"); +var path = __webpack_require__("path"); +var http = __webpack_require__("http"); +var https = __webpack_require__("https"); +var parseUrl = __webpack_require__("url").parse; +var fs = __webpack_require__("fs"); +var Stream = __webpack_require__("stream").Stream; +var mime = __webpack_require__("../../node_modules/mime-types/index.js"); +var asynckit = __webpack_require__("../../node_modules/asynckit/index.js"); +var populate = __webpack_require__("../../node_modules/form-data/lib/populate.js"); - Object.keys(src).forEach(function(prop) - { - dst[prop] = dst[prop] || src[prop]; - }); +// Public API +module.exports = FormData; - return dst; -}; +// make it a Stream +util.inherits(FormData, CombinedStream); +/** + * Create readable "multipart/form-data" streams. + * Can be used to submit forms + * and file uploads to other web applications. + * + * @constructor + * @param {Object} options - Properties to be added/overriden for FormData and CombinedStream + */ +function FormData(options) { + if (!(this instanceof FormData)) { + return new FormData(options); + } -/***/ }), + this._overheadLength = 0; + this._valueLength = 0; + this._valuesToMeasure = []; -/***/ "../../node_modules/fs.realpath/index.js": -/***/ (function(module, exports, __webpack_require__) { + CombinedStream.call(this); -module.exports = realpath -realpath.realpath = realpath -realpath.sync = realpathSync -realpath.realpathSync = realpathSync -realpath.monkeypatch = monkeypatch -realpath.unmonkeypatch = unmonkeypatch + options = options || {}; + for (var option in options) { + this[option] = options[option]; + } +} -var fs = __webpack_require__("fs") -var origRealpath = fs.realpath -var origRealpathSync = fs.realpathSync +FormData.LINE_BREAK = '\r\n'; +FormData.DEFAULT_CONTENT_TYPE = 'application/octet-stream'; -var version = process.version -var ok = /^v[0-5]\./.test(version) -var old = __webpack_require__("../../node_modules/fs.realpath/old.js") +FormData.prototype.append = function(field, value, options) { -function newError (er) { - return er && er.syscall === 'realpath' && ( - er.code === 'ELOOP' || - er.code === 'ENOMEM' || - er.code === 'ENAMETOOLONG' - ) -} + options = options || {}; -function realpath (p, cache, cb) { - if (ok) { - return origRealpath(p, cache, cb) + // allow filename as single option + if (typeof options == 'string') { + options = {filename: options}; } - if (typeof cache === 'function') { - cb = cache - cache = null - } - origRealpath(p, cache, function (er, result) { - if (newError(er)) { - old.realpath(p, cache, cb) - } else { - cb(er, result) - } - }) -} + var append = CombinedStream.prototype.append.bind(this); -function realpathSync (p, cache) { - if (ok) { - return origRealpathSync(p, cache) + // all that streamy business can't handle numbers + if (typeof value == 'number') { + value = '' + value; } - try { - return origRealpathSync(p, cache) - } catch (er) { - if (newError(er)) { - return old.realpathSync(p, cache) - } else { - throw er - } + // https://github.com/felixge/node-form-data/issues/38 + if (util.isArray(value)) { + // Please convert your array into string + // the way web server expects it + this._error(new Error('Arrays are not supported.')); + return; } -} - -function monkeypatch () { - fs.realpath = realpath - fs.realpathSync = realpathSync -} - -function unmonkeypatch () { - fs.realpath = origRealpath - fs.realpathSync = origRealpathSync -} - - -/***/ }), -/***/ "../../node_modules/fs.realpath/old.js": -/***/ (function(module, exports, __webpack_require__) { + var header = this._multiPartHeader(field, value, options); + var footer = this._multiPartFooter(); -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. + append(header); + append(value); + append(footer); -var pathModule = __webpack_require__("path"); -var isWindows = process.platform === 'win32'; -var fs = __webpack_require__("fs"); + // pass along options.knownLength + this._trackLength(header, value, options); +}; -// JavaScript implementation of realpath, ported from node pre-v6 +FormData.prototype._trackLength = function(header, value, options) { + var valueLength = 0; -var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG); + // used w/ getLengthSync(), when length is known. + // e.g. for streaming directly from a remote server, + // w/ a known file a size, and not wanting to wait for + // incoming file to finish to get its size. + if (options.knownLength != null) { + valueLength += +options.knownLength; + } else if (Buffer.isBuffer(value)) { + valueLength = value.length; + } else if (typeof value === 'string') { + valueLength = Buffer.byteLength(value); + } -function rethrow() { - // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and - // is fairly slow to generate. - var callback; - if (DEBUG) { - var backtrace = new Error; - callback = debugCallback; - } else - callback = missingCallback; + this._valueLength += valueLength; - return callback; + // @check why add CRLF? does this account for custom/multiple CRLFs? + this._overheadLength += + Buffer.byteLength(header) + + FormData.LINE_BREAK.length; - function debugCallback(err) { - if (err) { - backtrace.message = err.message; - err = backtrace; - missingCallback(err); - } + // empty or either doesn't have path or not an http response or not a stream + if (!value || ( !value.path && !(value.readable && value.hasOwnProperty('httpVersion')) && !(value instanceof Stream))) { + return; } - function missingCallback(err) { - if (err) { - if (process.throwDeprecation) - throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs - else if (!process.noDeprecation) { - var msg = 'fs: missing callback ' + (err.stack || err.message); - if (process.traceDeprecation) - console.trace(msg); - else - console.error(msg); - } - } + // no need to bother with the length + if (!options.knownLength) { + this._valuesToMeasure.push(value); } -} +}; -function maybeCallback(cb) { - return typeof cb === 'function' ? cb : rethrow(); -} +FormData.prototype._lengthRetriever = function(value, callback) { -var normalize = pathModule.normalize; + if (value.hasOwnProperty('fd')) { -// Regexp that finds the next partion of a (partial) path -// result is [base_with_slash, base], e.g. ['somedir/', 'somedir'] -if (isWindows) { - var nextPartRe = /(.*?)(?:[\/\\]+|$)/g; -} else { - var nextPartRe = /(.*?)(?:[\/]+|$)/g; -} + // take read range into a account + // `end` = Infinity –> read file till the end + // + // TODO: Looks like there is bug in Node fs.createReadStream + // it doesn't respect `end` options without `start` options + // Fix it when node fixes it. + // https://github.com/joyent/node/issues/7819 + if (value.end != undefined && value.end != Infinity && value.start != undefined) { -// Regex to find the device root, including trailing slash. E.g. 'c:\\'. -if (isWindows) { - var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; -} else { - var splitRootRe = /^[\/]*/; -} + // when end specified + // no need to calculate range + // inclusive, starts with 0 + callback(null, value.end + 1 - (value.start ? value.start : 0)); -exports.realpathSync = function realpathSync(p, cache) { - // make p is absolute - p = pathModule.resolve(p); + // not that fast snoopy + } else { + // still need to fetch file size from fs + fs.stat(value.path, function(err, stat) { - if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { - return cache[p]; - } + var fileSize; - var original = p, - seenLinks = {}, - knownHard = {}; + if (err) { + callback(err); + return; + } - // current character position in p - var pos; - // the partial path so far, including a trailing slash if any - var current; - // the partial path without a trailing slash (except when pointing at a root) - var base; - // the partial path scanned in the previous round, with slash - var previous; + // update final size based on the range options + fileSize = stat.size - (value.start ? value.start : 0); + callback(null, fileSize); + }); + } - start(); + // or http response + } else if (value.hasOwnProperty('httpVersion')) { + callback(null, +value.headers['content-length']); - function start() { - // Skip over roots - var m = splitRootRe.exec(p); - pos = m[0].length; - current = m[0]; - base = m[0]; - previous = ''; + // or request stream http://github.com/mikeal/request + } else if (value.hasOwnProperty('httpModule')) { + // wait till response come back + value.on('response', function(response) { + value.pause(); + callback(null, +response.headers['content-length']); + }); + value.resume(); - // On windows, check that the root exists. On unix there is no need. - if (isWindows && !knownHard[base]) { - fs.lstatSync(base); - knownHard[base] = true; - } + // something else + } else { + callback('Unknown stream'); } +}; - // walk down the path, swapping out linked pathparts for their real - // values - // NB: p.length changes. - while (pos < p.length) { - // find the next part - nextPartRe.lastIndex = pos; - var result = nextPartRe.exec(p); - previous = current; - current += result[0]; - base = previous + result[1]; - pos = nextPartRe.lastIndex; - - // continue if not a symlink - if (knownHard[base] || (cache && cache[base] === base)) { - continue; - } +FormData.prototype._multiPartHeader = function(field, value, options) { + // custom header specified (as string)? + // it becomes responsible for boundary + // (e.g. to handle extra CRLFs on .NET servers) + if (typeof options.header == 'string') { + return options.header; + } - var resolvedLink; - if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { - // some known symbolic link. no need to stat again. - resolvedLink = cache[base]; - } else { - var stat = fs.lstatSync(base); - if (!stat.isSymbolicLink()) { - knownHard[base] = true; - if (cache) cache[base] = base; - continue; - } + var contentDisposition = this._getContentDisposition(value, options); + var contentType = this._getContentType(value, options); - // read the link if it wasn't read before - // dev/ino always return 0 on windows, so skip the check. - var linkTarget = null; - if (!isWindows) { - var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); - if (seenLinks.hasOwnProperty(id)) { - linkTarget = seenLinks[id]; - } - } - if (linkTarget === null) { - fs.statSync(base); - linkTarget = fs.readlinkSync(base); - } - resolvedLink = pathModule.resolve(previous, linkTarget); - // track this, if given a cache. - if (cache) cache[base] = resolvedLink; - if (!isWindows) seenLinks[id] = linkTarget; - } + var contents = ''; + var headers = { + // add custom disposition as third element or keep it two elements if not + 'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || []), + // if no content type. allow it to be empty array + 'Content-Type': [].concat(contentType || []) + }; - // resolve the link, then start over - p = pathModule.resolve(resolvedLink, p.slice(pos)); - start(); + // allow custom headers. + if (typeof options.header == 'object') { + populate(headers, options.header); } - if (cache) cache[original] = p; + var header; + for (var prop in headers) { + if (!headers.hasOwnProperty(prop)) continue; + header = headers[prop]; - return p; -}; + // skip nullish headers. + if (header == null) { + continue; + } + // convert all headers to arrays. + if (!Array.isArray(header)) { + header = [header]; + } -exports.realpath = function realpath(p, cache, cb) { - if (typeof cb !== 'function') { - cb = maybeCallback(cache); - cache = null; + // add non-empty headers. + if (header.length) { + contents += prop + ': ' + header.join('; ') + FormData.LINE_BREAK; + } } - // make p is absolute - p = pathModule.resolve(p); + return '--' + this.getBoundary() + FormData.LINE_BREAK + contents + FormData.LINE_BREAK; +}; - if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { - return process.nextTick(cb.bind(null, null, cache[p])); +FormData.prototype._getContentDisposition = function(value, options) { + + var filename + , contentDisposition + ; + + if (typeof options.filepath === 'string') { + // custom filepath for relative paths + filename = path.normalize(options.filepath).replace(/\\/g, '/'); + } else if (options.filename || value.name || value.path) { + // custom filename take precedence + // formidable and the browser add a name property + // fs- and request- streams have path property + filename = path.basename(options.filename || value.name || value.path); + } else if (value.readable && value.hasOwnProperty('httpVersion')) { + // or try http response + filename = path.basename(value.client._httpMessage.path || ''); } - var original = p, - seenLinks = {}, - knownHard = {}; + if (filename) { + contentDisposition = 'filename="' + filename + '"'; + } - // current character position in p - var pos; - // the partial path so far, including a trailing slash if any - var current; - // the partial path without a trailing slash (except when pointing at a root) - var base; - // the partial path scanned in the previous round, with slash - var previous; + return contentDisposition; +}; - start(); +FormData.prototype._getContentType = function(value, options) { - function start() { - // Skip over roots - var m = splitRootRe.exec(p); - pos = m[0].length; - current = m[0]; - base = m[0]; - previous = ''; + // use custom content-type above all + var contentType = options.contentType; - // On windows, check that the root exists. On unix there is no need. - if (isWindows && !knownHard[base]) { - fs.lstat(base, function(err) { - if (err) return cb(err); - knownHard[base] = true; - LOOP(); - }); - } else { - process.nextTick(LOOP); - } + // or try `name` from formidable, browser + if (!contentType && value.name) { + contentType = mime.lookup(value.name); } - // walk down the path, swapping out linked pathparts for their real - // values - function LOOP() { - // stop if scanned past end of path - if (pos >= p.length) { - if (cache) cache[original] = p; - return cb(null, p); - } - - // find the next part - nextPartRe.lastIndex = pos; - var result = nextPartRe.exec(p); - previous = current; - current += result[0]; - base = previous + result[1]; - pos = nextPartRe.lastIndex; + // or try `path` from fs-, request- streams + if (!contentType && value.path) { + contentType = mime.lookup(value.path); + } - // continue if not a symlink - if (knownHard[base] || (cache && cache[base] === base)) { - return process.nextTick(LOOP); - } + // or if it's http-reponse + if (!contentType && value.readable && value.hasOwnProperty('httpVersion')) { + contentType = value.headers['content-type']; + } - if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { - // known symbolic link. no need to stat again. - return gotResolvedLink(cache[base]); - } + // or guess it from the filepath or filename + if (!contentType && (options.filepath || options.filename)) { + contentType = mime.lookup(options.filepath || options.filename); + } - return fs.lstat(base, gotStat); + // fallback to the default content type if `value` is not simple value + if (!contentType && typeof value == 'object') { + contentType = FormData.DEFAULT_CONTENT_TYPE; } - function gotStat(err, stat) { - if (err) return cb(err); + return contentType; +}; - // if not a symlink, skip to the next path part - if (!stat.isSymbolicLink()) { - knownHard[base] = true; - if (cache) cache[base] = base; - return process.nextTick(LOOP); - } +FormData.prototype._multiPartFooter = function() { + return function(next) { + var footer = FormData.LINE_BREAK; - // stat & read the link if not read before - // call gotTarget as soon as the link target is known - // dev/ino always return 0 on windows, so skip the check. - if (!isWindows) { - var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); - if (seenLinks.hasOwnProperty(id)) { - return gotTarget(null, seenLinks[id], base); - } + var lastPart = (this._streams.length === 0); + if (lastPart) { + footer += this._lastBoundary(); } - fs.stat(base, function(err) { - if (err) return cb(err); - fs.readlink(base, function(err, target) { - if (!isWindows) seenLinks[id] = target; - gotTarget(err, target); - }); - }); - } + next(footer); + }.bind(this); +}; - function gotTarget(err, target, base) { - if (err) return cb(err); +FormData.prototype._lastBoundary = function() { + return '--' + this.getBoundary() + '--' + FormData.LINE_BREAK; +}; - var resolvedLink = pathModule.resolve(previous, target); - if (cache) cache[base] = resolvedLink; - gotResolvedLink(resolvedLink); - } +FormData.prototype.getHeaders = function(userHeaders) { + var header; + var formHeaders = { + 'content-type': 'multipart/form-data; boundary=' + this.getBoundary() + }; - function gotResolvedLink(resolvedLink) { - // resolve the link, then start over - p = pathModule.resolve(resolvedLink, p.slice(pos)); - start(); + for (header in userHeaders) { + if (userHeaders.hasOwnProperty(header)) { + formHeaders[header.toLowerCase()] = userHeaders[header]; + } } + + return formHeaders; }; +FormData.prototype.setBoundary = function(boundary) { + this._boundary = boundary; +}; -/***/ }), +FormData.prototype.getBoundary = function() { + if (!this._boundary) { + this._generateBoundary(); + } -/***/ "../../node_modules/function-bind/implementation.js": -/***/ (function(module, exports, __webpack_require__) { + return this._boundary; +}; -"use strict"; +FormData.prototype.getBuffer = function() { + var dataBuffer = new Buffer.alloc( 0 ); + var boundary = this.getBoundary(); + // Create the form content. Add Line breaks to the end of data. + for (var i = 0, len = this._streams.length; i < len; i++) { + if (typeof this._streams[i] !== 'function') { -/* eslint no-invalid-this: 1 */ - -var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; -var slice = Array.prototype.slice; -var toStr = Object.prototype.toString; -var funcType = '[object Function]'; + // Add content to the buffer. + if(Buffer.isBuffer(this._streams[i])) { + dataBuffer = Buffer.concat( [dataBuffer, this._streams[i]]); + }else { + dataBuffer = Buffer.concat( [dataBuffer, Buffer.from(this._streams[i])]); + } -module.exports = function bind(that) { - var target = this; - if (typeof target !== 'function' || toStr.call(target) !== funcType) { - throw new TypeError(ERROR_MESSAGE + target); + // Add break after content. + if (typeof this._streams[i] !== 'string' || this._streams[i].substring( 2, boundary.length + 2 ) !== boundary) { + dataBuffer = Buffer.concat( [dataBuffer, Buffer.from(FormData.LINE_BREAK)] ); + } } - var args = slice.call(arguments, 1); + } - var bound; - var binder = function () { - if (this instanceof bound) { - var result = target.apply( - this, - args.concat(slice.call(arguments)) - ); - if (Object(result) === result) { - return result; - } - return this; - } else { - return target.apply( - that, - args.concat(slice.call(arguments)) - ); - } - }; + // Add the footer and return the Buffer object. + return Buffer.concat( [dataBuffer, Buffer.from(this._lastBoundary())] ); +}; - var boundLength = Math.max(0, target.length - args.length); - var boundArgs = []; - for (var i = 0; i < boundLength; i++) { - boundArgs.push('$' + i); - } +FormData.prototype._generateBoundary = function() { + // This generates a 50 character boundary similar to those used by Firefox. + // They are optimized for boyer-moore parsing. + var boundary = '--------------------------'; + for (var i = 0; i < 24; i++) { + boundary += Math.floor(Math.random() * 10).toString(16); + } - bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); + this._boundary = boundary; +}; - if (target.prototype) { - var Empty = function Empty() {}; - Empty.prototype = target.prototype; - bound.prototype = new Empty(); - Empty.prototype = null; - } +// Note: getLengthSync DOESN'T calculate streams length +// As workaround one can calculate file size manually +// and add it as knownLength option +FormData.prototype.getLengthSync = function() { + var knownLength = this._overheadLength + this._valueLength; - return bound; + // Don't get confused, there are 3 "internal" streams for each keyval pair + // so it basically checks if there is any value added to the form + if (this._streams.length) { + knownLength += this._lastBoundary().length; + } + + // https://github.com/form-data/form-data/issues/40 + if (!this.hasKnownLength()) { + // Some async length retrievers are present + // therefore synchronous length calculation is false. + // Please use getLength(callback) to get proper length + this._error(new Error('Cannot calculate proper length in synchronous way.')); + } + + return knownLength; }; +// Public API to check if length of added values is known +// https://github.com/form-data/form-data/issues/196 +// https://github.com/form-data/form-data/issues/262 +FormData.prototype.hasKnownLength = function() { + var hasKnownLength = true; -/***/ }), + if (this._valuesToMeasure.length) { + hasKnownLength = false; + } -/***/ "../../node_modules/function-bind/index.js": -/***/ (function(module, exports, __webpack_require__) { + return hasKnownLength; +}; -"use strict"; +FormData.prototype.getLength = function(cb) { + var knownLength = this._overheadLength + this._valueLength; + if (this._streams.length) { + knownLength += this._lastBoundary().length; + } -var implementation = __webpack_require__("../../node_modules/function-bind/implementation.js"); + if (!this._valuesToMeasure.length) { + process.nextTick(cb.bind(this, null, knownLength)); + return; + } -module.exports = Function.prototype.bind || implementation; + asynckit.parallel(this._valuesToMeasure, this._lengthRetriever, function(err, values) { + if (err) { + cb(err); + return; + } + values.forEach(function(length) { + knownLength += length; + }); -/***/ }), + cb(null, knownLength); + }); +}; -/***/ "../../node_modules/get-stream/buffer-stream.js": -/***/ (function(module, exports, __webpack_require__) { +FormData.prototype.submit = function(params, cb) { + var request + , options + , defaults = {method: 'post'} + ; -"use strict"; + // parse provided url if it's string + // or treat it as options object + if (typeof params == 'string') { -const {PassThrough: PassThroughStream} = __webpack_require__("stream"); + params = parseUrl(params); + options = populate({ + port: params.port, + path: params.pathname, + host: params.hostname, + protocol: params.protocol + }, defaults); -module.exports = options => { - options = {...options}; + // use custom params + } else { - const {array} = options; - let {encoding} = options; - const isBuffer = encoding === 'buffer'; - let objectMode = false; + options = populate(params, defaults); + // if no port provided use default one + if (!options.port) { + options.port = options.protocol == 'https:' ? 443 : 80; + } + } - if (array) { - objectMode = !(encoding || isBuffer); - } else { - encoding = encoding || 'utf8'; - } + // put that good code in getHeaders to some use + options.headers = this.getHeaders(params.headers); - if (isBuffer) { - encoding = null; - } + // https if specified, fallback to http in any other case + if (options.protocol == 'https:') { + request = https.request(options); + } else { + request = http.request(options); + } - const stream = new PassThroughStream({objectMode}); + // get content length and fire away + this.getLength(function(err, length) { + if (err && err !== 'Unknown stream') { + this._error(err); + return; + } - if (encoding) { - stream.setEncoding(encoding); - } + // add content length + if (length) { + request.setHeader('Content-Length', length); + } - let length = 0; - const chunks = []; + this.pipe(request); + if (cb) { + var onResponse; - stream.on('data', chunk => { - chunks.push(chunk); + var callback = function (error, responce) { + request.removeListener('error', callback); + request.removeListener('response', onResponse); - if (objectMode) { - length = chunks.length; - } else { - length += chunk.length; - } - }); + return cb.call(this, error, responce); + }; - stream.getBufferedValue = () => { - if (array) { - return chunks; - } + onResponse = callback.bind(this, null); - return isBuffer ? Buffer.concat(chunks, length) : chunks.join(''); - }; + request.on('error', callback); + request.on('response', onResponse); + } + }.bind(this)); - stream.getBufferedLength = () => length; + return request; +}; - return stream; +FormData.prototype._error = function(err) { + if (!this.error) { + this.error = err; + this.pause(); + this.emit('error', err); + } +}; + +FormData.prototype.toString = function () { + return '[object FormData]'; }; /***/ }), -/***/ "../../node_modules/get-stream/index.js": -/***/ (function(module, exports, __webpack_require__) { +/***/ "../../node_modules/form-data/lib/populate.js": +/***/ (function(module, exports) { -"use strict"; +// populates missing values +module.exports = function(dst, src) { -const pump = __webpack_require__("../../node_modules/pump/index.js"); -const bufferStream = __webpack_require__("../../node_modules/get-stream/buffer-stream.js"); + Object.keys(src).forEach(function(prop) + { + dst[prop] = dst[prop] || src[prop]; + }); -class MaxBufferError extends Error { - constructor() { - super('maxBuffer exceeded'); - this.name = 'MaxBufferError'; - } -} + return dst; +}; -async function getStream(inputStream, options) { - if (!inputStream) { - return Promise.reject(new Error('Expected a stream')); - } - options = { - maxBuffer: Infinity, - ...options - }; +/***/ }), - const {maxBuffer} = options; +/***/ "../../node_modules/fs.realpath/index.js": +/***/ (function(module, exports, __webpack_require__) { - let stream; - await new Promise((resolve, reject) => { - const rejectPromise = error => { - if (error) { // A null check - error.bufferedData = stream.getBufferedValue(); - } +module.exports = realpath +realpath.realpath = realpath +realpath.sync = realpathSync +realpath.realpathSync = realpathSync +realpath.monkeypatch = monkeypatch +realpath.unmonkeypatch = unmonkeypatch - reject(error); - }; +var fs = __webpack_require__("fs") +var origRealpath = fs.realpath +var origRealpathSync = fs.realpathSync - stream = pump(inputStream, bufferStream(options), error => { - if (error) { - rejectPromise(error); - return; - } +var version = process.version +var ok = /^v[0-5]\./.test(version) +var old = __webpack_require__("../../node_modules/fs.realpath/old.js") - resolve(); - }); +function newError (er) { + return er && er.syscall === 'realpath' && ( + er.code === 'ELOOP' || + er.code === 'ENOMEM' || + er.code === 'ENAMETOOLONG' + ) +} - stream.on('data', () => { - if (stream.getBufferedLength() > maxBuffer) { - rejectPromise(new MaxBufferError()); - } - }); - }); +function realpath (p, cache, cb) { + if (ok) { + return origRealpath(p, cache, cb) + } - return stream.getBufferedValue(); + if (typeof cache === 'function') { + cb = cache + cache = null + } + origRealpath(p, cache, function (er, result) { + if (newError(er)) { + old.realpath(p, cache, cb) + } else { + cb(er, result) + } + }) } -module.exports = getStream; -// TODO: Remove this for the next major release -module.exports.default = getStream; -module.exports.buffer = (stream, options) => getStream(stream, {...options, encoding: 'buffer'}); -module.exports.array = (stream, options) => getStream(stream, {...options, array: true}); -module.exports.MaxBufferError = MaxBufferError; +function realpathSync (p, cache) { + if (ok) { + return origRealpathSync(p, cache) + } + + try { + return origRealpathSync(p, cache) + } catch (er) { + if (newError(er)) { + return old.realpathSync(p, cache) + } else { + throw er + } + } +} + +function monkeypatch () { + fs.realpath = realpath + fs.realpathSync = realpathSync +} + +function unmonkeypatch () { + fs.realpath = origRealpath + fs.realpathSync = origRealpathSync +} /***/ }), -/***/ "../../node_modules/getopts/index.js": +/***/ "../../node_modules/fs.realpath/old.js": /***/ (function(module, exports, __webpack_require__) { -"use strict"; +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +var pathModule = __webpack_require__("path"); +var isWindows = process.platform === 'win32'; +var fs = __webpack_require__("fs"); -const EMPTYARR = [] -const SHORTSPLIT = /$|[!-@[-`{-~][\s\S]*/g -const isArray = Array.isArray +// JavaScript implementation of realpath, ported from node pre-v6 -const parseValue = function(any) { - if (any === "") return "" - if (any === "false") return false - const maybe = Number(any) - return maybe * 0 === 0 ? maybe : any -} +var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG); -const parseAlias = function(aliases) { - let out = {}, - key, - alias, - prev, - len, - any, - i, - k +function rethrow() { + // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and + // is fairly slow to generate. + var callback; + if (DEBUG) { + var backtrace = new Error; + callback = debugCallback; + } else + callback = missingCallback; - for (key in aliases) { - any = aliases[key] - alias = out[key] = isArray(any) ? any : [any] + return callback; - for (i = 0, len = alias.length; i < len; i++) { - prev = out[alias[i]] = [key] + function debugCallback(err) { + if (err) { + backtrace.message = err.message; + err = backtrace; + missingCallback(err); + } + } - for (k = 0; k < len; k++) { - if (i !== k) prev.push(alias[k]) + function missingCallback(err) { + if (err) { + if (process.throwDeprecation) + throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs + else if (!process.noDeprecation) { + var msg = 'fs: missing callback ' + (err.stack || err.message); + if (process.traceDeprecation) + console.trace(msg); + else + console.error(msg); } } } - - return out } -const parseDefault = function(aliases, defaults) { - let out = {}, - key, - alias, - value, - len, - i - - for (key in defaults) { - value = defaults[key] - alias = aliases[key] +function maybeCallback(cb) { + return typeof cb === 'function' ? cb : rethrow(); +} - out[key] = value +var normalize = pathModule.normalize; - if (alias === undefined) { - aliases[key] = EMPTYARR - } else { - for (i = 0, len = alias.length; i < len; i++) { - out[alias[i]] = value - } - } - } +// Regexp that finds the next partion of a (partial) path +// result is [base_with_slash, base], e.g. ['somedir/', 'somedir'] +if (isWindows) { + var nextPartRe = /(.*?)(?:[\/\\]+|$)/g; +} else { + var nextPartRe = /(.*?)(?:[\/]+|$)/g; +} - return out +// Regex to find the device root, including trailing slash. E.g. 'c:\\'. +if (isWindows) { + var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; +} else { + var splitRootRe = /^[\/]*/; } -const parseOptions = function(aliases, options, value) { - let out = {}, - key, - alias, - len, - end, - i, - k +exports.realpathSync = function realpathSync(p, cache) { + // make p is absolute + p = pathModule.resolve(p); - if (options !== undefined) { - for (i = 0, len = options.length; i < len; i++) { - key = options[i] - alias = aliases[key] + if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { + return cache[p]; + } - out[key] = value + var original = p, + seenLinks = {}, + knownHard = {}; - if (alias === undefined) { - aliases[key] = EMPTYARR - } else { - for (k = 0, end = alias.length; k < end; k++) { - out[alias[k]] = value - } - } - } - } + // current character position in p + var pos; + // the partial path so far, including a trailing slash if any + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; + // the partial path scanned in the previous round, with slash + var previous; - return out -} + start(); -const write = function(out, key, value, aliases, unknown) { - let i, - prev, - alias = aliases[key], - len = alias === undefined ? -1 : alias.length - - if (len >= 0 || unknown === undefined || unknown(key)) { - prev = out[key] - - if (prev === undefined) { - out[key] = value - } else { - if (isArray(prev)) { - prev.push(value) - } else { - out[key] = [prev, value] - } - } + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; - for (i = 0; i < len; i++) { - out[alias[i]] = out[key] + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstatSync(base); + knownHard[base] = true; } } -} -const getopts = function(argv, opts) { - let unknown = (opts = opts || {}).unknown, - aliases = parseAlias(opts.alias), - strings = parseOptions(aliases, opts.string, ""), - values = parseDefault(aliases, opts.default), - bools = parseOptions(aliases, opts.boolean, false), - stopEarly = opts.stopEarly, - _ = [], - out = { _ }, - i = 0, - k = 0, - len = argv.length, - key, - arg, - end, - match, - value + // walk down the path, swapping out linked pathparts for their real + // values + // NB: p.length changes. + while (pos < p.length) { + // find the next part + nextPartRe.lastIndex = pos; + var result = nextPartRe.exec(p); + previous = current; + current += result[0]; + base = previous + result[1]; + pos = nextPartRe.lastIndex; - for (; i < len; i++) { - arg = argv[i] + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { + continue; + } - if (arg[0] !== "-" || arg === "-") { - if (stopEarly) while (i < len) _.push(argv[i++]) - else _.push(arg) - } else if (arg === "--") { - while (++i < len) _.push(argv[i]) - } else if (arg[1] === "-") { - end = arg.indexOf("=", 2) - if (arg[2] === "n" && arg[3] === "o" && arg[4] === "-") { - key = arg.slice(5, end >= 0 ? end : undefined) - value = false - } else if (end >= 0) { - key = arg.slice(2, end) - value = - bools[key] !== undefined || - (strings[key] === undefined - ? parseValue(arg.slice(end + 1)) - : arg.slice(end + 1)) - } else { - key = arg.slice(2) - value = - bools[key] !== undefined || - (len === i + 1 || argv[i + 1][0] === "-" - ? strings[key] === undefined - ? true - : "" - : strings[key] === undefined - ? parseValue(argv[++i]) - : argv[++i]) - } - write(out, key, value, aliases, unknown) + var resolvedLink; + if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { + // some known symbolic link. no need to stat again. + resolvedLink = cache[base]; } else { - SHORTSPLIT.lastIndex = 2 - match = SHORTSPLIT.exec(arg) - end = match.index - value = match[0] + var stat = fs.lstatSync(base); + if (!stat.isSymbolicLink()) { + knownHard[base] = true; + if (cache) cache[base] = base; + continue; + } - for (k = 1; k < end; k++) { - write( - out, - (key = arg[k]), - k + 1 < end - ? strings[key] === undefined || - arg.substring(k + 1, (k = end)) + value - : value === "" - ? len === i + 1 || argv[i + 1][0] === "-" - ? strings[key] === undefined || "" - : bools[key] !== undefined || - (strings[key] === undefined ? parseValue(argv[++i]) : argv[++i]) - : bools[key] !== undefined || - (strings[key] === undefined ? parseValue(value) : value), - aliases, - unknown - ) + // read the link if it wasn't read before + // dev/ino always return 0 on windows, so skip the check. + var linkTarget = null; + if (!isWindows) { + var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); + if (seenLinks.hasOwnProperty(id)) { + linkTarget = seenLinks[id]; + } + } + if (linkTarget === null) { + fs.statSync(base); + linkTarget = fs.readlinkSync(base); } + resolvedLink = pathModule.resolve(previous, linkTarget); + // track this, if given a cache. + if (cache) cache[base] = resolvedLink; + if (!isWindows) seenLinks[id] = linkTarget; } + + // resolve the link, then start over + p = pathModule.resolve(resolvedLink, p.slice(pos)); + start(); } - for (key in values) if (out[key] === undefined) out[key] = values[key] - for (key in bools) if (out[key] === undefined) out[key] = false - for (key in strings) if (out[key] === undefined) out[key] = "" + if (cache) cache[original] = p; - return out -} + return p; +}; -module.exports = getopts +exports.realpath = function realpath(p, cache, cb) { + if (typeof cb !== 'function') { + cb = maybeCallback(cache); + cache = null; + } -/***/ }), + // make p is absolute + p = pathModule.resolve(p); -/***/ "../../node_modules/git-hooks-list/index.json": -/***/ (function(module) { + if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { + return process.nextTick(cb.bind(null, null, cache[p])); + } -module.exports = JSON.parse("[\"applypatch-msg\",\"pre-applypatch\",\"post-applypatch\",\"pre-commit\",\"pre-merge-commit\",\"prepare-commit-msg\",\"commit-msg\",\"post-commit\",\"pre-rebase\",\"post-checkout\",\"post-merge\",\"pre-push\",\"pre-receive\",\"update\",\"post-receive\",\"post-update\",\"push-to-checkout\",\"pre-auto-gc\",\"post-rewrite\",\"sendemail-validate\",\"fsmonitor-watchman\",\"p4-pre-submit\",\"post-index-change\"]"); + var original = p, + seenLinks = {}, + knownHard = {}; -/***/ }), + // current character position in p + var pos; + // the partial path so far, including a trailing slash if any + var current; + // the partial path without a trailing slash (except when pointing at a root) + var base; + // the partial path scanned in the previous round, with slash + var previous; -/***/ "../../node_modules/glob-parent/index.js": -/***/ (function(module, exports, __webpack_require__) { + start(); -"use strict"; + function start() { + // Skip over roots + var m = splitRootRe.exec(p); + pos = m[0].length; + current = m[0]; + base = m[0]; + previous = ''; + // On windows, check that the root exists. On unix there is no need. + if (isWindows && !knownHard[base]) { + fs.lstat(base, function(err) { + if (err) return cb(err); + knownHard[base] = true; + LOOP(); + }); + } else { + process.nextTick(LOOP); + } + } -var isGlob = __webpack_require__("../../node_modules/is-glob/index.js"); -var pathPosixDirname = __webpack_require__("path").posix.dirname; -var isWin32 = __webpack_require__("os").platform() === 'win32'; + // walk down the path, swapping out linked pathparts for their real + // values + function LOOP() { + // stop if scanned past end of path + if (pos >= p.length) { + if (cache) cache[original] = p; + return cb(null, p); + } -var slash = '/'; -var backslash = /\\/g; -var enclosure = /[\{\[].*[\}\]]$/; -var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/; -var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g; + // find the next part + nextPartRe.lastIndex = pos; + var result = nextPartRe.exec(p); + previous = current; + current += result[0]; + base = previous + result[1]; + pos = nextPartRe.lastIndex; -/** - * @param {string} str - * @param {Object} opts - * @param {boolean} [opts.flipBackslashes=true] - * @returns {string} - */ -module.exports = function globParent(str, opts) { - var options = Object.assign({ flipBackslashes: true }, opts); + // continue if not a symlink + if (knownHard[base] || (cache && cache[base] === base)) { + return process.nextTick(LOOP); + } - // flip windows path separators - if (options.flipBackslashes && isWin32 && str.indexOf(slash) < 0) { - str = str.replace(backslash, slash); - } + if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { + // known symbolic link. no need to stat again. + return gotResolvedLink(cache[base]); + } - // special case for strings ending in enclosure containing path separator - if (enclosure.test(str)) { - str += slash; + return fs.lstat(base, gotStat); } - // preserves full path in case of trailing path separator - str += 'a'; + function gotStat(err, stat) { + if (err) return cb(err); - // remove path parts that are globby - do { - str = pathPosixDirname(str); - } while (isGlob(str) || globby.test(str)); + // if not a symlink, skip to the next path part + if (!stat.isSymbolicLink()) { + knownHard[base] = true; + if (cache) cache[base] = base; + return process.nextTick(LOOP); + } - // remove escape chars and return result - return str.replace(escaped, '$1'); + // stat & read the link if not read before + // call gotTarget as soon as the link target is known + // dev/ino always return 0 on windows, so skip the check. + if (!isWindows) { + var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); + if (seenLinks.hasOwnProperty(id)) { + return gotTarget(null, seenLinks[id], base); + } + } + fs.stat(base, function(err) { + if (err) return cb(err); + + fs.readlink(base, function(err, target) { + if (!isWindows) seenLinks[id] = target; + gotTarget(err, target); + }); + }); + } + + function gotTarget(err, target, base) { + if (err) return cb(err); + + var resolvedLink = pathModule.resolve(previous, target); + if (cache) cache[base] = resolvedLink; + gotResolvedLink(resolvedLink); + } + + function gotResolvedLink(resolvedLink) { + // resolve the link, then start over + p = pathModule.resolve(resolvedLink, p.slice(pos)); + start(); + } }; /***/ }), -/***/ "../../node_modules/glob/common.js": +/***/ "../../node_modules/function-bind/implementation.js": /***/ (function(module, exports, __webpack_require__) { -exports.setopts = setopts -exports.ownProp = ownProp -exports.makeAbs = makeAbs -exports.finish = finish -exports.mark = mark -exports.isIgnored = isIgnored -exports.childrenIgnored = childrenIgnored - -function ownProp (obj, field) { - return Object.prototype.hasOwnProperty.call(obj, field) -} - -var fs = __webpack_require__("fs") -var path = __webpack_require__("path") -var minimatch = __webpack_require__("../../node_modules/minimatch/minimatch.js") -var isAbsolute = __webpack_require__("../../node_modules/path-is-absolute/index.js") -var Minimatch = minimatch.Minimatch +"use strict"; -function alphasort (a, b) { - return a.localeCompare(b, 'en') -} -function setupIgnores (self, options) { - self.ignore = options.ignore || [] +/* eslint no-invalid-this: 1 */ - if (!Array.isArray(self.ignore)) - self.ignore = [self.ignore] +var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; +var slice = Array.prototype.slice; +var toStr = Object.prototype.toString; +var funcType = '[object Function]'; - if (self.ignore.length) { - self.ignore = self.ignore.map(ignoreMap) - } -} +module.exports = function bind(that) { + var target = this; + if (typeof target !== 'function' || toStr.call(target) !== funcType) { + throw new TypeError(ERROR_MESSAGE + target); + } + var args = slice.call(arguments, 1); -// ignore patterns are always in dot:true mode. -function ignoreMap (pattern) { - var gmatcher = null - if (pattern.slice(-3) === '/**') { - var gpattern = pattern.replace(/(\/\*\*)+$/, '') - gmatcher = new Minimatch(gpattern, { dot: true }) - } + var bound; + var binder = function () { + if (this instanceof bound) { + var result = target.apply( + this, + args.concat(slice.call(arguments)) + ); + if (Object(result) === result) { + return result; + } + return this; + } else { + return target.apply( + that, + args.concat(slice.call(arguments)) + ); + } + }; - return { - matcher: new Minimatch(pattern, { dot: true }), - gmatcher: gmatcher - } -} + var boundLength = Math.max(0, target.length - args.length); + var boundArgs = []; + for (var i = 0; i < boundLength; i++) { + boundArgs.push('$' + i); + } -function setopts (self, pattern, options) { - if (!options) - options = {} + bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); - // base-matching: just use globstar for that. - if (options.matchBase && -1 === pattern.indexOf("/")) { - if (options.noglobstar) { - throw new Error("base matching requires globstar") + if (target.prototype) { + var Empty = function Empty() {}; + Empty.prototype = target.prototype; + bound.prototype = new Empty(); + Empty.prototype = null; } - pattern = "**/" + pattern - } - self.silent = !!options.silent - self.pattern = pattern - self.strict = options.strict !== false - self.realpath = !!options.realpath - self.realpathCache = options.realpathCache || Object.create(null) - self.follow = !!options.follow - self.dot = !!options.dot - self.mark = !!options.mark - self.nodir = !!options.nodir - if (self.nodir) - self.mark = true - self.sync = !!options.sync - self.nounique = !!options.nounique - self.nonull = !!options.nonull - self.nosort = !!options.nosort - self.nocase = !!options.nocase - self.stat = !!options.stat - self.noprocess = !!options.noprocess - self.absolute = !!options.absolute - self.fs = options.fs || fs + return bound; +}; - self.maxLength = options.maxLength || Infinity - self.cache = options.cache || Object.create(null) - self.statCache = options.statCache || Object.create(null) - self.symlinks = options.symlinks || Object.create(null) - setupIgnores(self, options) +/***/ }), - self.changedCwd = false - var cwd = process.cwd() - if (!ownProp(options, "cwd")) - self.cwd = cwd - else { - self.cwd = path.resolve(options.cwd) - self.changedCwd = self.cwd !== cwd - } +/***/ "../../node_modules/function-bind/index.js": +/***/ (function(module, exports, __webpack_require__) { - self.root = options.root || path.resolve(self.cwd, "/") - self.root = path.resolve(self.root) - if (process.platform === "win32") - self.root = self.root.replace(/\\/g, "/") +"use strict"; - // TODO: is an absolute `cwd` supposed to be resolved against `root`? - // e.g. { cwd: '/test', root: __dirname } === path.join(__dirname, '/test') - self.cwdAbs = isAbsolute(self.cwd) ? self.cwd : makeAbs(self, self.cwd) - if (process.platform === "win32") - self.cwdAbs = self.cwdAbs.replace(/\\/g, "/") - self.nomount = !!options.nomount - // disable comments and negation in Minimatch. - // Note that they are not supported in Glob itself anyway. - options.nonegate = true - options.nocomment = true +var implementation = __webpack_require__("../../node_modules/function-bind/implementation.js"); - self.minimatch = new Minimatch(pattern, options) - self.options = self.minimatch.options -} +module.exports = Function.prototype.bind || implementation; -function finish (self) { - var nou = self.nounique - var all = nou ? [] : Object.create(null) - for (var i = 0, l = self.matches.length; i < l; i ++) { - var matches = self.matches[i] - if (!matches || Object.keys(matches).length === 0) { - if (self.nonull) { - // do like the shell, and spit out the literal glob - var literal = self.minimatch.globSet[i] - if (nou) - all.push(literal) - else - all[literal] = true - } - } else { - // had matches - var m = Object.keys(matches) - if (nou) - all.push.apply(all, m) - else - m.forEach(function (m) { - all[m] = true - }) - } - } +/***/ }), - if (!nou) - all = Object.keys(all) +/***/ "../../node_modules/get-stream/buffer-stream.js": +/***/ (function(module, exports, __webpack_require__) { - if (!self.nosort) - all = all.sort(alphasort) +"use strict"; - // at *some* point we statted all of these - if (self.mark) { - for (var i = 0; i < all.length; i++) { - all[i] = self._mark(all[i]) - } - if (self.nodir) { - all = all.filter(function (e) { - var notDir = !(/\/$/.test(e)) - var c = self.cache[e] || self.cache[makeAbs(self, e)] - if (notDir && c) - notDir = c !== 'DIR' && !Array.isArray(c) - return notDir - }) - } - } +const {PassThrough: PassThroughStream} = __webpack_require__("stream"); - if (self.ignore.length) - all = all.filter(function(m) { - return !isIgnored(self, m) - }) +module.exports = options => { + options = {...options}; - self.found = all -} + const {array} = options; + let {encoding} = options; + const isBuffer = encoding === 'buffer'; + let objectMode = false; -function mark (self, p) { - var abs = makeAbs(self, p) - var c = self.cache[abs] - var m = p - if (c) { - var isDir = c === 'DIR' || Array.isArray(c) - var slash = p.slice(-1) === '/' - - if (isDir && !slash) - m += '/' - else if (!isDir && slash) - m = m.slice(0, -1) + if (array) { + objectMode = !(encoding || isBuffer); + } else { + encoding = encoding || 'utf8'; + } - if (m !== p) { - var mabs = makeAbs(self, m) - self.statCache[mabs] = self.statCache[abs] - self.cache[mabs] = self.cache[abs] - } - } + if (isBuffer) { + encoding = null; + } - return m -} + const stream = new PassThroughStream({objectMode}); -// lotta situps... -function makeAbs (self, f) { - var abs = f - if (f.charAt(0) === '/') { - abs = path.join(self.root, f) - } else if (isAbsolute(f) || f === '') { - abs = f - } else if (self.changedCwd) { - abs = path.resolve(self.cwd, f) - } else { - abs = path.resolve(f) - } + if (encoding) { + stream.setEncoding(encoding); + } - if (process.platform === 'win32') - abs = abs.replace(/\\/g, '/') + let length = 0; + const chunks = []; - return abs -} + stream.on('data', chunk => { + chunks.push(chunk); + if (objectMode) { + length = chunks.length; + } else { + length += chunk.length; + } + }); -// Return true, if pattern ends with globstar '**', for the accompanying parent directory. -// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents -function isIgnored (self, path) { - if (!self.ignore.length) - return false + stream.getBufferedValue = () => { + if (array) { + return chunks; + } - return self.ignore.some(function(item) { - return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path)) - }) -} + return isBuffer ? Buffer.concat(chunks, length) : chunks.join(''); + }; -function childrenIgnored (self, path) { - if (!self.ignore.length) - return false + stream.getBufferedLength = () => length; - return self.ignore.some(function(item) { - return !!(item.gmatcher && item.gmatcher.match(path)) - }) -} + return stream; +}; /***/ }), -/***/ "../../node_modules/glob/glob.js": +/***/ "../../node_modules/get-stream/index.js": /***/ (function(module, exports, __webpack_require__) { -// Approach: -// -// 1. Get the minimatch set -// 2. For each pattern in the set, PROCESS(pattern, false) -// 3. Store matches per-set, then uniq them -// -// PROCESS(pattern, inGlobStar) -// Get the first [n] items from pattern that are all strings -// Join these together. This is PREFIX. -// If there is no more remaining, then stat(PREFIX) and -// add to matches if it succeeds. END. -// -// If inGlobStar and PREFIX is symlink and points to dir -// set ENTRIES = [] -// else readdir(PREFIX) as ENTRIES -// If fail, END -// -// with ENTRIES -// If pattern[n] is GLOBSTAR -// // handle the case where the globstar match is empty -// // by pruning it out, and testing the resulting pattern -// PROCESS(pattern[0..n] + pattern[n+1 .. $], false) -// // handle other cases. -// for ENTRY in ENTRIES (not dotfiles) -// // attach globstar + tail onto the entry -// // Mark that this entry is a globstar match -// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true) -// -// else // not globstar -// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot) -// Test ENTRY against pattern[n] -// If fails, continue -// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $]) -// -// Caveat: -// Cache all stats and readdirs results to minimize syscall. Since all -// we ever care about is existence and directory-ness, we can just keep -// `true` for files, and [children,...] for directories, or `false` for -// things that don't exist. - -module.exports = glob - -var rp = __webpack_require__("../../node_modules/fs.realpath/index.js") -var minimatch = __webpack_require__("../../node_modules/minimatch/minimatch.js") -var Minimatch = minimatch.Minimatch -var inherits = __webpack_require__("../../node_modules/inherits/inherits.js") -var EE = __webpack_require__("events").EventEmitter -var path = __webpack_require__("path") -var assert = __webpack_require__("assert") -var isAbsolute = __webpack_require__("../../node_modules/path-is-absolute/index.js") -var globSync = __webpack_require__("../../node_modules/glob/sync.js") -var common = __webpack_require__("../../node_modules/glob/common.js") -var setopts = common.setopts -var ownProp = common.ownProp -var inflight = __webpack_require__("../../node_modules/inflight/inflight.js") -var util = __webpack_require__("util") -var childrenIgnored = common.childrenIgnored -var isIgnored = common.isIgnored - -var once = __webpack_require__("../../node_modules/once/once.js") - -function glob (pattern, options, cb) { - if (typeof options === 'function') cb = options, options = {} - if (!options) options = {} +"use strict"; - if (options.sync) { - if (cb) - throw new TypeError('callback provided to sync glob') - return globSync(pattern, options) - } +const pump = __webpack_require__("../../node_modules/pump/index.js"); +const bufferStream = __webpack_require__("../../node_modules/get-stream/buffer-stream.js"); - return new Glob(pattern, options, cb) +class MaxBufferError extends Error { + constructor() { + super('maxBuffer exceeded'); + this.name = 'MaxBufferError'; + } } -glob.sync = globSync -var GlobSync = glob.GlobSync = globSync.GlobSync - -// old api surface -glob.glob = glob +async function getStream(inputStream, options) { + if (!inputStream) { + return Promise.reject(new Error('Expected a stream')); + } -function extend (origin, add) { - if (add === null || typeof add !== 'object') { - return origin - } + options = { + maxBuffer: Infinity, + ...options + }; - var keys = Object.keys(add) - var i = keys.length - while (i--) { - origin[keys[i]] = add[keys[i]] - } - return origin -} + const {maxBuffer} = options; -glob.hasMagic = function (pattern, options_) { - var options = extend({}, options_) - options.noprocess = true + let stream; + await new Promise((resolve, reject) => { + const rejectPromise = error => { + if (error) { // A null check + error.bufferedData = stream.getBufferedValue(); + } - var g = new Glob(pattern, options) - var set = g.minimatch.set + reject(error); + }; - if (!pattern) - return false + stream = pump(inputStream, bufferStream(options), error => { + if (error) { + rejectPromise(error); + return; + } - if (set.length > 1) - return true + resolve(); + }); - for (var j = 0; j < set[0].length; j++) { - if (typeof set[0][j] !== 'string') - return true - } + stream.on('data', () => { + if (stream.getBufferedLength() > maxBuffer) { + rejectPromise(new MaxBufferError()); + } + }); + }); - return false + return stream.getBufferedValue(); } -glob.Glob = Glob -inherits(Glob, EE) -function Glob (pattern, options, cb) { - if (typeof options === 'function') { - cb = options - options = null - } +module.exports = getStream; +// TODO: Remove this for the next major release +module.exports.default = getStream; +module.exports.buffer = (stream, options) => getStream(stream, {...options, encoding: 'buffer'}); +module.exports.array = (stream, options) => getStream(stream, {...options, array: true}); +module.exports.MaxBufferError = MaxBufferError; - if (options && options.sync) { - if (cb) - throw new TypeError('callback provided to sync glob') - return new GlobSync(pattern, options) - } - if (!(this instanceof Glob)) - return new Glob(pattern, options, cb) +/***/ }), - setopts(this, pattern, options) - this._didRealPath = false +/***/ "../../node_modules/getopts/index.js": +/***/ (function(module, exports, __webpack_require__) { - // process each pattern in the minimatch set - var n = this.minimatch.set.length +"use strict"; - // The matches are stored as {: true,...} so that - // duplicates are automagically pruned. - // Later, we do an Object.keys() on these. - // Keep them as a list so we can fill in when nonull is set. - this.matches = new Array(n) - if (typeof cb === 'function') { - cb = once(cb) - this.on('error', cb) - this.on('end', function (matches) { - cb(null, matches) - }) - } - - var self = this - this._processing = 0 +const EMPTYARR = [] +const SHORTSPLIT = /$|[!-@[-`{-~][\s\S]*/g +const isArray = Array.isArray - this._emitQueue = [] - this._processQueue = [] - this.paused = false +const parseValue = function(any) { + if (any === "") return "" + if (any === "false") return false + const maybe = Number(any) + return maybe * 0 === 0 ? maybe : any +} - if (this.noprocess) - return this +const parseAlias = function(aliases) { + let out = {}, + key, + alias, + prev, + len, + any, + i, + k - if (n === 0) - return done() + for (key in aliases) { + any = aliases[key] + alias = out[key] = isArray(any) ? any : [any] - var sync = true - for (var i = 0; i < n; i ++) { - this._process(this.minimatch.set[i], i, false, done) - } - sync = false + for (i = 0, len = alias.length; i < len; i++) { + prev = out[alias[i]] = [key] - function done () { - --self._processing - if (self._processing <= 0) { - if (sync) { - process.nextTick(function () { - self._finish() - }) - } else { - self._finish() + for (k = 0; k < len; k++) { + if (i !== k) prev.push(alias[k]) } } } -} - -Glob.prototype._finish = function () { - assert(this instanceof Glob) - if (this.aborted) - return - - if (this.realpath && !this._didRealpath) - return this._realpath() - common.finish(this) - this.emit('end', this.found) + return out } -Glob.prototype._realpath = function () { - if (this._didRealpath) - return - - this._didRealpath = true +const parseDefault = function(aliases, defaults) { + let out = {}, + key, + alias, + value, + len, + i - var n = this.matches.length - if (n === 0) - return this._finish() + for (key in defaults) { + value = defaults[key] + alias = aliases[key] - var self = this - for (var i = 0; i < this.matches.length; i++) - this._realpathSet(i, next) + out[key] = value - function next () { - if (--n === 0) - self._finish() + if (alias === undefined) { + aliases[key] = EMPTYARR + } else { + for (i = 0, len = alias.length; i < len; i++) { + out[alias[i]] = value + } + } } -} -Glob.prototype._realpathSet = function (index, cb) { - var matchset = this.matches[index] - if (!matchset) - return cb() + return out +} - var found = Object.keys(matchset) - var self = this - var n = found.length +const parseOptions = function(aliases, options, value) { + let out = {}, + key, + alias, + len, + end, + i, + k - if (n === 0) - return cb() + if (options !== undefined) { + for (i = 0, len = options.length; i < len; i++) { + key = options[i] + alias = aliases[key] - var set = this.matches[index] = Object.create(null) - found.forEach(function (p, i) { - // If there's a problem with the stat, then it means that - // one or more of the links in the realpath couldn't be - // resolved. just return the abs value in that case. - p = self._makeAbs(p) - rp.realpath(p, self.realpathCache, function (er, real) { - if (!er) - set[real] = true - else if (er.syscall === 'stat') - set[p] = true - else - self.emit('error', er) // srsly wtf right here + out[key] = value - if (--n === 0) { - self.matches[index] = set - cb() + if (alias === undefined) { + aliases[key] = EMPTYARR + } else { + for (k = 0, end = alias.length; k < end; k++) { + out[alias[k]] = value + } } - }) - }) -} - -Glob.prototype._mark = function (p) { - return common.mark(this, p) -} + } + } -Glob.prototype._makeAbs = function (f) { - return common.makeAbs(this, f) + return out } -Glob.prototype.abort = function () { - this.aborted = true - this.emit('abort') -} +const write = function(out, key, value, aliases, unknown) { + let i, + prev, + alias = aliases[key], + len = alias === undefined ? -1 : alias.length -Glob.prototype.pause = function () { - if (!this.paused) { - this.paused = true - this.emit('pause') - } -} + if (len >= 0 || unknown === undefined || unknown(key)) { + prev = out[key] -Glob.prototype.resume = function () { - if (this.paused) { - this.emit('resume') - this.paused = false - if (this._emitQueue.length) { - var eq = this._emitQueue.slice(0) - this._emitQueue.length = 0 - for (var i = 0; i < eq.length; i ++) { - var e = eq[i] - this._emitMatch(e[0], e[1]) + if (prev === undefined) { + out[key] = value + } else { + if (isArray(prev)) { + prev.push(value) + } else { + out[key] = [prev, value] } } - if (this._processQueue.length) { - var pq = this._processQueue.slice(0) - this._processQueue.length = 0 - for (var i = 0; i < pq.length; i ++) { - var p = pq[i] - this._processing-- - this._process(p[0], p[1], p[2], p[3]) - } + + for (i = 0; i < len; i++) { + out[alias[i]] = out[key] } } } -Glob.prototype._process = function (pattern, index, inGlobStar, cb) { - assert(this instanceof Glob) - assert(typeof cb === 'function') - - if (this.aborted) - return - - this._processing++ - if (this.paused) { - this._processQueue.push([pattern, index, inGlobStar, cb]) - return - } - - //console.error('PROCESS %d', this._processing, pattern) - - // Get the first [n] parts of pattern that are all strings. - var n = 0 - while (typeof pattern[n] === 'string') { - n ++ - } - // now n is the index of the first one that is *not* a string. - - // see if there's anything else - var prefix - switch (n) { - // if not, then this is rather simple - case pattern.length: - this._processSimple(pattern.join('/'), index, cb) - return +const getopts = function(argv, opts) { + let unknown = (opts = opts || {}).unknown, + aliases = parseAlias(opts.alias), + strings = parseOptions(aliases, opts.string, ""), + values = parseDefault(aliases, opts.default), + bools = parseOptions(aliases, opts.boolean, false), + stopEarly = opts.stopEarly, + _ = [], + out = { _ }, + i = 0, + k = 0, + len = argv.length, + key, + arg, + end, + match, + value - case 0: - // pattern *starts* with some non-trivial item. - // going to readdir(cwd), but not include the prefix in matches. - prefix = null - break + for (; i < len; i++) { + arg = argv[i] - default: - // pattern has some string bits in the front. - // whatever it starts with, whether that's 'absolute' like /foo/bar, - // or 'relative' like '../baz' - prefix = pattern.slice(0, n).join('/') - break - } + if (arg[0] !== "-" || arg === "-") { + if (stopEarly) while (i < len) _.push(argv[i++]) + else _.push(arg) + } else if (arg === "--") { + while (++i < len) _.push(argv[i]) + } else if (arg[1] === "-") { + end = arg.indexOf("=", 2) + if (arg[2] === "n" && arg[3] === "o" && arg[4] === "-") { + key = arg.slice(5, end >= 0 ? end : undefined) + value = false + } else if (end >= 0) { + key = arg.slice(2, end) + value = + bools[key] !== undefined || + (strings[key] === undefined + ? parseValue(arg.slice(end + 1)) + : arg.slice(end + 1)) + } else { + key = arg.slice(2) + value = + bools[key] !== undefined || + (len === i + 1 || argv[i + 1][0] === "-" + ? strings[key] === undefined + ? true + : "" + : strings[key] === undefined + ? parseValue(argv[++i]) + : argv[++i]) + } + write(out, key, value, aliases, unknown) + } else { + SHORTSPLIT.lastIndex = 2 + match = SHORTSPLIT.exec(arg) + end = match.index + value = match[0] - var remain = pattern.slice(n) + for (k = 1; k < end; k++) { + write( + out, + (key = arg[k]), + k + 1 < end + ? strings[key] === undefined || + arg.substring(k + 1, (k = end)) + value + : value === "" + ? len === i + 1 || argv[i + 1][0] === "-" + ? strings[key] === undefined || "" + : bools[key] !== undefined || + (strings[key] === undefined ? parseValue(argv[++i]) : argv[++i]) + : bools[key] !== undefined || + (strings[key] === undefined ? parseValue(value) : value), + aliases, + unknown + ) + } + } + } - // get the list of entries. - var read - if (prefix === null) - read = '.' - else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { - if (!prefix || !isAbsolute(prefix)) - prefix = '/' + prefix - read = prefix - } else - read = prefix + for (key in values) if (out[key] === undefined) out[key] = values[key] + for (key in bools) if (out[key] === undefined) out[key] = false + for (key in strings) if (out[key] === undefined) out[key] = "" - var abs = this._makeAbs(read) + return out +} - //if ignored, skip _processing - if (childrenIgnored(this, read)) - return cb() +module.exports = getopts - var isGlobStar = remain[0] === minimatch.GLOBSTAR - if (isGlobStar) - this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb) - else - this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb) -} -Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) { - var self = this - this._readdir(abs, inGlobStar, function (er, entries) { - return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb) - }) -} +/***/ }), -Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { +/***/ "../../node_modules/git-hooks-list/index.json": +/***/ (function(module) { - // if the abs isn't a dir, then nothing can match! - if (!entries) - return cb() +module.exports = JSON.parse("[\"applypatch-msg\",\"pre-applypatch\",\"post-applypatch\",\"pre-commit\",\"pre-merge-commit\",\"prepare-commit-msg\",\"commit-msg\",\"post-commit\",\"pre-rebase\",\"post-checkout\",\"post-merge\",\"pre-push\",\"pre-receive\",\"update\",\"post-receive\",\"post-update\",\"push-to-checkout\",\"pre-auto-gc\",\"post-rewrite\",\"sendemail-validate\",\"fsmonitor-watchman\",\"p4-pre-submit\",\"post-index-change\"]"); - // It will only match dot entries if it starts with a dot, or if - // dot is set. Stuff like @(.foo|.bar) isn't allowed. - var pn = remain[0] - var negate = !!this.minimatch.negate - var rawGlob = pn._glob - var dotOk = this.dot || rawGlob.charAt(0) === '.' +/***/ }), - var matchedEntries = [] - for (var i = 0; i < entries.length; i++) { - var e = entries[i] - if (e.charAt(0) !== '.' || dotOk) { - var m - if (negate && !prefix) { - m = !e.match(pn) - } else { - m = e.match(pn) - } - if (m) - matchedEntries.push(e) - } - } +/***/ "../../node_modules/glob-parent/index.js": +/***/ (function(module, exports, __webpack_require__) { - //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries) +"use strict"; - var len = matchedEntries.length - // If there are no matched entries, then nothing matches. - if (len === 0) - return cb() - // if this is the last remaining pattern bit, then no need for - // an additional stat *unless* the user has specified mark or - // stat explicitly. We know they exist, since readdir returned - // them. +var isGlob = __webpack_require__("../../node_modules/is-glob/index.js"); +var pathPosixDirname = __webpack_require__("path").posix.dirname; +var isWin32 = __webpack_require__("os").platform() === 'win32'; - if (remain.length === 1 && !this.mark && !this.stat) { - if (!this.matches[index]) - this.matches[index] = Object.create(null) +var slash = '/'; +var backslash = /\\/g; +var enclosure = /[\{\[].*[\}\]]$/; +var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/; +var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g; - for (var i = 0; i < len; i ++) { - var e = matchedEntries[i] - if (prefix) { - if (prefix !== '/') - e = prefix + '/' + e - else - e = prefix + e - } +/** + * @param {string} str + * @param {Object} opts + * @param {boolean} [opts.flipBackslashes=true] + * @returns {string} + */ +module.exports = function globParent(str, opts) { + var options = Object.assign({ flipBackslashes: true }, opts); - if (e.charAt(0) === '/' && !this.nomount) { - e = path.join(this.root, e) - } - this._emitMatch(index, e) - } - // This was the last one, and no stats were needed - return cb() + // flip windows path separators + if (options.flipBackslashes && isWin32 && str.indexOf(slash) < 0) { + str = str.replace(backslash, slash); } - // now test all matched entries as stand-ins for that part - // of the pattern. - remain.shift() - for (var i = 0; i < len; i ++) { - var e = matchedEntries[i] - var newPattern - if (prefix) { - if (prefix !== '/') - e = prefix + '/' + e - else - e = prefix + e - } - this._process([e].concat(remain), index, inGlobStar, cb) + // special case for strings ending in enclosure containing path separator + if (enclosure.test(str)) { + str += slash; } - cb() -} - -Glob.prototype._emitMatch = function (index, e) { - if (this.aborted) - return - if (isIgnored(this, e)) - return + // preserves full path in case of trailing path separator + str += 'a'; - if (this.paused) { - this._emitQueue.push([index, e]) - return - } + // remove path parts that are globby + do { + str = pathPosixDirname(str); + } while (isGlob(str) || globby.test(str)); - var abs = isAbsolute(e) ? e : this._makeAbs(e) + // remove escape chars and return result + return str.replace(escaped, '$1'); +}; - if (this.mark) - e = this._mark(e) - if (this.absolute) - e = abs +/***/ }), - if (this.matches[index][e]) - return +/***/ "../../node_modules/glob/common.js": +/***/ (function(module, exports, __webpack_require__) { - if (this.nodir) { - var c = this.cache[abs] - if (c === 'DIR' || Array.isArray(c)) - return - } +exports.setopts = setopts +exports.ownProp = ownProp +exports.makeAbs = makeAbs +exports.finish = finish +exports.mark = mark +exports.isIgnored = isIgnored +exports.childrenIgnored = childrenIgnored - this.matches[index][e] = true +function ownProp (obj, field) { + return Object.prototype.hasOwnProperty.call(obj, field) +} - var st = this.statCache[abs] - if (st) - this.emit('stat', e, st) +var fs = __webpack_require__("fs") +var path = __webpack_require__("path") +var minimatch = __webpack_require__("../../node_modules/minimatch/minimatch.js") +var isAbsolute = __webpack_require__("../../node_modules/path-is-absolute/index.js") +var Minimatch = minimatch.Minimatch - this.emit('match', e) +function alphasort (a, b) { + return a.localeCompare(b, 'en') } -Glob.prototype._readdirInGlobStar = function (abs, cb) { - if (this.aborted) - return +function setupIgnores (self, options) { + self.ignore = options.ignore || [] - // follow all symlinked directories forever - // just proceed as if this is a non-globstar situation - if (this.follow) - return this._readdir(abs, false, cb) - - var lstatkey = 'lstat\0' + abs - var self = this - var lstatcb = inflight(lstatkey, lstatcb_) - - if (lstatcb) - self.fs.lstat(abs, lstatcb) + if (!Array.isArray(self.ignore)) + self.ignore = [self.ignore] - function lstatcb_ (er, lstat) { - if (er && er.code === 'ENOENT') - return cb() + if (self.ignore.length) { + self.ignore = self.ignore.map(ignoreMap) + } +} - var isSym = lstat && lstat.isSymbolicLink() - self.symlinks[abs] = isSym +// ignore patterns are always in dot:true mode. +function ignoreMap (pattern) { + var gmatcher = null + if (pattern.slice(-3) === '/**') { + var gpattern = pattern.replace(/(\/\*\*)+$/, '') + gmatcher = new Minimatch(gpattern, { dot: true }) + } - // If it's not a symlink or a dir, then it's definitely a regular file. - // don't bother doing a readdir in that case. - if (!isSym && lstat && !lstat.isDirectory()) { - self.cache[abs] = 'FILE' - cb() - } else - self._readdir(abs, false, cb) + return { + matcher: new Minimatch(pattern, { dot: true }), + gmatcher: gmatcher } } -Glob.prototype._readdir = function (abs, inGlobStar, cb) { - if (this.aborted) - return +function setopts (self, pattern, options) { + if (!options) + options = {} - cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb) - if (!cb) - return + // base-matching: just use globstar for that. + if (options.matchBase && -1 === pattern.indexOf("/")) { + if (options.noglobstar) { + throw new Error("base matching requires globstar") + } + pattern = "**/" + pattern + } - //console.error('RD %j %j', +inGlobStar, abs) - if (inGlobStar && !ownProp(this.symlinks, abs)) - return this._readdirInGlobStar(abs, cb) + self.silent = !!options.silent + self.pattern = pattern + self.strict = options.strict !== false + self.realpath = !!options.realpath + self.realpathCache = options.realpathCache || Object.create(null) + self.follow = !!options.follow + self.dot = !!options.dot + self.mark = !!options.mark + self.nodir = !!options.nodir + if (self.nodir) + self.mark = true + self.sync = !!options.sync + self.nounique = !!options.nounique + self.nonull = !!options.nonull + self.nosort = !!options.nosort + self.nocase = !!options.nocase + self.stat = !!options.stat + self.noprocess = !!options.noprocess + self.absolute = !!options.absolute + self.fs = options.fs || fs - if (ownProp(this.cache, abs)) { - var c = this.cache[abs] - if (!c || c === 'FILE') - return cb() + self.maxLength = options.maxLength || Infinity + self.cache = options.cache || Object.create(null) + self.statCache = options.statCache || Object.create(null) + self.symlinks = options.symlinks || Object.create(null) - if (Array.isArray(c)) - return cb(null, c) + setupIgnores(self, options) + + self.changedCwd = false + var cwd = process.cwd() + if (!ownProp(options, "cwd")) + self.cwd = cwd + else { + self.cwd = path.resolve(options.cwd) + self.changedCwd = self.cwd !== cwd } - var self = this - self.fs.readdir(abs, readdirCb(this, abs, cb)) -} + self.root = options.root || path.resolve(self.cwd, "/") + self.root = path.resolve(self.root) + if (process.platform === "win32") + self.root = self.root.replace(/\\/g, "/") -function readdirCb (self, abs, cb) { - return function (er, entries) { - if (er) - self._readdirError(abs, er, cb) - else - self._readdirEntries(abs, entries, cb) - } + // TODO: is an absolute `cwd` supposed to be resolved against `root`? + // e.g. { cwd: '/test', root: __dirname } === path.join(__dirname, '/test') + self.cwdAbs = isAbsolute(self.cwd) ? self.cwd : makeAbs(self, self.cwd) + if (process.platform === "win32") + self.cwdAbs = self.cwdAbs.replace(/\\/g, "/") + self.nomount = !!options.nomount + + // disable comments and negation in Minimatch. + // Note that they are not supported in Glob itself anyway. + options.nonegate = true + options.nocomment = true + + self.minimatch = new Minimatch(pattern, options) + self.options = self.minimatch.options } -Glob.prototype._readdirEntries = function (abs, entries, cb) { - if (this.aborted) - return +function finish (self) { + var nou = self.nounique + var all = nou ? [] : Object.create(null) - // if we haven't asked to stat everything, then just - // assume that everything in there exists, so we can avoid - // having to stat it a second time. - if (!this.mark && !this.stat) { - for (var i = 0; i < entries.length; i ++) { - var e = entries[i] - if (abs === '/') - e = abs + e + for (var i = 0, l = self.matches.length; i < l; i ++) { + var matches = self.matches[i] + if (!matches || Object.keys(matches).length === 0) { + if (self.nonull) { + // do like the shell, and spit out the literal glob + var literal = self.minimatch.globSet[i] + if (nou) + all.push(literal) + else + all[literal] = true + } + } else { + // had matches + var m = Object.keys(matches) + if (nou) + all.push.apply(all, m) else - e = abs + '/' + e - this.cache[e] = true + m.forEach(function (m) { + all[m] = true + }) } } - this.cache[abs] = entries - return cb(null, entries) -} - -Glob.prototype._readdirError = function (f, er, cb) { - if (this.aborted) - return - - // handle errors, and cache the information - switch (er.code) { - case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 - case 'ENOTDIR': // totally normal. means it *does* exist. - var abs = this._makeAbs(f) - this.cache[abs] = 'FILE' - if (abs === this.cwdAbs) { - var error = new Error(er.code + ' invalid cwd ' + this.cwd) - error.path = this.cwd - error.code = er.code - this.emit('error', error) - this.abort() - } - break + if (!nou) + all = Object.keys(all) - case 'ENOENT': // not terribly unusual - case 'ELOOP': - case 'ENAMETOOLONG': - case 'UNKNOWN': - this.cache[this._makeAbs(f)] = false - break + if (!self.nosort) + all = all.sort(alphasort) - default: // some unusual error. Treat as failure. - this.cache[this._makeAbs(f)] = false - if (this.strict) { - this.emit('error', er) - // If the error is handled, then we abort - // if not, we threw out of here - this.abort() - } - if (!this.silent) - console.error('glob error', er) - break + // at *some* point we statted all of these + if (self.mark) { + for (var i = 0; i < all.length; i++) { + all[i] = self._mark(all[i]) + } + if (self.nodir) { + all = all.filter(function (e) { + var notDir = !(/\/$/.test(e)) + var c = self.cache[e] || self.cache[makeAbs(self, e)] + if (notDir && c) + notDir = c !== 'DIR' && !Array.isArray(c) + return notDir + }) + } } - return cb() -} + if (self.ignore.length) + all = all.filter(function(m) { + return !isIgnored(self, m) + }) -Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) { - var self = this - this._readdir(abs, inGlobStar, function (er, entries) { - self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb) - }) + self.found = all } +function mark (self, p) { + var abs = makeAbs(self, p) + var c = self.cache[abs] + var m = p + if (c) { + var isDir = c === 'DIR' || Array.isArray(c) + var slash = p.slice(-1) === '/' -Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { - //console.error('pgs2', prefix, remain[0], entries) + if (isDir && !slash) + m += '/' + else if (!isDir && slash) + m = m.slice(0, -1) - // no entries means not a dir, so it can never have matches - // foo.txt/** doesn't match foo.txt - if (!entries) - return cb() + if (m !== p) { + var mabs = makeAbs(self, m) + self.statCache[mabs] = self.statCache[abs] + self.cache[mabs] = self.cache[abs] + } + } - // test without the globstar, and with every child both below - // and replacing the globstar. - var remainWithoutGlobStar = remain.slice(1) - var gspref = prefix ? [ prefix ] : [] - var noGlobStar = gspref.concat(remainWithoutGlobStar) + return m +} - // the noGlobStar pattern exits the inGlobStar state - this._process(noGlobStar, index, false, cb) - - var isSym = this.symlinks[abs] - var len = entries.length - - // If it's a symlink, and we're in a globstar, then stop - if (isSym && inGlobStar) - return cb() - - for (var i = 0; i < len; i++) { - var e = entries[i] - if (e.charAt(0) === '.' && !this.dot) - continue - - // these two cases enter the inGlobStar state - var instead = gspref.concat(entries[i], remainWithoutGlobStar) - this._process(instead, index, true, cb) - - var below = gspref.concat(entries[i], remain) - this._process(below, index, true, cb) - } - - cb() -} - -Glob.prototype._processSimple = function (prefix, index, cb) { - // XXX review this. Shouldn't it be doing the mounting etc - // before doing stat? kinda weird? - var self = this - this._stat(prefix, function (er, exists) { - self._processSimple2(prefix, index, er, exists, cb) - }) -} -Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) { - - //console.error('ps2', prefix, exists) - - if (!this.matches[index]) - this.matches[index] = Object.create(null) - - // If it doesn't exist, then just mark the lack of results - if (!exists) - return cb() - - if (prefix && isAbsolute(prefix) && !this.nomount) { - var trail = /[\/\\]$/.test(prefix) - if (prefix.charAt(0) === '/') { - prefix = path.join(this.root, prefix) - } else { - prefix = path.resolve(this.root, prefix) - if (trail) - prefix += '/' - } +// lotta situps... +function makeAbs (self, f) { + var abs = f + if (f.charAt(0) === '/') { + abs = path.join(self.root, f) + } else if (isAbsolute(f) || f === '') { + abs = f + } else if (self.changedCwd) { + abs = path.resolve(self.cwd, f) + } else { + abs = path.resolve(f) } if (process.platform === 'win32') - prefix = prefix.replace(/\\/g, '/') + abs = abs.replace(/\\/g, '/') - // Mark this as a match - this._emitMatch(index, prefix) - cb() + return abs } -// Returns either 'DIR', 'FILE', or false -Glob.prototype._stat = function (f, cb) { - var abs = this._makeAbs(f) - var needDir = f.slice(-1) === '/' - - if (f.length > this.maxLength) - return cb() - - if (!this.stat && ownProp(this.cache, abs)) { - var c = this.cache[abs] - - if (Array.isArray(c)) - c = 'DIR' - - // It exists, but maybe not how we need it - if (!needDir || c === 'DIR') - return cb(null, c) - - if (needDir && c === 'FILE') - return cb() - - // otherwise we have to stat, because maybe c=true - // if we know it exists, but not what it is. - } - - var exists - var stat = this.statCache[abs] - if (stat !== undefined) { - if (stat === false) - return cb(null, stat) - else { - var type = stat.isDirectory() ? 'DIR' : 'FILE' - if (needDir && type === 'FILE') - return cb() - else - return cb(null, type, stat) - } - } - var self = this - var statcb = inflight('stat\0' + abs, lstatcb_) - if (statcb) - self.fs.lstat(abs, statcb) +// Return true, if pattern ends with globstar '**', for the accompanying parent directory. +// Ex:- If node_modules/** is the pattern, add 'node_modules' to ignore list along with it's contents +function isIgnored (self, path) { + if (!self.ignore.length) + return false - function lstatcb_ (er, lstat) { - if (lstat && lstat.isSymbolicLink()) { - // If it's a symlink, then treat it as the target, unless - // the target does not exist, then treat it as a file. - return self.fs.stat(abs, function (er, stat) { - if (er) - self._stat2(f, abs, null, lstat, cb) - else - self._stat2(f, abs, er, stat, cb) - }) - } else { - self._stat2(f, abs, er, lstat, cb) - } - } + return self.ignore.some(function(item) { + return item.matcher.match(path) || !!(item.gmatcher && item.gmatcher.match(path)) + }) } -Glob.prototype._stat2 = function (f, abs, er, stat, cb) { - if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { - this.statCache[abs] = false - return cb() - } - - var needDir = f.slice(-1) === '/' - this.statCache[abs] = stat - - if (abs.slice(-1) === '/' && stat && !stat.isDirectory()) - return cb(null, false, stat) - - var c = true - if (stat) - c = stat.isDirectory() ? 'DIR' : 'FILE' - this.cache[abs] = this.cache[abs] || c - - if (needDir && c === 'FILE') - return cb() +function childrenIgnored (self, path) { + if (!self.ignore.length) + return false - return cb(null, c, stat) + return self.ignore.some(function(item) { + return !!(item.gmatcher && item.gmatcher.match(path)) + }) } /***/ }), -/***/ "../../node_modules/glob/sync.js": +/***/ "../../node_modules/glob/glob.js": /***/ (function(module, exports, __webpack_require__) { -module.exports = globSync -globSync.GlobSync = GlobSync +// Approach: +// +// 1. Get the minimatch set +// 2. For each pattern in the set, PROCESS(pattern, false) +// 3. Store matches per-set, then uniq them +// +// PROCESS(pattern, inGlobStar) +// Get the first [n] items from pattern that are all strings +// Join these together. This is PREFIX. +// If there is no more remaining, then stat(PREFIX) and +// add to matches if it succeeds. END. +// +// If inGlobStar and PREFIX is symlink and points to dir +// set ENTRIES = [] +// else readdir(PREFIX) as ENTRIES +// If fail, END +// +// with ENTRIES +// If pattern[n] is GLOBSTAR +// // handle the case where the globstar match is empty +// // by pruning it out, and testing the resulting pattern +// PROCESS(pattern[0..n] + pattern[n+1 .. $], false) +// // handle other cases. +// for ENTRY in ENTRIES (not dotfiles) +// // attach globstar + tail onto the entry +// // Mark that this entry is a globstar match +// PROCESS(pattern[0..n] + ENTRY + pattern[n .. $], true) +// +// else // not globstar +// for ENTRY in ENTRIES (not dotfiles, unless pattern[n] is dot) +// Test ENTRY against pattern[n] +// If fails, continue +// If passes, PROCESS(pattern[0..n] + item + pattern[n+1 .. $]) +// +// Caveat: +// Cache all stats and readdirs results to minimize syscall. Since all +// we ever care about is existence and directory-ness, we can just keep +// `true` for files, and [children,...] for directories, or `false` for +// things that don't exist. + +module.exports = glob var rp = __webpack_require__("../../node_modules/fs.realpath/index.js") var minimatch = __webpack_require__("../../node_modules/minimatch/minimatch.js") var Minimatch = minimatch.Minimatch -var Glob = __webpack_require__("../../node_modules/glob/glob.js").Glob -var util = __webpack_require__("util") +var inherits = __webpack_require__("../../node_modules/inherits/inherits.js") +var EE = __webpack_require__("events").EventEmitter var path = __webpack_require__("path") var assert = __webpack_require__("assert") var isAbsolute = __webpack_require__("../../node_modules/path-is-absolute/index.js") +var globSync = __webpack_require__("../../node_modules/glob/sync.js") var common = __webpack_require__("../../node_modules/glob/common.js") var setopts = common.setopts var ownProp = common.ownProp +var inflight = __webpack_require__("../../node_modules/inflight/inflight.js") +var util = __webpack_require__("util") var childrenIgnored = common.childrenIgnored var isIgnored = common.isIgnored -function globSync (pattern, options) { - if (typeof options === 'function' || arguments.length === 3) - throw new TypeError('callback provided to sync glob\n'+ - 'See: https://github.com/isaacs/node-glob/issues/167') - - return new GlobSync(pattern, options).found -} +var once = __webpack_require__("../../node_modules/once/once.js") -function GlobSync (pattern, options) { - if (!pattern) - throw new Error('must provide pattern') +function glob (pattern, options, cb) { + if (typeof options === 'function') cb = options, options = {} + if (!options) options = {} - if (typeof options === 'function' || arguments.length === 3) - throw new TypeError('callback provided to sync glob\n'+ - 'See: https://github.com/isaacs/node-glob/issues/167') + if (options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return globSync(pattern, options) + } - if (!(this instanceof GlobSync)) - return new GlobSync(pattern, options) + return new Glob(pattern, options, cb) +} - setopts(this, pattern, options) +glob.sync = globSync +var GlobSync = glob.GlobSync = globSync.GlobSync - if (this.noprocess) - return this +// old api surface +glob.glob = glob - var n = this.minimatch.set.length - this.matches = new Array(n) - for (var i = 0; i < n; i ++) { - this._process(this.minimatch.set[i], i, false) +function extend (origin, add) { + if (add === null || typeof add !== 'object') { + return origin } - this._finish() -} -GlobSync.prototype._finish = function () { - assert(this instanceof GlobSync) - if (this.realpath) { - var self = this - this.matches.forEach(function (matchset, index) { - var set = self.matches[index] = Object.create(null) - for (var p in matchset) { - try { - p = self._makeAbs(p) - var real = rp.realpathSync(p, self.realpathCache) - set[real] = true - } catch (er) { - if (er.syscall === 'stat') - set[self._makeAbs(p)] = true - else - throw er - } - } + var keys = Object.keys(add) + var i = keys.length + while (i--) { + origin[keys[i]] = add[keys[i]] + } + return origin +} + +glob.hasMagic = function (pattern, options_) { + var options = extend({}, options_) + options.noprocess = true + + var g = new Glob(pattern, options) + var set = g.minimatch.set + + if (!pattern) + return false + + if (set.length > 1) + return true + + for (var j = 0; j < set[0].length; j++) { + if (typeof set[0][j] !== 'string') + return true + } + + return false +} + +glob.Glob = Glob +inherits(Glob, EE) +function Glob (pattern, options, cb) { + if (typeof options === 'function') { + cb = options + options = null + } + + if (options && options.sync) { + if (cb) + throw new TypeError('callback provided to sync glob') + return new GlobSync(pattern, options) + } + + if (!(this instanceof Glob)) + return new Glob(pattern, options, cb) + + setopts(this, pattern, options) + this._didRealPath = false + + // process each pattern in the minimatch set + var n = this.minimatch.set.length + + // The matches are stored as {: true,...} so that + // duplicates are automagically pruned. + // Later, we do an Object.keys() on these. + // Keep them as a list so we can fill in when nonull is set. + this.matches = new Array(n) + + if (typeof cb === 'function') { + cb = once(cb) + this.on('error', cb) + this.on('end', function (matches) { + cb(null, matches) }) } + + var self = this + this._processing = 0 + + this._emitQueue = [] + this._processQueue = [] + this.paused = false + + if (this.noprocess) + return this + + if (n === 0) + return done() + + var sync = true + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false, done) + } + sync = false + + function done () { + --self._processing + if (self._processing <= 0) { + if (sync) { + process.nextTick(function () { + self._finish() + }) + } else { + self._finish() + } + } + } +} + +Glob.prototype._finish = function () { + assert(this instanceof Glob) + if (this.aborted) + return + + if (this.realpath && !this._didRealpath) + return this._realpath() + common.finish(this) + this.emit('end', this.found) } +Glob.prototype._realpath = function () { + if (this._didRealpath) + return -GlobSync.prototype._process = function (pattern, index, inGlobStar) { - assert(this instanceof GlobSync) + this._didRealpath = true + + var n = this.matches.length + if (n === 0) + return this._finish() + + var self = this + for (var i = 0; i < this.matches.length; i++) + this._realpathSet(i, next) + + function next () { + if (--n === 0) + self._finish() + } +} + +Glob.prototype._realpathSet = function (index, cb) { + var matchset = this.matches[index] + if (!matchset) + return cb() + + var found = Object.keys(matchset) + var self = this + var n = found.length + + if (n === 0) + return cb() + + var set = this.matches[index] = Object.create(null) + found.forEach(function (p, i) { + // If there's a problem with the stat, then it means that + // one or more of the links in the realpath couldn't be + // resolved. just return the abs value in that case. + p = self._makeAbs(p) + rp.realpath(p, self.realpathCache, function (er, real) { + if (!er) + set[real] = true + else if (er.syscall === 'stat') + set[p] = true + else + self.emit('error', er) // srsly wtf right here + + if (--n === 0) { + self.matches[index] = set + cb() + } + }) + }) +} + +Glob.prototype._mark = function (p) { + return common.mark(this, p) +} + +Glob.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) +} + +Glob.prototype.abort = function () { + this.aborted = true + this.emit('abort') +} + +Glob.prototype.pause = function () { + if (!this.paused) { + this.paused = true + this.emit('pause') + } +} + +Glob.prototype.resume = function () { + if (this.paused) { + this.emit('resume') + this.paused = false + if (this._emitQueue.length) { + var eq = this._emitQueue.slice(0) + this._emitQueue.length = 0 + for (var i = 0; i < eq.length; i ++) { + var e = eq[i] + this._emitMatch(e[0], e[1]) + } + } + if (this._processQueue.length) { + var pq = this._processQueue.slice(0) + this._processQueue.length = 0 + for (var i = 0; i < pq.length; i ++) { + var p = pq[i] + this._processing-- + this._process(p[0], p[1], p[2], p[3]) + } + } + } +} + +Glob.prototype._process = function (pattern, index, inGlobStar, cb) { + assert(this instanceof Glob) + assert(typeof cb === 'function') + + if (this.aborted) + return + + this._processing++ + if (this.paused) { + this._processQueue.push([pattern, index, inGlobStar, cb]) + return + } + + //console.error('PROCESS %d', this._processing, pattern) // Get the first [n] parts of pattern that are all strings. var n = 0 @@ -31105,12 +30544,12 @@ GlobSync.prototype._process = function (pattern, index, inGlobStar) { } // now n is the index of the first one that is *not* a string. - // See if there's anything else + // see if there's anything else var prefix switch (n) { // if not, then this is rather simple case pattern.length: - this._processSimple(pattern.join('/'), index) + this._processSimple(pattern.join('/'), index, cb) return case 0: @@ -31142,24 +30581,29 @@ GlobSync.prototype._process = function (pattern, index, inGlobStar) { var abs = this._makeAbs(read) - //if ignored, skip processing + //if ignored, skip _processing if (childrenIgnored(this, read)) - return + return cb() var isGlobStar = remain[0] === minimatch.GLOBSTAR if (isGlobStar) - this._processGlobStar(prefix, read, abs, remain, index, inGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar, cb) else - this._processReaddir(prefix, read, abs, remain, index, inGlobStar) + this._processReaddir(prefix, read, abs, remain, index, inGlobStar, cb) } +Glob.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + return self._processReaddir2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) +} -GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) { - var entries = this._readdir(abs, inGlobStar) +Glob.prototype._processReaddir2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { // if the abs isn't a dir, then nothing can match! if (!entries) - return + return cb() // It will only match dot entries if it starts with a dot, or if // dot is set. Stuff like @(.foo|.bar) isn't allowed. @@ -31183,10 +30627,12 @@ GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, } } + //console.error('prd2', prefix, entries, remain[0]._glob, matchedEntries) + var len = matchedEntries.length // If there are no matched entries, then nothing matches. if (len === 0) - return + return cb() // if this is the last remaining pattern bit, then no need for // an additional stat *unless* the user has specified mark or @@ -31200,7 +30646,7 @@ GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, for (var i = 0; i < len; i ++) { var e = matchedEntries[i] if (prefix) { - if (prefix.slice(-1) !== '/') + if (prefix !== '/') e = prefix + '/' + e else e = prefix + e @@ -31212,7 +30658,7 @@ GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, this._emitMatch(index, e) } // This was the last one, and no stats were needed - return + return cb() } // now test all matched entries as stand-ins for that part @@ -31221,27 +30667,36 @@ GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, for (var i = 0; i < len; i ++) { var e = matchedEntries[i] var newPattern - if (prefix) - newPattern = [prefix, e] - else - newPattern = [e] - this._process(newPattern.concat(remain), index, inGlobStar) + if (prefix) { + if (prefix !== '/') + e = prefix + '/' + e + else + e = prefix + e + } + this._process([e].concat(remain), index, inGlobStar, cb) } + cb() } +Glob.prototype._emitMatch = function (index, e) { + if (this.aborted) + return -GlobSync.prototype._emitMatch = function (index, e) { if (isIgnored(this, e)) return - var abs = this._makeAbs(e) + if (this.paused) { + this._emitQueue.push([index, e]) + return + } + + var abs = isAbsolute(e) ? e : this._makeAbs(e) if (this.mark) e = this._mark(e) - if (this.absolute) { + if (this.absolute) e = abs - } if (this.matches[index][e]) return @@ -31254,66 +30709,84 @@ GlobSync.prototype._emitMatch = function (index, e) { this.matches[index][e] = true - if (this.stat) - this._stat(e) + var st = this.statCache[abs] + if (st) + this.emit('stat', e, st) + + this.emit('match', e) } +Glob.prototype._readdirInGlobStar = function (abs, cb) { + if (this.aborted) + return -GlobSync.prototype._readdirInGlobStar = function (abs) { // follow all symlinked directories forever // just proceed as if this is a non-globstar situation if (this.follow) - return this._readdir(abs, false) + return this._readdir(abs, false, cb) - var entries - var lstat - var stat - try { - lstat = this.fs.lstatSync(abs) - } catch (er) { - if (er.code === 'ENOENT') { - // lstat failed, doesn't exist - return null - } - } + var lstatkey = 'lstat\0' + abs + var self = this + var lstatcb = inflight(lstatkey, lstatcb_) - var isSym = lstat && lstat.isSymbolicLink() - this.symlinks[abs] = isSym + if (lstatcb) + self.fs.lstat(abs, lstatcb) - // If it's not a symlink or a dir, then it's definitely a regular file. - // don't bother doing a readdir in that case. - if (!isSym && lstat && !lstat.isDirectory()) - this.cache[abs] = 'FILE' - else - entries = this._readdir(abs, false) + function lstatcb_ (er, lstat) { + if (er && er.code === 'ENOENT') + return cb() - return entries + var isSym = lstat && lstat.isSymbolicLink() + self.symlinks[abs] = isSym + + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && lstat && !lstat.isDirectory()) { + self.cache[abs] = 'FILE' + cb() + } else + self._readdir(abs, false, cb) + } } -GlobSync.prototype._readdir = function (abs, inGlobStar) { - var entries +Glob.prototype._readdir = function (abs, inGlobStar, cb) { + if (this.aborted) + return + + cb = inflight('readdir\0'+abs+'\0'+inGlobStar, cb) + if (!cb) + return + //console.error('RD %j %j', +inGlobStar, abs) if (inGlobStar && !ownProp(this.symlinks, abs)) - return this._readdirInGlobStar(abs) + return this._readdirInGlobStar(abs, cb) if (ownProp(this.cache, abs)) { var c = this.cache[abs] if (!c || c === 'FILE') - return null + return cb() if (Array.isArray(c)) - return c + return cb(null, c) } - try { - return this._readdirEntries(abs, this.fs.readdirSync(abs)) - } catch (er) { - this._readdirError(abs, er) - return null + var self = this + self.fs.readdir(abs, readdirCb(this, abs, cb)) +} + +function readdirCb (self, abs, cb) { + return function (er, entries) { + if (er) + self._readdirError(abs, er, cb) + else + self._readdirEntries(abs, entries, cb) } } -GlobSync.prototype._readdirEntries = function (abs, entries) { +Glob.prototype._readdirEntries = function (abs, entries, cb) { + if (this.aborted) + return + // if we haven't asked to stat everything, then just // assume that everything in there exists, so we can avoid // having to stat it a second time. @@ -31329,12 +30802,13 @@ GlobSync.prototype._readdirEntries = function (abs, entries) { } this.cache[abs] = entries - - // mark and cache dir-ness - return entries + return cb(null, entries) } -GlobSync.prototype._readdirError = function (f, er) { +Glob.prototype._readdirError = function (f, er, cb) { + if (this.aborted) + return + // handle errors, and cache the information switch (er.code) { case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 @@ -31345,7 +30819,8 @@ GlobSync.prototype._readdirError = function (f, er) { var error = new Error(er.code + ' invalid cwd ' + this.cwd) error.path = this.cwd error.code = er.code - throw error + this.emit('error', error) + this.abort() } break @@ -31358,22 +30833,35 @@ GlobSync.prototype._readdirError = function (f, er) { default: // some unusual error. Treat as failure. this.cache[this._makeAbs(f)] = false - if (this.strict) - throw er + if (this.strict) { + this.emit('error', er) + // If the error is handled, then we abort + // if not, we threw out of here + this.abort() + } if (!this.silent) console.error('glob error', er) break } + + return cb() } -GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) { +Glob.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar, cb) { + var self = this + this._readdir(abs, inGlobStar, function (er, entries) { + self._processGlobStar2(prefix, read, abs, remain, index, inGlobStar, entries, cb) + }) +} - var entries = this._readdir(abs, inGlobStar) + +Glob.prototype._processGlobStar2 = function (prefix, read, abs, remain, index, inGlobStar, entries, cb) { + //console.error('pgs2', prefix, remain[0], entries) // no entries means not a dir, so it can never have matches // foo.txt/** doesn't match foo.txt if (!entries) - return + return cb() // test without the globstar, and with every child both below // and replacing the globstar. @@ -31382,14 +30870,14 @@ GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index var noGlobStar = gspref.concat(remainWithoutGlobStar) // the noGlobStar pattern exits the inGlobStar state - this._process(noGlobStar, index, false) + this._process(noGlobStar, index, false, cb) - var len = entries.length var isSym = this.symlinks[abs] + var len = entries.length // If it's a symlink, and we're in a globstar, then stop if (isSym && inGlobStar) - return + return cb() for (var i = 0; i < len; i++) { var e = entries[i] @@ -31398,24 +30886,33 @@ GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index // these two cases enter the inGlobStar state var instead = gspref.concat(entries[i], remainWithoutGlobStar) - this._process(instead, index, true) + this._process(instead, index, true, cb) var below = gspref.concat(entries[i], remain) - this._process(below, index, true) + this._process(below, index, true, cb) } + + cb() } -GlobSync.prototype._processSimple = function (prefix, index) { +Glob.prototype._processSimple = function (prefix, index, cb) { // XXX review this. Shouldn't it be doing the mounting etc // before doing stat? kinda weird? - var exists = this._stat(prefix) + var self = this + this._stat(prefix, function (er, exists) { + self._processSimple2(prefix, index, er, exists, cb) + }) +} +Glob.prototype._processSimple2 = function (prefix, index, er, exists, cb) { + + //console.error('ps2', prefix, exists) if (!this.matches[index]) this.matches[index] = Object.create(null) // If it doesn't exist, then just mark the lack of results if (!exists) - return + return cb() if (prefix && isAbsolute(prefix) && !this.nomount) { var trail = /[\/\\]$/.test(prefix) @@ -31433,15 +30930,16 @@ GlobSync.prototype._processSimple = function (prefix, index) { // Mark this as a match this._emitMatch(index, prefix) + cb() } // Returns either 'DIR', 'FILE', or false -GlobSync.prototype._stat = function (f) { +Glob.prototype._stat = function (f, cb) { var abs = this._makeAbs(f) var needDir = f.slice(-1) === '/' if (f.length > this.maxLength) - return false + return cb() if (!this.stat && ownProp(this.cache, abs)) { var c = this.cache[abs] @@ -31451,10 +30949,10 @@ GlobSync.prototype._stat = function (f) { // It exists, but maybe not how we need it if (!needDir || c === 'DIR') - return c + return cb(null, c) if (needDir && c === 'FILE') - return false + return cb() // otherwise we have to stat, because maybe c=true // if we know it exists, but not what it is. @@ -31462,2028 +30960,868 @@ GlobSync.prototype._stat = function (f) { var exists var stat = this.statCache[abs] - if (!stat) { - var lstat - try { - lstat = this.fs.lstatSync(abs) - } catch (er) { - if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { - this.statCache[abs] = false - return false - } + if (stat !== undefined) { + if (stat === false) + return cb(null, stat) + else { + var type = stat.isDirectory() ? 'DIR' : 'FILE' + if (needDir && type === 'FILE') + return cb() + else + return cb(null, type, stat) } + } + + var self = this + var statcb = inflight('stat\0' + abs, lstatcb_) + if (statcb) + self.fs.lstat(abs, statcb) + function lstatcb_ (er, lstat) { if (lstat && lstat.isSymbolicLink()) { - try { - stat = this.fs.statSync(abs) - } catch (er) { - stat = lstat - } + // If it's a symlink, then treat it as the target, unless + // the target does not exist, then treat it as a file. + return self.fs.stat(abs, function (er, stat) { + if (er) + self._stat2(f, abs, null, lstat, cb) + else + self._stat2(f, abs, er, stat, cb) + }) } else { - stat = lstat + self._stat2(f, abs, er, lstat, cb) } } +} + +Glob.prototype._stat2 = function (f, abs, er, stat, cb) { + if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { + this.statCache[abs] = false + return cb() + } + var needDir = f.slice(-1) === '/' this.statCache[abs] = stat + if (abs.slice(-1) === '/' && stat && !stat.isDirectory()) + return cb(null, false, stat) + var c = true if (stat) c = stat.isDirectory() ? 'DIR' : 'FILE' - this.cache[abs] = this.cache[abs] || c if (needDir && c === 'FILE') - return false + return cb() - return c -} - -GlobSync.prototype._mark = function (p) { - return common.mark(this, p) -} - -GlobSync.prototype._makeAbs = function (f) { - return common.makeAbs(this, f) + return cb(null, c, stat) } /***/ }), -/***/ "../../node_modules/globby/gitignore.js": +/***/ "../../node_modules/glob/sync.js": /***/ (function(module, exports, __webpack_require__) { -"use strict"; +module.exports = globSync +globSync.GlobSync = GlobSync -const {promisify} = __webpack_require__("util"); -const fs = __webpack_require__("fs"); -const path = __webpack_require__("path"); -const fastGlob = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/index.js"); -const gitIgnore = __webpack_require__("../../node_modules/ignore/index.js"); -const slash = __webpack_require__("../../node_modules/slash/index.js"); +var rp = __webpack_require__("../../node_modules/fs.realpath/index.js") +var minimatch = __webpack_require__("../../node_modules/minimatch/minimatch.js") +var Minimatch = minimatch.Minimatch +var Glob = __webpack_require__("../../node_modules/glob/glob.js").Glob +var util = __webpack_require__("util") +var path = __webpack_require__("path") +var assert = __webpack_require__("assert") +var isAbsolute = __webpack_require__("../../node_modules/path-is-absolute/index.js") +var common = __webpack_require__("../../node_modules/glob/common.js") +var setopts = common.setopts +var ownProp = common.ownProp +var childrenIgnored = common.childrenIgnored +var isIgnored = common.isIgnored -const DEFAULT_IGNORE = [ - '**/node_modules/**', - '**/flow-typed/**', - '**/coverage/**', - '**/.git' -]; +function globSync (pattern, options) { + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') -const readFileP = promisify(fs.readFile); + return new GlobSync(pattern, options).found +} -const mapGitIgnorePatternTo = base => ignore => { - if (ignore.startsWith('!')) { - return '!' + path.posix.join(base, ignore.slice(1)); - } +function GlobSync (pattern, options) { + if (!pattern) + throw new Error('must provide pattern') - return path.posix.join(base, ignore); -}; + if (typeof options === 'function' || arguments.length === 3) + throw new TypeError('callback provided to sync glob\n'+ + 'See: https://github.com/isaacs/node-glob/issues/167') -const parseGitIgnore = (content, options) => { - const base = slash(path.relative(options.cwd, path.dirname(options.fileName))); + if (!(this instanceof GlobSync)) + return new GlobSync(pattern, options) - return content - .split(/\r?\n/) - .filter(Boolean) - .filter(line => !line.startsWith('#')) - .map(mapGitIgnorePatternTo(base)); -}; + setopts(this, pattern, options) -const reduceIgnore = files => { - const ignores = gitIgnore(); - for (const file of files) { - ignores.add(parseGitIgnore(file.content, { - cwd: file.cwd, - fileName: file.filePath - })); - } + if (this.noprocess) + return this - return ignores; -}; + var n = this.minimatch.set.length + this.matches = new Array(n) + for (var i = 0; i < n; i ++) { + this._process(this.minimatch.set[i], i, false) + } + this._finish() +} -const ensureAbsolutePathForCwd = (cwd, p) => { - cwd = slash(cwd); - if (path.isAbsolute(p)) { - if (slash(p).startsWith(cwd)) { - return p; - } +GlobSync.prototype._finish = function () { + assert(this instanceof GlobSync) + if (this.realpath) { + var self = this + this.matches.forEach(function (matchset, index) { + var set = self.matches[index] = Object.create(null) + for (var p in matchset) { + try { + p = self._makeAbs(p) + var real = rp.realpathSync(p, self.realpathCache) + set[real] = true + } catch (er) { + if (er.syscall === 'stat') + set[self._makeAbs(p)] = true + else + throw er + } + } + }) + } + common.finish(this) +} - throw new Error(`Path ${p} is not in cwd ${cwd}`); - } - return path.join(cwd, p); -}; +GlobSync.prototype._process = function (pattern, index, inGlobStar) { + assert(this instanceof GlobSync) -const getIsIgnoredPredecate = (ignores, cwd) => { - return p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p.path || p)))); -}; + // Get the first [n] parts of pattern that are all strings. + var n = 0 + while (typeof pattern[n] === 'string') { + n ++ + } + // now n is the index of the first one that is *not* a string. -const getFile = async (file, cwd) => { - const filePath = path.join(cwd, file); - const content = await readFileP(filePath, 'utf8'); + // See if there's anything else + var prefix + switch (n) { + // if not, then this is rather simple + case pattern.length: + this._processSimple(pattern.join('/'), index) + return - return { - cwd, - filePath, - content - }; -}; + case 0: + // pattern *starts* with some non-trivial item. + // going to readdir(cwd), but not include the prefix in matches. + prefix = null + break -const getFileSync = (file, cwd) => { - const filePath = path.join(cwd, file); - const content = fs.readFileSync(filePath, 'utf8'); + default: + // pattern has some string bits in the front. + // whatever it starts with, whether that's 'absolute' like /foo/bar, + // or 'relative' like '../baz' + prefix = pattern.slice(0, n).join('/') + break + } - return { - cwd, - filePath, - content - }; -}; + var remain = pattern.slice(n) -const normalizeOptions = ({ - ignore = [], - cwd = slash(process.cwd()) -} = {}) => { - return {ignore, cwd}; -}; + // get the list of entries. + var read + if (prefix === null) + read = '.' + else if (isAbsolute(prefix) || isAbsolute(pattern.join('/'))) { + if (!prefix || !isAbsolute(prefix)) + prefix = '/' + prefix + read = prefix + } else + read = prefix -module.exports = async options => { - options = normalizeOptions(options); + var abs = this._makeAbs(read) - const paths = await fastGlob('**/.gitignore', { - ignore: DEFAULT_IGNORE.concat(options.ignore), - cwd: options.cwd - }); + //if ignored, skip processing + if (childrenIgnored(this, read)) + return - const files = await Promise.all(paths.map(file => getFile(file, options.cwd))); - const ignores = reduceIgnore(files); + var isGlobStar = remain[0] === minimatch.GLOBSTAR + if (isGlobStar) + this._processGlobStar(prefix, read, abs, remain, index, inGlobStar) + else + this._processReaddir(prefix, read, abs, remain, index, inGlobStar) +} - return getIsIgnoredPredecate(ignores, options.cwd); -}; -module.exports.sync = options => { - options = normalizeOptions(options); +GlobSync.prototype._processReaddir = function (prefix, read, abs, remain, index, inGlobStar) { + var entries = this._readdir(abs, inGlobStar) - const paths = fastGlob.sync('**/.gitignore', { - ignore: DEFAULT_IGNORE.concat(options.ignore), - cwd: options.cwd - }); + // if the abs isn't a dir, then nothing can match! + if (!entries) + return - const files = paths.map(file => getFileSync(file, options.cwd)); - const ignores = reduceIgnore(files); + // It will only match dot entries if it starts with a dot, or if + // dot is set. Stuff like @(.foo|.bar) isn't allowed. + var pn = remain[0] + var negate = !!this.minimatch.negate + var rawGlob = pn._glob + var dotOk = this.dot || rawGlob.charAt(0) === '.' - return getIsIgnoredPredecate(ignores, options.cwd); -}; + var matchedEntries = [] + for (var i = 0; i < entries.length; i++) { + var e = entries[i] + if (e.charAt(0) !== '.' || dotOk) { + var m + if (negate && !prefix) { + m = !e.match(pn) + } else { + m = e.match(pn) + } + if (m) + matchedEntries.push(e) + } + } + var len = matchedEntries.length + // If there are no matched entries, then nothing matches. + if (len === 0) + return -/***/ }), + // if this is the last remaining pattern bit, then no need for + // an additional stat *unless* the user has specified mark or + // stat explicitly. We know they exist, since readdir returned + // them. -/***/ "../../node_modules/globby/index.js": -/***/ (function(module, exports, __webpack_require__) { + if (remain.length === 1 && !this.mark && !this.stat) { + if (!this.matches[index]) + this.matches[index] = Object.create(null) -"use strict"; + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + if (prefix) { + if (prefix.slice(-1) !== '/') + e = prefix + '/' + e + else + e = prefix + e + } -const fs = __webpack_require__("fs"); -const arrayUnion = __webpack_require__("../../node_modules/array-union/index.js"); -const merge2 = __webpack_require__("../../node_modules/merge2/index.js"); -const fastGlob = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/index.js"); -const dirGlob = __webpack_require__("../../node_modules/dir-glob/index.js"); -const gitignore = __webpack_require__("../../node_modules/globby/gitignore.js"); -const {FilterStream, UniqueStream} = __webpack_require__("../../node_modules/globby/stream-utils.js"); + if (e.charAt(0) === '/' && !this.nomount) { + e = path.join(this.root, e) + } + this._emitMatch(index, e) + } + // This was the last one, and no stats were needed + return + } -const DEFAULT_FILTER = () => false; + // now test all matched entries as stand-ins for that part + // of the pattern. + remain.shift() + for (var i = 0; i < len; i ++) { + var e = matchedEntries[i] + var newPattern + if (prefix) + newPattern = [prefix, e] + else + newPattern = [e] + this._process(newPattern.concat(remain), index, inGlobStar) + } +} -const isNegative = pattern => pattern[0] === '!'; -const assertPatternsInput = patterns => { - if (!patterns.every(pattern => typeof pattern === 'string')) { - throw new TypeError('Patterns must be a string or an array of strings'); - } -}; +GlobSync.prototype._emitMatch = function (index, e) { + if (isIgnored(this, e)) + return -const checkCwdOption = (options = {}) => { - if (!options.cwd) { - return; - } + var abs = this._makeAbs(e) - let stat; - try { - stat = fs.statSync(options.cwd); - } catch { - return; - } + if (this.mark) + e = this._mark(e) - if (!stat.isDirectory()) { - throw new Error('The `cwd` option must be a path to a directory'); - } -}; + if (this.absolute) { + e = abs + } -const getPathString = p => p.stats instanceof fs.Stats ? p.path : p; + if (this.matches[index][e]) + return -const generateGlobTasks = (patterns, taskOptions) => { - patterns = arrayUnion([].concat(patterns)); - assertPatternsInput(patterns); - checkCwdOption(taskOptions); + if (this.nodir) { + var c = this.cache[abs] + if (c === 'DIR' || Array.isArray(c)) + return + } - const globTasks = []; + this.matches[index][e] = true - taskOptions = { - ignore: [], - expandDirectories: true, - ...taskOptions - }; + if (this.stat) + this._stat(e) +} - for (const [index, pattern] of patterns.entries()) { - if (isNegative(pattern)) { - continue; - } - const ignore = patterns - .slice(index) - .filter(pattern => isNegative(pattern)) - .map(pattern => pattern.slice(1)); +GlobSync.prototype._readdirInGlobStar = function (abs) { + // follow all symlinked directories forever + // just proceed as if this is a non-globstar situation + if (this.follow) + return this._readdir(abs, false) - const options = { - ...taskOptions, - ignore: taskOptions.ignore.concat(ignore) - }; + var entries + var lstat + var stat + try { + lstat = this.fs.lstatSync(abs) + } catch (er) { + if (er.code === 'ENOENT') { + // lstat failed, doesn't exist + return null + } + } - globTasks.push({pattern, options}); - } + var isSym = lstat && lstat.isSymbolicLink() + this.symlinks[abs] = isSym - return globTasks; -}; + // If it's not a symlink or a dir, then it's definitely a regular file. + // don't bother doing a readdir in that case. + if (!isSym && lstat && !lstat.isDirectory()) + this.cache[abs] = 'FILE' + else + entries = this._readdir(abs, false) -const globDirs = (task, fn) => { - let options = {}; - if (task.options.cwd) { - options.cwd = task.options.cwd; - } + return entries +} - if (Array.isArray(task.options.expandDirectories)) { - options = { - ...options, - files: task.options.expandDirectories - }; - } else if (typeof task.options.expandDirectories === 'object') { - options = { - ...options, - ...task.options.expandDirectories - }; - } +GlobSync.prototype._readdir = function (abs, inGlobStar) { + var entries - return fn(task.pattern, options); -}; + if (inGlobStar && !ownProp(this.symlinks, abs)) + return this._readdirInGlobStar(abs) -const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; + if (ownProp(this.cache, abs)) { + var c = this.cache[abs] + if (!c || c === 'FILE') + return null -const getFilterSync = options => { - return options && options.gitignore ? - gitignore.sync({cwd: options.cwd, ignore: options.ignore}) : - DEFAULT_FILTER; -}; + if (Array.isArray(c)) + return c + } -const globToTask = task => glob => { - const {options} = task; - if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { - options.ignore = dirGlob.sync(options.ignore); - } + try { + return this._readdirEntries(abs, this.fs.readdirSync(abs)) + } catch (er) { + this._readdirError(abs, er) + return null + } +} - return { - pattern: glob, - options - }; -}; +GlobSync.prototype._readdirEntries = function (abs, entries) { + // if we haven't asked to stat everything, then just + // assume that everything in there exists, so we can avoid + // having to stat it a second time. + if (!this.mark && !this.stat) { + for (var i = 0; i < entries.length; i ++) { + var e = entries[i] + if (abs === '/') + e = abs + e + else + e = abs + '/' + e + this.cache[e] = true + } + } -module.exports = async (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); + this.cache[abs] = entries - const getFilter = async () => { - return options && options.gitignore ? - gitignore({cwd: options.cwd, ignore: options.ignore}) : - DEFAULT_FILTER; - }; + // mark and cache dir-ness + return entries +} - const getTasks = async () => { - const tasks = await Promise.all(globTasks.map(async task => { - const globs = await getPattern(task, dirGlob); - return Promise.all(globs.map(globToTask(task))); - })); +GlobSync.prototype._readdirError = function (f, er) { + // handle errors, and cache the information + switch (er.code) { + case 'ENOTSUP': // https://github.com/isaacs/node-glob/issues/205 + case 'ENOTDIR': // totally normal. means it *does* exist. + var abs = this._makeAbs(f) + this.cache[abs] = 'FILE' + if (abs === this.cwdAbs) { + var error = new Error(er.code + ' invalid cwd ' + this.cwd) + error.path = this.cwd + error.code = er.code + throw error + } + break - return arrayUnion(...tasks); - }; + case 'ENOENT': // not terribly unusual + case 'ELOOP': + case 'ENAMETOOLONG': + case 'UNKNOWN': + this.cache[this._makeAbs(f)] = false + break - const [filter, tasks] = await Promise.all([getFilter(), getTasks()]); - const paths = await Promise.all(tasks.map(task => fastGlob(task.pattern, task.options))); + default: // some unusual error. Treat as failure. + this.cache[this._makeAbs(f)] = false + if (this.strict) + throw er + if (!this.silent) + console.error('glob error', er) + break + } +} - return arrayUnion(...paths).filter(path_ => !filter(getPathString(path_))); -}; +GlobSync.prototype._processGlobStar = function (prefix, read, abs, remain, index, inGlobStar) { -module.exports.sync = (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); + var entries = this._readdir(abs, inGlobStar) - const tasks = []; - for (const task of globTasks) { - const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); - tasks.push(...newTask); - } + // no entries means not a dir, so it can never have matches + // foo.txt/** doesn't match foo.txt + if (!entries) + return - const filter = getFilterSync(options); + // test without the globstar, and with every child both below + // and replacing the globstar. + var remainWithoutGlobStar = remain.slice(1) + var gspref = prefix ? [ prefix ] : [] + var noGlobStar = gspref.concat(remainWithoutGlobStar) - let matches = []; - for (const task of tasks) { - matches = arrayUnion(matches, fastGlob.sync(task.pattern, task.options)); - } - - return matches.filter(path_ => !filter(path_)); -}; - -module.exports.stream = (patterns, options) => { - const globTasks = generateGlobTasks(patterns, options); - - const tasks = []; - for (const task of globTasks) { - const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); - tasks.push(...newTask); - } - - const filter = getFilterSync(options); - const filterStream = new FilterStream(p => !filter(p)); - const uniqueStream = new UniqueStream(); - - return merge2(tasks.map(task => fastGlob.stream(task.pattern, task.options))) - .pipe(filterStream) - .pipe(uniqueStream); -}; - -module.exports.generateGlobTasks = generateGlobTasks; - -module.exports.hasMagic = (patterns, options) => [] - .concat(patterns) - .some(pattern => fastGlob.isDynamicPattern(pattern, options)); - -module.exports.gitignore = gitignore; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/index.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -const taskManager = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/managers/tasks.js"); -const async_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/providers/async.js"); -const stream_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/providers/stream.js"); -const sync_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/providers/sync.js"); -const settings_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/settings.js"); -const utils = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/index.js"); -async function FastGlob(source, options) { - assertPatternsInput(source); - const works = getWorks(source, async_1.default, options); - const result = await Promise.all(works); - return utils.array.flatten(result); -} -// https://github.com/typescript-eslint/typescript-eslint/issues/60 -// eslint-disable-next-line no-redeclare -(function (FastGlob) { - function sync(source, options) { - assertPatternsInput(source); - const works = getWorks(source, sync_1.default, options); - return utils.array.flatten(works); - } - FastGlob.sync = sync; - function stream(source, options) { - assertPatternsInput(source); - const works = getWorks(source, stream_1.default, options); - /** - * The stream returned by the provider cannot work with an asynchronous iterator. - * To support asynchronous iterators, regardless of the number of tasks, we always multiplex streams. - * This affects performance (+25%). I don't see best solution right now. - */ - return utils.stream.merge(works); - } - FastGlob.stream = stream; - function generateTasks(source, options) { - assertPatternsInput(source); - const patterns = [].concat(source); - const settings = new settings_1.default(options); - return taskManager.generate(patterns, settings); - } - FastGlob.generateTasks = generateTasks; - function isDynamicPattern(source, options) { - assertPatternsInput(source); - const settings = new settings_1.default(options); - return utils.pattern.isDynamicPattern(source, settings); - } - FastGlob.isDynamicPattern = isDynamicPattern; - function escapePath(source) { - assertPatternsInput(source); - return utils.path.escape(source); - } - FastGlob.escapePath = escapePath; -})(FastGlob || (FastGlob = {})); -function getWorks(source, _Provider, options) { - const patterns = [].concat(source); - const settings = new settings_1.default(options); - const tasks = taskManager.generate(patterns, settings); - const provider = new _Provider(settings); - return tasks.map(provider.read, provider); -} -function assertPatternsInput(input) { - const source = [].concat(input); - const isValidSource = source.every((item) => utils.string.isString(item) && !utils.string.isEmpty(item)); - if (!isValidSource) { - throw new TypeError('Patterns must be a string (non empty) or an array of strings'); - } -} -module.exports = FastGlob; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/managers/tasks.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.convertPatternGroupToTask = exports.convertPatternGroupsToTasks = exports.groupPatternsByBaseDirectory = exports.getNegativePatternsAsPositive = exports.getPositivePatterns = exports.convertPatternsToTasks = exports.generate = void 0; -const utils = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/index.js"); -function generate(patterns, settings) { - const positivePatterns = getPositivePatterns(patterns); - const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); - const staticPatterns = positivePatterns.filter((pattern) => utils.pattern.isStaticPattern(pattern, settings)); - const dynamicPatterns = positivePatterns.filter((pattern) => utils.pattern.isDynamicPattern(pattern, settings)); - const staticTasks = convertPatternsToTasks(staticPatterns, negativePatterns, /* dynamic */ false); - const dynamicTasks = convertPatternsToTasks(dynamicPatterns, negativePatterns, /* dynamic */ true); - return staticTasks.concat(dynamicTasks); -} -exports.generate = generate; -/** - * Returns tasks grouped by basic pattern directories. - * - * Patterns that can be found inside (`./`) and outside (`../`) the current directory are handled separately. - * This is necessary because directory traversal starts at the base directory and goes deeper. - */ -function convertPatternsToTasks(positive, negative, dynamic) { - const tasks = []; - const patternsOutsideCurrentDirectory = utils.pattern.getPatternsOutsideCurrentDirectory(positive); - const patternsInsideCurrentDirectory = utils.pattern.getPatternsInsideCurrentDirectory(positive); - const outsideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsOutsideCurrentDirectory); - const insideCurrentDirectoryGroup = groupPatternsByBaseDirectory(patternsInsideCurrentDirectory); - tasks.push(...convertPatternGroupsToTasks(outsideCurrentDirectoryGroup, negative, dynamic)); - /* - * For the sake of reducing future accesses to the file system, we merge all tasks within the current directory - * into a global task, if at least one pattern refers to the root (`.`). In this case, the global task covers the rest. - */ - if ('.' in insideCurrentDirectoryGroup) { - tasks.push(convertPatternGroupToTask('.', patternsInsideCurrentDirectory, negative, dynamic)); - } - else { - tasks.push(...convertPatternGroupsToTasks(insideCurrentDirectoryGroup, negative, dynamic)); - } - return tasks; -} -exports.convertPatternsToTasks = convertPatternsToTasks; -function getPositivePatterns(patterns) { - return utils.pattern.getPositivePatterns(patterns); -} -exports.getPositivePatterns = getPositivePatterns; -function getNegativePatternsAsPositive(patterns, ignore) { - const negative = utils.pattern.getNegativePatterns(patterns).concat(ignore); - const positive = negative.map(utils.pattern.convertToPositivePattern); - return positive; -} -exports.getNegativePatternsAsPositive = getNegativePatternsAsPositive; -function groupPatternsByBaseDirectory(patterns) { - const group = {}; - return patterns.reduce((collection, pattern) => { - const base = utils.pattern.getBaseDirectory(pattern); - if (base in collection) { - collection[base].push(pattern); - } - else { - collection[base] = [pattern]; - } - return collection; - }, group); -} -exports.groupPatternsByBaseDirectory = groupPatternsByBaseDirectory; -function convertPatternGroupsToTasks(positive, negative, dynamic) { - return Object.keys(positive).map((base) => { - return convertPatternGroupToTask(base, positive[base], negative, dynamic); - }); -} -exports.convertPatternGroupsToTasks = convertPatternGroupsToTasks; -function convertPatternGroupToTask(base, positive, negative, dynamic) { - return { - dynamic, - positive, - negative, - base, - patterns: [].concat(positive, negative.map(utils.pattern.convertToNegativePattern)) - }; -} -exports.convertPatternGroupToTask = convertPatternGroupToTask; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/providers/async.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/readers/stream.js"); -const provider_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/providers/provider.js"); -class ProviderAsync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = []; - return new Promise((resolve, reject) => { - const stream = this.api(root, task, options); - stream.once('error', reject); - stream.on('data', (entry) => entries.push(options.transform(entry))); - stream.once('end', () => resolve(entries)); - }); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderAsync; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/providers/filters/deep.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/index.js"); -const partial_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/providers/matchers/partial.js"); -class DeepFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - } - getFilter(basePath, positive, negative) { - const matcher = this._getMatcher(positive); - const negativeRe = this._getNegativePatternsRe(negative); - return (entry) => this._filter(basePath, entry, matcher, negativeRe); - } - _getMatcher(patterns) { - return new partial_1.default(patterns, this._settings, this._micromatchOptions); - } - _getNegativePatternsRe(patterns) { - const affectDepthOfReadingPatterns = patterns.filter(utils.pattern.isAffectDepthOfReadingPattern); - return utils.pattern.convertPatternsToRe(affectDepthOfReadingPatterns, this._micromatchOptions); - } - _filter(basePath, entry, matcher, negativeRe) { - if (this._isSkippedByDeep(basePath, entry.path)) { - return false; - } - if (this._isSkippedSymbolicLink(entry)) { - return false; - } - const filepath = utils.path.removeLeadingDotSegment(entry.path); - if (this._isSkippedByPositivePatterns(filepath, matcher)) { - return false; - } - return this._isSkippedByNegativePatterns(filepath, negativeRe); - } - _isSkippedByDeep(basePath, entryPath) { - /** - * Avoid unnecessary depth calculations when it doesn't matter. - */ - if (this._settings.deep === Infinity) { - return false; - } - return this._getEntryLevel(basePath, entryPath) >= this._settings.deep; - } - _getEntryLevel(basePath, entryPath) { - const entryPathDepth = entryPath.split('/').length; - if (basePath === '') { - return entryPathDepth; - } - const basePathDepth = basePath.split('/').length; - return entryPathDepth - basePathDepth; - } - _isSkippedSymbolicLink(entry) { - return !this._settings.followSymbolicLinks && entry.dirent.isSymbolicLink(); - } - _isSkippedByPositivePatterns(entryPath, matcher) { - return !this._settings.baseNameMatch && !matcher.match(entryPath); - } - _isSkippedByNegativePatterns(entryPath, patternsRe) { - return !utils.pattern.matchAny(entryPath, patternsRe); - } -} -exports.default = DeepFilter; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/providers/filters/entry.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/index.js"); -class EntryFilter { - constructor(_settings, _micromatchOptions) { - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this.index = new Map(); - } - getFilter(positive, negative) { - const positiveRe = utils.pattern.convertPatternsToRe(positive, this._micromatchOptions); - const negativeRe = utils.pattern.convertPatternsToRe(negative, this._micromatchOptions); - return (entry) => this._filter(entry, positiveRe, negativeRe); - } - _filter(entry, positiveRe, negativeRe) { - if (this._settings.unique && this._isDuplicateEntry(entry)) { - return false; - } - if (this._onlyFileFilter(entry) || this._onlyDirectoryFilter(entry)) { - return false; - } - if (this._isSkippedByAbsoluteNegativePatterns(entry.path, negativeRe)) { - return false; - } - const filepath = this._settings.baseNameMatch ? entry.name : entry.path; - const isMatched = this._isMatchToPatterns(filepath, positiveRe) && !this._isMatchToPatterns(entry.path, negativeRe); - if (this._settings.unique && isMatched) { - this._createIndexRecord(entry); - } - return isMatched; - } - _isDuplicateEntry(entry) { - return this.index.has(entry.path); - } - _createIndexRecord(entry) { - this.index.set(entry.path, undefined); - } - _onlyFileFilter(entry) { - return this._settings.onlyFiles && !entry.dirent.isFile(); - } - _onlyDirectoryFilter(entry) { - return this._settings.onlyDirectories && !entry.dirent.isDirectory(); - } - _isSkippedByAbsoluteNegativePatterns(entryPath, patternsRe) { - if (!this._settings.absolute) { - return false; - } - const fullpath = utils.path.makeAbsolute(this._settings.cwd, entryPath); - return utils.pattern.matchAny(fullpath, patternsRe); - } - _isMatchToPatterns(entryPath, patternsRe) { - const filepath = utils.path.removeLeadingDotSegment(entryPath); - return utils.pattern.matchAny(filepath, patternsRe); - } -} -exports.default = EntryFilter; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/providers/filters/error.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/index.js"); -class ErrorFilter { - constructor(_settings) { - this._settings = _settings; - } - getFilter() { - return (error) => this._isNonFatalError(error); - } - _isNonFatalError(error) { - return utils.errno.isEnoentCodeError(error) || this._settings.suppressErrors; - } -} -exports.default = ErrorFilter; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/providers/matchers/matcher.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/index.js"); -class Matcher { - constructor(_patterns, _settings, _micromatchOptions) { - this._patterns = _patterns; - this._settings = _settings; - this._micromatchOptions = _micromatchOptions; - this._storage = []; - this._fillStorage(); - } - _fillStorage() { - /** - * The original pattern may include `{,*,**,a/*}`, which will lead to problems with matching (unresolved level). - * So, before expand patterns with brace expansion into separated patterns. - */ - const patterns = utils.pattern.expandPatternsWithBraceExpansion(this._patterns); - for (const pattern of patterns) { - const segments = this._getPatternSegments(pattern); - const sections = this._splitSegmentsIntoSections(segments); - this._storage.push({ - complete: sections.length <= 1, - pattern, - segments, - sections - }); - } - } - _getPatternSegments(pattern) { - const parts = utils.pattern.getPatternParts(pattern, this._micromatchOptions); - return parts.map((part) => { - const dynamic = utils.pattern.isDynamicPattern(part, this._settings); - if (!dynamic) { - return { - dynamic: false, - pattern: part - }; - } - return { - dynamic: true, - pattern: part, - patternRe: utils.pattern.makeRe(part, this._micromatchOptions) - }; - }); - } - _splitSegmentsIntoSections(segments) { - return utils.array.splitWhen(segments, (segment) => segment.dynamic && utils.pattern.hasGlobStar(segment.pattern)); - } -} -exports.default = Matcher; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/providers/matchers/partial.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/providers/matchers/matcher.js"); -class PartialMatcher extends matcher_1.default { - match(filepath) { - const parts = filepath.split('/'); - const levels = parts.length; - const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); - for (const pattern of patterns) { - const section = pattern.sections[0]; - /** - * In this case, the pattern has a globstar and we must read all directories unconditionally, - * but only if the level has reached the end of the first group. - * - * fixtures/{a,b}/** - * ^ true/false ^ always true - */ - if (!pattern.complete && levels > section.length) { - return true; - } - const match = parts.every((part, index) => { - const segment = pattern.segments[index]; - if (segment.dynamic && segment.patternRe.test(part)) { - return true; - } - if (!segment.dynamic && segment.pattern === part) { - return true; - } - return false; - }); - if (match) { - return true; - } - } - return false; - } -} -exports.default = PartialMatcher; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/providers/provider.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__("path"); -const deep_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/providers/filters/deep.js"); -const entry_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/providers/filters/entry.js"); -const error_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/providers/filters/error.js"); -const entry_2 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/providers/transformers/entry.js"); -class Provider { - constructor(_settings) { - this._settings = _settings; - this.errorFilter = new error_1.default(this._settings); - this.entryFilter = new entry_1.default(this._settings, this._getMicromatchOptions()); - this.deepFilter = new deep_1.default(this._settings, this._getMicromatchOptions()); - this.entryTransformer = new entry_2.default(this._settings); - } - _getRootDirectory(task) { - return path.resolve(this._settings.cwd, task.base); - } - _getReaderOptions(task) { - const basePath = task.base === '.' ? '' : task.base; - return { - basePath, - pathSegmentSeparator: '/', - concurrency: this._settings.concurrency, - deepFilter: this.deepFilter.getFilter(basePath, task.positive, task.negative), - entryFilter: this.entryFilter.getFilter(task.positive, task.negative), - errorFilter: this.errorFilter.getFilter(), - followSymbolicLinks: this._settings.followSymbolicLinks, - fs: this._settings.fs, - stats: this._settings.stats, - throwErrorOnBrokenSymbolicLink: this._settings.throwErrorOnBrokenSymbolicLink, - transform: this.entryTransformer.getTransformer() - }; - } - _getMicromatchOptions() { - return { - dot: this._settings.dot, - matchBase: this._settings.baseNameMatch, - nobrace: !this._settings.braceExpansion, - nocase: !this._settings.caseSensitiveMatch, - noext: !this._settings.extglob, - noglobstar: !this._settings.globstar, - posix: true, - strictSlashes: false - }; - } -} -exports.default = Provider; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/providers/stream.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__("stream"); -const stream_2 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/readers/stream.js"); -const provider_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/providers/provider.js"); -class ProviderStream extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new stream_2.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const source = this.api(root, task, options); - const destination = new stream_1.Readable({ objectMode: true, read: () => { } }); - source - .once('error', (error) => destination.emit('error', error)) - .on('data', (entry) => destination.emit('data', options.transform(entry))) - .once('end', () => destination.emit('end')); - destination - .once('close', () => source.destroy()); - return destination; - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderStream; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/providers/sync.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/readers/sync.js"); -const provider_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/providers/provider.js"); -class ProviderSync extends provider_1.default { - constructor() { - super(...arguments); - this._reader = new sync_1.default(this._settings); - } - read(task) { - const root = this._getRootDirectory(task); - const options = this._getReaderOptions(task); - const entries = this.api(root, task, options); - return entries.map(options.transform); - } - api(root, task, options) { - if (task.dynamic) { - return this._reader.dynamic(root, options); - } - return this._reader.static(task.patterns, options); - } -} -exports.default = ProviderSync; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/providers/transformers/entry.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/index.js"); -class EntryTransformer { - constructor(_settings) { - this._settings = _settings; - } - getTransformer() { - return (entry) => this._transform(entry); - } - _transform(entry) { - let filepath = entry.path; - if (this._settings.absolute) { - filepath = utils.path.makeAbsolute(this._settings.cwd, filepath); - filepath = utils.path.unixify(filepath); - } - if (this._settings.markDirectories && entry.dirent.isDirectory()) { - filepath += '/'; - } - if (!this._settings.objectMode) { - return filepath; - } - return Object.assign(Object.assign({}, entry), { path: filepath }); - } -} -exports.default = EntryTransformer; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/readers/reader.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __webpack_require__("path"); -const fsStat = __webpack_require__("../../node_modules/@nodelib/fs.stat/out/index.js"); -const utils = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/index.js"); -class Reader { - constructor(_settings) { - this._settings = _settings; - this._fsStatSettings = new fsStat.Settings({ - followSymbolicLink: this._settings.followSymbolicLinks, - fs: this._settings.fs, - throwErrorOnBrokenSymbolicLink: this._settings.followSymbolicLinks - }); - } - _getFullEntryPath(filepath) { - return path.resolve(this._settings.cwd, filepath); - } - _makeEntry(stats, pattern) { - const entry = { - name: pattern, - path: pattern, - dirent: utils.fs.createDirentFromStats(pattern, stats) - }; - if (this._settings.stats) { - entry.stats = stats; - } - return entry; - } - _isFatalError(error) { - return !utils.errno.isEnoentCodeError(error) && !this._settings.suppressErrors; - } -} -exports.default = Reader; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/readers/stream.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__("stream"); -const fsStat = __webpack_require__("../../node_modules/@nodelib/fs.stat/out/index.js"); -const fsWalk = __webpack_require__("../../node_modules/@nodelib/fs.walk/out/index.js"); -const reader_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/readers/reader.js"); -class ReaderStream extends reader_1.default { - constructor() { - super(...arguments); - this._walkStream = fsWalk.walkStream; - this._stat = fsStat.stat; - } - dynamic(root, options) { - return this._walkStream(root, options); - } - static(patterns, options) { - const filepaths = patterns.map(this._getFullEntryPath, this); - const stream = new stream_1.PassThrough({ objectMode: true }); - stream._write = (index, _enc, done) => { - return this._getEntry(filepaths[index], patterns[index], options) - .then((entry) => { - if (entry !== null && options.entryFilter(entry)) { - stream.push(entry); - } - if (index === filepaths.length - 1) { - stream.end(); - } - done(); - }) - .catch(done); - }; - for (let i = 0; i < filepaths.length; i++) { - stream.write(i); - } - return stream; - } - _getEntry(filepath, pattern, options) { - return this._getStat(filepath) - .then((stats) => this._makeEntry(stats, pattern)) - .catch((error) => { - if (options.errorFilter(error)) { - return null; - } - throw error; - }); - } - _getStat(filepath) { - return new Promise((resolve, reject) => { - this._stat(filepath, this._fsStatSettings, (error, stats) => { - return error === null ? resolve(stats) : reject(error); - }); - }); - } -} -exports.default = ReaderStream; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/readers/sync.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__("../../node_modules/@nodelib/fs.stat/out/index.js"); -const fsWalk = __webpack_require__("../../node_modules/@nodelib/fs.walk/out/index.js"); -const reader_1 = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/readers/reader.js"); -class ReaderSync extends reader_1.default { - constructor() { - super(...arguments); - this._walkSync = fsWalk.walkSync; - this._statSync = fsStat.statSync; - } - dynamic(root, options) { - return this._walkSync(root, options); - } - static(patterns, options) { - const entries = []; - for (const pattern of patterns) { - const filepath = this._getFullEntryPath(pattern); - const entry = this._getEntry(filepath, pattern, options); - if (entry === null || !options.entryFilter(entry)) { - continue; - } - entries.push(entry); - } - return entries; - } - _getEntry(filepath, pattern, options) { - try { - const stats = this._getStat(filepath); - return this._makeEntry(stats, pattern); - } - catch (error) { - if (options.errorFilter(error)) { - return null; - } - throw error; - } - } - _getStat(filepath) { - return this._statSync(filepath, this._fsStatSettings); - } -} -exports.default = ReaderSync; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/settings.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0; -const fs = __webpack_require__("fs"); -const os = __webpack_require__("os"); -/** - * The `os.cpus` method can return zero. We expect the number of cores to be greater than zero. - * https://github.com/nodejs/node/blob/7faeddf23a98c53896f8b574a6e66589e8fb1eb8/lib/os.js#L106-L107 - */ -const CPU_COUNT = Math.max(os.cpus().length, 1); -exports.DEFAULT_FILE_SYSTEM_ADAPTER = { - lstat: fs.lstat, - lstatSync: fs.lstatSync, - stat: fs.stat, - statSync: fs.statSync, - readdir: fs.readdir, - readdirSync: fs.readdirSync -}; -class Settings { - constructor(_options = {}) { - this._options = _options; - this.absolute = this._getValue(this._options.absolute, false); - this.baseNameMatch = this._getValue(this._options.baseNameMatch, false); - this.braceExpansion = this._getValue(this._options.braceExpansion, true); - this.caseSensitiveMatch = this._getValue(this._options.caseSensitiveMatch, true); - this.concurrency = this._getValue(this._options.concurrency, CPU_COUNT); - this.cwd = this._getValue(this._options.cwd, process.cwd()); - this.deep = this._getValue(this._options.deep, Infinity); - this.dot = this._getValue(this._options.dot, false); - this.extglob = this._getValue(this._options.extglob, true); - this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, true); - this.fs = this._getFileSystemMethods(this._options.fs); - this.globstar = this._getValue(this._options.globstar, true); - this.ignore = this._getValue(this._options.ignore, []); - this.markDirectories = this._getValue(this._options.markDirectories, false); - this.objectMode = this._getValue(this._options.objectMode, false); - this.onlyDirectories = this._getValue(this._options.onlyDirectories, false); - this.onlyFiles = this._getValue(this._options.onlyFiles, true); - this.stats = this._getValue(this._options.stats, false); - this.suppressErrors = this._getValue(this._options.suppressErrors, false); - this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, false); - this.unique = this._getValue(this._options.unique, true); - if (this.onlyDirectories) { - this.onlyFiles = false; - } - if (this.stats) { - this.objectMode = true; - } - } - _getValue(option, value) { - return option === undefined ? value : option; - } - _getFileSystemMethods(methods = {}) { - return Object.assign(Object.assign({}, exports.DEFAULT_FILE_SYSTEM_ADAPTER), methods); - } -} -exports.default = Settings; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/utils/array.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.splitWhen = exports.flatten = void 0; -function flatten(items) { - return items.reduce((collection, item) => [].concat(collection, item), []); -} -exports.flatten = flatten; -function splitWhen(items, predicate) { - const result = [[]]; - let groupIndex = 0; - for (const item of items) { - if (predicate(item)) { - groupIndex++; - result[groupIndex] = []; - } - else { - result[groupIndex].push(item); - } - } - return result; -} -exports.splitWhen = splitWhen; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/utils/errno.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isEnoentCodeError = void 0; -function isEnoentCodeError(error) { - return error.code === 'ENOENT'; -} -exports.isEnoentCodeError = isEnoentCodeError; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/utils/fs.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createDirentFromStats = void 0; -class DirentFromStats { - constructor(name, stats) { - this.name = name; - this.isBlockDevice = stats.isBlockDevice.bind(stats); - this.isCharacterDevice = stats.isCharacterDevice.bind(stats); - this.isDirectory = stats.isDirectory.bind(stats); - this.isFIFO = stats.isFIFO.bind(stats); - this.isFile = stats.isFile.bind(stats); - this.isSocket = stats.isSocket.bind(stats); - this.isSymbolicLink = stats.isSymbolicLink.bind(stats); - } -} -function createDirentFromStats(name, stats) { - return new DirentFromStats(name, stats); -} -exports.createDirentFromStats = createDirentFromStats; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/utils/index.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.string = exports.stream = exports.pattern = exports.path = exports.fs = exports.errno = exports.array = void 0; -const array = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/array.js"); -exports.array = array; -const errno = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/errno.js"); -exports.errno = errno; -const fs = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/fs.js"); -exports.fs = fs; -const path = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/path.js"); -exports.path = path; -const pattern = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/pattern.js"); -exports.pattern = pattern; -const stream = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/stream.js"); -exports.stream = stream; -const string = __webpack_require__("../../node_modules/globby/node_modules/fast-glob/out/utils/string.js"); -exports.string = string; - - -/***/ }), - -/***/ "../../node_modules/globby/node_modules/fast-glob/out/utils/path.js": -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -exports.removeLeadingDotSegment = exports.escape = exports.makeAbsolute = exports.unixify = void 0; -const path = __webpack_require__("path"); -const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2; // ./ or .\\ -const UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\())/g; -/** - * Designed to work only with simple paths: `dir\\file`. - */ -function unixify(filepath) { - return filepath.replace(/\\/g, '/'); -} -exports.unixify = unixify; -function makeAbsolute(cwd, filepath) { - return path.resolve(cwd, filepath); -} -exports.makeAbsolute = makeAbsolute; -function escape(pattern) { - return pattern.replace(UNESCAPED_GLOB_SYMBOLS_RE, '\\$2'); -} -exports.escape = escape; -function removeLeadingDotSegment(entry) { - // We do not use `startsWith` because this is 10x slower than current implementation for some cases. - // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with - if (entry.charAt(0) === '.') { - const secondCharactery = entry.charAt(1); - if (secondCharactery === '/' || secondCharactery === '\\') { - return entry.slice(LEADING_DOT_SEGMENT_CHARACTERS_COUNT); - } - } - return entry; -} -exports.removeLeadingDotSegment = removeLeadingDotSegment; - - -/***/ }), + // the noGlobStar pattern exits the inGlobStar state + this._process(noGlobStar, index, false) -/***/ "../../node_modules/globby/node_modules/fast-glob/out/utils/pattern.js": -/***/ (function(module, exports, __webpack_require__) { + var len = entries.length + var isSym = this.symlinks[abs] -"use strict"; + // If it's a symlink, and we're in a globstar, then stop + if (isSym && inGlobStar) + return -Object.defineProperty(exports, "__esModule", { value: true }); -exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0; -const path = __webpack_require__("path"); -const globParent = __webpack_require__("../../node_modules/glob-parent/index.js"); -const micromatch = __webpack_require__("../../node_modules/globby/node_modules/micromatch/index.js"); -const GLOBSTAR = '**'; -const ESCAPE_SYMBOL = '\\'; -const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; -const REGEX_CHARACTER_CLASS_SYMBOLS_RE = /\[.*]/; -const REGEX_GROUP_SYMBOLS_RE = /(?:^|[^!*+?@])\(.*\|.*\)/; -const GLOB_EXTENSION_SYMBOLS_RE = /[!*+?@]\(.*\)/; -const BRACE_EXPANSIONS_SYMBOLS_RE = /{.*(?:,|\.\.).*}/; -function isStaticPattern(pattern, options = {}) { - return !isDynamicPattern(pattern, options); -} -exports.isStaticPattern = isStaticPattern; -function isDynamicPattern(pattern, options = {}) { - /** - * A special case with an empty string is necessary for matching patterns that start with a forward slash. - * An empty string cannot be a dynamic pattern. - * For example, the pattern `/lib/*` will be spread into parts: '', 'lib', '*'. - */ - if (pattern === '') { - return false; - } - /** - * When the `caseSensitiveMatch` option is disabled, all patterns must be marked as dynamic, because we cannot check - * filepath directly (without read directory). - */ - if (options.caseSensitiveMatch === false || pattern.includes(ESCAPE_SYMBOL)) { - return true; - } - if (COMMON_GLOB_SYMBOLS_RE.test(pattern) || REGEX_CHARACTER_CLASS_SYMBOLS_RE.test(pattern) || REGEX_GROUP_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.extglob !== false && GLOB_EXTENSION_SYMBOLS_RE.test(pattern)) { - return true; - } - if (options.braceExpansion !== false && BRACE_EXPANSIONS_SYMBOLS_RE.test(pattern)) { - return true; - } - return false; -} -exports.isDynamicPattern = isDynamicPattern; -function convertToPositivePattern(pattern) { - return isNegativePattern(pattern) ? pattern.slice(1) : pattern; -} -exports.convertToPositivePattern = convertToPositivePattern; -function convertToNegativePattern(pattern) { - return '!' + pattern; -} -exports.convertToNegativePattern = convertToNegativePattern; -function isNegativePattern(pattern) { - return pattern.startsWith('!') && pattern[1] !== '('; -} -exports.isNegativePattern = isNegativePattern; -function isPositivePattern(pattern) { - return !isNegativePattern(pattern); -} -exports.isPositivePattern = isPositivePattern; -function getNegativePatterns(patterns) { - return patterns.filter(isNegativePattern); -} -exports.getNegativePatterns = getNegativePatterns; -function getPositivePatterns(patterns) { - return patterns.filter(isPositivePattern); -} -exports.getPositivePatterns = getPositivePatterns; -/** - * Returns patterns that can be applied inside the current directory. - * - * @example - * // ['./*', '*', 'a/*'] - * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) - */ -function getPatternsInsideCurrentDirectory(patterns) { - return patterns.filter((pattern) => !isPatternRelatedToParentDirectory(pattern)); -} -exports.getPatternsInsideCurrentDirectory = getPatternsInsideCurrentDirectory; -/** - * Returns patterns to be expanded relative to (outside) the current directory. - * - * @example - * // ['../*', './../*'] - * getPatternsInsideCurrentDirectory(['./*', '*', 'a/*', '../*', './../*']) - */ -function getPatternsOutsideCurrentDirectory(patterns) { - return patterns.filter(isPatternRelatedToParentDirectory); -} -exports.getPatternsOutsideCurrentDirectory = getPatternsOutsideCurrentDirectory; -function isPatternRelatedToParentDirectory(pattern) { - return pattern.startsWith('..') || pattern.startsWith('./..'); -} -exports.isPatternRelatedToParentDirectory = isPatternRelatedToParentDirectory; -function getBaseDirectory(pattern) { - return globParent(pattern, { flipBackslashes: false }); -} -exports.getBaseDirectory = getBaseDirectory; -function hasGlobStar(pattern) { - return pattern.includes(GLOBSTAR); -} -exports.hasGlobStar = hasGlobStar; -function endsWithSlashGlobStar(pattern) { - return pattern.endsWith('/' + GLOBSTAR); -} -exports.endsWithSlashGlobStar = endsWithSlashGlobStar; -function isAffectDepthOfReadingPattern(pattern) { - const basename = path.basename(pattern); - return endsWithSlashGlobStar(pattern) || isStaticPattern(basename); -} -exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern; -function expandPatternsWithBraceExpansion(patterns) { - return patterns.reduce((collection, pattern) => { - return collection.concat(expandBraceExpansion(pattern)); - }, []); -} -exports.expandPatternsWithBraceExpansion = expandPatternsWithBraceExpansion; -function expandBraceExpansion(pattern) { - return micromatch.braces(pattern, { - expand: true, - nodupes: true - }); -} -exports.expandBraceExpansion = expandBraceExpansion; -function getPatternParts(pattern, options) { - let { parts } = micromatch.scan(pattern, Object.assign(Object.assign({}, options), { parts: true })); - /** - * The scan method returns an empty array in some cases. - * See micromatch/picomatch#58 for more details. - */ - if (parts.length === 0) { - parts = [pattern]; - } - /** - * The scan method does not return an empty part for the pattern with a forward slash. - * This is another part of micromatch/picomatch#58. - */ - if (parts[0].startsWith('/')) { - parts[0] = parts[0].slice(1); - parts.unshift(''); - } - return parts; -} -exports.getPatternParts = getPatternParts; -function makeRe(pattern, options) { - return micromatch.makeRe(pattern, options); -} -exports.makeRe = makeRe; -function convertPatternsToRe(patterns, options) { - return patterns.map((pattern) => makeRe(pattern, options)); -} -exports.convertPatternsToRe = convertPatternsToRe; -function matchAny(entry, patternsRe) { - return patternsRe.some((patternRe) => patternRe.test(entry)); + for (var i = 0; i < len; i++) { + var e = entries[i] + if (e.charAt(0) === '.' && !this.dot) + continue + + // these two cases enter the inGlobStar state + var instead = gspref.concat(entries[i], remainWithoutGlobStar) + this._process(instead, index, true) + + var below = gspref.concat(entries[i], remain) + this._process(below, index, true) + } } -exports.matchAny = matchAny; +GlobSync.prototype._processSimple = function (prefix, index) { + // XXX review this. Shouldn't it be doing the mounting etc + // before doing stat? kinda weird? + var exists = this._stat(prefix) -/***/ }), + if (!this.matches[index]) + this.matches[index] = Object.create(null) -/***/ "../../node_modules/globby/node_modules/fast-glob/out/utils/stream.js": -/***/ (function(module, exports, __webpack_require__) { + // If it doesn't exist, then just mark the lack of results + if (!exists) + return -"use strict"; + if (prefix && isAbsolute(prefix) && !this.nomount) { + var trail = /[\/\\]$/.test(prefix) + if (prefix.charAt(0) === '/') { + prefix = path.join(this.root, prefix) + } else { + prefix = path.resolve(this.root, prefix) + if (trail) + prefix += '/' + } + } -Object.defineProperty(exports, "__esModule", { value: true }); -exports.merge = void 0; -const merge2 = __webpack_require__("../../node_modules/merge2/index.js"); -function merge(streams) { - const mergedStream = merge2(streams); - streams.forEach((stream) => { - stream.once('error', (error) => mergedStream.emit('error', error)); - }); - mergedStream.once('close', () => propagateCloseEventToSources(streams)); - mergedStream.once('end', () => propagateCloseEventToSources(streams)); - return mergedStream; -} -exports.merge = merge; -function propagateCloseEventToSources(streams) { - streams.forEach((stream) => stream.emit('close')); + if (process.platform === 'win32') + prefix = prefix.replace(/\\/g, '/') + + // Mark this as a match + this._emitMatch(index, prefix) } +// Returns either 'DIR', 'FILE', or false +GlobSync.prototype._stat = function (f) { + var abs = this._makeAbs(f) + var needDir = f.slice(-1) === '/' -/***/ }), + if (f.length > this.maxLength) + return false -/***/ "../../node_modules/globby/node_modules/fast-glob/out/utils/string.js": -/***/ (function(module, exports, __webpack_require__) { + if (!this.stat && ownProp(this.cache, abs)) { + var c = this.cache[abs] -"use strict"; + if (Array.isArray(c)) + c = 'DIR' -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isEmpty = exports.isString = void 0; -function isString(input) { - return typeof input === 'string'; + // It exists, but maybe not how we need it + if (!needDir || c === 'DIR') + return c + + if (needDir && c === 'FILE') + return false + + // otherwise we have to stat, because maybe c=true + // if we know it exists, but not what it is. + } + + var exists + var stat = this.statCache[abs] + if (!stat) { + var lstat + try { + lstat = this.fs.lstatSync(abs) + } catch (er) { + if (er && (er.code === 'ENOENT' || er.code === 'ENOTDIR')) { + this.statCache[abs] = false + return false + } + } + + if (lstat && lstat.isSymbolicLink()) { + try { + stat = this.fs.statSync(abs) + } catch (er) { + stat = lstat + } + } else { + stat = lstat + } + } + + this.statCache[abs] = stat + + var c = true + if (stat) + c = stat.isDirectory() ? 'DIR' : 'FILE' + + this.cache[abs] = this.cache[abs] || c + + if (needDir && c === 'FILE') + return false + + return c } -exports.isString = isString; -function isEmpty(input) { - return input === ''; + +GlobSync.prototype._mark = function (p) { + return common.mark(this, p) +} + +GlobSync.prototype._makeAbs = function (f) { + return common.makeAbs(this, f) } -exports.isEmpty = isEmpty; /***/ }), -/***/ "../../node_modules/globby/node_modules/micromatch/index.js": +/***/ "../../node_modules/globby/gitignore.js": /***/ (function(module, exports, __webpack_require__) { "use strict"; +const {promisify} = __webpack_require__("util"); +const fs = __webpack_require__("fs"); +const path = __webpack_require__("path"); +const fastGlob = __webpack_require__("../../node_modules/fast-glob/out/index.js"); +const gitIgnore = __webpack_require__("../../node_modules/ignore/index.js"); +const slash = __webpack_require__("../../node_modules/slash/index.js"); -const util = __webpack_require__("util"); -const braces = __webpack_require__("../../node_modules/braces/index.js"); -const picomatch = __webpack_require__("../../node_modules/picomatch/index.js"); -const utils = __webpack_require__("../../node_modules/picomatch/lib/utils.js"); -const isEmptyString = val => val === '' || val === './'; +const DEFAULT_IGNORE = [ + '**/node_modules/**', + '**/flow-typed/**', + '**/coverage/**', + '**/.git' +]; -/** - * Returns an array of strings that match one or more glob patterns. - * - * ```js - * const mm = require('micromatch'); - * // mm(list, patterns[, options]); - * - * console.log(mm(['a.js', 'a.txt'], ['*.js'])); - * //=> [ 'a.js' ] - * ``` - * @param {String|Array} `list` List of strings to match. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) - * @return {Array} Returns an array of matches - * @summary false - * @api public - */ +const readFileP = promisify(fs.readFile); -const micromatch = (list, patterns, options) => { - patterns = [].concat(patterns); - list = [].concat(list); +const mapGitIgnorePatternTo = base => ignore => { + if (ignore.startsWith('!')) { + return '!' + path.posix.join(base, ignore.slice(1)); + } - let omit = new Set(); - let keep = new Set(); - let items = new Set(); - let negatives = 0; + return path.posix.join(base, ignore); +}; - let onResult = state => { - items.add(state.output); - if (options && options.onResult) { - options.onResult(state); - } - }; +const parseGitIgnore = (content, options) => { + const base = slash(path.relative(options.cwd, path.dirname(options.fileName))); - for (let i = 0; i < patterns.length; i++) { - let isMatch = picomatch(String(patterns[i]), { ...options, onResult }, true); - let negated = isMatch.state.negated || isMatch.state.negatedExtglob; - if (negated) negatives++; + return content + .split(/\r?\n/) + .filter(Boolean) + .filter(line => !line.startsWith('#')) + .map(mapGitIgnorePatternTo(base)); +}; - for (let item of list) { - let matched = isMatch(item, true); +const reduceIgnore = files => { + const ignores = gitIgnore(); + for (const file of files) { + ignores.add(parseGitIgnore(file.content, { + cwd: file.cwd, + fileName: file.filePath + })); + } - let match = negated ? !matched.isMatch : matched.isMatch; - if (!match) continue; + return ignores; +}; - if (negated) { - omit.add(matched.output); - } else { - omit.delete(matched.output); - keep.add(matched.output); - } - } - } +const ensureAbsolutePathForCwd = (cwd, p) => { + cwd = slash(cwd); + if (path.isAbsolute(p)) { + if (slash(p).startsWith(cwd)) { + return p; + } - let result = negatives === patterns.length ? [...items] : [...keep]; - let matches = result.filter(item => !omit.has(item)); + throw new Error(`Path ${p} is not in cwd ${cwd}`); + } - if (options && matches.length === 0) { - if (options.failglob === true) { - throw new Error(`No matches found for "${patterns.join(', ')}"`); - } + return path.join(cwd, p); +}; - if (options.nonull === true || options.nullglob === true) { - return options.unescape ? patterns.map(p => p.replace(/\\/g, '')) : patterns; - } - } +const getIsIgnoredPredecate = (ignores, cwd) => { + return p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p.path || p)))); +}; - return matches; +const getFile = async (file, cwd) => { + const filePath = path.join(cwd, file); + const content = await readFileP(filePath, 'utf8'); + + return { + cwd, + filePath, + content + }; }; -/** - * Backwards compatibility - */ +const getFileSync = (file, cwd) => { + const filePath = path.join(cwd, file); + const content = fs.readFileSync(filePath, 'utf8'); -micromatch.match = micromatch; + return { + cwd, + filePath, + content + }; +}; -/** - * Returns a matcher function from the given glob `pattern` and `options`. - * The returned function takes a string to match as its only argument and returns - * true if the string is a match. - * - * ```js - * const mm = require('micromatch'); - * // mm.matcher(pattern[, options]); - * - * const isMatch = mm.matcher('*.!(*a)'); - * console.log(isMatch('a.a')); //=> false - * console.log(isMatch('a.b')); //=> true - * ``` - * @param {String} `pattern` Glob pattern - * @param {Object} `options` - * @return {Function} Returns a matcher function. - * @api public - */ +const normalizeOptions = ({ + ignore = [], + cwd = slash(process.cwd()) +} = {}) => { + return {ignore, cwd}; +}; -micromatch.matcher = (pattern, options) => picomatch(pattern, options); +module.exports = async options => { + options = normalizeOptions(options); -/** - * Returns true if **any** of the given glob `patterns` match the specified `string`. - * - * ```js - * const mm = require('micromatch'); - * // mm.isMatch(string, patterns[, options]); - * - * console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true - * console.log(mm.isMatch('a.a', 'b.*')); //=> false - * ``` - * @param {String} `str` The string to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `[options]` See available [options](#options). - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ + const paths = await fastGlob('**/.gitignore', { + ignore: DEFAULT_IGNORE.concat(options.ignore), + cwd: options.cwd + }); -micromatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); + const files = await Promise.all(paths.map(file => getFile(file, options.cwd))); + const ignores = reduceIgnore(files); -/** - * Backwards compatibility - */ + return getIsIgnoredPredecate(ignores, options.cwd); +}; -micromatch.any = micromatch.isMatch; +module.exports.sync = options => { + options = normalizeOptions(options); -/** - * Returns a list of strings that _**do not match any**_ of the given `patterns`. - * - * ```js - * const mm = require('micromatch'); - * // mm.not(list, patterns[, options]); - * - * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a')); - * //=> ['b.b', 'c.c'] - * ``` - * @param {Array} `list` Array of strings to match. - * @param {String|Array} `patterns` One or more glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Array} Returns an array of strings that **do not match** the given patterns. - * @api public - */ + const paths = fastGlob.sync('**/.gitignore', { + ignore: DEFAULT_IGNORE.concat(options.ignore), + cwd: options.cwd + }); -micromatch.not = (list, patterns, options = {}) => { - patterns = [].concat(patterns).map(String); - let result = new Set(); - let items = []; + const files = paths.map(file => getFileSync(file, options.cwd)); + const ignores = reduceIgnore(files); - let onResult = state => { - if (options.onResult) options.onResult(state); - items.push(state.output); - }; + return getIsIgnoredPredecate(ignores, options.cwd); +}; - let matches = micromatch(list, patterns, { ...options, onResult }); - for (let item of items) { - if (!matches.includes(item)) { - result.add(item); - } - } - return [...result]; -}; +/***/ }), -/** - * Returns true if the given `string` contains the given pattern. Similar - * to [.isMatch](#isMatch) but the pattern can match any part of the string. - * - * ```js - * var mm = require('micromatch'); - * // mm.contains(string, pattern[, options]); - * - * console.log(mm.contains('aa/bb/cc', '*b')); - * //=> true - * console.log(mm.contains('aa/bb/cc', '*d')); - * //=> false - * ``` - * @param {String} `str` The string to match. - * @param {String|Array} `patterns` Glob pattern to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any of the patterns matches any part of `str`. - * @api public - */ +/***/ "../../node_modules/globby/index.js": +/***/ (function(module, exports, __webpack_require__) { -micromatch.contains = (str, pattern, options) => { - if (typeof str !== 'string') { - throw new TypeError(`Expected a string: "${util.inspect(str)}"`); - } +"use strict"; - if (Array.isArray(pattern)) { - return pattern.some(p => micromatch.contains(str, p, options)); - } +const fs = __webpack_require__("fs"); +const arrayUnion = __webpack_require__("../../node_modules/array-union/index.js"); +const merge2 = __webpack_require__("../../node_modules/merge2/index.js"); +const fastGlob = __webpack_require__("../../node_modules/fast-glob/out/index.js"); +const dirGlob = __webpack_require__("../../node_modules/dir-glob/index.js"); +const gitignore = __webpack_require__("../../node_modules/globby/gitignore.js"); +const {FilterStream, UniqueStream} = __webpack_require__("../../node_modules/globby/stream-utils.js"); - if (typeof pattern === 'string') { - if (isEmptyString(str) || isEmptyString(pattern)) { - return false; - } +const DEFAULT_FILTER = () => false; - if (str.includes(pattern) || (str.startsWith('./') && str.slice(2).includes(pattern))) { - return true; - } - } +const isNegative = pattern => pattern[0] === '!'; - return micromatch.isMatch(str, pattern, { ...options, contains: true }); +const assertPatternsInput = patterns => { + if (!patterns.every(pattern => typeof pattern === 'string')) { + throw new TypeError('Patterns must be a string or an array of strings'); + } }; -/** - * Filter the keys of the given object with the given `glob` pattern - * and `options`. Does not attempt to match nested keys. If you need this feature, - * use [glob-object][] instead. - * - * ```js - * const mm = require('micromatch'); - * // mm.matchKeys(object, patterns[, options]); - * - * const obj = { aa: 'a', ab: 'b', ac: 'c' }; - * console.log(mm.matchKeys(obj, '*b')); - * //=> { ab: 'b' } - * ``` - * @param {Object} `object` The object with keys to filter. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Object} Returns an object with only keys that match the given patterns. - * @api public - */ +const checkCwdOption = (options = {}) => { + if (!options.cwd) { + return; + } -micromatch.matchKeys = (obj, patterns, options) => { - if (!utils.isObject(obj)) { - throw new TypeError('Expected the first argument to be an object'); - } - let keys = micromatch(Object.keys(obj), patterns, options); - let res = {}; - for (let key of keys) res[key] = obj[key]; - return res; + let stat; + try { + stat = fs.statSync(options.cwd); + } catch { + return; + } + + if (!stat.isDirectory()) { + throw new Error('The `cwd` option must be a path to a directory'); + } }; -/** - * Returns true if some of the strings in the given `list` match any of the given glob `patterns`. - * - * ```js - * const mm = require('micromatch'); - * // mm.some(list, patterns[, options]); - * - * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); - * // true - * console.log(mm.some(['foo.js'], ['*.js', '!foo.js'])); - * // false - * ``` - * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any `patterns` matches any of the strings in `list` - * @api public - */ +const getPathString = p => p.stats instanceof fs.Stats ? p.path : p; -micromatch.some = (list, patterns, options) => { - let items = [].concat(list); +const generateGlobTasks = (patterns, taskOptions) => { + patterns = arrayUnion([].concat(patterns)); + assertPatternsInput(patterns); + checkCwdOption(taskOptions); - for (let pattern of [].concat(patterns)) { - let isMatch = picomatch(String(pattern), options); - if (items.some(item => isMatch(item))) { - return true; - } - } - return false; -}; + const globTasks = []; -/** - * Returns true if every string in the given `list` matches - * any of the given glob `patterns`. - * - * ```js - * const mm = require('micromatch'); - * // mm.every(list, patterns[, options]); - * - * console.log(mm.every('foo.js', ['foo.js'])); - * // true - * console.log(mm.every(['foo.js', 'bar.js'], ['*.js'])); - * // true - * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); - * // false - * console.log(mm.every(['foo.js'], ['*.js', '!foo.js'])); - * // false - * ``` - * @param {String|Array} `list` The string or array of strings to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if all `patterns` matches all of the strings in `list` - * @api public - */ + taskOptions = { + ignore: [], + expandDirectories: true, + ...taskOptions + }; -micromatch.every = (list, patterns, options) => { - let items = [].concat(list); + for (const [index, pattern] of patterns.entries()) { + if (isNegative(pattern)) { + continue; + } - for (let pattern of [].concat(patterns)) { - let isMatch = picomatch(String(pattern), options); - if (!items.every(item => isMatch(item))) { - return false; - } - } - return true; + const ignore = patterns + .slice(index) + .filter(pattern => isNegative(pattern)) + .map(pattern => pattern.slice(1)); + + const options = { + ...taskOptions, + ignore: taskOptions.ignore.concat(ignore) + }; + + globTasks.push({pattern, options}); + } + + return globTasks; }; -/** - * Returns true if **all** of the given `patterns` match - * the specified string. - * - * ```js - * const mm = require('micromatch'); - * // mm.all(string, patterns[, options]); - * - * console.log(mm.all('foo.js', ['foo.js'])); - * // true - * - * console.log(mm.all('foo.js', ['*.js', '!foo.js'])); - * // false - * - * console.log(mm.all('foo.js', ['*.js', 'foo.js'])); - * // true - * - * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); - * // true - * ``` - * @param {String|Array} `str` The string to test. - * @param {String|Array} `patterns` One or more glob patterns to use for matching. - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Boolean} Returns true if any patterns match `str` - * @api public - */ +const globDirs = (task, fn) => { + let options = {}; + if (task.options.cwd) { + options.cwd = task.options.cwd; + } -micromatch.all = (str, patterns, options) => { - if (typeof str !== 'string') { - throw new TypeError(`Expected a string: "${util.inspect(str)}"`); - } + if (Array.isArray(task.options.expandDirectories)) { + options = { + ...options, + files: task.options.expandDirectories + }; + } else if (typeof task.options.expandDirectories === 'object') { + options = { + ...options, + ...task.options.expandDirectories + }; + } - return [].concat(patterns).every(p => picomatch(p, options)(str)); + return fn(task.pattern, options); }; -/** - * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match. - * - * ```js - * const mm = require('micromatch'); - * // mm.capture(pattern, string[, options]); - * - * console.log(mm.capture('test/*.js', 'test/foo.js')); - * //=> ['foo'] - * console.log(mm.capture('test/*.js', 'foo/bar.css')); - * //=> null - * ``` - * @param {String} `glob` Glob pattern to use for matching. - * @param {String} `input` String to match - * @param {Object} `options` See available [options](#options) for changing how matches are performed - * @return {Array|null} Returns an array of captures if the input matches the glob pattern, otherwise `null`. - * @api public - */ +const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; -micromatch.capture = (glob, input, options) => { - let posix = utils.isWindows(options); - let regex = picomatch.makeRe(String(glob), { ...options, capture: true }); - let match = regex.exec(posix ? utils.toPosixSlashes(input) : input); +const getFilterSync = options => { + return options && options.gitignore ? + gitignore.sync({cwd: options.cwd, ignore: options.ignore}) : + DEFAULT_FILTER; +}; - if (match) { - return match.slice(1).map(v => v === void 0 ? '' : v); - } +const globToTask = task => glob => { + const {options} = task; + if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { + options.ignore = dirGlob.sync(options.ignore); + } + + return { + pattern: glob, + options + }; }; -/** - * Create a regular expression from the given glob `pattern`. - * - * ```js - * const mm = require('micromatch'); - * // mm.makeRe(pattern[, options]); - * - * console.log(mm.makeRe('*.js')); - * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ - * ``` - * @param {String} `pattern` A glob pattern to convert to regex. - * @param {Object} `options` - * @return {RegExp} Returns a regex created from the given pattern. - * @api public - */ +module.exports = async (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); -micromatch.makeRe = (...args) => picomatch.makeRe(...args); + const getFilter = async () => { + return options && options.gitignore ? + gitignore({cwd: options.cwd, ignore: options.ignore}) : + DEFAULT_FILTER; + }; -/** - * Scan a glob pattern to separate the pattern into segments. Used - * by the [split](#split) method. - * - * ```js - * const mm = require('micromatch'); - * const state = mm.scan(pattern[, options]); - * ``` - * @param {String} `pattern` - * @param {Object} `options` - * @return {Object} Returns an object with - * @api public - */ + const getTasks = async () => { + const tasks = await Promise.all(globTasks.map(async task => { + const globs = await getPattern(task, dirGlob); + return Promise.all(globs.map(globToTask(task))); + })); -micromatch.scan = (...args) => picomatch.scan(...args); + return arrayUnion(...tasks); + }; -/** - * Parse a glob pattern to create the source string for a regular - * expression. - * - * ```js - * const mm = require('micromatch'); - * const state = mm(pattern[, options]); - * ``` - * @param {String} `glob` - * @param {Object} `options` - * @return {Object} Returns an object with useful properties and output to be used as regex source string. - * @api public - */ + const [filter, tasks] = await Promise.all([getFilter(), getTasks()]); + const paths = await Promise.all(tasks.map(task => fastGlob(task.pattern, task.options))); -micromatch.parse = (patterns, options) => { - let res = []; - for (let pattern of [].concat(patterns || [])) { - for (let str of braces(String(pattern), options)) { - res.push(picomatch.parse(str, options)); - } - } - return res; + return arrayUnion(...paths).filter(path_ => !filter(getPathString(path_))); }; -/** - * Process the given brace `pattern`. - * - * ```js - * const { braces } = require('micromatch'); - * console.log(braces('foo/{a,b,c}/bar')); - * //=> [ 'foo/(a|b|c)/bar' ] - * - * console.log(braces('foo/{a,b,c}/bar', { expand: true })); - * //=> [ 'foo/a/bar', 'foo/b/bar', 'foo/c/bar' ] - * ``` - * @param {String} `pattern` String with brace pattern to process. - * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options. - * @return {Array} - * @api public - */ +module.exports.sync = (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); -micromatch.braces = (pattern, options) => { - if (typeof pattern !== 'string') throw new TypeError('Expected a string'); - if ((options && options.nobrace === true) || !/\{.*\}/.test(pattern)) { - return [pattern]; - } - return braces(pattern, options); + const tasks = []; + for (const task of globTasks) { + const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); + tasks.push(...newTask); + } + + const filter = getFilterSync(options); + + let matches = []; + for (const task of tasks) { + matches = arrayUnion(matches, fastGlob.sync(task.pattern, task.options)); + } + + return matches.filter(path_ => !filter(path_)); }; -/** - * Expand braces - */ +module.exports.stream = (patterns, options) => { + const globTasks = generateGlobTasks(patterns, options); -micromatch.braceExpand = (pattern, options) => { - if (typeof pattern !== 'string') throw new TypeError('Expected a string'); - return micromatch.braces(pattern, { ...options, expand: true }); + const tasks = []; + for (const task of globTasks) { + const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); + tasks.push(...newTask); + } + + const filter = getFilterSync(options); + const filterStream = new FilterStream(p => !filter(p)); + const uniqueStream = new UniqueStream(); + + return merge2(tasks.map(task => fastGlob.stream(task.pattern, task.options))) + .pipe(filterStream) + .pipe(uniqueStream); }; -/** - * Expose micromatch - */ +module.exports.generateGlobTasks = generateGlobTasks; -module.exports = micromatch; +module.exports.hasMagic = (patterns, options) => [] + .concat(patterns) + .some(pattern => fastGlob.isDynamicPattern(pattern, options)); + +module.exports.gitignore = gitignore; /***/ }), diff --git a/yarn.lock b/yarn.lock index d7257d4d825fc..f736837089b5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14952,18 +14952,6 @@ fast-equals@^2.0.0: resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-2.0.0.tgz#bef2c423af3939f2c54310df54c57e64cd2adefc" integrity sha512-u6RBd8cSiLLxAiC04wVsLV6GBFDOXcTCgWkd3wEoFXgidPSoAJENqC9m7Jb2vewSvjBIfXV6icKeh3GTKfIaXA== -fast-glob@2.2.7, fast-glob@^2.2.6: - version "2.2.7" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" - integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== - dependencies: - "@mrmlnc/readdir-enhanced" "^2.2.1" - "@nodelib/fs.stat" "^1.1.2" - glob-parent "^3.1.0" - is-glob "^4.0.0" - merge2 "^1.2.3" - micromatch "^3.1.10" - fast-glob@3.2.7, fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.2, fast-glob@^3.2.4: version "3.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" @@ -14975,6 +14963,18 @@ fast-glob@3.2.7, fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.2, fast-glob merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + fast-glob@^3.2.7: version "3.2.11" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" From 787d196a1030a13352ab9bc3337bf1f9153b8dc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yulia=20=C4=8Cech?= <6585477+yuliacech@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:23:52 +0200 Subject: [PATCH 08/42] Removed Frank from the readme because he's left the company (#136201) --- x-pack/plugins/security_solution/cypress/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/README.md b/x-pack/plugins/security_solution/cypress/README.md index 620a2148f6cf7..b97e48c14cb05 100644 --- a/x-pack/plugins/security_solution/cypress/README.md +++ b/x-pack/plugins/security_solution/cypress/README.md @@ -7,7 +7,7 @@ Currently with Cypress you can develop `functional` tests and coming soon `CCS` If you are still having doubts, questions or queries, please feel free to ping our Cypress champions: - Functional Tests: - - Gloria Hornero, Frank Hassanabad and Patryk Kopycinsky + - Gloria Hornero and Patryk Kopycinsky - CCS Tests: - Technical questions around the https://github.com/elastic/integration-test repo: From 6f0f59e1f112fd88fddcb9b7dcf209e880120aa9 Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Tue, 12 Jul 2022 11:31:55 -0400 Subject: [PATCH 09/42] Create prop for `ExploratoryViewEmbeddable` to specify legend position. (#136137) --- .../shared/exploratory_view/embeddable/embeddable.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx index 8a42111622781..11d789f97da2a 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import { Position } from '@elastic/charts'; import React, { useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiText, EuiTitle } from '@elastic/eui'; import styled from 'styled-components'; @@ -34,6 +35,7 @@ export interface ExploratoryEmbeddableProps { dataTypesIndexPatterns?: Partial>; isSingleMetric?: boolean; legendIsVisible?: boolean; + legendPosition?: Position; onBrushEnd?: (param: { range: number[] }) => void; caseOwner?: string; reportConfigMap?: ReportConfigMap; @@ -61,6 +63,7 @@ export default function Embeddable({ indexPatterns, isSingleMetric = false, legendIsVisible, + legendPosition, lens, onBrushEnd, caseOwner = observabilityFeatureId, @@ -106,6 +109,9 @@ export default function Embeddable({ if (typeof legendIsVisible !== 'undefined') { (attributesJSON.state.visualization as XYState).legend.isVisible = legendIsVisible; } + if (typeof legendPosition !== 'undefined') { + (attributesJSON.state.visualization as XYState).legend.position = legendPosition; + } const actions = useActions({ withActions, From cd8a4ae677536a9ddf72fa8d6e6572882d2097bd Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Tue, 12 Jul 2022 10:34:27 -0500 Subject: [PATCH 10/42] [Shared UX] Move Page Template Solution Nav to package (#134974) * [Shared UX] Move Page Template Solution Nav to package * Fixing test import; fix i18n scope. * Update packages/shared-ux/page/solution_nav/src/collapse_button.scss * Address feedback, simplify generic * Tweak types * Addressing review feedback * REverting mistakes, ugh * Fix Observability tour * Apply suggestions from code review Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> * Addressing review feedback * Addressing review feedback * Fix i18n keys * Apply suggestions from code review Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> * Fix solution nav collapse Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> --- package.json | 2 + packages/BUILD.bazel | 2 + packages/kbn-shared-ux-components/BUILD.bazel | 48 +++--- .../kbn-shared-ux-components/src/index.ts | 2 - .../no_data_config_page.tsx | 6 +- .../page_template/page_template.stories.tsx | 5 +- .../src/page_template/page_template.test.tsx | 5 +- .../src/page_template/page_template.tsx | 2 - .../src/page_template/page_template_inner.tsx | 2 +- .../src/page_template/types.ts | 5 +- .../shared-ux/page/solution_nav/BUILD.bazel | 144 +++++++++++++++++ .../shared-ux/page/solution_nav/README.mdx | 17 ++ .../page/solution_nav/jest.config.js | 13 ++ .../shared-ux/page/solution_nav/package.json | 8 + .../collapse_button.test.tsx.snap} | 8 +- .../__snapshots__/solution_nav.test.tsx.snap | 147 +++++++++++++++--- .../with_solution_nav.test.tsx.snap | 8 +- .../solution_nav/src/collapse_button.scss} | 10 +- .../src/collapse_button.test.tsx} | 12 +- .../solution_nav/src/collapse_button.tsx} | 27 ++-- .../page/solution_nav/src}/index.ts | 8 +- .../page/solution_nav/src}/solution_nav.scss | 10 +- .../src}/solution_nav.stories.tsx | 19 ++- .../solution_nav/src}/solution_nav.test.tsx | 35 ++--- .../page/solution_nav/src}/solution_nav.tsx | 62 ++++---- .../solution_nav/src/with_solution_nav.scss} | 8 +- .../src}/with_solution_nav.test.tsx | 7 +- .../solution_nav/src}/with_solution_nav.tsx | 46 ++++-- .../shared-ux/page/solution_nav/tsconfig.json | 19 +++ .../public/page_template/page_template.scss | 7 +- .../public/components/shared/tour/tour.tsx | 2 +- .../app/home/template_wrapper/index.tsx | 2 +- .../translations/translations/fr-FR.json | 8 +- .../translations/translations/ja-JP.json | 8 +- .../translations/translations/zh-CN.json | 8 +- yarn.lock | 8 + 36 files changed, 528 insertions(+), 202 deletions(-) create mode 100644 packages/shared-ux/page/solution_nav/BUILD.bazel create mode 100644 packages/shared-ux/page/solution_nav/README.mdx create mode 100644 packages/shared-ux/page/solution_nav/jest.config.js create mode 100644 packages/shared-ux/page/solution_nav/package.json rename packages/{kbn-shared-ux-components/src/page_template/solution_nav/__snapshots__/solution_nav_collapse_button.test.tsx.snap => shared-ux/page/solution_nav/src/__snapshots__/collapse_button.test.tsx.snap} (51%) rename packages/{kbn-shared-ux-components/src/page_template/solution_nav => shared-ux/page/solution_nav/src}/__snapshots__/solution_nav.test.tsx.snap (73%) rename packages/{kbn-shared-ux-components/src/page_template => shared-ux/page/solution_nav/src}/__snapshots__/with_solution_nav.test.tsx.snap (94%) rename packages/{kbn-shared-ux-components/src/page_template/solution_nav/solution_nav_collapse_button.scss => shared-ux/page/solution_nav/src/collapse_button.scss} (76%) rename packages/{kbn-shared-ux-components/src/page_template/solution_nav/solution_nav_collapse_button.test.tsx => shared-ux/page/solution_nav/src/collapse_button.test.tsx} (54%) rename packages/{kbn-shared-ux-components/src/page_template/solution_nav/solution_nav_collapse_button.tsx => shared-ux/page/solution_nav/src/collapse_button.tsx} (62%) rename packages/{kbn-shared-ux-components/src/page_template/solution_nav => shared-ux/page/solution_nav/src}/index.ts (51%) rename packages/{kbn-shared-ux-components/src/page_template/solution_nav => shared-ux/page/solution_nav/src}/solution_nav.scss (74%) rename packages/{kbn-shared-ux-components/src/page_template/solution_nav => shared-ux/page/solution_nav/src}/solution_nav.stories.tsx (74%) rename packages/{kbn-shared-ux-components/src/page_template/solution_nav => shared-ux/page/solution_nav/src}/solution_nav.test.tsx (66%) rename packages/{kbn-shared-ux-components/src/page_template/solution_nav => shared-ux/page/solution_nav/src}/solution_nav.tsx (78%) rename packages/{kbn-shared-ux-components/src/page_template/page_template.scss => shared-ux/page/solution_nav/src/with_solution_nav.scss} (63%) rename packages/{kbn-shared-ux-components/src/page_template => shared-ux/page/solution_nav/src}/with_solution_nav.test.tsx (93%) rename packages/{kbn-shared-ux-components/src/page_template => shared-ux/page/solution_nav/src}/with_solution_nav.tsx (71%) create mode 100644 packages/shared-ux/page/solution_nav/tsconfig.json diff --git a/package.json b/package.json index e66145e4d50b1..3fb3ae6b9c87d 100644 --- a/package.json +++ b/package.json @@ -251,6 +251,7 @@ "@kbn/shared-ux-link-redirect-app": "link:bazel-bin/packages/shared-ux/link/redirect_app", "@kbn/shared-ux-page-analytics-no-data": "link:bazel-bin/packages/shared-ux/page/analytics_no_data", "@kbn/shared-ux-page-kibana-no-data": "link:bazel-bin/packages/shared-ux/page/kibana_no_data", + "@kbn/shared-ux-page-solution-nav": "link:bazel-bin/packages/shared-ux/page/solution_nav", "@kbn/shared-ux-prompt-no-data-views": "link:bazel-bin/packages/shared-ux/prompt/no_data_views", "@kbn/shared-ux-services": "link:bazel-bin/packages/kbn-shared-ux-services", "@kbn/shared-ux-storybook": "link:bazel-bin/packages/kbn-shared-ux-storybook", @@ -834,6 +835,7 @@ "@types/kbn__shared-ux-link-redirect-app": "link:bazel-bin/packages/shared-ux/link/redirect_app/npm_module_types", "@types/kbn__shared-ux-page-analytics-no-data": "link:bazel-bin/packages/shared-ux/page/analytics_no_data/npm_module_types", "@types/kbn__shared-ux-page-kibana-no-data": "link:bazel-bin/packages/shared-ux/page/kibana_no_data/npm_module_types", + "@types/kbn__shared-ux-page-solution-nav": "link:bazel-bin/packages/shared-ux/page/solution_nav/npm_module_types", "@types/kbn__shared-ux-prompt-no-data-views": "link:bazel-bin/packages/shared-ux/prompt/no_data_views/npm_module_types", "@types/kbn__shared-ux-services": "link:bazel-bin/packages/kbn-shared-ux-services/npm_module_types", "@types/kbn__shared-ux-storybook": "link:bazel-bin/packages/kbn-shared-ux-storybook/npm_module_types", diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel index 588a8037dcb3d..cfa092c51c3e0 100644 --- a/packages/BUILD.bazel +++ b/packages/BUILD.bazel @@ -186,6 +186,7 @@ filegroup( "//packages/shared-ux/link/redirect_app:build", "//packages/shared-ux/page/analytics_no_data:build", "//packages/shared-ux/page/kibana_no_data:build", + "//packages/shared-ux/page/solution_nav:build", "//packages/shared-ux/prompt/no_data_views:build", "//x-pack/packages/ml/agg_utils:build", "//x-pack/packages/ml/aiops_components:build", @@ -361,6 +362,7 @@ filegroup( "//packages/shared-ux/link/redirect_app:build_types", "//packages/shared-ux/page/analytics_no_data:build_types", "//packages/shared-ux/page/kibana_no_data:build_types", + "//packages/shared-ux/page/solution_nav:build_types", "//packages/shared-ux/prompt/no_data_views:build_types", "//x-pack/packages/ml/agg_utils:build_types", "//x-pack/packages/ml/aiops_components:build_types", diff --git a/packages/kbn-shared-ux-components/BUILD.bazel b/packages/kbn-shared-ux-components/BUILD.bazel index 03468daf75fb8..5739076517f0b 100644 --- a/packages/kbn-shared-ux-components/BUILD.bazel +++ b/packages/kbn-shared-ux-components/BUILD.bazel @@ -40,23 +40,24 @@ NPM_MODULE_EXTRA_FILES = [ # "@npm//name-of-package" # eg. "@npm//lodash" RUNTIME_DEPS = [ - "//packages/kbn-i18n-react", - "//packages/kbn-i18n", - "//packages/shared-ux/avatar/solution", - "//packages/shared-ux/link/redirect_app", - "//packages/shared-ux/prompt/no_data_views", - "//packages/shared-ux/card/no_data", - "//packages/kbn-shared-ux-services", - "//packages/kbn-shared-ux-storybook", - "//packages/kbn-shared-ux-utility", "@npm//@elastic/eui", - "@npm//@emotion/react", "@npm//@emotion/css", + "@npm//@emotion/react", "@npm//classnames", "@npm//react-use", "@npm//react", "@npm//rxjs", "@npm//url-loader", + "//packages/kbn-i18n-react", + "//packages/kbn-i18n", + "//packages/kbn-shared-ux-services", + "//packages/kbn-shared-ux-storybook", + "//packages/kbn-shared-ux-utility", + "//packages/shared-ux/avatar/solution", + "//packages/shared-ux/card/no_data", + "//packages/shared-ux/link/redirect_app", + "//packages/shared-ux/page/solution_nav", + "//packages/shared-ux/prompt/no_data_views", ] # In this array place dependencies necessary to build the types, which will include the @@ -69,25 +70,26 @@ RUNTIME_DEPS = [ # # References to NPM packages work the same as RUNTIME_DEPS TYPES_DEPS = [ + "@npm//@elastic/eui", + "@npm//@emotion/css", + "@npm//@emotion/react", + "@npm//@types/classnames", + "@npm//@types/jest", + "@npm//@types/node", + "@npm//@types/react", + "@npm//react-use", + "@npm//rxjs", "//packages/kbn-ambient-ui-types", "//packages/kbn-i18n-react:npm_module_types", "//packages/kbn-i18n:npm_module_types", - "//packages/shared-ux/avatar/solution:npm_module_types", - "//packages/shared-ux/link/redirect_app:npm_module_types", - "//packages/shared-ux/prompt/no_data_views:npm_module_types", - "//packages/shared-ux/card/no_data:npm_module_types", "//packages/kbn-shared-ux-services:npm_module_types", "//packages/kbn-shared-ux-storybook:npm_module_types", "//packages/kbn-shared-ux-utility:npm_module_types", - "@npm//@types/node", - "@npm//@types/jest", - "@npm//@types/react", - "@npm//@types/classnames", - "@npm//@emotion/react", - "@npm//@emotion/css", - "@npm//@elastic/eui", - "@npm//react-use", - "@npm//rxjs", + "//packages/shared-ux/avatar/solution:npm_module_types", + "//packages/shared-ux/card/no_data:npm_module_types", + "//packages/shared-ux/link/redirect_app:npm_module_types", + "//packages/shared-ux/page/solution_nav:npm_module_types", + "//packages/shared-ux/prompt/no_data_views:npm_module_types", ] jsts_transpiler( diff --git a/packages/kbn-shared-ux-components/src/index.ts b/packages/kbn-shared-ux-components/src/index.ts index 970e584d95782..9e11d9341fe59 100644 --- a/packages/kbn-shared-ux-components/src/index.ts +++ b/packages/kbn-shared-ux-components/src/index.ts @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -export { KibanaPageTemplateSolutionNav } from './page_template/solution_nav'; - // TODO: clintandrewhall - NoDataPageProps is a temporary addition until it is split into its own package export type { KibanaPageTemplateProps, NoDataPageProps } from './page_template'; diff --git a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_config_page/no_data_config_page.tsx b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_config_page/no_data_config_page.tsx index 77c2d659b56ef..c21431a8107fe 100644 --- a/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_config_page/no_data_config_page.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/no_data_page/no_data_config_page/no_data_config_page.tsx @@ -5,10 +5,12 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { EuiPageTemplate } from '@elastic/eui'; + import React from 'react'; +import { EuiPageTemplate } from '@elastic/eui'; +import { withSolutionNav } from '@kbn/shared-ux-page-solution-nav'; + import { NoDataPage } from '../no_data_page'; -import { withSolutionNav } from '../../with_solution_nav'; import { KibanaPageTemplateProps } from '../../types'; import { getClasses, NO_DATA_PAGE_TEMPLATE_PROPS } from '../../util'; diff --git a/packages/kbn-shared-ux-components/src/page_template/page_template.stories.tsx b/packages/kbn-shared-ux-components/src/page_template/page_template.stories.tsx index 6e0cf9fdb2e52..ae6be1297f018 100644 --- a/packages/kbn-shared-ux-components/src/page_template/page_template.stories.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/page_template.stories.tsx @@ -8,9 +8,10 @@ import React from 'react'; import { EuiButton, EuiText } from '@elastic/eui'; +import { SolutionNavProps } from '@kbn/shared-ux-page-solution-nav'; + import { KibanaPageTemplate } from './page_template'; import mdx from './page_template.mdx'; -import { KibanaPageTemplateSolutionNavProps } from './solution_nav'; import { KibanaPageTemplateProps } from './types'; export default { @@ -36,7 +37,7 @@ const noDataConfig = { docsLink: 'http://wwww.docs.elastic.co', }; -const items: KibanaPageTemplateSolutionNavProps['items'] = [ +const items: SolutionNavProps['items'] = [ { name: 'Ingest', id: '1', diff --git a/packages/kbn-shared-ux-components/src/page_template/page_template.test.tsx b/packages/kbn-shared-ux-components/src/page_template/page_template.test.tsx index 8d073e14f7776..1324b60b92870 100644 --- a/packages/kbn-shared-ux-components/src/page_template/page_template.test.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/page_template.test.tsx @@ -8,11 +8,12 @@ import React from 'react'; import { shallow, render } from 'enzyme'; +import { SolutionNavProps } from '@kbn/shared-ux-page-solution-nav'; + import { KibanaPageTemplate } from './page_template'; -import { KibanaPageTemplateSolutionNavProps } from './solution_nav'; import { NoDataPageProps } from './no_data_page'; -const items: KibanaPageTemplateSolutionNavProps['items'] = [ +const items: SolutionNavProps['items'] = [ { name: 'Ingest', id: '1', diff --git a/packages/kbn-shared-ux-components/src/page_template/page_template.tsx b/packages/kbn-shared-ux-components/src/page_template/page_template.tsx index 6d63d54e9b9dd..467f02224b0de 100644 --- a/packages/kbn-shared-ux-components/src/page_template/page_template.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/page_template.tsx @@ -6,8 +6,6 @@ * Side Public License, v 1. */ -import './page_template.scss'; - import React, { FunctionComponent } from 'react'; import { NoDataConfigPage, NoDataConfigPageWithSolutionNavBar } from './no_data_page'; diff --git a/packages/kbn-shared-ux-components/src/page_template/page_template_inner.tsx b/packages/kbn-shared-ux-components/src/page_template/page_template_inner.tsx index cef22f2713efc..46424348f2ff3 100644 --- a/packages/kbn-shared-ux-components/src/page_template/page_template_inner.tsx +++ b/packages/kbn-shared-ux-components/src/page_template/page_template_inner.tsx @@ -9,8 +9,8 @@ import React, { FunctionComponent } from 'react'; import { EuiEmptyPrompt, EuiPageTemplate } from '@elastic/eui'; +import { withSolutionNav } from '@kbn/shared-ux-page-solution-nav'; -import { withSolutionNav } from './with_solution_nav'; import { KibanaPageTemplateProps } from './types'; import { getClasses } from './util'; diff --git a/packages/kbn-shared-ux-components/src/page_template/types.ts b/packages/kbn-shared-ux-components/src/page_template/types.ts index cd4764a976db8..6a8b0d583ed18 100644 --- a/packages/kbn-shared-ux-components/src/page_template/types.ts +++ b/packages/kbn-shared-ux-components/src/page_template/types.ts @@ -7,7 +7,8 @@ */ import { EuiPageTemplateProps } from '@elastic/eui'; -import { KibanaPageTemplateSolutionNavProps } from './solution_nav'; +import { SolutionNavProps } from '@kbn/shared-ux-page-solution-nav'; + import { NoDataPageProps } from './no_data_page'; export type KibanaPageTemplateProps = EuiPageTemplateProps & { @@ -21,7 +22,7 @@ export type KibanaPageTemplateProps = EuiPageTemplateProps & { /** * Quick creation of EuiSideNav. Hooks up mobile instance too */ - solutionNav?: KibanaPageTemplateSolutionNavProps; + solutionNav?: SolutionNavProps; /** * Accepts a configuration object, that when provided, ignores pageHeader and children and instead * displays Agent, Beats, and custom cards to direct users to the right ingest location diff --git a/packages/shared-ux/page/solution_nav/BUILD.bazel b/packages/shared-ux/page/solution_nav/BUILD.bazel new file mode 100644 index 0000000000000..ef939dfdbbc5b --- /dev/null +++ b/packages/shared-ux/page/solution_nav/BUILD.bazel @@ -0,0 +1,144 @@ +load("@npm//@bazel/typescript:index.bzl", "ts_config") +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") +load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", "ts_project") + +PKG_DIRNAME = "solution_nav" +PKG_REQUIRE_NAME = "@kbn/shared-ux-page-solution-nav" + +SOURCE_FILES = glob( + [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.scss", + "src/**/*.mdx", + ], + exclude = [ + "**/*.test.*", + "**/*.stories.*", + ], +) + +SRCS = SOURCE_FILES + +filegroup( + name = "srcs", + srcs = SRCS, +) + +NPM_MODULE_EXTRA_FILES = [ + "package.json", +] + +# In this array place runtime dependencies, including other packages and NPM packages +# which must be available for this code to run. +# +# To reference other packages use: +# "//repo/relative/path/to/package" +# eg. "//packages/kbn-utils" +# +# To reference a NPM package use: +# "@npm//name-of-package" +# eg. "@npm//lodash" +RUNTIME_DEPS = [ + "@npm//@elastic/eui", + "@npm//classnames", + "@npm//enzyme", + "@npm//react",\ + "//packages/kbn-i18n-react", + "//packages/kbn-i18n", + "//packages/shared-ux/avatar/solution", +] + +# In this array place dependencies necessary to build the types, which will include the +# :npm_module_types target of other packages and packages from NPM, including @types/* +# packages. +# +# To reference the types for another package use: +# "//repo/relative/path/to/package:npm_module_types" +# eg. "//packages/kbn-utils:npm_module_types" +# +# References to NPM packages work the same as RUNTIME_DEPS +TYPES_DEPS = [ + "@npm//@elastic/eui", + "@npm//@types/classnames", + "@npm//@types/enzyme", + "@npm//@types/jest", + "@npm//@types/node", + "@npm//@types/react", + "//packages/kbn-ambient-ui-types", + "//packages/kbn-i18n-react:npm_module_types", + "//packages/kbn-i18n:npm_module_types", + "//packages/shared-ux/avatar/solution:npm_module_types", +] + +jsts_transpiler( + name = "target_node", + srcs = SRCS, + build_pkg_name = package_name(), +) + +jsts_transpiler( + name = "target_web", + srcs = SRCS, + build_pkg_name = package_name(), + web = True, + additional_args = [ + "--copy-files", + "--quiet" + ], +) + +ts_config( + name = "tsconfig", + src = "tsconfig.json", + deps = [ + "//:tsconfig.base.json", + "//:tsconfig.bazel.json", + ], +) + +ts_project( + name = "tsc_types", + args = ['--pretty'], + srcs = SRCS, + deps = TYPES_DEPS, + declaration = True, + emit_declaration_only = True, + out_dir = "target_types", + root_dir = "src", + tsconfig = ":tsconfig", +) + +js_library( + name = PKG_DIRNAME, + srcs = NPM_MODULE_EXTRA_FILES, + deps = RUNTIME_DEPS + [":target_node", ":target_web"], + package_name = PKG_REQUIRE_NAME, + visibility = ["//visibility:public"], +) + +pkg_npm( + name = "npm_module", + deps = [":" + PKG_DIRNAME], +) + +filegroup( + name = "build", + srcs = [":npm_module"], + visibility = ["//visibility:public"], +) + +pkg_npm_types( + name = "npm_module_types", + srcs = SRCS, + deps = [":tsc_types"], + package_name = PKG_REQUIRE_NAME, + tsconfig = ":tsconfig", + visibility = ["//visibility:public"], +) + +filegroup( + name = "build_types", + srcs = [":npm_module_types"], + visibility = ["//visibility:public"], +) diff --git a/packages/shared-ux/page/solution_nav/README.mdx b/packages/shared-ux/page/solution_nav/README.mdx new file mode 100644 index 0000000000000..abad92c1e4108 --- /dev/null +++ b/packages/shared-ux/page/solution_nav/README.mdx @@ -0,0 +1,17 @@ +--- +id: sharedUX/Page/SolutionNav +slug: /shared-ux/page/solution-nav +title: Solution Page Navigation +summary: A customized `EuiSideNav` for pages within Solutions in Kibana. +tags: ['shared-ux', 'component'] +date: 2022-06-30 +--- + +This is a customized `EuiSideNav` for pages within Solutions in Kibana. It also includes a Higher-order Component (HOC) to supply Side Navigation to an existing component. It is currently used in the Kibana Page Template for solutions that provide side navigation, (e.g. Observability). It does not currently depend on any Kibana services. + +## API + +| Export | Description | +|---|---| +| `SolutionNav` | The `SolutionNav` component | +| `withSolutionNav` | The HOC which can provide the navigation to any component aligning with `KibanaPageTemplate` and its props. | \ No newline at end of file diff --git a/packages/shared-ux/page/solution_nav/jest.config.js b/packages/shared-ux/page/solution_nav/jest.config.js new file mode 100644 index 0000000000000..243409e35b2e4 --- /dev/null +++ b/packages/shared-ux/page/solution_nav/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../..', + roots: ['/packages/shared-ux/page/solution_nav'], +}; diff --git a/packages/shared-ux/page/solution_nav/package.json b/packages/shared-ux/page/solution_nav/package.json new file mode 100644 index 0000000000000..f57abed80f231 --- /dev/null +++ b/packages/shared-ux/page/solution_nav/package.json @@ -0,0 +1,8 @@ +{ + "name": "@kbn/shared-ux-page-solution-nav", + "private": true, + "version": "1.0.0", + "main": "./target_node/index.js", + "browser": "./target_web/index.js", + "license": "SSPL-1.0 OR Elastic License 2.0" +} diff --git a/packages/kbn-shared-ux-components/src/page_template/solution_nav/__snapshots__/solution_nav_collapse_button.test.tsx.snap b/packages/shared-ux/page/solution_nav/src/__snapshots__/collapse_button.test.tsx.snap similarity index 51% rename from packages/kbn-shared-ux-components/src/page_template/solution_nav/__snapshots__/solution_nav_collapse_button.test.tsx.snap rename to packages/shared-ux/page/solution_nav/src/__snapshots__/collapse_button.test.tsx.snap index d2548b3e8df43..04fb0dfc2a965 100644 --- a/packages/kbn-shared-ux-components/src/page_template/solution_nav/__snapshots__/solution_nav_collapse_button.test.tsx.snap +++ b/packages/shared-ux/page/solution_nav/src/__snapshots__/collapse_button.test.tsx.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`KibanaPageTemplateSolutionNavCollapseButton isCollapsed 1`] = ` +exports[`SolutionNavCollapseButton isCollapsed 1`] = ` `; -exports[`KibanaPageTemplateSolutionNavCollapseButton renders 1`] = ` +exports[`SolutionNavCollapseButton renders 1`] = `

`; -exports[`KibanaPageTemplateSolutionNav accepts canBeCollapsed prop 1`] = ` +exports[`SolutionNav accepts canBeCollapsed prop 1`] = `

`; -exports[`KibanaPageTemplateSolutionNav heading accepts more headingProps 1`] = ` +exports[`SolutionNav accepts canBeCollapsed prop 2`] = ` +

+ + + +

+ + } + titleElement="span" + > + + + +
+
+`; + +exports[`SolutionNav heading accepts more headingProps 1`] = ` + + `; -exports[`KibanaPageTemplateSolutionNav renders 1`] = ` +exports[`SolutionNav renders 1`] = `

`; -exports[`KibanaPageTemplateSolutionNav renders with icon 1`] = ` +exports[`SolutionNav renders with icon 1`] = `

{ +describe('SolutionNavCollapseButton', () => { test('renders', () => { - const component = shallow(); + const component = shallow(); expect(component).toMatchSnapshot(); - expect(component.find('.kbnPageTemplateSolutionNavCollapseButton').prop('title')).toBe( + expect(component.find('.kbnSolutionNavCollapseButton').prop('title')).toBe( 'Collapse side navigation' ); }); test('isCollapsed', () => { - const component = shallow(); + const component = shallow(); expect(component).toMatchSnapshot(); - expect(component.find('.kbnPageTemplateSolutionNavCollapseButton').prop('title')).toBe( + expect(component.find('.kbnSolutionNavCollapseButton').prop('title')).toBe( 'Open side navigation' ); }); diff --git a/packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav_collapse_button.tsx b/packages/shared-ux/page/solution_nav/src/collapse_button.tsx similarity index 62% rename from packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav_collapse_button.tsx rename to packages/shared-ux/page/solution_nav/src/collapse_button.tsx index 35890b935ad3e..2cfc2b61a2b46 100644 --- a/packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav_collapse_button.tsx +++ b/packages/shared-ux/page/solution_nav/src/collapse_button.tsx @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import './solution_nav_collapse_button.scss'; +import './collapse_button.scss'; import React from 'react'; import classNames from 'classnames'; @@ -13,34 +13,33 @@ import classNames from 'classnames'; import { EuiButtonIcon, EuiButtonIconPropsForButton } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -export type KibanaPageTemplateSolutionNavCollapseButtonProps = - Partial & { - /** - * Boolean state of current collapsed status - */ - isCollapsed: boolean; - }; +export type SolutionNavCollapseButtonProps = Partial & { + /** + * Boolean state of current collapsed status + */ + isCollapsed: boolean; +}; -const collapseLabel = i18n.translate('sharedUXComponents.solutionNav.collapsibleLabel', { +const collapseLabel = i18n.translate('sharedUXPackages.solutionNav.collapsibleLabel', { defaultMessage: 'Collapse side navigation', }); -const openLabel = i18n.translate('sharedUXComponents.solutionNav.openLabel', { +const openLabel = i18n.translate('sharedUXPackages.solutionNav.openLabel', { defaultMessage: 'Open side navigation', }); /** * Creates the styled icon button for showing/hiding solution nav */ -export const KibanaPageTemplateSolutionNavCollapseButton = ({ +export const SolutionNavCollapseButton = ({ className, isCollapsed, ...rest -}: KibanaPageTemplateSolutionNavCollapseButtonProps) => { +}: SolutionNavCollapseButtonProps) => { const classes = classNames( - 'kbnPageTemplateSolutionNavCollapseButton', + 'kbnSolutionNavCollapseButton', { - 'kbnPageTemplateSolutionNavCollapseButton-isCollapsed': isCollapsed, + 'kbnSolutionNavCollapseButton-isCollapsed': isCollapsed, }, className ); diff --git a/packages/kbn-shared-ux-components/src/page_template/solution_nav/index.ts b/packages/shared-ux/page/solution_nav/src/index.ts similarity index 51% rename from packages/kbn-shared-ux-components/src/page_template/solution_nav/index.ts rename to packages/shared-ux/page/solution_nav/src/index.ts index 59ef2924b048d..d116afa7accaa 100644 --- a/packages/kbn-shared-ux-components/src/page_template/solution_nav/index.ts +++ b/packages/shared-ux/page/solution_nav/src/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -export type { KibanaPageTemplateSolutionNavProps } from './solution_nav'; -export { KibanaPageTemplateSolutionNav } from './solution_nav'; -export type { KibanaPageTemplateSolutionNavCollapseButtonProps } from './solution_nav_collapse_button'; -export { KibanaPageTemplateSolutionNavCollapseButton } from './solution_nav_collapse_button'; +export { SolutionNav } from './solution_nav'; +export type { SolutionNavProps } from './solution_nav'; + +export { withSolutionNav } from './with_solution_nav'; diff --git a/packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.scss b/packages/shared-ux/page/solution_nav/src/solution_nav.scss similarity index 74% rename from packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.scss rename to packages/shared-ux/page/solution_nav/src/solution_nav.scss index c21f5e1bbab99..06fb58cf3de03 100644 --- a/packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.scss +++ b/packages/shared-ux/page/solution_nav/src/solution_nav.scss @@ -2,28 +2,28 @@ $euiSideNavEmphasizedBackgroundColor: transparentize($euiColorLightShade, .7); @import '@elastic/eui/src/components/side_nav/mixins'; // Put the page background color in the flyout version too -.kbnPageTemplateSolutionNav__flyout { +.kbnSolutionNav__flyout { background-color: $euiPageBackgroundColor; } -.kbnPageTemplateSolutionNav { +.kbnSolutionNav { @include euiSideNavEmbellish; @include euiYScroll; display: flex; flex-direction: column; - @include euiBreakpoint('m' ,'l', 'xl') { + @include euiBreakpoint('m', 'l', 'xl') { width: 248px; padding: $euiSizeL; } - .kbnPageTemplateSolutionNav__avatar { + .kbnSolutionNav__avatar { margin-right: $euiSize; } } -.kbnPageTemplateSolutionNav--hidden { +.kbnSolutionNav--hidden { pointer-events: none; opacity: 0; diff --git a/packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.stories.tsx b/packages/shared-ux/page/solution_nav/src/solution_nav.stories.tsx similarity index 74% rename from packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.stories.tsx rename to packages/shared-ux/page/solution_nav/src/solution_nav.stories.tsx index 03bf76da8b285..9613fbac42f6e 100644 --- a/packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.stories.tsx +++ b/packages/shared-ux/page/solution_nav/src/solution_nav.stories.tsx @@ -7,16 +7,17 @@ */ import React from 'react'; -import { KibanaPageTemplateSolutionNav, KibanaPageTemplateSolutionNavProps } from './solution_nav'; +import { action } from '@storybook/addon-actions'; +import { SolutionNav as Component, SolutionNavProps } from './solution_nav'; export default { - title: 'Page Template/Solution Nav/Solution Nav', + title: 'Page Template', description: 'Solution-specific navigation for the sidebar', }; -type Params = Pick; +type Params = Pick; -const items: KibanaPageTemplateSolutionNavProps['items'] = [ +const items: SolutionNavProps['items'] = [ { name:
Ingest
, id: '1', @@ -55,11 +56,13 @@ const items: KibanaPageTemplateSolutionNavProps['items'] = [ }, ]; -export const PureComponent = (params: Params) => { - return ; +export const SolutionNav = (params: Params) => { + return ( + + ); }; -PureComponent.argTypes = { +SolutionNav.argTypes = { name: { control: 'text', defaultValue: 'Kibana', @@ -79,6 +82,6 @@ PureComponent.argTypes = { }, }; -PureComponent.parameters = { +SolutionNav.parameters = { layout: 'fullscreen', }; diff --git a/packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.test.tsx b/packages/shared-ux/page/solution_nav/src/solution_nav.test.tsx similarity index 66% rename from packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.test.tsx rename to packages/shared-ux/page/solution_nav/src/solution_nav.test.tsx index 459d186e04d2c..7be2fc7c397f7 100644 --- a/packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.test.tsx +++ b/packages/shared-ux/page/solution_nav/src/solution_nav.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { KibanaPageTemplateSolutionNav, KibanaPageTemplateSolutionNavProps } from './solution_nav'; +import { SolutionNav, SolutionNavProps } from './solution_nav'; jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); @@ -20,7 +20,7 @@ jest.mock('@elastic/eui', () => { }; }); -const items: KibanaPageTemplateSolutionNavProps['items'] = [ +const items: SolutionNavProps['items'] = [ { name: 'Ingest', id: '1', @@ -59,14 +59,11 @@ const items: KibanaPageTemplateSolutionNavProps['items'] = [ }, ]; -describe('KibanaPageTemplateSolutionNav', () => { +describe('SolutionNav', () => { describe('heading', () => { test('accepts more headingProps', () => { const component = shallow( - + ); expect(component).toMatchSnapshot(); @@ -74,37 +71,37 @@ describe('KibanaPageTemplateSolutionNav', () => { }); test('renders', () => { - const component = shallow(); + const component = shallow(); expect(component).toMatchSnapshot(); }); test('renders with icon', () => { - const component = shallow( - - ); + const component = shallow(); expect(component).toMatchSnapshot(); }); test('renders with children', () => { const component = shallow( - + - + ); expect(component.find('#dummy_component').length > 0).toBeTruthy(); }); test('accepts EuiSideNavProps', () => { - const component = shallow( - - ); + const component = shallow(); expect(component).toMatchSnapshot(); }); test('accepts canBeCollapsed prop', () => { - const component = shallow( - + const canBeCollapsed = shallow( + ); - expect(component).toMatchSnapshot(); + expect(canBeCollapsed).toMatchSnapshot(); + const noCollapse = shallow( + + ); + expect(noCollapse).toMatchSnapshot(); }); }); diff --git a/packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.tsx b/packages/shared-ux/page/solution_nav/src/solution_nav.tsx similarity index 78% rename from packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.tsx rename to packages/shared-ux/page/solution_nav/src/solution_nav.tsx index 9ba6438bf94f8..31950f716c225 100644 --- a/packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.tsx +++ b/packages/shared-ux/page/solution_nav/src/solution_nav.tsx @@ -7,7 +7,7 @@ */ import './solution_nav.scss'; -import React, { FunctionComponent, useState, useMemo } from 'react'; +import React, { FC, useState, useMemo } from 'react'; import classNames from 'classnames'; import { EuiAvatarProps, @@ -27,12 +27,12 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { KibanaSolutionAvatar } from '@kbn/shared-ux-avatar-solution'; -import { KibanaPageTemplateSolutionNavCollapseButton } from './solution_nav_collapse_button'; +import { SolutionNavCollapseButton } from './collapse_button'; -export type KibanaPageTemplateSolutionNavProps = Omit< - EuiSideNavProps<{}>, - 'children' | 'items' | 'heading' -> & { +/** + * Props for the `SolutionNav` component. + */ +export type SolutionNavProps = Omit, 'children' | 'items' | 'heading'> & { /** * Name of the solution, i.e. "Observability" */ @@ -58,6 +58,9 @@ export type KibanaPageTemplateSolutionNavProps = Omit< * Control the collapsed state */ isOpenOnDesktop?: boolean; + /** + * Handler for when the navigation flyout is collapsed. + */ onCollapse?: () => void; /** * Allows hiding of the navigation by the user. @@ -77,14 +80,12 @@ const setTabIndex = (items: Array>, isHidden: boolean) => }); }; -const generateId = htmlIdGenerator('KibanaPageTemplateSolutionNav'); +const generateId = htmlIdGenerator('SolutionNav'); /** - * A wrapper around EuiSideNav but also creates the appropriate title with optional solution logo + * A wrapper around `EuiSideNav` that includes the appropriate title with optional solution logo. */ -export const KibanaPageTemplateSolutionNav: FunctionComponent< - KibanaPageTemplateSolutionNavProps -> = ({ +export const SolutionNav: FC = ({ children, headingProps, icon, @@ -101,7 +102,7 @@ export const KibanaPageTemplateSolutionNav: FunctionComponent< const isMediumBreakpoint = useIsWithinBreakpoints(['m']); const isLargerBreakpoint = useIsWithinBreakpoints(['l', 'xl']); - // This is used for both the EuiSideNav and EuiFlyout toggling + // This is used for both the `EuiSideNav` and `EuiFlyout` toggling const [isSideNavOpenOnMobile, setIsSideNavOpenOnMobile] = useState(false); const toggleOpenOnMobile = () => { setIsSideNavOpenOnMobile(!isSideNavOpenOnMobile); @@ -110,33 +111,28 @@ export const KibanaPageTemplateSolutionNav: FunctionComponent< const isHidden = isLargerBreakpoint && !isOpenOnDesktop && canBeCollapsed; const isCustomSideNav = !!children; - const sideNavClasses = classNames('kbnPageTemplateSolutionNav', { - 'kbnPageTemplateSolutionNav--hidden': isHidden, + const sideNavClasses = classNames('kbnSolutionNav', { + 'kbnSolutionNav--hidden': isHidden, }); - /** - * Create the avatar and titles - */ + // Create the avatar and titles. const headingID = headingProps?.id || generateId('heading'); const HeadingElement = headingProps?.element || 'h2'; + const titleText = ( {icon && ( - + )} ); - /** - * Create the side nav content - */ + // Create the side nav content const sideNavContent = useMemo(() => { if (isCustomSideNav) { return children; } + if (!items) { return null; } + return (
@@ -207,10 +203,7 @@ export const KibanaPageTemplateSolutionNav: FunctionComponent< )} {canBeCollapsed && ( - + )} )} @@ -222,10 +215,7 @@ export const KibanaPageTemplateSolutionNav: FunctionComponent< {sideNavContent}
{canBeCollapsed && ( - + )} )} diff --git a/packages/kbn-shared-ux-components/src/page_template/page_template.scss b/packages/shared-ux/page/solution_nav/src/with_solution_nav.scss similarity index 63% rename from packages/kbn-shared-ux-components/src/page_template/page_template.scss rename to packages/shared-ux/page/solution_nav/src/with_solution_nav.scss index aec93da6217ee..1e4dfba82d13c 100644 --- a/packages/kbn-shared-ux-components/src/page_template/page_template.scss +++ b/packages/shared-ux/page/solution_nav/src/with_solution_nav.scss @@ -1,4 +1,4 @@ -.kbnPageTemplate__pageSideBar { +.kbnSolutionNav__sidebar { overflow: hidden; // Temporary hack till the sizing is changed directly in EUI min-width: 248px; @@ -7,13 +7,11 @@ transition: min-width $euiAnimSpeedFast $euiAnimSlightResistance; } - &.kbnPageTemplate__pageSideBar--shrink { + &.kbnSolutionNav__sidebar--shrink { min-width: $euiSizeXXL; } .kbnPageTemplate--centeredBody & { - @include euiBreakpoint('m', 'l', 'xl') { - border-right: $euiBorderThin; - } + border-right: $euiBorderThin; } } diff --git a/packages/kbn-shared-ux-components/src/page_template/with_solution_nav.test.tsx b/packages/shared-ux/page/solution_nav/src/with_solution_nav.test.tsx similarity index 93% rename from packages/kbn-shared-ux-components/src/page_template/with_solution_nav.test.tsx rename to packages/shared-ux/page/solution_nav/src/with_solution_nav.test.tsx index 0d0ac4cf71bfc..b179eb6f3fab9 100644 --- a/packages/kbn-shared-ux-components/src/page_template/with_solution_nav.test.tsx +++ b/packages/shared-ux/page/solution_nav/src/with_solution_nav.test.tsx @@ -6,16 +6,17 @@ * Side Public License, v 1. */ -import { shallow } from 'enzyme'; import React from 'react'; +import { shallow } from 'enzyme'; + +import { SolutionNavProps } from './solution_nav'; import { withSolutionNav } from './with_solution_nav'; -import { KibanaPageTemplateSolutionNavProps } from './solution_nav'; const TestComponent = () => { return
This is a wrapped component
; }; -const items: KibanaPageTemplateSolutionNavProps['items'] = [ +const items: SolutionNavProps['items'] = [ { name: 'Ingest', id: '1', diff --git a/packages/kbn-shared-ux-components/src/page_template/with_solution_nav.tsx b/packages/shared-ux/page/solution_nav/src/with_solution_nav.tsx similarity index 71% rename from packages/kbn-shared-ux-components/src/page_template/with_solution_nav.tsx rename to packages/shared-ux/page/solution_nav/src/with_solution_nav.tsx index 8d804030f9199..5b6fc9e083dbe 100644 --- a/packages/kbn-shared-ux-components/src/page_template/with_solution_nav.tsx +++ b/packages/shared-ux/page/solution_nav/src/with_solution_nav.tsx @@ -8,24 +8,34 @@ import React, { ComponentType, useState } from 'react'; import classNames from 'classnames'; -import { useIsWithinBreakpoints } from '@elastic/eui'; -import { EuiPageSideBarProps } from '@elastic/eui/src/components/page/page_side_bar'; -import { KibanaPageTemplateSolutionNav, KibanaPageTemplateSolutionNavProps } from './solution_nav'; -import { KibanaPageTemplateProps } from './types'; +import { useIsWithinBreakpoints, EuiPageTemplateProps } from '@elastic/eui'; +import { SolutionNav, SolutionNavProps } from './solution_nav'; + +import './with_solution_nav.scss'; // https://reactjs.org/docs/higher-order-components.html#convention-wrap-the-display-name-for-easy-debugging function getDisplayName(Component: ComponentType) { return Component.displayName || Component.name || 'UnnamedComponent'; } -type SolutionNavProps = KibanaPageTemplateProps & { - solutionNav: KibanaPageTemplateSolutionNavProps; +type TemplateProps = Pick< + EuiPageTemplateProps, + 'pageSideBar' | 'pageSideBarProps' | 'template' | 'children' +>; + +type ComponentProps = TemplateProps & { + isEmptyState?: boolean; }; +type Props

= P & + ComponentProps & { + solutionNav: SolutionNavProps; + }; + const SOLUTION_NAV_COLLAPSED_KEY = 'solutionNavIsCollapsed'; -export const withSolutionNav = (WrappedComponent: ComponentType) => { - const WithSolutionNav = (props: SolutionNavProps) => { +export const withSolutionNav =

(WrappedComponent: ComponentType

) => { + const WithSolutionNav = (props: Props

) => { const isMediumBreakpoint = useIsWithinBreakpoints(['m']); const isLargerBreakpoint = useIsWithinBreakpoints(['l', 'xl']); const [isSideNavOpenOnDesktop, setisSideNavOpenOnDesktop] = useState( @@ -33,6 +43,7 @@ export const withSolutionNav = (WrappedComponent: ComponentType { setisSideNavOpenOnDesktop(!isSideNavOpenOnDesktop); // Have to store it as the opposite of the default we want @@ -42,9 +53,9 @@ export const withSolutionNav = (WrappedComponent: ComponentType ); + const pageSideBarProps = { - paddingSize: 'none', + paddingSize: 'none' as 'none', ...props.pageSideBarProps, className: sideBarClasses, - } as EuiPageSideBarProps; // needed because for some reason 'none' is not recognized as a valid value for paddingSize + }; + return ( {children} diff --git a/packages/shared-ux/page/solution_nav/tsconfig.json b/packages/shared-ux/page/solution_nav/tsconfig.json new file mode 100644 index 0000000000000..93076efae5d7c --- /dev/null +++ b/packages/shared-ux/page/solution_nav/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../../tsconfig.bazel.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "target_types", + "rootDir": "src", + "stripInternal": false, + "types": [ + "jest", + "node", + "react", + "@kbn/ambient-ui-types" + ] + }, + "include": [ + "src/**/*" + ] +} diff --git a/src/plugins/kibana_react/public/page_template/page_template.scss b/src/plugins/kibana_react/public/page_template/page_template.scss index 6b1c17e870e8f..d94daec56235f 100644 --- a/src/plugins/kibana_react/public/page_template/page_template.scss +++ b/src/plugins/kibana_react/public/page_template/page_template.scss @@ -12,6 +12,11 @@ } .kbnPageTemplate--centeredBody & { - border-right: $euiBorderThin; + border-bottom: $euiBorderThin; + + @include euiBreakpoint('m', 'l', 'xl') { + border-bottom: none; + border-right: $euiBorderThin; + } } } diff --git a/x-pack/plugins/observability/public/components/shared/tour/tour.tsx b/x-pack/plugins/observability/public/components/shared/tour/tour.tsx index 60d675e7ca7f0..e73140dec8a93 100644 --- a/x-pack/plugins/observability/public/components/shared/tour/tour.tsx +++ b/x-pack/plugins/observability/public/components/shared/tour/tour.tsx @@ -58,7 +58,7 @@ const tourStepsConfig: TourStep[] = [ })} ), - anchor: `[id^="KibanaPageTemplateSolutionNav"]`, + anchor: `[id^="SolutionNav"]`, anchorPosition: 'rightUp', dataTestSubj: 'overviewStep', showOverlay: true, diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx index ff786ba943dae..21db51173ca20 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx @@ -66,7 +66,7 @@ const StyledKibanaPageTemplate = styled(KibanaPageTemplate)<{ $addBottomPadding && ` @media (min-width: 768px) { - .kbnPageTemplateSolutionNav { + .kbnSolutionNav { padding-bottom: ${gutterTimeline}; } } diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index d1a3658b7389f..750bc121875b4 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -5568,10 +5568,10 @@ "sharedUXComponents.noDataPage.intro": "Ajoutez vos données pour commencer, ou {link} sur {solution}.", "sharedUXComponents.noDataPage.intro.link": "en savoir plus", "sharedUXComponents.noDataPage.welcomeTitle": "Bienvenue dans Elastic {solution}.", - "sharedUXComponents.solutionNav.collapsibleLabel": "Réduire la navigation latérale", - "sharedUXComponents.solutionNav.menuText": "menu", - "sharedUXComponents.solutionNav.mobileTitleText": "{solutionName} {menuText}", - "sharedUXComponents.solutionNav.openLabel": "Ouvrir la navigation latérale", + "sharedUXPackages.solutionNav.collapsibleLabel": "Réduire la navigation latérale", + "sharedUXPackages.solutionNav.menuText": "menu", + "sharedUXPackages.solutionNav.mobileTitleText": "{solutionName} {menuText}", + "sharedUXPackages.solutionNav.openLabel": "Ouvrir la navigation latérale", "sharedUXPackages.exitFullScreenButton.exitFullScreenModeButtonText": "Quitter le plein écran", "sharedUXPackages.exitFullScreenButton.fullScreenModeDescription": "En mode Plein écran, appuyez sur Échap pour quitter.", "sharedUXPackages.noDataConfig.addIntegrationsDescription": "Utilisez Elastic Agent pour collecter des données et créer des solutions Analytics.", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 9468f1398ddae..0ca4cbaa696b7 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -5568,10 +5568,10 @@ "sharedUXComponents.noDataPage.intro": "データを追加して開始するか、{solution}については{link}をご覧ください。", "sharedUXComponents.noDataPage.intro.link": "詳細", "sharedUXComponents.noDataPage.welcomeTitle": "Elastic {solution}へようこそ。", - "sharedUXComponents.solutionNav.collapsibleLabel": "サイドナビゲーションを折りたたむ", - "sharedUXComponents.solutionNav.menuText": "メニュー", - "sharedUXComponents.solutionNav.mobileTitleText": "{solutionName} {menuText}", - "sharedUXComponents.solutionNav.openLabel": "サイドナビゲーションを開く", + "sharedUXPackages.solutionNav.collapsibleLabel": "サイドナビゲーションを折りたたむ", + "sharedUXPackages.solutionNav.menuText": "メニュー", + "sharedUXPackages.solutionNav.mobileTitleText": "{solutionName} {menuText}", + "sharedUXPackages.solutionNav.openLabel": "サイドナビゲーションを開く", "sharedUXPackages.exitFullScreenButton.exitFullScreenModeButtonText": "全画面を終了", "sharedUXPackages.exitFullScreenButton.fullScreenModeDescription": "ESC キーで全画面モードを終了します。", "sharedUXPackages.noDataConfig.addIntegrationsDescription": "Elasticエージェントを使用して、データを収集し、分析ソリューションを構築します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 48a6fbd79c2c6..9d3b92f48be3e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -5573,10 +5573,10 @@ "sharedUXComponents.noDataPage.intro": "添加您的数据以开始,或{link}{solution}。", "sharedUXComponents.noDataPage.intro.link": "了解详情", "sharedUXComponents.noDataPage.welcomeTitle": "欢迎使用 Elastic {solution}!", - "sharedUXComponents.solutionNav.collapsibleLabel": "折叠侧边导航", - "sharedUXComponents.solutionNav.menuText": "菜单", - "sharedUXComponents.solutionNav.mobileTitleText": "{solutionName} {menuText}", - "sharedUXComponents.solutionNav.openLabel": "打开侧边导航", + "sharedUXPackages.solutionNav.collapsibleLabel": "折叠侧边导航", + "sharedUXPackages.solutionNav.menuText": "菜单", + "sharedUXPackages.solutionNav.mobileTitleText": "{solutionName} {menuText}", + "sharedUXPackages.solutionNav.openLabel": "打开侧边导航", "sharedUXPackages.exitFullScreenButton.exitFullScreenModeButtonText": "退出全屏", "sharedUXPackages.exitFullScreenButton.fullScreenModeDescription": "在全屏模式下,按 ESC 键可退出。", "sharedUXPackages.noDataConfig.addIntegrationsDescription": "使用 Elastic 代理收集数据并增建分析解决方案。", diff --git a/yarn.lock b/yarn.lock index f736837089b5d..889e7b6e81cb5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3560,6 +3560,10 @@ version "0.0.0" uid "" +"@kbn/shared-ux-page-solution-nav@link:bazel-bin/packages/shared-ux/page/solution_nav": + version "0.0.0" + uid "" + "@kbn/shared-ux-prompt-no-data-views@link:bazel-bin/packages/shared-ux/prompt/no_data_views": version "0.0.0" uid "" @@ -7139,6 +7143,10 @@ version "0.0.0" uid "" +"@types/kbn__shared-ux-page-solution-nav@link:bazel-bin/packages/shared-ux/page/solution_nav/npm_module_types": + version "0.0.0" + uid "" + "@types/kbn__shared-ux-prompt-no-data-views@link:bazel-bin/packages/shared-ux/prompt/no_data_views/npm_module_types": version "0.0.0" uid "" From ec64c5d2728440c06741d7fc723cb660eddaa659 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 12 Jul 2022 17:41:48 +0200 Subject: [PATCH 11/42] [Synthetics]Add availability metric to summary page (#135296) --- .../observability/e2e/synthetics_runner.ts | 18 +- .../sample_formula_metric_attribute.ts | 14 +- .../single_metric_attributes.test.ts | 2 + .../single_metric_attributes.ts | 3 +- .../synthetics/single_metric_config.ts | 5 +- .../embeddable/embeddable.tsx | 38 +- .../exploratory_view/embeddable/index.tsx | 14 +- .../shared/exploratory_view/types.ts | 2 +- .../es_archiver/synthetics_data/data.json.gz | Bin 0 -> 1334076 bytes .../es_archiver/synthetics_data/mappings.json | 6673 +++++++++++++++++ .../plugins/synthetics/e2e/synthetics_run.ts | 6 +- .../synthetics/e2e/tasks/import_monitors.ts | 61 +- .../tabs_content/availability_panel.tsx | 39 + .../tabs_content/monitor_details_panel.tsx | 2 +- .../tabs_content/summary_tab_content.tsx | 19 + .../overview/empty_state/use_has_data.tsx | 9 +- .../routes/monitor_cruds/add_monitor.ts | 14 +- 17 files changed, 6878 insertions(+), 41 deletions(-) create mode 100644 x-pack/plugins/synthetics/e2e/fixtures/es_archiver/synthetics_data/data.json.gz create mode 100644 x-pack/plugins/synthetics/e2e/fixtures/es_archiver/synthetics_data/mappings.json create mode 100644 x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_summary/tabs_content/availability_panel.tsx diff --git a/x-pack/plugins/observability/e2e/synthetics_runner.ts b/x-pack/plugins/observability/e2e/synthetics_runner.ts index 9f7a6a7e97821..25c236c6da585 100644 --- a/x-pack/plugins/observability/e2e/synthetics_runner.ts +++ b/x-pack/plugins/observability/e2e/synthetics_runner.ts @@ -53,16 +53,20 @@ export class SyntheticsRunner { } async loadTestData(e2eDir: string, dataArchives: string[]) { - console.log('Loading esArchiver...'); + try { + console.log('Loading esArchiver...'); - const esArchiver = this.getService('esArchiver'); + const esArchiver = this.getService('esArchiver'); - const promises = dataArchives.map((archive) => esArchiver.loadIfNeeded(e2eDir + archive)); + const promises = dataArchives.map((archive) => esArchiver.loadIfNeeded(e2eDir + archive)); - await Promise.all([ - ...promises, - esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'), - ]); + await Promise.all([ + ...promises, + esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'), + ]); + } catch (e) { + console.log(e); + } } getKibanaUrl() { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes/sample_formula_metric_attribute.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes/sample_formula_metric_attribute.ts index 5bf68bee14edf..e6cce173a7c49 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes/sample_formula_metric_attribute.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes/sample_formula_metric_attribute.ts @@ -35,13 +35,13 @@ export const sampleMetricFormulaAttribute = { customLabel: true, dataType: 'number', isBucketed: false, - label: 'Monitor availability', + label: 'Availability', operationType: 'formula', params: { format: { id: 'percent', params: { - decimals: 2, + decimals: 1, }, }, formula: "1- (count(kql='summary.down > 0') / count())", @@ -57,7 +57,7 @@ export const sampleMetricFormulaAttribute = { query: 'summary.down > 0', }, isBucketed: false, - label: 'Part of Monitor availability', + label: 'Part of Availability', operationType: 'count', params: { emptyAsNull: false, @@ -69,7 +69,7 @@ export const sampleMetricFormulaAttribute = { customLabel: true, dataType: 'number', isBucketed: false, - label: 'Part of Monitor availability', + label: 'Part of Availability', operationType: 'count', params: { emptyAsNull: false, @@ -81,7 +81,7 @@ export const sampleMetricFormulaAttribute = { customLabel: true, dataType: 'number', isBucketed: false, - label: 'Part of Monitor availability', + label: 'Part of Availability', operationType: 'math', params: { tinymathAst: { @@ -148,7 +148,7 @@ export const sampleMetricFormulaAttribute = { name: 'custom', progression: 'fixed', rangeMax: 1, - rangeMin: 0.8, + rangeMin: 0, rangeType: 'number', reverse: false, steps: 3, @@ -169,6 +169,8 @@ export const sampleMetricFormulaAttribute = { }, type: 'palette', }, + size: 's', + titlePosition: 'bottom', }, }, title: 'Prefilled from exploratory view app', diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes/single_metric_attributes.test.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes/single_metric_attributes.test.ts index 88859f22f38a9..38ce676398cd6 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes/single_metric_attributes.test.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes/single_metric_attributes.test.ts @@ -103,6 +103,7 @@ describe('SingleMetricAttributes', () => { accessor: 'layer-0-column-1', layerId: 'layer0', layerType: 'data', + size: 's', }, }, title: 'Prefilled from exploratory view app', @@ -168,6 +169,7 @@ describe('SingleMetricAttributes', () => { accessor: 'layer-0-column-1', layerId: 'layer0', layerType: 'data', + size: 's', }, }, title: 'Prefilled from exploratory view app', diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes/single_metric_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes/single_metric_attributes.ts index 33ab1ab9b11a4..9f10e7ea50d5c 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes/single_metric_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes/single_metric_attributes.ts @@ -112,7 +112,7 @@ export class SingleMetricLensAttributes extends LensAttributes { format: { id: 'percent', params: { - decimals: 2, + decimals: 1, }, }, }, @@ -159,6 +159,7 @@ export class SingleMetricLensAttributes extends LensAttributes { layerId: 'layer0', layerType: 'data', ...(this.metricStateOptions ?? {}), + size: 's', }; } diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/single_metric_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/single_metric_config.ts index 156edb0207e97..c425b103a7007 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/single_metric_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/single_metric_config.ts @@ -33,7 +33,7 @@ export function getSyntheticsSingleMetricConfig({ dataView }: ConfigProps): Seri { id: 'monitor_availability', columnType: FORMULA_COLUMN, - label: 'Monitor availability', + label: 'Availability', formula: "1- (count(kql='summary.down > 0') / count())", metricStateOptions: { colorMode: 'Labels', @@ -45,7 +45,7 @@ export function getSyntheticsSingleMetricConfig({ dataView }: ConfigProps): Seri name: 'custom', reverse: false, rangeType: 'number', - rangeMin: 0.8, + rangeMin: 0, rangeMax: 1, progression: 'fixed', stops: [ @@ -62,6 +62,7 @@ export function getSyntheticsSingleMetricConfig({ dataView }: ConfigProps): Seri maxSteps: 5, }, }, + titlePosition: 'bottom', }, }, ], diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx index 11d789f97da2a..abbb81f91ee53 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx @@ -9,9 +9,15 @@ import { Position } from '@elastic/charts'; import React, { useState } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiText, EuiTitle } from '@elastic/eui'; import styled from 'styled-components'; -import { LensEmbeddableInput, LensPublicStart, XYState } from '@kbn/lens-plugin/public'; +import { + FormulaPublicApi, + LensEmbeddableInput, + LensPublicStart, + XYState, +} from '@kbn/lens-plugin/public'; import { ViewMode } from '@kbn/embeddable-plugin/common'; -import { AllSeries, useTheme } from '../../../..'; +import { SingleMetricLensAttributes } from '../configurations/lens_attributes/single_metric_attributes'; +import { AllSeries, ReportTypes, useTheme } from '../../../..'; import { LayerConfig, LensAttributes } from '../configurations/lens_attributes'; import { AppDataType, ReportViewType } from '../types'; import { getLayerConfigs } from '../hooks/use_lens_attributes'; @@ -44,11 +50,13 @@ export interface ExploratoryEmbeddableProps { singleMetricOptions?: SingleMetricOptions; title?: string | JSX.Element; withActions?: boolean | ActionTypes[]; + align?: 'left' | 'right' | 'center'; } export interface ExploratoryEmbeddableComponentProps extends ExploratoryEmbeddableProps { lens: LensPublicStart; indexPatterns: DataViewState; + lensFormulaHelper?: FormulaPublicApi; } // eslint-disable-next-line import/no-default-export @@ -73,6 +81,8 @@ export default function Embeddable({ singleMetricOptions, title, withActions = true, + lensFormulaHelper, + align, }: ExploratoryEmbeddableComponentProps) { const LensComponent = lens?.EmbeddableComponent; const LensSaveModalComponent = lens?.SaveModalComponent; @@ -95,7 +105,11 @@ export default function Embeddable({ let lensAttributes; try { - lensAttributes = new LensAttributes(layerConfigs, reportType); + if (reportType === ReportTypes.SINGLE_METRIC) { + lensAttributes = new SingleMetricLensAttributes(layerConfigs, reportType, lensFormulaHelper!); + } else { + lensAttributes = new LensAttributes(layerConfigs, reportType, lensFormulaHelper); + } // eslint-disable-next-line no-empty } catch (error) {} @@ -133,7 +147,7 @@ export default function Embeddable({ } return ( - + {title && ( @@ -206,6 +220,7 @@ export default function Embeddable({ const Wrapper = styled.div<{ $customHeight?: string | number; + align?: 'left' | 'right' | 'center'; }>` height: 100%; &&& { @@ -225,11 +240,26 @@ const Wrapper = styled.div<{ .embPanel__optionsMenuPopover { visibility: collapse; } + .expExpressionRenderer__expression { + padding-top: 0; + } &&&:hover { .embPanel__optionsMenuPopover { visibility: visible; } } + .legacyMtrVis > :first-child { + justify-content: ${(props) => + props.align === 'left' ? `flex-start;` : props.align === 'right' ? `flex-end;` : 'center;'}; + .legacyMtrVis__container { + padding-top: 4px; + padding-left: ${(props) => (props.align === 'left' ? `0` : '16px;')}; + padding-right: ${(props) => (props.align === 'right' ? `0` : '16px;')}; + } + > :first-child { + transform: none !important; + } + } } `; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx index f95c30ba26880..e8c9387570a58 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx @@ -11,6 +11,7 @@ import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import type { IUiSettingsClient } from '@kbn/core/public'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { LensPublicStart } from '@kbn/lens-plugin/public'; +import { useFetcher } from '../../../..'; import type { ExploratoryEmbeddableProps, ExploratoryEmbeddableComponentProps } from './embeddable'; import { ObservabilityDataViews } from '../../../../utils/observability_data_views'; import type { DataViewState } from '../hooks/use_app_data_view'; @@ -43,6 +44,10 @@ export function getExploratoryViewEmbeddable( const isDarkMode = uiSettings?.get('theme:darkMode'); + const { data: lensHelper, loading: lensLoading } = useFetcher(async () => { + return lens.stateHelperApi(); + }, []); + const loadIndexPattern = useCallback( async ({ dataType }: { dataType: AppDataType }) => { const dataTypesIndexPatterns = props.dataTypesIndexPatterns; @@ -70,13 +75,18 @@ export function getExploratoryViewEmbeddable( } }, [series?.dataType, loadIndexPattern]); - if (Object.keys(indexPatterns).length === 0 || loading) { + if (Object.keys(indexPatterns).length === 0 || loading || !lensHelper || lensLoading) { return ; } return ( - + ); }; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts index d2dd9a20f8be8..a30a146aaf844 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/types.ts @@ -67,7 +67,7 @@ export interface MetricOption { timeScale?: string; showPercentileAnnotations?: boolean; formula?: string; - metricStateOptions?: Pick; + metricStateOptions?: Pick; palette?: PaletteOutput; } diff --git a/x-pack/plugins/synthetics/e2e/fixtures/es_archiver/synthetics_data/data.json.gz b/x-pack/plugins/synthetics/e2e/fixtures/es_archiver/synthetics_data/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..bac5b551c783c7241d9fbddf7f998f81ce680dbe GIT binary patch literal 1334076 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 Yd0dCemi16#z4TPT``9GB4I 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%0D>ECIHZ6e_!iU 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 z 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

!fVssq71*U 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

    3L;ud)>y;+M4DX$nvA$sHf8mxU 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 z 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

    ps={o<4yh$4C85_Df*zBb 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 F{hW07cB$j~ZSL#{_B&Q~zCCwI~Ah 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 XyW9Y))O1tQeZ^(}) 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�jcW~>2M}{pxLPO?J)-ygl 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

  1. v+9oKlZb-RRi5p_sYyj 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

    )08LId= zV(M^U=M_8b=Z(&D>z0jtPHAA+x8<78TRfe=R@S1gyeZ|V**f)YdOtUPXdreViMY1> zbd*-yMR{c3Mz3l)cJf29FQI}*rQgwHf@X~9kHw_&iOb7aTO+fPR>Q7`zY!cCW>>Dp zTgr1c4Etu^>ES}G?FA~fX=BBTSE97eIZ-kW7M%{SxRQf*yHDEv=^$RmKfdY?5#JB4 zwfpb8U4;#fDONsQW&~7;mf0|WC^kHNR64f5D{ERx=G%ys&i_h8?1DL}1s99Abeu)0 zR*7Dl;cm=nm?a=evVDcR9itNSK8#CJaBedu@}1-a#zq= zsN=-9nPJ|-cM%!g!Q1k6=IGE@BIm)F*%#+>i=@G?T`@$PUEy+{=UG3qmez9T{Jqayn-`6+^vo+epw>Ab}&{*t)XUdP|~fo zV)HcDknbUVwW%jK%+7f{F%@~`P-ohw$InFuB~4EN8|JLG9(3=B5nJw&eZhHVJi-sip4f|!FD!Z<&0L3 z+y;+pon{8kPB*Yw{dAi@e2u;|mM!yD+KU6zB^vc0f20}mwlBVFX2&yCHdNOcnC+-FD)HaU5FWcd3JPjh=#)a8ZuUKVou0f zHe{6uoD4PI3byX09iqfZROP$637~woWFd1ZUS@WW&2?-%VngZp)yB2_!L8QEQn>y&>F{H&4XpLRp|9e38AijXH!e}4{Pe|}go?nEUV}|EEab9Dt7D0-a2t8Y zfOSdp@tI0K%eHap81{6GIC^u9&p8NMY(tllAG8DzsX}Dk+IA=2!KOQ!Yu8Y%RwDjc zm(P3~dz~$yKpgGmR)c@E_v6R#aqJt{V`SWi&ajhYrnY!>{v1OE0z~K)XeR}`EZOhL zd{?&RHg~EGzmAKmZP?W>ovT`RFT<=4FN;1l{p^!|o~id1adGAZ#oGDK+0`qW>n^=_ zzf^I1U)D1C$h~sBChsEeWONv`c$HG#Qo1v1o0eOYY?q(Il>O~akn6|kk4nX*D-z=9 z+waL0mw3uM*Yfqt%TcpsM$Tq?s~WGh9_d94sn^7&nDvOjU!iQU{=gL&Jsb>~+>ILaTcO7sYqJ}Bf4tna zE4b*(&U8zoErzsBqu#^ zuwaiz^%t>)tMlU7Woy%A=%e_CAJuJ#m#TdCj1t#h@+vQX)7Vlq`w*0=`SX*3~5!kr--|VdH&e~^dKCS)JJs0(2Jb{yXsHqjY)s&|Dx%i>u*~BE4AzHy%>(o z4q&FSVaKU+~o6Gsl2;^)L>d<30%y6-o9A_(#g0RzekcUilF)K1AS`c>b zu)5;NEyMV`O{&Gsa9mF~(bFX1vS>y%y1wQKuD}6_N(UND>cy#smaC7;lt*0?slAG& zHXJN}OJklt4uVD3pV0dy3*T47#ezPRbX84;9U+hh_nnjZJ<hpx7K!`Cs?XYYY zS?d^yw@3q7KVO>391b)sdiSIILE1gw-QWa;cwTghSiPeliRvcVAcMms6XihyxNCt& zqYCPUkz{;gKG!7cE6w9J1_?~7Ae4$Mc!ZekF{U*mJQ?@D>q}fIilG^Z{`H!#|4vj1 zmqj8l5NFgAeNMI+LGZoWGkS+mm)^;rFL$Z1Pk2Xx>M-vd^X;wW291t#@kyBr@u|ws zNl8jUOMn(s-8&5WS&dQaC8XE#V85UWFB@n0poX!kHN;s41N5)Yx??Y+|NQx zA~GXkV()@!Ks0=)JURT>+>00o(}V(pBw-?SJyV6S68iJb#VI}Jmqh`@d2TxjO#ee& zx(Po?O{!1Jjaf0FQevF!q;5DzCzZ$ahw&p6NP(9O*F8zoGKn?bvmA+y=qRAwaxUP% z9+IQt-Z7Ybg55N!?sbm1zC(dR&p3@WNFb@zqcCu~LiDFFK*!v6( zVhjtKeTCMu8e;x$6Y{hO`1YMJ`11?0bxaYUyDR<#0hjG^=PQ5uYKDeE9T`;}=VSUa zQ+l1^0Jeb%79{990HA>#-?GDNsL`0$fymLzGtqc0rw-FION7vXW|s%UrGVBnvA{7=6yep9|7MYyw#NAnJjMvk&F z@o*yjPP$+S?QG2scpUIYaoxk)@Q1S&CVwYmhQ31?B+<@bCw7NeUPO?9l%d3agGoS0 z=+tkyP01o(OLnppJV5KN$+UwY{l5kJpw>gn(P4waArwuNu(9<)tA;6s=vrqHBRcRh4bhAW{I(64L0%_Nzz$J-M|QDy zAZdeaG8@5AWX%ls{0!Fs91-i(nA?mb8yuqH2=5XWq(lsFs1Zw5sKAAJKqs$Jc`Sqy zfq>;^cWqfh-7_pd84i07&K+(4#jmjWKYj&^6DTWzeQL>p5U4auEDQ{^ku*p=YcX`8 z<{x2wbTDFk_s8gJb=ph)7BgbI-g7m^^+wwE#{xaBf8eIS?| ze3obFc&027j~fWV7=3;lfSWG@`Z@G2@Tu1n)n7Gcx7~6({%64NFNeews4P(veIkDV zpsEOLH*}mgRKPCYHZo<-LE!H>jmpCu7r@TEe9{~JnL^uL==yf?s@WOxAV^xq)W7szb! z3rv+M%~l2Kw0^?pztOz_OjYsa(FRPwy*^FsIsZYizTjDJR9~R1R4~AGfAWzRDC-k0 zbe<^9r<+M8{pUz1$gKR(^EVjdDxrz0*Cc1{Y~3oaSYS|9U!6w2f#^Q_y<+jSfhQ?? z1$Zsb>R*A&Sn_e{a4<3Np)g|7Ox19ED<HjE-BgqO)tE^( zhqICUHL_!UbK(&P8a1ACl$JJ7MFn4^DP`)N2sDRC5bVEPKd)X~KmV&_fe(ti9^J*& z1Cyw~>B|(iV>bE%)8Amt`PBX4I0uDeY(@YS0Lx!K+!4K-Uef2WpV0a8MKl7S}9t| zLe-^K(5Js=>1$y%84S97F;UO0HA1XbjIIS$BlEAP-^K?)UxG!O%tQ*dSXz;kS zC|zjOxT2s0PLF-=rw;;;$Awo_%;wpzNPui=imy<~OeKuoys41YJ?Vr|YMJsFV^E!O zB5cKk>Pdx0jYdb?e)u8>#V@%oFs#TtTzVD=vVc-7OnW?%&q zXqhZ_iuOJLZy^7b71arF^WRhG6+{NXnx!91CEyMXXOb6!hEkvSJ?-tAgecff19ai2 zP`IjklnFcvw5SsjfdXu$E*WmLIxZQ{i}Y8Z*SBW%YcZYUmeNU3PDKtYPBNckd3lSQ zm^Ue2gTokhlvU7J!dKWNN3A!FXF4}e3s`XnjE)z5n9o5ZX)04zTmh1}rpdKjKi2$iU+ zC2Yr+$&#>%H+OC4!|f&gibdkEl`_rHlBCJ{4|K{GJIq~UBG~VpG6 zOp%JZJLq9(q&M*r8Fa{?i?=Bj8SR_P$uh3>C zsmiTm3d_CjHdw$AF3~4sI?d}JmorQ3HdmwAD~Z=`eJ;%gZD7v zHU%}<&@5}iYzQIT;ZX1*e-42q`qG(4I#t@=-qb4HNyr99U@S;H8Bi@qy2pSyV0#6A zGtLvgnfMbD@>T3-97`NILm~5T+h21Z(}IDm>e8Y7i~X%hI&P=4O=2$5Z^ObVo!46* z5GNd-6JF)X+~AdlpP{dtO0x_ZC6V*Wb6cY4pY`)l{v$K~1+8H?lDf>vW_eA5vU69^ znJ!7CbW#+lt?$cgEA};sO2zvGk|2Q|eHi3LiPsp&$>j)hY&dmm5fOlX!Vw{o^f#a1Q+tx3*)BLV@A`m_!vH{w#{n;b;Q@>@R4=Dw{+(`53mFCsi2v7`ob%mt3_+n7&;;_~;EW zH6M0fE-)-NpGb1iGZYm~5ci&~`+gXZ@0)&b)o)XDzkGU9Ix2N$i@h3oa4DWc3zw%wGGP|Z+b-i+q^EPPRMNN zz2D_Dy+zBk%F#jvWvun;?@BuiA}+x5-6d10^Dq|XT6;@>;8df*kb2fbuvhQYrQFi~ zqj}3kNjxUmzR<7i*nF#}e%05dq5l3}vM6n2#CcW!y#4$6szK91tCpO8t(UCv*KNc5 z6a01`shZR*_xoi#XQlU8_LKVlxx9FZBl-w09J{FuB{t@2RgASmd#yjJ{p3crE?Dj~ z6RRVWS6T+X&U}xnfJ|>YE}r&$RnZ*ZSbVCcS$^wyLh@$SCBLKZ>=#3h^nOW3{fOI2 ziF{S8Tf^c=%gsz` zoiDx_b`7h*{ECCCV1|?D&XJFn(D2(fxmuwScTTM)?MRQon-il@Ek3(*2o0W&tr5t+sOJt@9&AKehl}oz`kDA>o`K%Rx#!J``l?afd$R+KB_>d|DVf`YX^+Pjlvs?rg>1(1a)e~zO&tiAp>hQVzPJC2pQzOLKXFNP>BA}LWbrZ9CXymhE zoZ1%~u~-rL)jfx>wpYG(cGRzqK={goE!Dej>vT;;N4?&A@z8Cynt$p|@A2Vnbj)H< z$L;~`{ItrITUMuz$+Tn?bgy-O|wIN{BgO8Wox+Q zVoh$;keT&VrhwbX9O+=&2ZBoik$v}A&hwtNc;B1(gBik{joem$rqC6*u^ZhH#r)c1 zlWRwTs#F4tm}27^?3+^JU&+K)vnzI4C>zSVLc9z*nKd+JL!SrJIvl^-_6{arM3^)) z#3;5t*u{=wt@P#(O0qHHJ2_}=TNHMVaUJA$-q}kX?JVkRJk3T|G`C?1joK<#G%e&= zJRje4EgzD!e_0%L%}1#N;*&l^_U5mgXxax`QVNms89dw`H}&Q%E6*?3+kWLFjCtOd zIK)xr+)rMeQ^JUuxV@8Kg-@0{N>QF)KK-&+h}g!ZM&~?|BQbL85?4hjWY;$+lj`7S^IPfsTCN2l*>$17+c<}SLS=&>6IXijB++}`3x}jT>ahs%;WB#1YVDTxd z&otd!a~FOI1L{B861Y*0=g&7+(pQLDWQ}|n6(g+ly@b}Cw@rLK>%0U+_s9g&>uiq? zhGrve+L(S(aC0%)e^kbz!%_i7%I>>~6!+%3@%K*~-aGnj+EW=Eq;rjfU7D{ax2cQs4_m>J2YQNH=j#5)bdeZyZaK-JMm+XdgUhul-N&AE?rYTq0-%kIq# z6`DwU=~xYdz5RW)yYH@ih4_+WQ;{P3O5vkB+G>YmiHZjfT;Pc#`VX7WaCYa6y@3Z{lR+EuTrF zLl&)UtJ=u|I!n7j3rfBLe?kila;7>K{b9!xZoq z)At1`bM;nR9d@qAN!REbu@l2L(YV(`HXagGB3HG7yeLCW?k!UdHgU|{gJs$knXXSy z5*8aZpVzesZ1HP&&6~D=$!7#LxY;k7&Zg%omM0( zL(u{HIAhUS;l&}vwI&LS=a%5EyMuQ#cd}i>MCMwaIf!?nv5Lz-uWe?u^uj1s=&nMM zq^vOj2mQa)YO8(BPHF?@3 z$y75VR?YfFUfFX1TK_k>Sj;**U1wOq@oc&3@_94g$XCN}JrN(L$`~{Txv=DdmW`P_ zr)X0ve!R!2GicNhYChbmOdZmjNp@u#HcGfbzc#OedzBVEx@ zg#W;|b$3>NvX!?z=#$Lg_u<;YX2B*CGMd(7a3VNgXE%q=G?fB2N=-Ryqy9ZU zWL(^Xn9ffr%%xsYai~(LG&JVuy%BXC3nkCc0#CQ07W&*cX)_&Uy?7ePszEF%;eW6> zzYr4^5ZKQl;Ai7Op$q&;q=mNlf0=s!BH%Z_3$2b1?V#w(ogMGEJ__zF8$*3O%wq=l=-s4BEE|ipVYxP9ieIRUYOlix{K|A0~U`4 z>iVrLl~F4y`~r6xs_=?ZXH8PTAb>^p_Dgw2ANT(ej)L=LJa_OMo)7*rt7D7XoHs$m z20uI&SyH7!YXJ}nfMz9F07V+3g^sA)FI>1|DQa)HX*5hHcL9u#*3XGgL#==@Nwozb zSL59bB!}*t-OjMgH3R0=D{>~9rbdE~8ZpdW-XuEZgwMZ2f-Y+8I$$tya`0z_4=`iB zy+tMUvckg_RC${&j9LuEfvg;y4~qt`bq{E5O!3?RbT`Iz%7V{UoODVO`zvKkNQKG08u=CNCcFY4fgT!d(Q5V zBt=8QUytdf14Z@pQ{iknQVAfbkr4(V+)+q_DbR~@P&Y+4^~pb>BBlLIf=(C?%8MoX zR_y){Tp57v!lZQMQ)6->@&}FfhogfawHvm)=y{<|)oAeOXS?Qy!En>pV*%#Xhltcu4 zT#WyW#eMq8 zK-m7%o%9dw!-EsB_&}-*SYTSu#k+qQJBn}r)d%oGJhzeOZ3^*+OZl;Fo;|fbTh`_< zxeL8Vr(J(XiZ&pW2)W%Wi-XYq1>FNM{C56u>hWVLw8Xv_28pLTnwKri^xZuDUt9a7 zokQRafL#NIv;TAPCRwWcr!W(`Bpx+xG`*xloN=GXA~?7{pv{R|;xSzSEeQIu(^e|e zIl5zowlmxXeNYgqE^vLoxbiaq=VgJmWH5_B8%TDR5H(L_)szEGd|AN4d>^25mOu{r z0Bf#N{040hMkH|ySrREa$&(w}6u^`g$h;t$6@f2UY9b^GhOQ0{Vp$$k4T4`UR}Msr z%t})KoL1-U13Vu@%29x2705B+v|SzSk@XX?%R;#IB%K66R2!dVi?zPx+tS2DgtwvP zKZt1l7TMUmkWwk~CnIiU;?aOoEH?wvIkR6<6o~cqeZcwuDEXz9^Mc_8 z@T7mG`Y-woK)%1P1i*Iw0w(>B%3jz^0NXj}NgqA_8*w`o=y4=2vc;ME@r}4k@$asq zR`!f*;$SHkxsS4~wGcj|IB|ARf2{hvVfX~QG5qy|Pu>kv5oXUS7B)>`no5L0xXOzG zNK-X}y67*C)DH=KY5)Oo$Jm}-MCqILb7=IN7X!I?vRkTfi8q?cH%dY=auB;Ld3b@*BkN@~ge6;`QZ_~1G z=BZ(XH8C$pXAJ1Np|*b z0GhHEwJ$3Wbnk0=otoY(8c(P@7%}hH&f8qJF2J1~^KLu$D|m=b?7e_4)@|OUO{7Nu zhgabfpzS1H;rZQ%+r``cE-0_UMu)>3ZS$(n((qctGH$09>!ZFghsOARdzalU@I`0% zyLU@Q7eUe;`Y#9Gq*F5B*q+PA>!)RBrd-tw*DC*Vt8*J_Wf?4_p_72X_B=<3slV6R z4d47@0C3XXs`cEfN^GGQn{+nn1D9Uv>9a+%h{swo7-h6SN0tCsS0*P9FHv?(XPE?Pmr0)eLy|JfTJ|~{115+1 zbq(}@^XpiG3oC{lx|sdHeBD=PK{F7%8rXx zm6ok5typ{}6UW_@4LROtJ_h7a&8D@*l9R54&2hKiWcu2xo=iozOc{B|^qW%@KFxN^ znhgm<$I0Ml&oTVp}PEP)Jc1`?GT3=c>JjN9anCXH2T3y=Sp4;PxQA>ahPP87lY8 zFhBID|7ub(h5fNLp<{QxvyQpJChqhyP346>oqUru?uvj1+}>m4(U7)nvPo2HizviS zI6cX!rM7C(UmH<$F_P==ERX|AUMH>3h7)Neh?!y*GY`DO97kNOwB4(B9rT(Z0TO zuQT*3TVSzrKESmrci+LzEfF#%Tee1-ZCjF{xo*K1apil2!!$4CJUiJA+#TBU=O>F- zXdYSZ7+I1ZB-*63Q28&KbYCXi{4j2^rK|6roB#41SaqVj8DLD|zYoeXEXk~jbI5tB z;LxR{u+ur>_5iQ&C^X;BnDJ~qOgG}q2_?yxXaPpOdzn`g94h$vLIkF`!?Bm@zoO7n zPAhluOtV1r$ZYQAJ-DW#Va@C#CS1Dwr{-@U%a1o04Fy8)nx>-5?bd5sY#d11Wc&@m zXQM^)!kacgA)uN;?51hhXF0a1=!C1ev3*HL5(wR@Up50Fk9oqpY|X3=sk0u-S;n5M zFYx3*t7gsYUWTC71mvgA3)C?wvAq>7jVpKqx~k{^VW}E6u(@beR||FW1dLy{&15T` zmNs?8eIWNIJ8dz#f>@M0M%xmxCumyn{E1ktZ8#=a?j|1^vJDK^HWwvjJ8Slkuc4eD zG|Sk8yawJ(+AcdoNuB{Sx3LtZe9`IXLAm+x62I58wWP&WfXLR9X9|Ow_QB*dw#LyJZ!eU!oLYWcMCt~Wr3WU+`O^JOPsc*D>9j-`@pjJ zWA#wN@>xOKz?T=b)7+`!Qe;imoeQu&H$8dN3GDE&Y^i;to>oD?4^Q45SYJNPj*FjgFi=g{e`8;`~bGDX-43GTMZ8htAgbw+41AN7l5% z)6_g!H#los)iqDD1~>xMMQXN_prSj;yRzl_gvVU=zP(W1-0kki4U8<<3m-H0 zWWqeV-z0Il+bhZu9WFREk4^wQ;qACfd_&_dHTy;zyNDr8Fxkhf_aeXoku#Q0&jF%= zmRz{TijQ_4Ho#0n{y)gB!S0P-?k&aDtzEfqFe2WQH>Hry&0j0c>anBGko#kSBZO^b zYsuUey(&&^XS=Q`xSD+m_g>hSOQ6_K!XeqSDhv7sSe^o0&|-JPaQILbMOP+fC*QLN zo0wQVAT-T{1_G%T(F02LYdd>(xjg%q>{4X{cIb>V+1jl+{8o3aNocHYBxR1^2jAeu>B)CAOpL{d$<}@PVLKBXC0-p=cVzD6uQs@K0>CkWp4X5$*GJsDHyyaO`1*f zA6c1eQr`3WE3gywfPGf87l!R`Va4&lsx3g<jh-g-fH-_ zgWlpjNt(0Q@ffvCN5VF~vlBMo_MlW%6f%*9I<-xem9=61Ra_^%WDH*LWd(>sar++q~xDodCI&gm~D?wLZZ^Z8TOO7 zV@O?mqwHGS35T%ok0%^Nyuk8(`BIsP1=qTK$c>CA!|La?sOs4GM(+uv3%ZJtPBHcS z0?m=dEh1jhh~3e^mbJssF2+hBZR=Q{uLL~KEHjxpTOINYaDqOa9?3H%k85oylS!$I z)(05*`p0F%tpdc0Y(A-TkzfJGXN^tGERgeA!amv+EaSHJv%oAqcgO2OG3`?xix*{q4ZzoqPs1sO*W^o?9G$N`)(U!Z=NwjF_lk^KGxOE!lSSF<+3f44 z2PDl{#BQFN34p!4o>+hsx ziGMOKujby<#l9ux%lL(&CboeZypXPxkL^a{ci?WkT#|}K!kV84i1n#qtl@gYh5B8c z_{|M;5d6zG>)0_sS?a{$_ly~ISe*Nm5JANEr|XmvY*4{DtP|(G!cX~{ zM<@2z-d`_>Dj}OJH*8KM*^iiE;ViI(dQpZ%A$JSs{-eSLqJE?3WxajY=YYw~ zU=)^$y-dk?|Cnt~OxSsPcqDOqV%KiEZ+S=DavZRFXk`L#NA*COFz!?#>0ggZ(mfO! zVhc`aa*$Yt0vz=A$Y!MFK%UZ%me}$L&6AOCXz1TjA&Dv>Fj6O)>3h+Ff!SRrkaqIB zPWTVkk8qvQ*E&o!?_#5u6yXeQ&M6)+VAe*1rQgknZdX zIn4?X!cD&wghc8yx0xIGSeqgK37h@0&3za1E{{0#e#9nn>oZ4&#BQUg*-`@0FWmld zq8QFg=;+2+HLp0mfnl>-X;L6D>2u?4?(&b8ersE+BuVVw^)}Px#1<7gYcjT}x*XI7@@|9Du z%?-t*pM_e7J+u+(&y&Ny(LB*#n6i<+Aq3puA>@2t`yeJHzu)ma)@70zLCM?S?%P|+ zBkuC=gcb(6hx(2OR`fo`Lea1wyc`-COO>yt8T0?V)8WS?PzibMRtfnbH^fl}XFBBH znXb0k*?b3tiQ0=>#9 z{R-=U`C=t8p!7VFA@=TyA#PK;kg^Cd@(w;YQVz}PeQ&(CrXrbiw-aM&&6FH*aVQOl zsif!uUSfdFnDd!Dk)N)h6b@Y?`7Me7LYSITH`|JRg>if(s}eVdnOEu91m)oQ?Q5|Zh&fbbHEg^1!W~;XKXvh#QugWH<#&D(7N`g zZjn@%*s`tC$U~uKR6yZD;M}n-YvOMV=K9D5O8GYX;Q!7AJ`w?1OWm7~O~w(_3Ww1; zuBt0*fJ9#SyX=XtRq}OkZs#?1C2(jdN^Jl8eXGCL2+l=VWT$JKBe;P3#khOa6pQ;o z8cNF%h^-YADJBeMnz(DOCD&ArDhU^Qh>*!$|)m3312%6`C-zcx&A~X1c_>n@~M3| zmko{#|6ou46C8$_rfc;)jijdopR@1CJV$LqmQ0CC9pL?g4Vq^tCB~lh%PmIky^$)Z zQtzWQtX#2^BOb0iiYpc#4bkAIVGMuT$aH}}+5d=N&(U(@mUnU|^&HwfU z+=7yUf0l8f%W#TjJSYb9zb{fJE>AYOv$qcQS2P`mnqr*0kB)NzB?$@e&U}3rJS4`s zaE%W|{pPhl`62;6hPMLLB4GwOjZ2>6onTretk#G1Tz39&I~g^Nh)59@-z#x16(A4F@>diIn_slsfWQ4v#V%= z!9oA~_^;@3JYn4Jj@8>iR0@B8Fz5BLDyaSkHA>DE9P;DiG6P(sXb5_`nl$H6B8H#1 zQJiK>!QYLpA z(FLe`DWij`93xxjF$q}V?EJG4PW*`wK9cyt2bWl>GdNMaXIcs#6S6W0`#xop34Y_S zR~9ak`1UyLcwDA&=|{;Ld}A2#NYi+>y@AmB8{+QvmuMQWJD?ua(J=2}OReLsn# zcyRg1^B@OKgpd^*~0Lfk1&EIl0n|&%{)3 zMq{n_iT)@5?CcI-{)#aymEh0TYiqtke`_VJaF+-}O`ECs#}~H;Jz7Dz`QmvvOM!MFfwGs}HB?ax zCK55|P6L+eKe>s7DRf_WN5vHxyO-TIF3+}K185g@D{rNmvnlib`_hgoVvx??Hqb&0 zA7%%+?Eb$Igr@r6Q{KPq_Gp|#`?a0%``3sPUOY7PX&Y7g30tc6ly&*2Y$Bk3<=I>% zW5OdXWhMLdz4XpLIBH;IMS`Qo!aCI`xIN`Ax7K^WXQN1!a_+TP%FGJS9*=i>j0xK}+$te4D^Z2kx$WF8@i6 zT8({TqbmNVdAy#bUlV`Ga2hS!NVWF$(`R`y)r-ULq((hrIKYg;HWKmao3{Q|Bdq+r&u2Kz zHTs{$-{yhn)VSZoUU+b)#W33Hdrn^AXel;64^W0kBZ!9SOJOc%Bu>T<(3Urh^Z(3; z%=}W5LWRGiIg}oMg?Cc_9fg>4M>8E2xNdV#I{7(2>g|Q`H~LFBW#-=lJ%M9{1yQdd zM3+}OJ)1_UAkOxPd8+jJJ})io5Z(UQbqsK$mIbkN%Eygb=G_2DKmsUXNz8iY!KE}) z{+ML!s-1X}zGl>a+Go;3twD@Cd}pR6$RGIQtmqHhYJA-0#)cz+^j_>SJg{f;{N?q= z%CH-`x9&|SV`Q++_om=4y67RYEX*+({+YDkE-NApz@K&vt7N zfFdZM$9B8x<{<5t1l@Uu0ci^fMBx-RyvszPNbSx}WLMsP-FCq^#@jxdf;7RSS^L7* zxDBLozm5F|IQ8V1_qbtMMS?ZjoIgVzunN7OiWqaoW{e9}g|A6ntZcUOU&@)A>8eVd zranXhx2C>a@V`ffPp{UCS*X-pRz1H9R*}WxIX%U?gLO0GD4!&}t!uiS`NBGK;>@tO z@phr((Y9DORRm(vC8#GF59j4?P#R%zi_zy@Hk~fUl=7vC6;NvVi6ZOjLzl4a>ZFZ- zK47mjF;k+<$3EG6>nw(o_&Pj7-6}D3NZpEszRcx!j_9Y*pRKrO<{7!}`$8S)+wKcweWwCyuw95I)`ne8 zdN;ayhUfP!E7eoYGZ(FbGL4HVj~qC+3+-#i6Meqx!R{r zydLMsmwHwV;>QKsh`9sXu96-vG>+PPE5SlXvlkiNEuA&^rfm-OUwt!&-HUVs$IlT? zNG#J9y=&^TH?bo}K^`v4lK^eQ7N8t<*{!vP2-}dZ(8>+`9BNCu4AR6y_pWERKV_<1 zyHL{JOKWBw>I4HPe%8w$)TxIpAiOeScjgPcjpTJcR z--Y}%Gw%FcPeVh2mUA9bDtyNKNZ$Y)s&iiRIIomkW~!G^tAjZywy)a+Hf#Ntbc!MsUZ(mFOUo<9 zdDuc@FF{ZJR)zD&J&yDg&1tXf*?u}z^B#9AnJNAh25BGJi4x>iK5SlonBVgaOX!GM zE}!vv^&-nsS@2}Rs!Ulfesi0mx8t7{%Xp>6bk>q)_-gF5xscl|wwWcuM1OaMPBI9w zoMMeS=+`6Lls-Se-b%Y}b5pM#C1-%E#_}$AX!EJ|gr99KKWkPFne_9U*~`Dro&-LM zEJDiNgin2Z-3+HnJz`9G$*&)sdA6&n)eW!E5}ZT6@N5K;m0mh~DdQSWZn@8Qa{GRv z;FgIvJM1KNQ$2tcBqvMU9jW}07r#2T{aaGru??##DJ%HaPOk@6rBe7wdOn6Z$ve;&iHa492_&AHG@kH+PWvXy+xX!C+u7*6baK@T@y)F z(Q0e@2+y6uD8^Zz@{xnha&i`AEFu)Ac(Nv}-*^J|mrq&W9+Uq(W%bzgOVdWu4LhFo z*jOFm5t%5Pr6`v#l?6V@t?#kxcJ?hM#}N=N(MYsu8X9;W*vm_htKw3uIyD5PSq0cs zObDEKv^*0#C@sb)05&Ou>KEZ1&=hWG8uxZCRA9$^`y~+PDcc+hxw_{8hSW#jGUuJ? z>NK9w!XuuIlvx+sZ9WecE>`nA1Ah!m1a#nc5)3dDD;(WttMCFh?ETeG7pr8RV80`O zR9*FR=9sst!aptO{H!Z0>$cL$?9hgxOtW!=$FIO@80V88AlYxbG}ZTA%fclyFU{Gf zVlr*f*$Qq_Ci^`_AH*j2`+BtK%4yvu9UClYH*E?Vk8`K!$lMuJdyts97SDAm=o=>c z^}2dN>5C~@cnC>7_Kd*s##_uiVir|${I+jdBI)RDJWgR)@dbq{+x5)*i#p=#-aR_# zKdSyFxRkfdQh3h5*p74KPz~+NY&ndeaO$j+7$x(*XWdd*!cb1c?!*-lNwZ+V?$Kw; zbu}4r+o?}(0uREA!bK5lT6R3nAdknyEQe5fVtFdDAGmTBH0sENovi(rwPlOV%}Egl zqC3L-d)9h@(l3qNm}`-|_tv7Dkjk;?qAC>oWZ3yxZax@HkV?8~o!It%Oic&meGR6e5XctBYbhUpEL1yK-xV`qwSj11zUsguS5( zn;ai+(tFn>v$UGjy+!P6bi_ngrR)n_HFiP{%-Pdst@nHq#aDN?IhDJD_+_kJbdO{Z zYYE?NYIu^5{K?P4`_w}ncqp`jgI_~||F+Mksw+Tr_Yv{NcVoEgKTu%R}t z?)P*F^ZPjKq@-QO!V6dyZ2QMcW-iIviD}deS-~uvjfVV5hxT!uEz^J)Og1}V&<&<(q4Ir}n z`ZQ(iK>_aGezn&Q)P-ArKq~DNHIL~rw7(l8p-t{_mcI@(@Vr|T%Cy2s=>{OaS+j=u4z42I{R@7)Z(XKz0^cFjx16SNR8L<4<%wMvMV8$>C;kGRElq zi=#ot%Wv^kXu4tL;*J1b>O{5AoQh=T<6_6|XrETp7{6|N-S}qSKaD2Izkt1;J!8r5 zts|*uKa*O}4jlTs5Kh7P$C#8gy@jz%{;BFF3{OJE%>7wLQN=%uT2v3Vi@!fqcQk$O zt)z|U^u@{Ft+sfpe_L(BG~*z)ZM=7bYX{yxaGCs~-(rz4b#W$%J5vSp<2RxYH6|#4 zd&*)GK1voS-SwW?l_vjOuq7VZXh^dN`XD|H#qlM^TM>JLnX2H?p~SCW!%#nFM*_zh z@=!)&e5wVID381b>6+PHmpX^yxn*D{*gpnYzo&&@wo)lMON!21oa9($yT_8)!>X^` z%Eh9vF{4U$Z=oGN4d1gMEgElo(Q}&6U4#rysZe)wz$3Yo5rvRZ@4A*X6aToQA)m@m zF&cJCRjZ=nTT37e?c$yMoa*fzf*Y1?l};FM6}G#~mTIL#{|$Uu9pvaKdDwMrhg)ANZ{T+z~8fv~@nbig9X3)|J_mR?LY5w%5jp|8$r-#YfN_N@AI!o8SCkvc#b3M!fZJO@!fK;lGMQov+bqzKf8iHK66=jLp{tPpW?so7s{;6WN^Qf zwYrjZ7M_hJq4{RYeMSANs`j>G;;@VCCs*=)_lhFh*XCyw!D&ZnINiU?ARTCDP%eZ= z{IAqZzX#xy|1L?PabCV)Mj_ZFn|hh_sMg0OY}WH~^mi2oi3DtWwWMnX_Xy@TSDN5F z&0H`m4V|jliWp=%PFk&(W$;iyyif5Ri$;C*A&x3my<)9An<`(ePkVk{9_Z_yJ!83T zV%bYk2C}zmFPnfJo0HyFa<`?xe7R?P>543r%T{WrwiC2I{qB}vPg0CYiVn<9hwQe_ z1zyl)_6xE*4KGeOpaKh5VBPj!ej5XW%j~P1RgM;7Fu#`WNw`E)HXCw^O;M&D+iN-^l|D>k-yt1QYE(XO$d|9-`m~Bt- zopbJT@LfGJw9VHPDa?pxh)`U@n(^;0zp0A3pN$11g^FA&()c(qsMd+mUzFsK6$%@Z z%>Y5Fj_I{mZbp1`bFx{s%?MNmN_+__qO!FjJ?9|Kh7j+vdE$+J*rL@Sl^j!8BXw=*G9St*lU6QWT9u|iAA2ZMS0C{UArE(vcWz6QYJfx*XPWd5P~!1m zgC3e^O0j9?gyr5Ezk5rSJ7_LaHfGRS&u00O)xjNjy3BtfGIuPPslpd9$)njxEV}E} zO3Zs{*JH&(p!g{L+JJin+yp`Z5RwCI*})=o)2 z9Z0(tI0i` zc~^XYpZ>}PGZz1fhCOz))_FC1RKki|ke=^}Urn=HyBqWOEJB)s|Av1{H6i=#SHif; zsMnQ2IpwN|Yo)Kh<7@3XbD8Rotn4Hz5<6yHJPuiEW!ic*-9KB;cZgcHi`Zp~mq`@| z`x_}Mx&EA6IMZVgy+97Ra)%Vl3gQl zWFf8oQaor9n!;|p5jLdfxfIU&;^if*0CBFUt#IPv1Gp0ui5l!&HAc-`)i-7)f7xf4 zGF$9yLe4R$pIxQ5atDF6&lF+|tx0I9#wr)ADykeSi|;FV zu3%4z^ZP}aVL)sE}aantuit9 z=J$5DAnL$oL4c-3TDDLf5Aob;(>;o>l$W5CSJs>Uq@j3PbH7dKB@9D|;{CQ}rhP+C zyZDDxZoQ=S_ zRLRCbisOVQ9`EKfh7_f(ihBK1_~s3JBiY`DZjRb|mCyetSfsc?(Dc?s)HZBls9>sn z8E!PFM3Vp$0NU?9*m)_vkP;#L*lXL`wd4{Clp{-e`hGT&FGF#T0GN_UQ{>NKKJi$$ z-Su|0RXlMcd1+Pc?3ues#(U4If}pT$ZomjJu+qaKSvAxnz5Nux8+W||TK7qFSI_F* zpc>InGGw~;7UoO1gajXV&D#drd6JG)b%~1DE?!l@75b_hRugvTw+``3Wfj*(tfhg5 zPMZQ#ev%nh+Vq`cC?ZTh@sCY$c-_owTL>s&k612?84hZ4ui^2oH6J}_ZQIFf9r&3T z?e4o@b(ZW}HT%xndbd+~9JM;TQp8M1L|(k}wcp`42#wM|0PU6UMN|T?t0c5~wgMgy zTDyfk1S{)T-U~@ub5-6|CSyvq5n_EuE17UNz#do`>RP?=1*WG<0}rxDSDWT5JNO1^ zJ7m-gI^TFMGA%A?3)Wt7<{S}w8qZuBLa`B^TO%QfWy zRC@=)czOdgt&dmpUsl#{pO(M2_;xPTvdBjHmOga)t?!w5B`}pqEN%7Z8h;#wkv?t#;RystMMr;&&jMxwe!W>($v9b>)!se{{w9Subo7&}ppC z&FxJ%J?fQpnHf#2AGxtb7B!RXjk>8_M{mLw9D zEAa+9GbUPU+d3`${{kDa5K^k_AYED~HS}t;U0cOq%i6z=LfP$l@{2M3J_fI@{4t)SVOc`-{3M|Uo_}PvDnU7RwPA|0E*J5ONqTRqo~oU`uIdB-zQ6h#HV|7 znfsejvNChOoICPTsOIusdVzj+P^ZQxMKj>5Mxv(HbAaVMQuZto$k?H1a4A>9Lc=g2 z{%fP%Y`-fpFzfb4N;=EzbA>S}VB<(nL}m5`}~3w?lc1@-4K;Q5R=ax?t+aTV)D12O771H=B&^+WXo5sSoF9MfnV{EJfc50 zuzkDkV@;vOi}|uhIu9~wW4_uvVSPVLIcj)*@UYo|9Ao-Z4BL$De9|nL+C5`jP+|iHwynx4y|lkta4SFOjNjJ*l|Wvl)5+mg&kDW zDmoRMO4F-rQvU}gfLxN2&_wE8dZ>Q=@sNu+bl;LbCqj=33$e#Gmm6gBjX0~(U33;L}3=Yqm zR}WxfA>J8Fuq5NtFqPvgvt(kz(!EWkfOF6tt<(rZJGy#c(6=`O@`vMj6N=6MpdVr{ z8G!PEK)xNkb;KV16Jsci*t+Q}oM$h_L%>uW5cNIId)FmY8UCA!2=NC|<=jt{qI+Q- zU$$BAj0+y?-!&Dd+M)^Eu!LXfQhc)K`~owVS`V~>N1!}n{l9`gp2HoEnAQ;g|KJ~I z!TUR6qqZL!T%lpVfcTCe=x5hqC=^_uT)36pTi2pYv2<)d;bb_xzRJSg--4Hrrb?NHYg9s+#P9#?V#gIuqy@%PC)ObT&4>pDV*LHw z#3Jw3C0}2tcK&{JA(8PPV5)f(+HX^8!w4(hs_&EsM|!aY#qJr6Mq~e1#y;}q1Fob^ zSkMe-2JsNTcOW4S@!%apaO479OwAPXd#H^4rWMs*vZaxuby?U{cJ~Cq3DgkNE6F2pEq^5w#FDC6A@g)+O+7L01wWG#J@p$VG@^qm$C4BOuCS>~EXLb?p4QGF$rl#c(LMtFCji~tah$iIhQOLpAj;wtBBI4ch>&=G<$YbrI0@iS#i-JL?4MA7`pEZT-h2vLoWW1!X`_V9J;-zSZt&cR3xa23 z_^;wPxL>6Cr>f_S;$h8klkWU;+z#DITDl=YGPW1`qaZ>jWh7l$jyZ$F@dTf!4;D*Q zBVk3c-5Fo=g4wP9U+~9Y92R1vTqK|%h-P?$MCbkjOp3S7RC_6cy6{_9{ELhjp0t|^ z$>WVkgu(qE);5#xzV0-gtfj=knbx)bj1O`+xPn2g;CqzcXr|_VDVO&~1xntCc@iq+ zc*EdXtKi6PYTP6A6zbpF*x@Kh!vcymuIcx+0|x;LxNgv?D#?(crbI$r&Vc}j_Eb;E zj5lDx*`ERb9S272jr2e|DngAaPxtm%bsGHT>JE{Fu1pQV<+=(k7PR*1QX?%e@-KkJ zT=etU2G56GmHXt$=Df1;I|t!e<{~(ffwyyi{k*RjgCd-??RR6Tw6ZX)OLF>*-l5Rp z!x5kB4)e9bjbJ>56Wl!Z^T6O9x)yOR+^#5?DfnGMky6^f1^TGi8OJ(R9-(eB9*p5l z;`9;ejF+(eN6sEgO3dD(6ykW!{(y#a@%JC?-%L^zm!8-N0DOimrX3{a`eXuN?$Y^` zPRd*l?#to%Ymj#)6av#D!-m@eHg=|w>S7C7xm7^D_{)C_J z-bSwZ4Roqs?0;>Q?sK7J@aAirOZWj)F$-s6@Y;VLR{r1Fh9^$3}b9J?E z%g_%t(ke*s+~YoA^_Nh7S1w1SD{T7c;UnQ~cNWh-NYEy%G;Pq+wuJN@wv@vO+hbnp zqy5e7Hs;4C4smG^j&vlA{P3fxT%`V{$(`F79mcJVxQh~E{o4STY`GpoI<-qgRiBoU>0qqXOUZC%#0TzmlswJu>CLFmiHmWm6abVjCHVy~y*IvWZLWo_n3R z94kwTdWpQ37T*(-pqD&fTqH2xxuwQ?l~8z~9F~5fMClF8RQhwMy7V$~C^mAUI`bSA zLYbuWMA`KF5p5Ic;eCv!?j*{ry;r*)9-xy9$zdnd;u& z1oL&i_B`vRfhE|h5em=>^GRrHsB{+kw$#wasIfA?^|0u@G}`uVZnQ(3rYpm4^+3<< z8~tK$J*#@DYr|uj2$`vL+0E;gM8rUB0Vk6x$3Y|RHcrYSEAjwsn z{V8fH}c51S<6^9`Yio^NKOZx@| zvZah?WcWpC&zi`wJv%l@kraaf80o`_$FQ~Lpj|adO?I3QD8SM22Seo?R+4fPKV{L! z?-5DIlM`~S#v-=pRX+8v-&cbH@q$7%6H0cy==TH<&9&ToEQZ!|Sc3J5hfUj^6Tv)s zrAajFovC|a=3m`bC{pQ`jq=9#^^9(@O|(W-zYxa97rCY&`3NI)7@4In*jI!J)-KvR z3U%3HHdV@U&tCj-LS2<;pCX(OaOVijym0?f7ZG4BJ$3pA^=62+yD3R3`5;D053xq; ze{4L?FhDmR4MLna)`bP{$=Yt7`T%3YSgAQ$R$kZuo*A3n0K+S1b03YnI;(~?l>}>1 zTF5~uVWr>lj53Yz%C{SOK)LFo zPVQ*bZz8%W*72uFF~KyNK0$WYMJT<7vU72Azn{I!aqjwM9CS6(hQO#bPGRexnXC_N z`rL8)ZYb?=ZJ4IR{OUumAQ(XVJ zB%e*@ByV4=5DT3%Hd2`yMuDTE0V6(FE}|S>WtaAa#l>VL(7aoKva^1Z5L%t`oIx(* z%|azlh#6wY+$L|lh|1;8z&6b4?2K*-q>1bU>EckCyZ7Qa$(0_Z;X~QP*I6S9^(Ovk zBP-N+JhPA$OUdaARP7J=L&pwR)XUA?qiSIfokoR^V^PgcW!=J#Ha8ST;S+n^4bi@* z9UmOyT}`!sF>0nh9UhNqV_Sl5_*wg7lhkdqUZbRD22A)$5kBo)mOSoDS zwzZub*Bcv#+dtmre@=%i!}|tnxADdS^Y(c6+{MCGwh0JiIgJtfvG=oJNT>@fbL#Fd56{oIziy?awF%EI_mzTBc0e^fv zq?2N=jh&j_Yp=P?+p3;{hU&(z`pxc%l0)hMmzqHC$zaR8lgAax9IKAT92F#3=p7d^ zNaomHC)`=9Xa^skc_$eBaN{g(7vFVkfT4uUm8W-euNLvZ*>R}vwjoS$0A>BJ+U99V z7}F7Ejo}_1En7-vGySMECEco;kap&7em%*3urh}oF^wi-{o(X8Mw?^sA$?8XbAG^) zebRnQqsV2kfrBGn=0YA+bmB6W@01&$wbSbHrC5|a)?wAXuyWd_S#My#gD*mWgnVXw zYo4NDW6RALc;;+Tr`X`8g_e)urDrjH-sZ4D3iQ{g;fw1nJlGM%P^elvp`;eB$2mQ6 zQm)ka)HM+DTtMt@n6H5Dd-7TZ+;N;Yl9FIn$yk_@O1Ap}2?mq+3-BHAoTQx+fwQij znMUZPT6_cZwubz7az+_)FTN>ovNq)+N*Wx%-=1rIp3Xd^^>UtqAJ$k`hnFG#~>?N!mGJ$(m* ze6@5^bR}%wOs}`8F#ARD+?4tpvFV_H(h_0h_;Om*u_ft}W1UQYYH#-Tec>W$!sn^N`8hR=XN^VXjSmhLB3!sMZj z*zrYxx==V(yEY-6q~3Ecui(-u^*0M511^7%7CqN&#-A=%Z*u7yT~9?4VUV)%x%k!ZK#6RJP}1luS*r;qw08uiVM1jw)e{=eHu5Gt3(R^H%|DB~pFi_27V46rTt; z%IpWjH<62lgBoj!#DQPnh~?)j=j;HUA)vGRs^;D2oo}aY_}q$?Yn`6wG~5q@bQPHf zq)dR)T9J9`Mg(T?qg!CCgL^AVydC{y2Bt*g{F*YbKWy=P_irUfuTJiMtFV^+G^^7& zk$eIjNh0b`?cl8GEgod_*Nq3ez5%LfY<*Ci4UoureFp`xkdhXQ9t}MvjnmD(6xmu_MIpD|1 z%!U*zrf$d~@WNDWq~NTg{U+k1-Iws>^IgL3(Xnpsize;9SseFv4S$^~Ls#btBpV9) z-PEt-Gu9B2KVw8a9uMpvrZOMQA1B_jDV}iqLxv*A6ot@mnt&NW!u`@Avd&L&L1!oxQ*&b)V@TqBs{?nca;+D|)Cl9H8KiUB3% zqFL|_H}@2!2kk?GGPeAkY4))@KkNE$hMmqN^aHFJU%+H372hN)JsS z{NRL&w&gZtA>2;(Sn`2+4yvp0;j^);dO-GNT*useN(H_qR8FJiuu&T|>S0Zgcz80* zqmfhe$UiA!w6-+$ijKU7@Ok!O-kD3okF4@}Vpkkey}=XnP}0p0J%?{za8jc=b1@<- z6DN2~+X=g7;NuxrEmZF60Ju$p4Azw>-}PTxe9&Hs2x;T}tFGXc+c2T!pDvJxw(1uU zSSh0V?s2bQ(jWd&0d@mve|~!Qq9O2nOz@EVA*MThKkZ6zNvUD}{sw1Zk*-JimJVTO zK_KGA36JL*F9$nE4YS%R#j0a5PZ_PGzjW5lv)#6Z<(6^X)U5T88O`^tR^{QR6o@yN zH?+&a^Lt!jQf6#f$VTO~aVq7Mc5T+Xm~S3;orhQ7Km-i{)fUs>j6Dz z6t3X$r0z&_*+VMg@zanw`m-7$3m_}l{=3-sHq({xQU zBwu@4?C=#kwW7I+GZuduv+wS4)9+EYArCxayW1Q)qFwJ(1)k5cJUOoPy{bMV1`Zhd z#V%8B{Cf4VYmdo0+j*tyxZ4~t#?d`Rh!S~#;T={SObn)g87gj)7GR*eye8H23~UNX z{l@v?KK*Vt&vVXsuHXC5x+d1FSv9l2d(9_)TTkCyU5tRlW36mre-a z(vc}ihMPWe>D(l}LZ#m7ra}BWzv(d*vkwzofRE!agY1x~Pef6>n*wq`!Ho|w#ec3Y zh|Wbt_gT28>yT)_;)jd%d2h4BSJT_R#bZ?f%I+?*6&;`(B0-~N$45gKLhnMCXC+Ay zThw{x37lQ~xFaHUPI;0RnwiU?@%E8W_ag^I_ag_nbkru)#3D*620Uf-^GL{9MC#|s zPa>a|)0raVdy4xYJwCimM5C+JkieNR8~9LKO_IWBS4@YE?or7~Ew-F%dW+5Urfi~ENTElhR^NeCAQx`cE_0Kn0M%*84#VXhUsg79A zp3=gik+HkxTika+jw#RF2=g^#>cl^2bj+jvnEjRGZ?!Km{W}}VekI45^z zqPi5k95Us`uh8F8=|wf+4&Epi$5qVl$Z>)pvBRg~zP#U*L*9u9Rl_mw7TR#<%oN>c zyN>wuFcM?*apChJ&=vN|XCFb=Kx^84r|B+;ZmDk(L`HaC09&>}z$5pta@GHpeaKkQ zJQOQ3!B{-&87q%fonabPqB$Rb7BIkAWIQDu>qg1%gkw8$0z_KdsP0zc&ghF+kKexv zy|OsV`-Y|^-LZphS@XsLdVYc*pD3Zg-;*eDyG#zs+s1N+8pSF;?O#X;6NMMkf{DdV zGLc`31VdrFP%I) z2u1sx`pY3y;SltY04`-0NCdXp3;4ujSxdn!@I5mXXVfX!>2VSE-2l=6$@@5rCKb;D z-HF#mubZ53OW$_l4p8Ifxu`&KLrz(s&vbg`f1m||FQnmE(RZa)k|crqoBG3rGlWQ{ zd&8wxb-jYV@o`PcF|=W8LY1O7Rq%(C_>G7e4;LNqUfYN1&9V=IR}lj;Dk3#jqnj2P zYP2FK{My897lV8=y!Rdvb25Bnpq57L0JO!-#ae7Y?p{76X{DH3lgY7;H%QIY>7#R& zrLxR*7++`Ni`p${Hxm6!J0^$NP*ZjrTcUADln5UU!{2OFR7cC2LAX`SL@H7MlX1%m z#NUI3c27b6DNezb_|2z{WK#H4HN$WmN*nqTYBwebXAia_IQ%<-iI&_!qiRqvdly9m zbshXm=2R0UJ=&1FFuKW;4Q%QuwaLaG);gtvmel_&!m_n-Byfh}4pNgAGhUE$VZ}Ej zV~5ls^NKB};(btw)+s}O7O{;TQ8k2+^|=mHoDY5|_iHkzZBRnd5XoNjb_gx-H@&MT zG$AP}hkd^Q6-|o|ZeODLSvtg42y2)?nJOHa9h52X!ZpqeMz;sNi%L!GLRz1D{t{@hD{)16Qd01o*ANnJR25d_}<3CQ=|yW{t?NRbK3p?4ql$ZQA;o zHE5}?)FX2qo6z>g(bh@={$-e`sF=qIf^(1uJ%!tIQE?ara5$6$4|y@pM!a+HZ5-UM zgI!{=G1aorsA0w~6JXF9jGAs4AFDqgUYy)(o3LdVnmFc~S~L=3vuwRv9DvAy;QBXP zrV~`emdqZ$dXXDxey85Zq1QnmJ zakm8L+3d(sPMBng5KJ5<%4GV(rv+GZot(|G;*i9zX2gXRE_(jKp_2l9e`nBRpAurK z)_gloVhTv?`Njc9fH}koMF?%`#0_U!e*FQ0d3t0myo2o@V+eY?RoNJyD@JS<7OxUX zirR=|n8$8IjwBdC4oW1H&&heo$6(rKEr9EZYg(ET^kg{2MYQ zVsuS5jVV+-LA4J9>!To|c&a^v@meP}6E>Mw-|rrre2Ll4t~ga{S(~WtfO#cx+HSzR z7IU&O)l~~sT%pPmf8V3HC3U4wt477}_qT)*x&#S)4(V=M+~jaRS^beIb~)3mK6GtE zhdstnh8=8m%v0@PL%xCBAaQom$Fid_SMdX_u1UyqP z&)-z_PN2$kJHsL2U?i10PT+3ke}b)%bb>+!M1`P=?Pd)gSS}M>h>ol<>M?yr%i%V2 z`Q)ZU)P6AG#7cMJNH`lOVfw2^mqvs)6*o@MTqq@+Bzd&6kywwaIoGOIWzvJ;j-nbV z2o?gk*COBmPAngSq3HLH*c`$Yt?emc_lm=8N^nM`Rsue8U7mDK zg+V{IQ*SPv-2klO_#C46PC&dAg&V;nL0lrZ{uE}2IF#$FHwrHB@qqTK_#S0iYr-aw zVKXfJi!vXhMiir(tP4m?a<6eN6*-8HppW4>r@&tILKG5bu^Ub#rp!w@FcgY_4DdrS zIZVjAPu;>hQE!MPw&VI|@pj?qfxrLW3Cd)kdl(Fywd~yvE>(adiiwR~1p9rFZ@Hksnf<0w9IAI>r;(-)K z9fxt8BkE%zn^6!HJQR@lCr_U`DwLv};u(z$m003If@+gnY!6+yok(ON6_g( zHDi1#ZbXM4L=IdiY1(zEr2aR@-)v@P$`L&x@eBWZPy{djI|JYT z8#gG2y4#5$U-Xa`gReKh_!eM`CPEYl+K>I;(rsPr_K_2{S!D;3)u2R{O>8o z_-k5P+y{H?h}(YqXz0DGZO_Mm9?BA4&>xnF)C_O|F9`Nu@}vv0l0y%C5fINOJwr; zcBXwALo{EnixEgB76n5<5m5MGYKI<}O;q-EgVZnC9!Nr;*zL<6gQ0&K%}3<-{l5Q^KC=0Q)ILIDR4{TNAv=? z;gzhCAh_`egPW;^og03;du7F_5cM0M6D-ii49|_2JawS0Z_jE~RP-F{kPT|QZv$|Li$21F(`H2+8#8D?rHExYKC zuI2zl*mj*Ab)UUF*y!-(7Zesey%)K$Pim7F`gWk|e%j|XICC&2eyRfaW0)2O# zLDvUI9o`|Cjl}m)DwD#Zm9u11f2gj?g!&2c^y|IQ&NUX<|y!(Z(qaw^G9Ky(e3jPTVqr_lvJnFaMxT4wf=B6K#4# zu2`@!K^X)}wNoa)ysBiYr7&|9T9Iye=_FwEBTEykwaD}0=m ze;G4z{_~RCT>x9RugS>p#Jl(7+6}eB%9brQb?LV5oJii|!pa$n*fuK{-RSR%-lgB{ z*@zWg#?(v)>1%HvywvcMjdXT>o$&TYYTKID-lVzzA+-x5KH_|uXCh$qE_`4!s=xE< zN^^0w`cMcdO%R>QczN-qU7DCJcl@Q+WRX&lD{Gd~{Cwtyg~_<#mfj0akLTvzXtk{M zB`4AR>4JZ-60h=9?KN!1svN2T#zrhwfJXu&6&8r&E;Gi-7ztu z7||oE))>~KUur@fNT%UyFOpZLPvEjH5G4&L%&VfZr}(Yu?9Iu;CR`YRoRpky^@hCjO}8OU6?yXYc6rdFhp}JjDuZ{Yl;n z6h=w~{@=Q{IJCpw&}ChW9)rY%o^Knr+$y~{Iq}e`!!VdcH#}l(T4;=(%b0cbWvySF z{v!Nxv5r=7kcoLzdpuddH9g6dvGv^1XK`TEQ%JBvI`4a$vleum)7G2LJbtm`z)m^XIDL+MhuSC#N?x;;eVFLzFBkMtip>hXP z<44`IyUULqm2(P5@dhVs9cRt`Yu@+z&aatnZ}UY&Hhh*|xV8A6445WG4;?AFxeG$Q z7~g-ym>l{t{4?z}ciHgIJk`}DX5x(5iluUv!aC^l7y@2@mh?JBXPlL8a*T%;+eN`P zCkGX~CPFRaMZvc;>a#1M*$=v(Z+2$!bJBb780OCgM?@jI|hAC@i*_p`0 zOkCB}6lsznfe*K4GwDX}Y+-k73oWgn9{g*|Z^d)Wzr4y@&eJpeP;aDQF+gsy?Nm)K zS60W!Kk;YVC$+Y;S+89tseNtqhjaF%`sbLGrSFdKT*IuqUsq@qNz@5ijSubE$)`Na zz~MjF@ON#-KDwy`rx3lG?vwjTbaRF8Sh&&dm~w6c(y{pLv6;x*21*JELloIv}M z;r7mp(^n#{1fT9En`dPv>(5KMFG^1v_l8>JLTBxbT?9BpE^16IjrioUDC>Cwv2{Twy*@NSQ#P4ndN z*2p{q`t|5z|2Eu_OL zQg{Dyl*L=o2ha1m0mJGC9G2|cg_uQ6p^qoYg^H3W_4V|Iqf{%QWNyo>?l*lFGsCAx zrX6~pnb$sur(P{hs>c>y99@M)KkR<0zFj89I;GW)^(Kdns*2UhAgNBXkJE{G?*tBVKZucuX z)s@anY}37ddJ3s|E&CoBnnpqF6^>HA+Gg&s?&17^bMCXDI->9~vo^YMx2AkD3Uzhd zbkedG&61a`qn1`5Lzjll7Fr^{Pd?v4+-IQs&Mi|`)|w3wN>p{27!o1k3G%rd2*z~< z(Ey)xGlh>$)T#@}Wj#ZS-s@8uK3dlM*FeS%{QWZr>>?& z!Fm;ftjSs_OuE|1N4M3^!uGAx4nxVmFHdvG$ztAC@Y-Ay)xXaAGkZR3ci}XoL3psT z$uG@jHOetF{${x_@1{;wsPxj&)kWDLdA{2=@SK9Jr`6U8(L|@2CRAGc=8K#G*8JRo zUaJDricDqO<&LhUYU7q$&CAiVkb1uQ}m(5&X7)EdAV7JEE>y8hOEZ)cg~-^ zWs{N8QAUo2z3b-Jm(FEgko}@=Gi^`T*H^>MWxKtLf&zo&s%*SQuGw3~Rj-rBhQ*r< zVs;50NU>TXn=Y~6y0{t|Xl3;3@>5=H7dsee?+VS=6^vK5D|~m3MP>I5RrozBRK?>at%(yi4)M^#AdEtMT@AN=>Wm72fn+kBbP0 zEB}q%>&R{8w~Prc@*FE_x9{umA1YfiRiF1kr=RDk@aP1J=ksYiL(Fh!z#o%fZOdT_ zWVM{5f<+I<^{8wOkoy_;EQy0qkD5az1SN+`F=u!IfYRUg%$xf4Kf^<8=`;~_F3Aan zVMiq1BuAPh=|i1YvZa((hcy^`tI`w$`N2$;sA58;g)ROWVN*pFUJ=5p6%XH>$@qpK za^X!M!(SD@=U=G0whl-Frn1$<)Kh9stk=)z*}%WY&;DGwETK+gi$RKch9FQ?A2A~L z02^{bnt}qD302q+-<=S-MRlx-gIxBw%T>UVFO0D*Q`zN7KBS976mbp4gqbgS8#8@@ zPY@3LG|b=GaB!*n1h+jmp#;=Ys94cSVrjBDeAu{!td=Fi3*$jBD506i!7Hze62J0T zkqnwH43it9s+?;s(ogM-I95tHArU$-fhSP$BKdHtYn)P%nV^rH;s_W1tpvUAf;^|+ z;`4*-W&u3=tzYu?uh{&&6)nn8OX?mIW@wNZ<{L zAo+?3qnsF+fEw_!cT9~`GRV2dF&&ap(*ReqHv0)fXHVi>2XCOfA8Z917UiHr67cMN z%cX&3boy6jKv==7NMMHEL0Exjwpf^7=%*jDMr*Z^@ z;0n%cNW=Ejt~28^vA1KRbxSq_6XoZL!#{W3E$Nhl6;0|NV>g!?IHAgnJYN+jny=bm z4F|fdkYl=!X+4TI?%?O=*PHy|-+D@)>zHv&NpC&1`fptxT39W?V5yTRju~6RH%ib2 z6vAX?`9RQfl5KJaV%n*lD1_7A>XeBd?>F$`Cz;7H-dmxwhK@V6docx%6H<5IDY-fG zX>$Sa`~_y{l`B6@z}-()%R_gGWBm&YPeUKoqMyfam$5e8qzQ~OZ5hmo`zGXuyRh;C z8?a5nP+SrPL~o6h=|R}j5#pajWavo?s0b!*qjm879Axh4q5}Q%GR?{)e(g!#+tlzo(GylAz8?%(D2odBgVrg(L<)IrQgPHyp z;tv>RH0alAM_Gqjz@tuJOWyf?{0ZTY>&*Is;D9K<*6uQ}50uVERikI}_R z;ymgnX2Bw;5UrUl%+Pk6@wd}X+y7&D{+s;sjHp{e+x1|9@F+8-rA^KF!fC3IK}cJ6 zsOZ&c+2~T}ho%PYqB$1~CpIF2H%*_K4cg;rp$`Q+3xCRXNKVe5*G{f#+H4gr1`#oz z1wD_8QTq)d9a(s>0 zuy*54b7d{-!zQAXa=(C+dsxf1WOEMw@GHO8x-5$+v%ktc!@%g*qzkVXLdf##cV>>3 zzAf#$ean(A)>?~&cJ{G`zZ>68&DYm#wNL%d`)uieZen&mGUigDLGQr*S?lK4OT*#r z%~`Cy)e^$q-!4ZTuUHH+;~7iN(gsQ&{S5yMQCvitwE}*IwLWt)W~gaL#ao37x9~@A zt81U@HRHalY;TUe>o|E?Cq-#5a1|}MZg}t!>3sjaZp|uTtiq|=@4@r8lk3$#795*? z{|@uhgowah`}yhB3VfNl{%!5=*SGxL{p9YBeq5w%*vC=k^Q?8MdZ=b9P1fXgd+bWw z9A0USPFCs8rZ33@Um3#SrNy`sW3#>dnX5_5N{d&nL92cEgM$E>hM7d|?&ik^$&=Gr z>I!!Ew$3W`q0r^H7v_UC)U3LGB=7uAPh6E&=Ju->T@`+RN}nfi?^o$9U zkivJ@TMxLEMbfe(L~Oh)zDOS{z5n6p{js)zJ~?-9IBmYpU}xxV&yJ!(0qX~}9L-n# zhoJJn@xIyo1YVNk-EE_R`yEDQtl`}+F83HGWA_%a#f#T7()}O8q$?4tT={Usi4XSX zD&N^xvqn*-T+*rQ);Oom3QcCc{XD-w>N&Gu$TPQo3`5C!r0`HX_=cx+qP+GtH>+Pl zkZpY+Q3q0Z&fUD-T2G^*7RvGYYW)4ywPCAKk&C&-X9~@3jp0j$dfu^ROegC3m66+- zFz=X|sid)JO#)Zenx!u$7FZm_g4Wg=9N)@*zWQ0UR(86hr?ztosb%UNa(#K^{^CgQ zrPrwJ!r`w4{}V3@Jwtac`SaG8=S9}RZp|N-o9p$A!q&MNhLc7ao950s?N(h^4u87X zR2=-K!b-2$6MOtCdirioI77@=4gZ5%g;s@-n>oF=NsfjK-JFgc!u+(v*ffRLq$SuB`JDx|1Q2T@EsE{m%i-^~9j@7= zOx_lM3wCd5(5(<@UgHTqtwPcATqdIU=8`oPsP(+5*%+HKO!;BxQsH*!GRhqLBRX#l zM?i%ZR?1$5Y~i+yyXNx{-Wtt{m1vHm8)SW%tEnxWj*_hlLDU0ABiEF&>2qjI28|<| zf!H*u)hwq)+l#B?tApRnCmDFpSKM#lY%xtlJ}i}J)>k+*X^s8rEVSjJsEm2LX;+2- z*mc(lH=Zy2s`BO0rgL6aaxNmpzw+T_LgOOJ85{E*TV+=L;13rQ1k66q-MIO3Obf)D1L;E&Ez)2zCyFAGYh}R4;xf=r{B;=_TCf zH7wKuz*CEg-P(!po1#G_8!)f*eLhx!ygj#YwlZ>TO2MzQVwvGZuX@fzY4z6;-vpHv zGD!8(e+&ntd>j^hehw3tUto(GznPru&_s%_Pi%3=qswb-+c55oFz@7Nty|E-oHCdh zJKME>>ZA3?T+_D#^*#SfRcBYcX}5aqLk#)_&5O&Pg%{*lwxQjQ%}b+>9}VxSW3Tc) z-j00+xZSfz84j346VWTXxA&$3y{@@}&8%o2I!jlQamMltkBMCy5Ptl&)?(hv_~jWJ z_1$o8#d^kZ&$`7q>cN+?ceT9ZRG{peO;K|qPFanOCS_M*=HZJr!qoTXaQK%+cRr2= z+H*p@Snp*!ewME`ADIJ-gyjcbw8?P69p6zF!1hy-|H`|N@>CWO=m9qtN!oq7{r?mW)I|QNTwj%+B?}9H`10TS zn5AajIk^=YT0Y?Q(_g$oCBYFz0&XEl`YGC z{{b@*hP(*nS_Pq#>N#0u&7$#QWza=#lg;sBlkA?d6wwM{$`Y675vFbMUDVZ|`sL9I zUCK$1<#qFwKWqz_de6+;i(@hGeDm6uW$LWXKhUUpE)-unR|+u; z#*i6K=5=ZlBTQW z5$3EvSu!(6tzDKb%u=Sa0suIm1A~M_*`jW@-h$F%=0{3rLM>Z`qGP@4*@b5GNv~pC zL!sK?uB-Q0{pT_c=I0wZedmluZwatQoy$teY^IBM(Y)12r1QF9vE;9Oubqo6-FL_W z7WUkDm#^n-x-^mj1E}vFli7HFewswc&vW?GC2BO+f~MZMu^%Zu)al6_&@8Y>XG}Xo zepgp5wZ^q1C)*|##Rd;d-0k#~LQ@F$6em}mQw+`P$Jtsc7FXWz_LFc9FG;-+ZZgO? z8ZK}?YAV9`=wr6pAyeNcRZzj&c5h5xAyyKBXjZ~{gE)`$K|6HLwaV}9O~!AFX*Nxy zvOindFHV>UTv}I@+iNxv3=L9m(X*DEmPQ46GqQh}xpc*rR-e@mKA{L_EGVAg^9g%jbCToY07)@_cjTaF6Sk>QcwYkNz6%Vr54 zy+(<9G0ZHRLOp?cHaG9Ks>2Oe>%_ZH)v4PRY2<%2$Nscn>c*rV;5%Kd+MV%Hv^1$T zeXVgaVoTFKEsSw%-m6%t(>Vh8>wAqMl5x^}kd;&C=3J+fZNVkHm!;Z5Ws~Ma z@Ybar!x(*+EX z7uEiyQ_5iq{rz=qKCa4nzclT58!vQ{iFjptxY187W*GJ)8xVKTImT=0RKYvvc9|nW zKTabw_sVseOyB|UFgtGwqeYyyOKB{EonucfxH8*{nYDJqrb#Il&Z|=%9%WPnZHo@O z#TI{baIP4aJ@om1mU`7qrYd0;BW7`2Z__oV1; z8yjGEmfEL2-WYZu6mq(o@>e}*xLU(;}UqitujjBxRN%qSKe zf6Um;>a5gb<>x@@T+v-G2XkSQe^T$S3zn1ErO-e5t-Y_$`L;7`S_IDDM|Zx`)3hDG z4=THMGoV{w3#|9jA*R;9q2MWM=D;NwY1{->MpTXVK~Q{i6hkuOzml?`f} z3{_s(cE8bF(#!j1^I2L-h$3dcyW7QKJI}7LcoA4-{ZV-9H+pDrp;4xlUe%Aa3ombR zZv)7zIC=|Ra2)2E6czdu< zgEui_+1byhy!+LUl&r``Y3+F$s`(?0jcuo#vLwVKIma9FahKf*$`kDixdJso!MU@t z*tt-q1)-QpToHsZiPcifeW{n$b{}UiEDD=%6wUUBiaKdX&4)t0D-Pdx%gqN2i#=aA zCNb>bkSscG;$c3NIg?{8j^dAxRow#p+Inqa!>_F7id7kG^+Zq6>KWX(lKx1GkkUtroadUEBtZ9nWwx3}G1 z{bl^E-)y)$J7e19)=U}xuPxV>#I-~(8W$m%0@*xM?FL_OC7QA42iC`~_R*~gByM)h zODnQ0%u*|(Z>Rkd=Z_BB)rq0E%ZD`;1|t58BQo2tvXE4HEewes7FOiC{W5hwKDO*@ zaJk<2=ZY5nqiX%^^cu0+)k>aox4@e|C+3!_)o~;Po9qo44hf}zH!vWpQ;%)=c`sib zvh*otR0$i`aaT@fDU&yD8+9&i-CXlp&O)Ds81`9tnoj+ESrh!+`HJ-CB<*IW3Ym>+ za8?dDtjGy>tkL(JQez&(Coay-m+se>q=hyG=hR@GctO%b8<~=5-#ZwUYnT>dlRy7m z`@**EZMdM&teIEP^p~`Hwhvv+jGruWJy6Eq#*81EoM#@{joSL5&eaJ&+jLshSXvqN zn8r|b0qLQPx1f#RFjH#5O_T)-8_wt#&Q+w{{=}9x=UKy;_S3Xp2dEg8i@(rTYYmTY zt~|VP*f8%XmK8?Z1eit_^K_3_DFm59XPqf2`PeICvSU*2g0)>Itj~#jIY!y`T&vRF zcs0%CXbdG)dApFM2!(~q`1=;in6);I{9(n%o3*r*ldp@mi)oN>q~{djQ?isIb7i~x zq~CyK<6FEtU$)1g%}5zD{M(0~w|U&qb2hWYgs5(!*08g;d9G(qp1Q?6Z#Qs)h{&`< znl=+>%ZBJExL&-0T6bF}>9p8oa@sFGMEq<{xQ?Rodh%7~HSQVa(7N^y{#j3M#w4$c z&5Fs?xy8E;Vf28DW5T3D=T+(A3Dj9#b7Z>a*rlg~kEgS4I^OKvKQtHR z1Wr+{KkL_V{LG(29c@Jifd~fZN})f~`ULhu#XSm58ZwDAmB)(645GeEtNN}E!Y6>D z4GTeXiq^wJ?@zOXC<*`cd!ssSy-ZjxVOI{e!@O^SUVX!n8Yx3=FDz30aWqXLHKw% z1wYdza?AgV5O9exJ0&3a|DpsSIEd3usbd>MgHdbrRVi2ZsJw&!MG1iSTn}nQ5(LVI zcvDebB&WqK2ICq>c2jK$IT6E#FuI-AvPYcbqksJTj5>qCk~#6K3N!mTw&Q!+l&E2< zcjW;kMaKJ#*Td8&=&mMTNI`s>3eA;-;HV9DQ+ye8E;D8)Fi^IA|MU|T0N+O-8))i% zjT^yONMq-`5SJ9A^6>Z;WBNZ=2FdTXT|^Bm>)z6fRx@Vd0zdtjd)y)Im51g*v9e*b(D?)(1kGfREx4KptIlm($7FnPEl2>&Z2k>!YM<=afB7On z6{nE;y<tmG-OLr~z-MsNA8--Oz{NR@%!-$fYo{(;Kb$rVNVYC zIceqA(d{!wreFf1mPOla2rwV40mSLQK+MyDQ2U3e3w3y);wbWG0BZCJI{Hsyr1muN zn6Cl!Dc9f2PA!=*#C~=ML%+LAu%58puw~r&Gy#C=5-4Sez80QnwCC=vf6p=kaW31yp%^tlhWEnJp(CKa1$+65#CF(?z+}Yfy7qEsVEHiS(I^6CX5F7(&NkD(blWRjaR|oy*9j z2@udDLyQV1G!Eiy4}+(rgA^YXy^3(5<~^9SNzPH&k(;$e29d<%hGjK8Es2BSLvB;Y zhersGL(&&`6axZ4nYUFOXNW*U076T|cL|F(i}5+G@s7zPL^J==0(vz}0w){~{)9Uc z{&EeaSE&2BF4#q37c>|q+T)vA}@u3S))!ft~-!6qNB(`Hz#i&m9LNspgIw4;Z?OVv=fsf1Mfvf+saYth{LgGv&K4Hbu zZtm#|_w7yG1z<-J>Si({ufW)iD9b8n&V4*a`b0x{YB46ztpJZB0s?(sEeo$(9;iwD zkjsu^(yN(E*GcMyhU~(f#)8w9(kBOxv&{r{AH)b2EGENo;c!K^DQv!%*`6v84hbQXl(CKwF^HIo^0k3ytZuQK_;C@w z*hf)Z9IruB`d9zWLE%W0hf$!5fToE~glXWR3R?z~#~JbVBRj?DvAod-WPqOh;r)NS zz!k4!HvB+-#gy;zxm(bjPGd`ni^ymuFg55EEnp&ls=e4pCRWaaXK9qP=1Pnx-Mlto)5Sgrh2T zxJp#DyfF^f1HHR8Ny+vro-8o0f1Xg`h%jF_)Jo1MgrO@Fh{~BBrUs%SnUnMcbuAl5 zNDv%%blw1d_IN3~9k%DVCS?A-(dZam#9UD!YBMTdg?^HTs&4VE1CZr^bbrJib$>9Q zbbl~I`F=6u2QBXd^G7VyGqegv7@gEoNTvrn6r2}D_FUK#J%nA0EAz5hkEOVmvs-x69kgOz5 zFt@7zv_wNm*$A)GwIhW+3p2K;Z6p+Aj2K;PfZ-KkK4ZZV+O znT8)=L8#ED7w%tjN$Tx?kV{^J3R!R-K2fCB(r!MyI0sV7&!47~q1N&}wX~*-xE${y zQtKrZrml>ij%*sCfL`IAgE&*+5oiJnsT9<#tR{G#fAz2*BVBKU2Yx|161+7YLabj{ z)rsOec1tYKXW}#U;g^740n7(yeOm>1u}oAme5|yrvu|(v;7BxDkfp-&NP%Cc9@vni z8Ay04d(4JMOSf^TZxprDz}7yaU0M=Qf!82E+1H@^9V!sD5?2X16|P)A_Zd1#1w0Kz zqPvN3$QEE|iAiD+r%+cI@6>Ll&8 z;Eb>YA^;Z_DhRudxpq;wm+eH@x55YKh&dbVd*$JX`| zA1+6XWE2g~7NiFm)E5sm0$4CIp!=!w|8rs!(!+;)dPYJi18UbxjioXrk02HU4n1BE zxWyrZ?p3jIK2Z9DLH7n9(3WZh?w#pA@xMB#-wW0I=!Xq*G`wYr=kskvj30O)%%+U! zfq& z8skuYFsdwq*g(ZStqjdyS3q2+e`f(GRpRjkKz>F_f^g9hyK&&4FXEKCk&DXdSH)r( z&;iQ|*y6ZKe$sgOrQj9nQa(v>5XidRaYWp@%bt38)8*Xku{y*NJsqhKZpqL^akwH! zXgn3m2SMc~NgHqtAr*$$;;TE_2uVWld$V+>V#SHJG58;$d>AOhD0YA@m_TH?hb#!k zi;n6gCc}$o>wUUk3pUXau5j2xOeOAQ4-O8OHPeyYnK1#_i%yGhp?R6Zg>#p$;*3zBL6); zDV*R~X;=m4Yg*6fpXmO$PL7x%iq?6m*yIh8Z>j2NET^X^DvYdkrSg9~*MJ zXGRl89N1$dvg7<(3P|K$U4+h&(Hf3*f25sr>n=a9)M-i5cdz7ZmCI z$?;@FFfmHR(9Bk5)9NK}rHVt&=hQRa4O!RcP@wIf@P(%sb^e`aSFq4hVzA*kg1Xz%}d^!MSL55r{0(i8T^+C4gWyc1H+76sDlhH z;U#r2Vdo7h)rQAi8@KG>Ekkz^tDg8NGKU^gD<^Tffq_ag2um~*lgEYv zZqGQSd=BRDpIIEn(8Rro}$yGgZ z>^aaf{8#o^*<$H(&9GLwI7o3MN8*qAp&dpyn8#J(&LRWuPRZf*ynJ zATp{UnKaob($($B*9La0U-Ra|uJXK@SWFaya}q4z!OV6%JQm4n{5=zRwsUKUD{7 z49+%ro!&rgCWu^`iUT&H{A7eE#-t%xCO>F`rq#*8=U%{Y5Qg5{}*XweXHb{7Y9P8e?ETduIKVHOBRS^`9CS z2}n>AhB2br43UXE(Ht8M)j60JQ?s#w?(yNjO}fA}$?)s>uiz7*(vkcp@Pz#T1fM`H zf>F;7h?nAXXi%U2bF@h7(-kL_t<)l zHGT-+J8b1#C4-gF4+Lgqx(t=ATak-E(NaUx4iOBwMds#A&pNr>NVJoKl# zBJ0su3)|UBpYKAEN>A)F5Mr7*G%*32jvd_u>ovUSpWiwRzZIBhBy{>`S`jk`9>Ne} z9ZW;;MDQ}N&$PIVtP3G@u%HL{GzOY9;=!IZ;ho@to`;;!F32D{|BhMn3t`OU~MOcOx28$C~Mwwy< z>*tn^$-F^@s8HJUQOyc>ke~&s5vp;O$M*%CuqvD4AQsB zC5)ggY#R<(NYXRXZ`f*~R2o41?q(S*EJMXFwMz11ZO6I|vZRI6T%DTUB*@!6`1lkh zjMK?hPgIQ5oQ+YqWGoDQS)wSBwU)*=r99dq*ltEh2gvBTK!R9d0l+r0!9Jm7+?CT zV8drD)EmPs74yNIHv+%@@YOf#AN38^O|%-N54gQ+;&<4-fBTWL$#Zmf>Ajx+73BZp z>n+3L=(crHNC@u1T@&10g9Zr}+yex6cL^RmxVyW%dvIvHaVNMt+#>5+d#!!$z2{Fo z-94+OmUNBr3LbdHgx|91NspyHGBoKUmydV@JV*0)hoI8A0^vI6a-{~8Q z)mL{|B{y=*yLZ>(ZA*AtFDt#RU~0_kIln)t&&$PP*6ipF+@~0BN40#1Z+2O>fFIw0 z-c=$+aLj2cL7A{%FF|rkJLGUTpHM-XD{->RH@1@36IAxot7FkPf;rHsVCDL*o+ZCX z*c_>0V0)Ht;l`DsPUTc0ICwv~b~FPgxuo7u-*8pjNDeoaB>cnL-q=;GLF%(O!=OXD zquPP8Xv8%~jeSLpS$YbL`h;u7ca_YNQGHvV#Tn<4AO?CcVfz<6RW7z&L*sPG^b524 zINkfDB8O6Itl^=mT98I_ovUWJbPr9F4!&}`PhVSlW0A(v_&dQyWTK0e-^waMDkry5 zy9%L>x9hEimW%BZLMh>U%I3T(7Io&5(S0nMrjsh9J;%$!M(kQDP&ttY+5HSjcQsy; z9J}@%7%L|^ZH^Pg`s``DPb`|A7By|{jk4GS8%J({rcm2do)czm>;b*|g_~&_?Cmw;QiAO4j#Vu7~$SI^}lO;y!#HD?IXMEuK2eOHEcHpS? zQSu3zrl$39nuViFRS=1k8~D1}pto;#{fKRTslUv~Jv61S<|KY^Yg+3lJ|Wk&FxZG? z&+w+~QHA5-=+TU-=YTP{b$s2Gk=SucuvA4k2A}fI)15bGd_8Mcv7y_jesu>!0Qp3{ z!qRj0nFiy90+AfujOH+x174 zm{PEA{bE}>*olN7!RDpxL!zex9!PsQ46Sf{)Ip1UT!G(WGn)1aRT+Hr%~R!Fyk-gd zm-;gI`0dsEp7;AB%`rRqT1T>SJvIKxmV5fl$4B+`4SQF<$~Q+B-ymEWGFnn`W5EOK zxr^VNCrt@>Yb!_FuJTLB+Ke9_6n>Tc*|r}a-7a@CNjuuHGfH}NVmAA3x4D$pIat^5 zN59hQ&UhV`E2Gv9Y2mv?oI^{^7hciK<u74cf)&2~4&hUhxX436Yy{51 zmZdj})2k@;N5*xL$wvnD(`lpd?RDraErUerSVu$ODzy?8Yw}NOzctOC{&Y)O5K+OR zt!<&WV;;+7*LH@tv`bCyedK5G0;f}}llOBO$jyn9_Ur;n$5^xF zRQXoy_7Bo3gN(O;(b(v0x!uIgAAh!M@qKx@1eLf4p(e*Xdn-q~@P+zr-)`<4efzT| z!s76+C+gZn!~w(miEryP83i?EB_?n~{aWW#*sCd?K07rLIyWTcs#Om}88aGBkIdT) z70cA=m;j=BzUhd8bzNRlg28&9v6K{5(rFj+>}h%JoJ&=P-p+5k+xv^Ly3cY_}zBPE&TBU*9mNj$(wRPF|UUAl z+qi8%Y;LHpayzYl6*6j;kGHaC=RV{%44ig$yv!$9N!n?62}T!omd9a)#}3x3zavI2 zClI=3yzTw-Q5{un7PtYE)~eIB0nCsf5)Ym}2Q?aE;p z-nQ0pp>6^TUcdsX~r^Aiza(q%&#w5^BV`>3#@m5waPv06pX?XeKFqdy$lI?~G%3VH>S|=AyMD}1xQNrU z)q21ydZACvbJH5gq4mYB<+3)aTx?|^eh@FY2n?LFvh~!BbQ&JB(miMz1{+LbeC@jT zsy2U9vYc;=3ev7pVXzF^bt^}CI9Mv3)457;P$Nc!cLdSwS5c!dJPj=?xgA(k@GafQ z-f7!rj3OByub9@)7OIx&-oKTcYw$8vna4j8FKnPubsNyRk^ggLTub48pkL2+%+E7G z*m9PScXR?Kos{=V(xJtFcGA=xyS!{hXqOxY zwwgw|-|FAZmc?p^IkgNKa+M(>*PivJ*w$ycb-`X)lRKHUFUtC&l`oFZSRyH!W%9OVikol}d)@9ByU5MVBN*=!MBYsBByO;6v=t1v2EIQm++-#*Q16 z2Mw0)gNxc-06)4tsguu*w-+;+=J+zr*g7+EHRjdA*5dU$3powraXk7kUQBL#CLi=27jGh)Ba zhL1JwHbomHZ=$0)5MPnd(4#zm@ea%KGBBfWKy-JaQSERR9)SORwY-AnErc#PVx^Wf z$_|ik$1ScohtM)Vu+Se6y>hZH=}JoTOE(*dKAKw|`|j10cv;8sBAC)AC}=oem^4gx zb38{&@S?F(aEUMIcpGW%?!B)eK=)i)NV_(42LaRAa z`o{bB)RVIr;PW1hNntqxQz@jw5`l3E!Sx}Ja)_JySHWPLmZH$xJfY6F5mg&n_Mb#> zCcmI&BEnKaGefEUoJQclq*z3tjqAZsfJzjV?LvJ@W^xLCJmt5>99bAMe8RlcG?u)~Sh5tN#j9r@Y%! z*_H37$amEY-=}Nkl3e=oeNT)YVAz1Zg9S5-GW~cAvvM1tV7K_LebFLB_DG}hRR`YV zjTAg$roemgO_2#&y1cZ9R|ii-Ym`%OamC`8GRl9dv+aZ^q&EZ?;X-~wS{2}Sj%-INuib{zG zrS|WNU9}lX@_O;Ke|l_@t2y_aP&bfsnGO+PX;^|EP6i9SSMz;|U?tiNKloo~LM3i6 z4Z1`U1d>?xCSOBi?@*?|2RhIBpvuIuVo80YLT(-bfNJqtEJU@*^>5A-ENE`e6rL5; zUY^6NqgdG}eMJg!RMz zW&PQAHsVA+85F zUmb~KWiDRO8UDmymz<)^SC<@Mp#K4Lu(B@2>d|sQ{R8G$;Jic%k$I;}H{u<^p_|e4 zdEqk~=A#Y!3w78_RKBmMTLd>Is(U)e;C~A`0-hCTj0;Nt;5m{x9{*d=(VFG(6EJr_ zOJ7DT+Xd_+tN%_6FkqY)G?{mT9+Mw{DI-mWZ`Z6SomV=o0K~vBA74DZ5D{|_^9xE( zg%r@g6tZ^aX{(p1et7TVc91=yJ}lp~{{h{Sg-$eKpNdv0rKvZ&@EJ7RsU z)H^oc*X9-bYH({_BQ&(+I)ANM=RDv6@5IV1f2x;Qp2%hs=;RGei|gFn)*SK zDq;AK>KG%S8P?4gzaoO~AyS|y9d#ZCrJL|?5D3%LlTa^;F5Oc^4hb5Nf+;Yx!N_lW zhZuOSYKpnk&k~K?RX>}A4TaW*_9f-A+T2*p#%52=h5!=Ir&nD+>`BkCWJ)a#s-p5tQQiimC%Jufr>@MmZo$^ z>23UuQh+X+Xe|_a#4k1>0UG*RXQtGYPU=!bpMQZsI{wOzQN=!Ju;%-LgAo6MKu(Er zsS$@#go1%XA5X`-fGz+a5R}N1RglX#SEnp=OI!6D<~-6$jjwV@4fA?%na=&c6QI!i zlJ$B?pu#SuS>BiSA?JTVg>CT5zTprZx5k;fM5Tc}6 zjBsSH&p+qoeO3A6_u}H^;-+J)wec*Ap1L+qn=5>egP6Fz@Wzc@BaR@N$^59}ys5k|<2j`&uDA@jMLiUJdgw88Bv zgd#iJUtWZSoYYw=Gz~MA`j3S?w2rwx3Fm843_z#+lN9UpabAzT>gC--3n1MpAg=|I zViIK0HY216PC|2F+!(pbF`QSp-LikVdG(dr0M zft_$=I?&tKDpS|QrSw~K2161k(Da7R6gGXT(S00FD4w@{>A!4b}iL`O{cfY%lo!GS~)gF$~Bx zplcqSn}WWgO5%2ufGifxiD4lZRnAx3!dSgT_+Fbu@;*^?=4c=d`no=U7f=Bq$YoF= z7@}Wm@asA;nUW-B*meZ-chZ6Q|FtKutkM){x?CflUypyCBw)e+)!kpB9v?S>kH$c9 z=k=$5X=0HBnpnw8nj`hPSYSXCi$au(oN}AzD4GT_yI+qKGFHkZPUlZjgmvdL<*O!^ zICG?uelEJ;`?$YhNap-t3gL(suINMo4zizUl8;x409f@pzqM-*pAymec@uXYFnYbv(` zJv@>Oa4lMUDpGw~>&VTchu`M^lvx=u@2mRYHdGfJf8(CVRzZ^2P88vGZ#IILbc%nv zPfim)PpPnAF~^viD_v4dp;@SWI>qbokjP3I;c1gCn5;lVxRGqAj>iq;gUE0{2K zh-sBz>CrI!MMqP!s0Bt-N!WCB(I#!U+CG+~%x?Pq(Xd`i@I;eGH~vD}!v%aa*XO=^ zlTm1mZ#3V*aGf@V%Z^g5plxk*^=Iqa!jUH}^(=c}7uQLv!cYF7^w{Vdm>x9GA8*f7 z#hIG4wF7BA&$>_l>Xx3yiIsF7JZ9mVdpx7n(P-3t%w;f>5SRk903Qp;boyTcC#-djg7U<#`!37 zxEVafjiZ)VSXD*BAfz1CpS{mom+A(Q?6nzX$nT>wQtd#BKt|HUDQOE2Y@xn#eRRja zN{BVK_E?&_kUl%&>Y2K>qRxqu>#|g9((;vZc*Zdb!ftrXV|RYB{^%?6(H|uCF;5Vq z18&@rllzCzwjX>%A30iDQ%8*Km2%_gu_vpyhwQWxdb~W(<~bgA>55AT1=jcR7itI2 zU99Tx?w;!{GHN3V8dS_vC8bgx>y<6^sx^@xC`B^13tHpBp-Y?J$7kFYR6b%FS|Rb_ zC3&5387+(OYwA#Q+r>ELL0`Gp5Y35sYAi%b2leo$;Ok<{r{mMlum(`e)E`{AIJyaZ zzKN!-v%}s3oAhyj4DT3{ORUbm?Dq9&-l*w=?V^9r#k+vo%eIM}Yr^{;ckyMKF_bIl z_AY68A1d%YIe%~Uo0ua6NUzuPzT7;xw|F_OtdFzow?VdpgC8{4-K?sH^wj8d@V#_) zpN55B4o+&WEH$w@UJ|TSUv5T~R2x>=cNNsP>^vh7kN{n$FH?3wN0yvpw?kJySh43T zV&NOa?l*IHC_rffi+x-5)MF}lqei=CcPi`kxP0(;TCm(tQUwZWoI5X#>}F2Ew(ZHw zUTrgKI=mpOEKpN>nFNe-EHj2Uv#w}-PdwDMc^z)^5z`Wio(ZY43%({ zrg28<*TW>IpcAG#o;-VKHIQO86d&Ao&ol(g6r<8vwk)rsM_8ilwthv#*q2g$;HP?U zm9g}~Kn_3n5?}L}Eh;Pu-eEi{J(5GQj=Xk#o2=+Oe{xOtImj;}hgX@D_t3n|KZ->& z1&Lt$dcfc=b@ca4GtXEXQVM9SX08x?PtV%;o_Vp{BgYgHP&(*>odO(sZv!D4|EqfT) zp)0XD3gVL}I=EM{mk;nW{q%R`b&-SUsrIeB1tP>#RAl4K4x-o>VZ0ONsLUYQ8C2<= z>SEsUdbtdjITEkVZR=OH&J*^()j7V>6cUO0@-7zp>5jd%_H%+rK-HaC`qMs_+s2Yk zl}6m*=eg}>jm27?$dTIVp=itP=Xm#)2{Cg|jiBIn$>7biMZpv23pd^r#hO_ScKAMn zms2g7^)0WH#0Pww?kmkEZjOb?Cjl)aObtfHJNV=ZSy{Gtk130AiMHLFuIP7oB5GV^ zA{4|%Lv@lhE2RlmX47t44Lx7=MW0o+PZ=66&^ACi##-*(7mb=c_}lR%j}FZxNJB^s zf((7yj;eQNdDC`%f8eg&dyt|R3Eg0^#&>tK+^i;IsN@)eiW~>dB0>(2^bq%&g&LS1 zRRM4#tczbaL8thOE6CjsTv;q+iSN4>e12v6i#W9;2a3QlYnrKyxg_%8#>`IlU>VVO zZDUUM!b!(*x>P>Ru-_Wdv8KiloEmM{BA|{-~Ek$gWu)*en^`88eil zAQ68kUmxdWNcvs{At|$$r(E;9`os&yO+0Jih3?DMa0q!B_Vc*W**N9643!qkaNC39 z%vFWsjIK_@SwZ}#mrb`GUGO^MbA8W(T3=mnV?53rxcj!Caho)F*G*PB{eaf%AO=)m8@84qcHTJVcF5@8>9y7E18Oa<*;5mym+=h zt}S{l7GT8m3xYYFTd?L_qLkP!wpkWb^;wGa(QCSj| zDxD-_OnUJwAJ_w;l2s3FuC405KLyn|*F3%DNkzlDOo07?YCyEXXWJWsN)`~*Ka@E_ zAx{7c2ediAerPP@<1Z`=WqdPBO<9SgAzrO?%>tA72E#M%nsH(MRS(Ms(8I#Bi&jaL z@BJy!LKE~}=$%3&`@k9{Z>p^%y#rCov~w;qyh$MSn=c}yrc|Bp^g`aBM-z@sG5sHc zSg7!Ds-KAYb~W)dyhVdp*Zt!tj(+!}q3Pm6zHL$s6GHw4R|V1tqVW2SY&FFs3J?d{v%$JlZmm(AOsT`Et z4fk0DTC3_=sBnAL#B%W{tcPPC`G%M^8GUc_Z%wRAU^N=E)o4kZ^!p*urB3C8@ZnHF zzBTfNpA2%lPJuzS0kEUV^~2?`mtbV$bi7gu2qo7;L`4<&l&Sqvgp{=e3jH!p$!9Zkv=_pu zj-YUP#212}uq4>iT=$9+Qs*DL!|x>FX}vaxn9GY)h9+LdY_p;&`AE(`>QO%ZL>=@- z4_V8oWFry@iS@Z0j}A_JuKjK@3cQbrL-z$Uj`kyi7+&{o{ns-R%XN^XXhnC*jkCxzL+>j8 zgZt?u@aiaxjs}FMDCcWP%zp+^O^bz?hEA3EzN+F^lCRpB{&|QACT=&n=NHtBmRI}{ zKq3Y2&*$4s^@01>5Nb|QUZUPlohZ>Qe^YhT`6;Sl(Ch5*t+a zpMtt8tew~wH2`Gs_myi=O@c8EhE9PaDIuWsEFEBf@icl4R<0LN%Dy`h;^~zd!}Y(# zl6BsAYLJ99u4f&-mkn{~kixR2}m3QDOo&>@H zYCcht;SRxYn>exwyof6E2KqhJouEH(_UFj}Hv;BP0uaUrJ_OyJnDYI9!?z2fwt;$x zm>5LVx`s^q*eRjUzg6f(b)FKD43~;RR0CZA84w*RPr!=sk!N-GYvpV6B3;C?W^s{! z?FI@-C$+NWL;bbomGb!!g!%O;RC}Rc(3V)`8ox6F`~o1JMX`bM>3`x`BS1Xs=k59& zgSTqpxMrk}11jz?_r=&S$&Te#Q$B|5ik0RE}8+;3Zq%6jdZw=d0 z=<9VyCe<7G8K>_r{UCm}ZCL;4HN95$5k(|>MU}!*c-fJhMTU!u+Op|Gq5p+yz2dC^ z%-jnjh&<}{BhCIl-(PX5z%IJft)EHGbSq&H0*Yd~tSU-BeKCc#;34(Fv#RPr|8x=- z0{|rdLrF_h3nUaJrFC;-5)uh?z|4g}en+>4 z@z(dunL_#BoqPBO_ir}Z?Ek?=+XHZQd?vckbdXaJ(Gdx||FF@bT2Kw=(aw_rI4f{t z{zL{aZg%1>A<##=+o=q5cX8aTzOJRdlF|O+Y!m)xaF6IeUIEenjPjp}@cQ{WWWkg( zGzpJ7CgRum`a3uOKK8i+0-f~k>@#`xDtl8c@5wV9#jzsI1`H`$PzNR2%#pH0uZ`+! z$Owonh1cIuWA&vVg%nHnrG3 zb-X~Zwv^C3wxNjYE~pAO(GQyf?T3;$!bmD=Ds5oV);tg3FE0z@CHfshA>n7> zDttX7{Y7K5R58vNeMh30CRbbfpByJD3lkTeT8>_ zosAY9m?%4e8$dQmN3GKInvIj$7B>QO3~mXvY!JEBUrt#9#jCr#TSQXOpysQ)ynhPd zF3$`%6I?eGH3!UIrKvk??yIp8&i^(xTK&Y~3Hy^OKoWtH7FVe2AP*jn_7L)lbO}Nx zw=O-%p-(qT6bc7zEG>>_klBY$AKg9x6_E!CEbsOyr|vfX@*eVAjL@tiB0KityNZOw zuYpL>S}sFWov1;m{@m`8z@&z~0C$?d+h3>&`Z2K8N^eWnUO8o?o5k`m-;mngLGi!? z0oO|%JX$o-s1npAN>0S{Oe@iezG>3Jq#YjrY+!I%Fu_5|kq~7h;vc>jp`Kvc(NMX- zaA)acR?1aKk_Co|ewP4zjiUZ?$};Mk&xvZ}xmxy9#>* zZX?ZMK2M$j@|0atbOg$m!#`R6lpBA`=dqbfJj#CBj1*)SV)Hk$`&?Zi!8B9N_L2Ko z8FpVQ=pr^rKAd-y0CR#QKQrQQv_8+W6|L87GfHKVmH@+7{wwL)r(of%PD}}&QzXj6 z2N2*VkE5T%!ZH2oCog;kDRQbh;STu84>e_ifN}c|1zq|R3gf&@kzGXo4NuFE3q{mp zvnW--z6LD$YnnsopRLsB_u$jNZt@Yhg*Ni>LUdFkNby`s>(BUZx?<=6E8x+&4lpwU zMgC)E1PbLS%JOkvV~5uPNM%68Uf8q>3SPs{k4#0&ln{zJyBD1h-gZl!3aV)aXbT#1 z2eVuJc8zV!Eh3l{UQ!&Fldtsx2Uf4tLlRx#-2tNX=1+2opklvw2@0eVW?_CkucR_k zxDJ3+mf9r*qJjnWyM+m-HM=cx!v6==OMxZ^KlT4Y^&Y9cqIzFL;E!?t7gX=-J&o8; z^#6wHMgPB`djIB82qi?JydM{WPX%Lw0YooAD%-f{B-zln^Tb5`C4w#lP?~^54&Yc6 z{REK8JT0JJGdc$ay}yEODK>O#-@FHyP90w&$xoV(P>JcPa!c(F-|QXi}$7QUt?-e)vvvWtKwu;2T3Ggs@^PeeOi1p%g{`2{aB0uc@tRh#-}ph{?;80N4bgxqR|H6&@kF-Hg_w?F zU=FI%JOkOuFp_L6gw>X>E?)De$KKa%BTLb_Dbm$6MU;2Bs*=&Jq$x`X3t@W{+Y}z7H0yN; zzbiWM`VLFw2Ulp~E^S9$8%scgA8W*^58XK)Dh8Zs?ku7)5SEAdcu$rPBF&tbN}XW} zr#1QUB8{5j_#HjkDtoh=D1x2KCVuq9uZs( z1buFCq+0oN(>f{LLKxOEu6H-ZBxhyRvQmE`8j5K?a;{vt5_u2yTg2!)J;#gs!e7SMyl(^o0tB;)-OC~ zc@M>iqO^Pi(>cP3J*Wsdu39K@UhU*T(A>RE-WxOV9#aASma-2woY(C4ATs6D{TLq%a#ol2Od_xdWZu$u353nbaI5&#J79X3TUZrBG~drZMgsyc%4*rp*S{ zleDm&{z$Taf1Z*c!nMh4W*hG?Y-rLMiG*U{b(4sgH;`d1Fu^QYP&cGu^q1-Z0)@(q z$46&@s=lh3cYWPzL7b-Cg@l2j((koXYDSBykI{eb@VkJI!SCy74x!5$9Dg6V6Vl3A z=Ov?#+D2?)M?@^mWAx`3xn^6gPnQCEk#+M6Gj*Zv5fvXKSo+7=|eX`bZ z{7-^p@wftnux12KDe4}+7Jp*_9o;Q#g8iP(ZKU%6H<6FVEDh?pL72N%9HkaFe;ft(=W4U5py9G$sr+@Fd z_F}z+2%XuXm^(pCzsL`SVQEQcJkWHU`&2u_C}W6?Aft#W2HpCLPDXzGXU{RrBN@`t+Eb9T>!vasBn`86l z`p*3cnj9}u?7bIJe&0fk*~Qc}rF)mFY)$eZIks62Db{z~xjr~CmPN!cEE=Agd1tJG z$(kCb%_d<5GHMUo435EL#crk{$8B!IIBn%EGhJguTZ!yzeAIF`gj1piX2Um({KN8; ztGOTDdD9jgneghW9E*-|Ca~J(hl*V-dwX+j$=&K5Ahxe2XQ2 z+IQ`+BJOcNnw_&Utv_ZwkQ$q#|HyNYoMbv?KW8_qG?sruFb8gK7C`5uqrB?AUpfgZ zVXE2Bd~SZ@@FVu{&ZE)lJjZA_t;Pbo9lp%2qgdRjFL0*71##urox6w+FW|dmmy@>H zcyc>Pg?06)4mzd*YDkpm`APGCK;Aja9p1ElUFW+W8Fjz0`UyGJSkOFkv~r9)R3h8f zxz~&v-th>Ot6D|gt^YCN;LDdP*!J@@_}uz%W4*n9ynt|m556B3s-6X|$6sdto0k>z zbh<{Gm1=Y!%*T&EyI$LM1kId*B@HjRa$=7hU|c3!R`dDaM13k$-)DgNG|OXeXcS;t zKNGM$qMl*a+<1iS7SrzrVvJN(<|V{*?pY{KrC+vAv&!e%3e_`oVO*^*@n~{XuVjEv$2m&Y}2_y2CxMx$GZbN&;Iu;C?GW9K!QF^j)D@=cf6VeW3* zj+rEKtWiwm4&KTdW?{oohOv)aA#F7C4Fl+97~rt;T4|X-RP3c2*4W#`F?Oa(qKOSw$2(FGHwj3lxAox~L>fgVcD}Q@`s2)g*1A{v5We%4?tb)4#-X2AF)_;Y zM#p<Q`IxNXbWxCnf#6MkzEX7-NywWRIB zn^xvD+`KXnvt9v?Wwh(rm-LINHhiS1A3yv+bvvptpln-$y3u-byAjg&)*-`RN3+3v z=i}P~sy0n-=7z*~8#G_4zI|oRc_`AQ#T+=W%4;mT{JduuK@%fnS=D^fVvh&ss4Iss zUc<9PtJS@qYq67%TRfV5XR&BlU}b3mO2>`oV0SY*8>|nV8`{=*qshFV5O{;Nr#11! zR15k}Xs0r1yW3h$*|ML!^pIouz$6#=6x12~W2kNW+6)Byz<)y2U6H-YVc3x1#E{m; zd?w78kg`#zf-}&U`^mL-_@o3Ie-1BN%Sp)kGo^TEj^Of)Q;oi(n!@lFhZ-Baq^UDZ z%jY^IP)dKA%*)pz8KPjO74?jYQJm6k$Ra%0qvXCEPx`PRjV}=ov|n!wEIIeLhX_^> zpzY1gKo`q8^=Ub_ViODmvvK`4jyhKN`O+bhd0Q>l)suEsjMOAfYi5s$*UdLW+m&V+ zH+^OwnPF^7ITM0bY83WHQ-(qLt0x0jFNED*9s-hA(RAEQJbr(&e!+8^(RE|INT@$~ zm=f!$E7OEiT%MZDcO1)Z`dKQ&C(KCtJs4&4DkPRbNTYyy2}c15n$WznQ9q3_Y&TnX z8Uf>!fzm~d*5=H_ywS-eanrW+ROv`PQ=7^IvF^FbU88)DRqW&#nw8uGk#z+D-#QKd z{W@?5GRjk92js3}jN|z7u8Tbzt4RY?6`Rb4qNAYn2F2iNagkWz=ASuqK1SXKNAC`T z3WHYjSWElq)~riz?v=$YBW1TF!SR4Q2YU-F%@o_OraI7;lAH}fj%~0)hhFd-IQ1)^ zNP~ad*~GYEu^e^&!JnCExSE-2)C=hmv9+Q|^j3RG-IcSI{|}u;p`+iNB_>C7 z;}r0hpbr@wsLkNUBBfd2WZ=C@aGyt=HhQxHrRP4AAjU`c?g>=uyYra4ut#S>#BkC_ zQ(8KKRju8Wez;(ek?z2}qAX%8v3@9-FPz31C+wQ4yTvpl!{|5V@JRAtK)y1O#K1=M zJKs%e_CSlDC(azf->Rl3U)LCnM0hO18{_^VXR&U|uE3?OWiF@R-sNOKAabMQ#f}q~)_`Jj~<;5=UWZ;Ay>d8-6=n;I?E;y%$`drepc9>qaZESwGpy9Q{ zbQ>3c!Q9#5%Kb8IU$gya!TlnqKK5emjKh|7{f`{q-LTN0=?7SOXr_{2(oU(|l?lBl z6XJa9?Ne;6WXUZI#O-k#N-DGv_}+l+XF@_~LAuLGXPn2%3BeV;DEx|FTe1PRX0hly zjz|ofbmGB*9v@*f3{pQ??=jsLALtn+XtLe1n9T&@`l%{q}Ge8_De`r2Sc{rvN;(hDJ!hEc4NpZ^1G(Ut0^{0 z{yUZH=O6(0J@^mqd*Yy&WESBKKh|I~Qw};|@fI%+B5-A7tT6#fecrx3nSS?<0UZ1E zkZ-sn+rIg4%DchlRw!=&mFnY0G(z?h)D}`geU<71F$y3K@ghOa^h3VQtPi`Ji0-^` zz{yR7pbwZMfHy2dqVaKCBl(~KO`YEsJyIn&LGKyZlex_mCR6e_43d?=cM=UULXm{3 za}sq60F?UL@_I-*QDOMMHDwDB{7b1X>-ZCVPrFYhnP1~ei0bH5au@m051(mGBVkh% zDIaQNzp$G@Jy_KI`L@6SSLvXx(|tVD;-7Z?!f=RvZ$wbg8r1B5yT}_Y!Vj_rb&cqq z8dIMjdTOsuqjnX!aa*LwU*YzU8vLWyw?z-E9HGj~2SAK4s7o$rrsLcb=XAs z`}sH2D^q&%StLV^)l9W30%2b+n~gBua$x+AW?xdzw;iU$za4sML-vc77-uecqdzXv znm9S}D%6MlSE#QIUhEPWW!&3An;e`hX!6lG)c+CcGXQ|y|E?mbXAR|VjklUZ9{>6A zUrjtmaU(vf1PMJ}Ail^FLhn}uYVmQ$H4{%$6QqVe$c91ZeWH8ZB~ZpeHK0PkwaT6J z{W^q-bVrwjeM-LupcRKm#@2cfWkDSTM@T*+oI@!z7>V zMk9Y=-*!-ImP=$%4MdszIHt9{lHPRZ~-PjmS%@SVm>DcR&g zd^Vn@W%|u>ozl^)*<~V_m?Q3?6D8f`8`y}gkpab)T`Voff|CR6TEg7M-(pLE9X?L! z@oV*qhRi%mXn*u!66^Qb)oH0nd|fRu2Zk-8yhokRKM``-j;0iHw61vD*zFwr`_DFU z`NbN`Nif_LO*BmNCbZ~?|16;Y0T_G@8)bAx{PE@O$3fp$?WdWk}2YY zn##gw+Nu~W#pV;7ut%^P`;zCe?MH2IIKB-S*n6uv-ZjZvi59-^_Px+xb9=KHy}SO5 zQ&t>QOaG97|0X-$30`cOOE)Mi8}g8kN9Hc|h{v@>NDMWufLqrIN$O+HmEN2XoG*20 zjsT8hKXpZYOWF2QG*PqVVo8I1o1zB;^nMK3sTeG0ZWGZ$s-d%+z_7HpPLEqBv(Cp* ztFXD#T;?*xpagTFY$dp~JgD!j6|s<5K43@1v3&M6{Yo#@DvP??c=T}AaMJrr#4i@m z-YhuM$&WB?hc`x@QM)d%(Sxx?`W7c6caGS~EhI~WZ@WNa(?&F5s@1#UVb|(c1>bqv zx?d{FIBDb&Iw5bX-(y~tuWKJni89olww6cFnbrF9pl*GhcEeQP_YORy;y?j&IoGnQ z_`ZRK{BV^~j(kT$D@#726rpW4y(fbbc}-ewCy-7X+|~C)Sq*?3Mf_oA^Gt_ImI3~7 z%1M&%5 zi$1u;h4aEDtqseQ`d=_hCyy6rT%>-=?UQpI=j{Oc#mjcqYSwFUPY$21wudCG!v0*R zcpKs7v8)7FJen!rl*9BJIYxrR`)bi9d#dVF1+n#9>aiy+ zHNE@=^Yjb(pKMC1@#=qXehC$9uPxfS`0P>>kf5K$c(-Iay&`ws?$s8+Jd0?Jkr(sX z*v9TsVieYdF#wWq9G@0fUq)S%~@30_!o!Q}Idkxz0 znQyZ8k;4~XVs^D@1zZI-w!Db>0_W826qrMJFA7`6Zs5mdgfe-3$&X}dbCyVtZZ*m{ zBsZ>S`4*3wgwx(OuJl^fu9SspE4Z#nK4iJKUK0p1V7FP@_&BvQyKPkjojt0%$j*OD z??(Q4el_)2&1-EB|HsAmbP%iGK2cz{pm{MB;b^v=w{PaRna-g@3qKP)Pp90c<9PRJ zPrgO!Z>|M^)6|e>nfYFr_VCEDOADAUd0-Ya#BX|c=G4SRB`W@4YGC*FgHvgZiD=;C zR@vf4S)j7P54&ZDj)OWFX*a5@QB!vXNwgI=(zA#XxHoN{ZUUBJ!hK5+N>()+vGw9< z9~yNFxQKL(mA~faYH8l2k2*XQVxUhfsO&S%{C>J?JQl`1=HMvHGe3IXcfTWu_>iXT zHW8|`SXE3#lyX7uk5vnEg^|=E9K;N<^J$ zz@W|eQ-MX`eRne&DSI=sCKm&dY=MRb)zDQwGxd&mn)>6HMS5`ac91h!e@h)>f?Uhl zHaD)c?qboIQ^V?y$s-31NAaV#Vkw~mC2;m_U)N_>Est}C*xNqM<3vu4ePgP<@idKX zcyCf1>R4V$+s60Jyl$$AC7tai?xD(6d0^Oens)S-MyFgBM9yOs(aYa6z;5XyOCA*6 zRVhY2k}S34LMM*O~bfb&L6ip1(~$ZHwd}AArV9aZ|yy}@OIjh0hqV>i!rixHrdY6m-eQjy}6E_RJ9JY9O#h=@G3bJoE7=w1|+m2&!6r z41gf8^I{6$6e~-}V-h`4{c46QiB-TH9&9t|;@+@riU%!vE*d%UTOebAi8o@=O>msB zJC>sanZx~CNd7b}y;aXI;_KmUl|sw+*%$JJ90AQoh(p3k3%M7uztyl%GBbsu}&2acF6x(7fREmZ+p&u5_>R2PZrb)En_A+pVBA8uz-I<{#GY04?%Kc zkAZTdk0EgHqnFpB{ss9vBtGWCK_{{cFfXV>S-1?$H;%{M42~UpBMgTLt^ba6OM?U% zQ;Z1~)*AEomvI(QO&>8=)|Oz!fi9?$9#I5}6b_P>oK(ExqNVYz(k9ctL46VV#-HB$ z2Luk8{5DO@k_i4^pgw0d{)ftW!LG~09{m?65No&ZGkO-P{)kB0#|Skvsoc{EHQd-F zsF1g$dPZ*XN$F1%uu<}ZKcfy;c=Dj?g@z@rquWLx^C84^bDuPm)47iwsg66}$Ob@Z z=n12J^tFZQ`6MqiZ7|C0A`N0?C<+)exELZd4SSQkp72r;C_FXcgV2rfaf@DNT*t6q zbG&d8izH7ZvNssEJDLRJgBGOcAc5Q!P%+%cQVLSj?mdbP1deYcHzCY}q%2uZ8V6Cm z)~0UIWr)Iote?w#mrkP?VPPx}WB(t<-a0CeWNZ5lfe_r?A-KCcgkZtlCBfYtf?IHR zcM0wg+}%C6y9aw4X6Brk^PT6c@1I&XcQ>`Gc6GDp-M_jnTY(iFVZt3a!#u;pz4_62 z!-Qx8J7*<+AEDgK9#GKIX)SDV9^{pPndu*O%sd+_0cr3M0TF~)9DZOjnUhtwAFcRx zghrrmL&}Gqsu-UYkG2JC0H4e4nB91H)O843(9jJCzth~xIE0*jV07aI&O!+ZAct>G zg)zws->AqRQs)W~vwysT&uK_V1^mVvqAo#bY>Uj7lmxfBagZnv zVekt=k%s`heSD=#rX=XYloo7`i;Hi@5{ZgQPS>{x;W1 zrt4zD8(NntZn{>cS*9i$4Q=A}f?7<=gZKp-Maou?Ac!Y4){b}2?!;M2!TSDt+FqHP zbiU5F=-n8w<9?zMu-O;rBgcGrZ$UXS6875&;K#KCb9U*(T`$mSImm{bK|HM6?^Z>K zyxW#oFL#-c&Rhu*+gBkToseXI&#Ho|wTTKpNF&KsT17((A;1K8dkPWju^VlcgPpDN z6SWp}u-z zT|%PE3gaHBw~#ff+WxxdG?LHCWWP5$FdcyUSla$O)F&_{dg)KBH}(ln;v^Hc;Z=Hk zRJ6|?Gmq+3dz=s9_e&2R!HdeTR3AOee8`ULJ9w{rnYY`JD1>%5jVJ0%Se_V zzYva^>DODXs6eDQK0jDFV?=Q$c!z$h)saZ~9SU$xy>flH0MXa>TUvz3=FT-~+*#q}KlT;CYCu_(#7 zr5F)sZ%vOdLRTTZ$eGMMha(md{1=%cB4*q$lgl__A~b@b91+egUu&4QxIei*gkR;y z0N0lW#74gh6@Gvd?UJUu&}MQHOfit-;3tj=Gv3BzRD%?W@b|Z+iSZPO1ERS6tcUsf zjw&B@HU^_lsM%lOZKt6Zbrljw-wFWt;9ek!Y7`9HEHTwP72YC6@OBhCN!B9(ANf8Z{dg zk}q!F5@&gWAf?N0X?h9w8-o)Y@ausSdo1!3`CYPs25F}yg9Kql+z!TndHKF|e&VlF z{y5L-kkWVPG`?HJ2t;85kvcX@lCvN}0l~Lny4Vp636;bb>n$dv?cc>&JyJ={-bDVw z{U%I|0rTwxID)bl$Vo@UANx=K5FX1Y;t=T)5tXZLB=}AEK0{Yg zZ}Vrvhz@ym!H7OYe-jo`*rj2%!V18G)H09Sp`(uc1DW{ct7HjDTuGpcGZ`af?tN@e zDoWR7obcC1)LnQT)^reJ!BMcB9+0mjZ964L6#cZOau&|NdIm; z`(E>w@r#Mz>?Y>!HIFBtqQ0B=ZOlY>(-F~IZa1AaH>n7L6QW+^QEyy^D$WS4L16so za-haEnrT@DXzuo~Tlo^jH$AsdzAEU6ka$QatV^p8d$uPq5=V;PEk<_D+Aw+gU?wv-B z;HT4l@~g-1ZoAf@2;Y{yAH1g>$hE=Q%3iT<(xBbxk6n>w zygIk?`W{3;A5g~eX0CbOEI3*Wt^2d73NP`IzK=*}(RV*(LpAm9!3ExE@gtAt+_l%2 z^&Qj4l3MG!`ajaoJoJjKb%&9xOt{li1!VF1WBUx$D9LIoT26{G78CXat)0YK&zuy3 zvWYoS(;VeOIJc~uX^(#Qo|4i`ee9*-MbbE8nW&YV3?Zz0_qnL9JBtInX;aN@&U$^_ zrHr=-hmOgId%wYMc-MNrygz2mzjb1VA#Di$^L#z*yqa)W3|h9D-p>-GaC)`R@*ozA zdzg^|+$U)UA=MeR8O^lBgTyA$7P_?aMH*bnEiLA1rC*$lP}=hvcuB>qk#F`d zI7*0@Jv@0_&MB1jCpLWg>wl4MNqDj$C)2e4I0xywa!#AcQfsgdd4FTkT`ykg9+s21 zJK|BRn=CSqI;4vb4u6#sTswx|1Uz?EXx3i3uw$j$nG7zrqYSAGOF}2RT~u=v+k`88 z&R(g<)K6lwZ!Iuzbyk9Y8_is5U67Z(xMgfxe2*53iFkDHGAZb%-puE4)0>%CoWr%O zP4&x0EBFi}KfK$_ja|At-(H@?cvZj?l?bH{067J%F>0& zZAd)Tu!;0{v*P(=H*+Ejc$3MRmU>BNm{!``iZDFVd|6k=`ji6((V1{;m6XYiQ|@v{ zWm{}?|K%iHMT`5#r@4olJ!h;9jk3YT@J87EuM$t*J=hmL9aksdabASTwmQnv#Hu^c-V|C~nvza*^2&{SXo^dg_D;6C_lR1c+wgy=EId9-4Yj7r&r{}9-@k~r`GSDo{^+ann zOe{;1_1+#P`FyfeRVbeP;=tKZ9B86xm9{kyA8h=yX>m$&>K%?J16{^4-&f?rX&!Yog#TVK=SJ2R^%h;B?6}IXb5wN`hZWWbS{t(MB-!$5j5Vd+>aTag0g_83L_j(^h% z$^20)KKf#@phCQvmFo00F1{n1tVP4jJr~ty{=Kp1ykj_dY3v~>U(V4;b$f0n$6#m_na7Ld-BZ0FYYL3 zxe2X8rHj}Ori*cni+Srj^NOJ4oEo&9T{!syZhGvjRf-?Q(c=TYOhPGY;+obeKEo+3 zw^wabvMXE-lpu~DRT!#c*YTAWGx?ahWyu%xH0a+{WR!xC&A29t-BJX5&QLXcDDJbGkXW}k#$AE*#d2?bTzSDx3OGo zg)*&XUW+mrY9Q?u5x6T$}SFg(qbK;BTKd~2IfBBWohd=xLas*rViFru{l)F z_7l>E4j(H0Y$_1&uV?!55C-{$TsB#tv|?dfNV=MeZT;~fOtS_j*2X}gsm4l;{x>6` z5KF5<{AP8tokFT4@{jtX0Nt==e)jM%yMa+9H2zYpi;Zi_{l<>7E;40qwS=c^(Sxc> zS>77>0ys1Tw7~NVU1NEDL$vUr2B%VuGLj#t(H_Q5mfuF+EyD|OeV!Tbq2fGNik{H5 zGPPUm95$-MJ2&)RTOnyscE z6tRcv;Gzc8r-qwA5fmnHN##1w2z*>%7c$AYR49Rw&b5|*E%DmF$si$`c@W~oXfgZGVu}{I&Z1`h-imv5zuu^AC07gw}iN1 zxOlifR%fg`F_jMAOlx*vcf(rXq&h|PP}0KDrJ72xMNKp^a8BtKB2KNn7z1uRfv3!V zp5bv!-xwYr%2-C~nqUr+o_?ebik@&&VyOvKDBl8%0X<@U<8oHT_kTBNIj{{6D}?Vs zN);O{d?w4aR~J5#l~_+rVgreG0#YqLi7nk!J<2`+MK|FZR?}bCi5B)v_kg8s4dpgXs;G0!8@V zEw|Qa?9?BClBA&tMmAg$t7-kxRMM?pF2aoS_2wp zur|P4gp}KrHQ||>rY3|s-@$Ce%0g@HyqE^UxF~WEA7VE`QGP*naRvcXl_F*g7B>hA z2=#OIkpdBLe!&zdZ&_fgJa&TA|G*SX&A#leb_i1n8Xz2(et5AHO!#~?nCRT78$R67}c9Tg)m zNQ6G;!*?HW0w#nA+*9$SjUdv`hZN~;Q6CChp^2(<;!J;cy9zkx6J{*1>%5tYW58EK zx=kzlzwHFyH|CC7p;mKC7U=T-jt2-k*26%!VZ0~^lcT^(DCl3lE)vQDUW9OzLpC4X z*+&Gf!I$9#m%pL-yMqXP1hR~2&z}@2-pbW~q`6QeT3!s6m zz5t`pVDg`gg62CuT$xu!LAhP}{nRU?p!h!+g+IFdzZr#UcVIqPoaW)$dJ|(AMj4x9 z>=F%^5P_~6)WbUiJ<1hTgv>e<+B(M8fKZ2WS06gZ z3Fybqn|x!SW6b5Ao^kwuQx;}UW|x~`!Z|bOtCC@vcVe;5Hf2Hs)YDBPlsFMjZwgc( z5B!;)h|uH!3!G*Nf(R4F^}y3Qam#bbkmvBP|C>^H=>$krVlhA|y!81>Dg5ipKZ8U7 zfs~X5z}g*805Lh1&Gr7msmBbdwO?jSj(W7aJyM z*yqPDh}ns2n|W_XJB5wS`$jkiRN!8vumo}BE5Ujl2!uQLQVtIMWC4l5Pj+FB{C^bn zw28O4yaO}6p?ArN;OKiGPlbsLL#>5Xh6}+DS@J0#fX!?q5mupJb~#HX1lwqdjv1_= zs6YwE5CUe@FrR>Unz; zR|$-}Vi)DJSOV&8FC`Bm#=g{dX^;gF%t+oDgq^SZH(_7*Z&rMHImab(l)vlb&1>aM z@t>Z8Mt}1Z+*S7u1a@&=3s&cXboq)TOXR+pP2w!H(Mm=I7b3%tek`d6(Dkdp%kg!A%gfh+V?POcGmMxff12#8|Y_Y%%p_Opy%H};RX5mJBEv<;N8`Ev&PnV zsZ4N|aRY)hm7kFs?DGs%opDQjU{d#8qpU{P^28CE^0Bqk7z|ZNVG)Utwq}ZQO^2NZ z-OBb=uidQVb%2R>&JodET}Y=p)1CEV1?1vlz)?-|+DR*x7Cvk9^XOCAc^&yadbW3P zk}QSzzHBu)dX{TL1iH0CNR}YVbf;^;y~o+)pSaJ}>_y{^mGTnYD7I4eU9()^;Ioq& z&OeYJ3K#(Sc{HMix>+k~+2`A7j7m_ox0YBpY&Pc}nRVl!9D>$fxkz8UTTK^awv5+4 zQEh9}D?;zM?sR>4pxE+Y4v%KHfZxwPcVCc3%ZgW)wWDfTr|3QMP(YIPm@1*m{_4J> zVZ_z5Z_^l7X1eLnI>T2iYFC?E(4&;R;?ktq*;KHu9c=o{0f({>*ERQVAj+J-UKAWd-3Vc+vaDD}utt?Rr?_+~LMH~8$ z#tnA0c#)`bV*LL`{qSi^<#n$RtrxRgG&S2Rvowbcn_U08D;W3BCXn%MN%7;aJF6OO z%8h`^~wgkMddAdzTSt$}c3)EiO;vLKpI^ z@X!|utZkQ-xQ)?^eC{ag@Q=NM+6z`IX)cT!vLzf!69sXtu=u8PGj{hGolrDWYjvjf zYO{@x#@+DJoRaWA8?@L>W38XJ9MZ1oyHzecB^=5XOWfD`6|;D`$kk8xzZ*)cdx-kI zVQtVO6|aPM9QvxdHE)vR96cOGwUJX>nVFkdd>Q$IfeRBN4;=>1N{$rx&~R zGq#(|LYbpqQ3$pjYgTEk*UpngMN<02vOHd6z|keGo3YZdDce!;f!Z=%z0?8jfLwFG<=P?Zq?k&N!Z7dNaFXm9<2@LRI$&l&o&AvHOVY&!{}s2 zhJ#LvP3D^*H&p!|>3?uQzw&zr;~D$;{5Q0nSI5iEJzH^6(D}@(FGgW|e12DISzaEK zw(eN=>^S!N7%83C>A5Ca-r?Za>`V$&JpH!kn^DrMDh|;tWXDC{D3gP(w2T(8W-aJF z>dC@=60W0H8w9k#{Bm8nq3@fp;@Tc8+i-ek-D*zQlo569=TI()e%mlvhA-CJ3|&c2 zmqoI@yMeD%|Fmb2qLn+BF<&wN@n+M7dm?{7<+{MzaIdk+nOFG$LZi=um#ks&o!xOf z+`^DqHq$=l*NkS#UfdS#)2B(LjgBPPf#x$eR%bE1dEc$Dd=Em*_I3=f8Vd!>o4a*p zG1R@9IO2U_2dN)^e;zhn!Rp-KHludfrE0&sXzYfE;&?0DgSu>JT8Wpw+J7`u*PF}N@C z{|E2W(o4Skh7Y7c{aE(q%qZV$E7hYskn*OcER(5KnbthY6sJHHFaE9TG>x9q+{rY1 zi-JN8J5s7zgM;el==vG+sqCE;h2gRUS8mdc;Tl`bR0a0kuQsw3+~inco!l(BpITOy zgc9RaB-r+Hl19o+Rq3al7C-+Y5L@J&N*#c_qVp@GLNO8}&7XHhgrA%+v&O9H`DNA> zw9czRV+c7WXt%xgKoO4FP3<$3v>`+}vX0YRFY2AR_+XZ*4c*BD?@Njs`a1Px3xK6F zn3~ndXT>zX)Pc|$E`V@hjEe5?m`NVB&}pSwI$_hoFvsilmK{XqOp}Ujhvj&?@9Lkl zca}k0!_+uvJ|2hUsaf8itzNpTG|C*~o7bLXp|}TAeu25LWBJa7ak6Hl^SFS1&sPvb z*fd&FfsHRFRn3Hb;_!lhWYB6-?tAe9$VGm z%Ts4^!kb3(rF!~;7$nQLGJmwZC8~KVEOvj-BA?jjh6m#asTEugy5h z&UZYVN2PTf3*I^0Eou5y`6zlO@Bn^w)_t6ltxxwJ40)|pXI@_2IQIZsM@!~XfT3cDtoF}|ubS#N%S%d)gL?flu2;FC zDjUi5buoWDE{APQ(>=UAE>G;RGSmL?xIA#Wv5=kNX+?3lTE_O!AZ54+6uwHuWm(iE zhGvkeYPz1A7<(A=tN3bD`aFAWh(_a(tG2rMj5*HQmiPXq0VW*m^Tami+mvgCXS4TC z(Q?<$0G23I(;LaDyx9woNdnPcO%|lmT1`MA_42-iO&#HV3>~t(bsH*ApeLM%g@YW< z^+tR96GJD^7Di0Q-TO}@NXjs-Whg4g4h=!PfF6Mgf^hb&pH>QykzhkbaC7D)paMon zIX0O6UGxh|><|(>CiD$>>Ra#gJcY5Ly+hNa+lP^8(Wi?>+P3Z7>6%XnNJZZnk@PM( z3q!d0S>U2&5qjV9e}Z)Tl+Tm_(jh4HwR)3zNQPLLN(aF(fnEYLI!RD;n2R&j{e|hb zyMAH%Coy#eJcDE)7`8JnJV{o zG2`ndpGh;QBuH?p=zX0Xr|r^#%+j}aDNvBrQ1Ynj!p%#u|0ag=|3M7tVr?tK`sHMT zp!-rt^{I2FwhV;UrO9T~@QVlF1tB8(k_eDX*1!E7!Qt68_nRqY#1~m9DuP+p4(ZGf zl|I6?8)Fk8QHHD3UUVG@?4txu*qNBEBw*pxhwkgc>M+niU`d}9VH@DQZ5PLp`Y74M zL?*C%UgAZkEO_+&J@`WWyTHsTrZsmXO;}fne5Paf-8(NU2+XWVdC@nhoID}gbASri z=8Fng?j$DEw8&e;B>AjVL~v{029O?WU%^{BRSAfRO`6mZa##2KH}s%$Aiw$#me45Q z%%E^d7$ZQAAolJ7({krzzGv}K{>h1-)tkg`z(Xp4O#j8#wc(@w8y;I;=1R^#(i+Et zg!I9;QUF7Ot`Keu?+sc9ToFHJN4=uJW&|)lP`YeYyamKf?Nx6A#H<}k1&McHeo4lI zrDPqz_+E)2sTX3%kR4b~V1t0&{~(5>3==2+lNdrsitl#Cc#lAcrZ#5<-2zNm8O4-@ z`2Qe={u%Ip5JP)*c?^?1l)pkrA`YykXwFiE%(RN|RP-4pL^g)@s9&cJ+A?cqpNco$_I%=s*foTq!@eT4)C#C?{T(Mj_p zf-W4u)_}=j($L!qT9tW&N8qUUB6JRj31G;>8Eti8M+M@ey%0KoZ_`?6wE(lrCws;s z`aW5V9GX|RORd>b+o52#f0?b5Aa=~gw!BE=Afv!JyQ3js_PmfZGjBu83*Pr(hD5;D z$T;V!j;SxSk;@?cHfsk4LM*bEfj|OrM-vF5LLgazAS!3c3^9RZd%*&EVM%x`{ioOZ z&jVI7VPgrahi#0zX??BW3^Lb_vLy>2h3P60d*i z22_W#;A10=KY7o5>`^t)kZDp;m0&?sli#H_JcJ*6Ocx-cY_V^9-`ONM+%o-)ArVKUnLkJ6~u3B#9-6^==pMIAw7z}V(3lKr|SNs3l z@3QZ94Dh?Gkk=le5Wb}OmpqP3#V8$KxBtZ74A%k5Mm9oQjnc#lS^;6FB;pJdS5(!Vk1H(l`Fjl7eh{rHo+BBtfFSinP0SFOHXl|LJ(iBPL8SR4fZP zu3uJC$?0}Lx$?V{|LM5ixNa#*^fnY?XG9!#6N4X(AxJx)u`^@x2W;tkX0;GL#uksa z|FT>sJRr%7(e)ulPh*5S4dHyBPz4MMFbf}jwG0PZNJv}qUl5h?;7WwgLi$7gBskl=`Vmc^&O%yf%B5ctR60R7s$&l$a?0^2Y#x+NXROM zf*U9fRFr0)6d5|1x%?Yq#7;mSC!ql4o9?wvppI8_-IPnX>aMup5n#Fg><{?0M`3p7 z5PD+?5o8eHxVEPdtcV4qzK=k7vl=Dbl>iB5PR^3KX3N?Ka`)9blwhY|TCjD2qGL@x z3Lwyq&)EOKZx_aO8~IZNoaTFIArU{~LIyZct)_AdM6gzpzeIKz+S6nU(uJ1& z`hx-I_Uaes_9GxW>yWP^JA_i_W(LL>B+{upFv?n1I~0B=fQk_Ja6SNc`Dn5CWqJ$J z|9+TKv7#%?l2RFX6WeDi=rzSkf+&x4c29s<5d(fo;6vlD#Ex|hizCczSJ@*V5gHu~ zH3Tqj!qP%c@a*s{Y^)QYkC*d8DbPP-vPa>Mh1g8xkQhi1`MiXQy*zG+i5`X{1{Wu< z2hExsv>l|$*YDpT2ILx?gCUM)Mc_}{%O#NgDhEd#4+ndI*j;@i;t3$evV4p{MdQM-av{mCS|`9u+i~=fX%uUu!8V_m`sK~5gHsk!heSd za=X?SCXwhJxcV;e)?OIt{EUFec1}iO7(Pw)895LmX%|0ZLyg~37o#Zso$$kbi2pPA zx7#`by}7ujW^*Va4}`#5b=AG+H;8K|;E0*`kq7wi9>y2(3GW#~$5DhO1-kxsgZ?hv zJMh_}F^^y!zweA9Fu^ z<91;=gryRCCtQs>=f*~_KIU+L&@GYB4ZPMuHlI(2c--LVg2$%EA0=e`SpYE^;Yary z=I`ZfYrwxk2rd#D@v@p>i2*%HdPlIeVhxsGnDdnTv*e3`#YrEb$6Vl@RoC{0Qa)dr zxl7tP+>gJPwPTqBKc4vfY+tK&{RGGG2)VCKHzc)LwX6QVB^Z=z;*H}%$-gm<#+4~aBluVs&aJ2d%}?N zUF7SWPv+fN_RlnPw|Nf!!u^{LB`F0nvk^9YhFg>sxD#Ewx=o;VJ5SNGlrfc24L^!? zr_dr<3d+71g`C(WaBRf7lmQXUnleg z6-qt|VYs_0*N&85b(;vyh)CQd;1H39)5)NttRRBLKi5D$21aTiV)umqN^|ZYag~5O+cJK# z{gSL~6-Xy_k?sTYdq_oofbdDNz0F&w`juFfhUXb+zF-V=#xAmez@?;xs97*gvk9aK zxJA#q8x;U|Hz?Ud#aSPme8`7*dilU-_nD*X#7D@@Jn0$d%a~zLl2$t*@Q2GY_?PzU zhkTrM$96&w46SIJK+n&oKu;$#JYKpn?K?53X;cFAnIms*z#2-GMfj;Z+xLKE@tMu# z;H+!Mne)O2KtSFd3kW|X6QZ+`Io?(-NbMxkZT~7+OxZTEf6xl-n&v|e5;~_9CVJ0E zf+SDGK!U`_5gFKbLI$X_26e>%2I&Dg!g}HW)w*CTPMr7h@wM@=It=D5SGcyXK~#Ga zYX>HgOCAkJNvt?Vfp=Ti%=o9fN~Xi#+*J;CoIAh~AXg$qg}vJm*!A96ze_{!g!oGc zEap`R?53HK2(lueUP#G0&{t46t>I6170QrwWe>@h5dlqGf+(}F--;k;zI9*ZuX$4E zr%j~7$RRZ^W-#fU%Q|8t)d=b^XC3euJ%I-?^) zQr!iVz)-kHF}#GKMuXl*A#C%IiqMBa@MDB8*Yw_LLU1QA; zBswKClhqPh>U@8E{%bCn6uxe{^$FqOIIbq7ojjPdEsb zXfV=%dseoYwPxW9S%0uo9USE>?YLT?Sms;{C#4wfDVmUQb9T^BumN^Dd!{O#YlQBw<${K}14xE&dOUnytDB%_5inAZGWD&v55w8!!@!a8na4IaPBKjNAeqzx7a zN<(qU_7)#y_DS>Cn}6?K_eR&R&Q)79EYjBJ>!$oZU0KlZp!6Efw>9;r_~vH~pG+(;JBVQ@LjsUw!RoDGPgtRDD0kb2N;7vKz`=qkgSlX~Vu?4C)4sm`$j@qg)`#B%4O`FN; zm&#JH&h0p{ZBoaz?l_aXg?{X5o;z|KHW({fyw81f)(a#vcT{6+#J`uY9J2bXO`H71 zrT+@Ngb(&pkd52IlBKF=OTqEQj+tyc?ScK7tlCC1<#vVzbKMo#=lDtXJ$swv)3|Y^ z&;u4O-f?8Mg8JlwFEYh&@l9W*5A37d!%#!Ej#^#Au`jCFP$xSZ4mB!FAElITz%A** zMb}4)YBVp@v# z_m+;}ruO9Cwusq|FPj~itKkLR0^SB(^Lv|(DHb+$gJvoiC3wra9@wAp@{e0%ZaFju z34^Q7mhAAmCCY95y6-H;Tj)lw+Nsk|GM~!KPFkL-ZRW#g1HUfht2a=p*5ynS+YByb zCDFGhePUGELgsDQ&Ir6vR#UZ$_vzhd)UE=>9L>M7zAhoLu=qXfwn%GeLJ|IPJ}J54 z)MgB$C#P*WCO8?;>RAWf-l(CaJ94qN3DHF|aDZKg4zWQAE3;eiFV{@bjG==tI63%S zd0y8YSWJC%b2I+*rJ3O2cfkn4l0X$(lL(WR&xBDWQ>o%f#hl0>k45|1soa6($YLCJ zjbSLv##4POg(aRF&!_mK4xE53*NJXfOtd2xtXZNT{XC2Z%LWeNOs5X@4KNLiV2ssW zraxw>(rmBD-?PRSXS%8svGm?~kU0+}VIB8h+HjVa)tG~hE46V41>CJ=Bn_#37rIk> z2WF_`BPk}omhYidB7IN8-r4K=j-=P!op*>Iopi)po@099u~aX>Z{_Pw{7eru^jF2X zm`w_4k*)T}C9ey~d$shDxcm#NPr9Gs4NLbd*tzUY!u5MUcRF#HpLe_QI5tnzucV~Y z?)XSseb}oIh&+j=H@Oc{5*m1Nk!MvBdAy4ex9^)~?Y!o$lZ)Mt3O#=8xtu8KVQtll znpYuCE~P^QG@v{}{U<=hl|ruV?RkdOb~xuyOJT}c(%=(fd&SERXC|(*GEg1}v8U^- z`z3?p?^eR=~9fJh>=qRv*z*nhBO-L~hNMOD4e7tZ1eTI%;Xw9^ARfeO3xsA+na|W=1B> zY*ZjvUeeKb647i?BfCRHEfN=wZPIG(i-Jwl^#y0g>0 z_hZiSczQ9b%#CB!p#f{I?%W@c+ojIi>7BZC)m7P^Nj)N68OHWLYV=TD>TJY3G6mHL z!HX{({?_tENt7c9VTF}Hiug`kw~l*S-4UD4*Z}<=t`8?9+J<*ue7cQ}^5%R?W!`>a zQIo{twrD_OdEe6`gz+MGex>X1r!Dne`YIIlG4Pi282gYkEc=tAd*wVvdF0%UAlz~k zS_{oDkd>1o@KG3%04XZ7DwN*t>{v}$40^t2C3f%rGR^YfUO%{{!t)DDM9a9&gUfIp zyHx}2!)NNMi{;#UHOZ6WMOu{j?0xEiCR_;vXa4Mc9!s?QTHl?6WA^G?3v(BIDXbZL zB`X%Z5&yd|ihXff<^Hbs8cU9F7uO}4G<793_hwhtaX_n|7oH+RXrL1X3aVAY; zcXwrVMLS2LM^wEZZz`#hhg(VZhd ztLgwL;-H$#%C2m@ThK=-w;t|&K2y_*RZ}2UthlwKy42`#BejCHYviVQJStB-oUr|j z7-|aH@Yg!D)%Fr>`0^}{rG%wjx0;LNZKZ*4;!H!*Uy&;p+PTe+1$g)ZRg6=beg`V3 zAGtSI#T&yL=mvh&#=2XZn+h1*W{3_#Y7HfUlTNm660r=hAnn5Qe z&7PuB>Z^%WjS2c5eI2Nx77+Iy0jv4{JN6| z`@A(u`Yess8E$gZ4!wp#x!*7b9Hafv&8%(g_7EdV_JdC9Y1TeH>()ScbVpG~SP9dQ z?+>|lttda$LL2=ymEI3CH69^x9n)mTD%;I*(^9nzJBe8=w>MTKUq>Bl>U8n%DLJaw z6m4ZWEiCxqJGX^@Ep|6aOIFwCWa3FLKlc&0nCri>@%@y1SmRckD*id@f)wK5T6erW zcDrZ(tNjG#q3M-IaXq!7I_=`c_$@T(d9R-b%kdFqv>Z3twVFl`;a6L1 zeLU-8N99^Hb8eZDkptGLW3hbTA$wt!Bvqhr=cc9kgU2dQ zHT^uRtNZ7!R&AA9X)4%8WG2kRVuCQ&!QnNVC0aLEr~L|DjUTauD>Um&at$kowLXoG znghNLwcYQiqK1mzOEhrP4$UC!=(syuhMsq8k|hen;t<8jr;eCtBw7q6C74v ze#oT*|7x#M)fIgFi6fBsu*DNbKqGAeetEFzBfg#fSZ7sIbd%KZX0)b<3FD1a!exkf zdON3RaA|kw+T>o`R8JnRwlE5cRul&w)Pb9aYr!)7v=XnSWj^$r64dDgTX-!VJYcS@ z%wRO@T9t;evoncNA~Jtw*8+XAl%n0Ui1gMUr1F5IxS;H0x84-qM2Dl$OR0_x6IHxP zv1#1IG0sf$Sc$XSh0#(_~8lT!v@5e|8tLWMMeI~@hT|Xiv0e;aL+}kTxzVP_{@OFUjPPVg6WA@%O z6>N=<19Am9u53Qv^m;GJeV_QYH122(U!^u+uhPBIES(ebiABmAT-?X&J&t~N!?tuj z0lLWtkO40l<)A;5xx^xj=t>meJiD_J0bAyXKW&*IYL;xC!=$`lYV%x>9xA)|C&Yc< zXGLxDJGfcIAwT%JCKc-V8eyD(37Cc=@4}{nr91N|I27gv_(NlCQ)Q!ZkMb}hF+oPi z!9m{pOL;@7`lUVa>Bs!z6<*AB3{(;)q=$TT37uB$xQ@{Y; z6`dWDvw(qK(nZ3He?*@SV~srr@9o}2jFX_yZA`wDcVYA|K0$bWAtN!G*~{Wp?{g`k ze;F}X6QA;=f>Dt2ORTX32&!U3q2}YU2L{Cn1RIwV+a5~WNt9b5RpSIS7{_|L%t9J> ze%l=be3%7W?Eg~bihfb$LNx$XxtjmvT~`3!RsS#El@szW-qqxvnp`~jfF>7N`{^+j zLgjyGa=q}bP<`@b?Nef>=t3))`yF3cgsqCwq!viOxc69LFa&?_Ph>#jIFFO^rVE(h z`*;cr(v2hLiNf2{FFFHl#J9eEmW5Q8pe7SrAT#IRl3bjSc~ahO5&vD1%Pp-m^GKZ& za$?Nb^#LW?AN(UINkEV4K<+09YzxG8Z^FZs39-}8mk_^z-TF{ufbDX^BbWdk@eSpW z)Vd)0I{D6!;7^6IUNq4&U}RA5WP4)$GOw<64YGD>GJ(DPCQXGLRhD%L z`H8d>BO<1aO-)rGjzX9jAirS^B5&vD@2dPZ2h= zQ{ALuK!u4v!w|u(fC=Y#b_V`gj$9{ukBIpENU(0Z_*!Aq<|BTGsQs3Rh@v?Y%oR<6 z<_y8FR+PaUaRW}SBXVnvzraHX;cQDHZxta(9?1j?Ecl;}G(tQ-VI(g735dvs^!IqG zbV*cFka`+X{Ebdn#^WA?#`y4r-R|`E*qS++brgJ!`_kA~nVjBm`ytUwXFz01 zp!a-#jFq_^b=kFK*YSmLS-?0>fL_kGC0w051t%cFJ&jX)VO?_x)_30c^5R*@(O&A! z0UOwHvQp%cQA*$ThzI{=O9K$8Lg4?hr3n%q!Pp3Vv89=-%KI1Unge+*Yn+*m4tImW z?iUO4r!5Wen}68Sh(>b4+{@^8N=u|bWxwBxpyDt@SO<=3KXIr4#bXyB8F!EB;3t^x z=lT)ql$8kbn8B1J_6yh0xTrxc{~PG~UjWOrIS7f3c>(;N+*4>iZZY+O#X&iMrI#@Mou!oKU{2(5FPF=hWp*_Wt10V>K2-J2ET zoHh)@!Zh5k!%Co4DKM*$QS7Oq#`K^<6kd>jT3_)I_|bun zXqvWu)un=(z z?vm~D$$j_cAlF6B1}drV+;gnw{jA52)hA()_ly3IEbFH&Gy%NfQEgXdAuDE)qko(}U0Q3^c4rxo%~u=HeOhrLfFR4x1$7#>zWr-3zapQ(UyA^;#~~n(llLDKN)H^;nU6Q z#UyXH^6v0mU+`%+7%hi645T!xa9LtKbWtC&!1+zw zFxr1ynceK5Rkx1YO0w7y5ud4!eiANa?2IGLI&H!-YhsxF&{y4^Cgso7QoeaRrLe>6;~=G2t*j#3DY>xhrvg2T7qSp`xV&o3itGudrq)Jb@dP(kvI22bMU!h( z6%+A^bkkHDNjNa#Vm6B^D8nK0nmvi4n$VL8l}b&lAih0yAH8QRLVDF2-l|C(4wGOV z`9u^|u+Ws_@bJ#e_d{(7DBxU3DqiB>Tx8Bz?rrSbjolwxS1cDti+r!Zj73ulsQZ1) znXUgRmi6xY3IVIEQKi8pQRG~zIIrO$p-9u74IioB83P~3b$$m-{*HBPMZe+~!`ZZD zsAYjOFsbnUpd%wQsp3wI8>z+yvfR$bkBie-s?>C1V0|oXSJOhKleN$sKL%k;Qhw)Y zA2L!d9$hQMxdd_J^sv^k;}5&I%jCos$BbodQR*qg`*^r}tTNx{;}jLs@UDybmn?qh zmo8Px9$Qs7^qD!^Trl}gT#x0loM~2AaJ|u+$1-w@oSDUhLoL;`kZ`i!An2K@UUjd} z%UftxK98&1tyk^IZNtpQO&&25H^UdS3(~L?yRJ?!swDU9fY&g{3*Eg#pj$(``b&_W`CKU^# zo}=OCJ(KlM*rV8^p8C(J9_BM^&vSdf@wrN7;+FL)>IrxD*(K60iU{8ysqRG4I6m$Z z5tTR$3VJNj_~UmPe7fC=RN9`j=JPz-!h6io956rM@Zj#>G5EevSz8hpGHY*$XEAL{ zY9b@IxC32kr5%45H|=JhM)$S+u7_Lw29wvw?9=?mE1TYVrbFe6G3B+a37ee4pIw(H zymlB*gq7;HiJuz-?wTH(`HyH{Kb1wN_E@y)>WY`&`WPKtI_y;c4Ap9KUZ=|qI0O(x8)ci& zSn>%vp(AT+N2>JSUiExAaib+fCTzRncMh}rW)Iubw4EyMj)!n50NM8I`1Qc4MW~8y zmLps=TT_5Ot~}HUQO=A_v+B6WYAn}jl+aUoAS|OWRa#f08jkf4!|$pQQ>=|T-8r$y zE<(a{z&!ER6h`U_QaruuXYm+Ad!jSX(f*S;KZj7XZWt#Gfhp<)K?Dk zUgB|KhAu85m5*)r!C_ft!>zV?;OVhvn7;UF(@kKR8VJ2?Yv}-0u&r+YB@{tJ4!*qG z!H&~Wr&5C*gKCDyk`wH9E9VSV$5S>@k{Gn$xIOczGkhy(uU#-Cd2bc@WMn|w^Tkp%v*od%Q44FxV9IIv zMMKR2{H&#f5O<)o@CbIhg_5<@8`P4B-R`SnVvLO%;|Q?lHF=8l+*=bU7qZ{FYdMYB zJtRKlYF1UP>GM(u4x=7iYpO(Qg6xJ$8DD8@;2ZS-Q+z2~><(K2UZ1fl);b#Ye|K#& zrQjo-vACxly$BrCCLwHCw&_y(-rIbl7aOgIZ!&kTZAO$zoWbp0vavC{dkSu|gwIwF z&!V14NM0$J97&bRSK>+t%ys$Ne7YUFlo{#gq}Pp|{(FmQE%(JtenmuWvJpC^p2_qv zlm-_$LfloG;9{d0HfUDj8{>%_eU03vU3pski>gVLVi{e~_xfMwPbH4CV|kw=6I)1r zly5eVM26d1do;`qlz%Z7m0S9K2-#;`UD_|l;bc&AI(t{EybT@kO`1ZD&{*q%@g9ZT zr?ZCYBc=CBx50Lo=I*AG?((Sdt(|nDQ>XT#7r#z*^OD-1bf~& z<;bq1cyG-jWFbe&pBi9I(~1Gzy<2Abs9N;I(9(xbh9W}uvAbcFyVZ2#o~&3IbCEs! zp*p;d=xcaAlc{|HRO&Ym0RvCNyyN6Xh;Tpx+ z6abQiij@RTeqpo7d}cQ4^%Dx=WBv|I9vju8Lw?P1CIk^i>I^;i!+ZA7)TwPJOULi^ zMuIbUv% zy5)~cfUiU3lip3^7YjUTh_AmkEim%UL5Fzy&4EcgX`ZD#5B%~3qyk4=U5=kdt#x))Ip@yuA7 zB+Lr_uvx^OB0;r@nP6|*iJs@c4-^Paemy_-l!V|Rz9BI6atHbcRd?6VECo&71R|Lv z@XP)OtW{3|=5KS`RdrGDEo4YaQ7{h<$He}N1uEq(1tm)`-k$F|LB!{}^LgU<>Z^{D zwmT$A_7W9X2X7*_>5mScLRehF->BB0|3$T`P@&~uGJ#lSlJdhOWl8e(2nfan{yYq) zfw8TOHe9512aKp_B>j9lu3I2X5>Yth@OYtQ$t*4uv;riP;+|gLmDDb?<|fIq?MuC0`h6c_W=LZ!3+8a)r!wMjRVEj zg8@;H9mU4B;mb!$3)Y#gHA%UgXEnP~Mmv}!Wd`_Gk--G`(q-hB6GT2X_AB$#Z|F4#QjY-Uw#<3skho+Vmy@ zAEL4Ackw81FF4;nQg~7SBZUX^1ilFv&q|9APUM5ypG1Nm?786G=wudA*nyA5ra$f_ zgvn6>g6cbY*J6UFNu{akRX9NB@mVI*ww=J!m<#KLrSRhw9D;7nFf z%}YsCBr*T+$Kt!$6no!Ww?W57Gl^r$3QGgiTd~uWiVG!S>W+eQw+w{P9+8E zWhCeo^n0Bv*E1almI{w9AH?=?-Gl%XJh$M=J2< zKwQX647N{uAd4?~k=SyD+4f3wn{QdyI{ z!vLMhr=cdtfZpzX6{P^Lud^93AnM?x{|*noa^xj@-*}Y^z!s$v*Z;5o>;8>MqkD)0 z%1HM96P}&Bz&JP}ssEpgslXRMlbQ<# z8ZhtqJ{B}#`PbOIU!vlG9?|Kn^>9b zH8PPvHmmXtRjCyN#uYL^QN|7sPa>u)&D?|5MiM*-d#&zD$}3H~hVD$Kw7Un^g4 zz)d72_&Gdene%VaKg3_L`|i;HA^O+d_=g`H{0~1E9*=+v`zjpK8Z7z;VO<VjSta1hTD> zSP$@cKY|Zto*NK!>;V|>u&AgUL0HrUB0U3nyHW5TeIP)>!eE5zj?fd!H`pj>6m&dG zhWPhd=0|x*_3(;;1SBV5mAyaj8f5=L4zB*k5s=N7!dk5z|t8k5&0Kj>C$8` zMMntG{=Bkl4R`JOf7VPq@fa?^!~9ciLA&H7{-0`#y=>*bFaAFGYkB<@J`)5#T(P}T z`-0cKH@4@Woxzs*KbzuVUHT7AI2LrkDZ*eazsy*A>d^kX#$5?@{bMi{#1plK6Ui#2gsbK!K3ttN7VY%+n7#6puML11J*)98TeE|HmZX^$+iUVqXGv8ZmW#Ti zNbAdXE*^`Qcb}hh3@a69$HciY`xHrzRCB+785M2NXQ1U#B!|^W7zd5cHqvoXF zvtN+AZPKR2K09LTc9P`AcW1z6e{I(&)P(V-!EBkcqt_E!HHNiH2)Qvvx??ZGq1-*H4gnZb)MAO0a8_p z#?i0K*ZOVNsm!ayDaU1+VCOWI53xln>vQe{vMmsw937HOs6Q3iISxIJiIz68xA6{R zJNKya@t0b(#jP3=YxI9>!1wwYgFFyk*vCfbe%$sg_0=)sbOPfyZfi_>ds4>dvt_IYWX^q zb)g^Y4V&otlo!T!?=GiDvFCcAS5dgM`86{mU2Q9pMURD!$3mg%Un3SMxaUGN=UyF} zlT(vGE^}H$INYSGuCsb8r6GK$<%NYhCcpUD?>(g9LW?{8Mzsi^24-=ZGBl@_x5 zqGkp)SW@z)YbDOeo;kG;+CA~E652`5W|1kf!nIsg%_~GA>+ABW;_++T2IOH^rp7ml z%k}8jf61&%K(EoV%DDqHC71O1O}Dz@b?G-K5E(g1du3#Ny)~EKV%5Mh4c9%}!r#$7}91wR5+(1fc`^BDe zi5Djpn{Ha}7TL$5c{XIsp-vF9>PHsu>u5wEzkIhdGZXfjzTj7dx#?Uyp+@ zl(T36m3eJ3Jzl9>1tH`1cmbcf%=OrpQqOhWijV6$7w;>_WxRYN=$p&mdW;9a;BW8?TMfr!)=l`56~f81T@PrdCEx*V_|qxd9fh91bjahO+&B zib{pV8>MmtMM?^&rQ}rW#LqX7^!ttIRBoq)?=$FNCXN7H>AHF4of~A0rl?~qN09Fq z)*u-s9az#<&C6rd1xjuQ_&N>=j;nYqIL+&>2H9IKZ2T`+0D<>bpQcIuj*V6Hhg#z` z&Ipq)hXXV`+52GGL+Ep@oKq}KCT31Iex3}yAdO>-?+n&|RnL5k^`aUQw~0A!9lPh# zVi;;ujgT)LYqE)hHPz)bY&}4XP|bK4CF3xdnaI#I?XxsdBdQqw%G`oD=5H*O5_vZFWXJib_^WOu8^d^W&)0y|m8wYhQoJw_ z)Aoz@?a(*wu{b-w6IBL6NCpc|KBL^~v*>Coy5e-YRuEx2z2yS-%oasS0u7MBeRr>> zd7U3F5)c>V$ikWe&$t?O(tO_?j`gu*Mxwvm$@~#qoJlV+EqC9b3b9ad2e|{_J)KS& z2IjPOtIeweTZD0|^E$WATot)FJPYe-yh~cPx!fEU62Ca*6D=W{wHan64X=dZ@}-}j z`b%@+$(_1Qm2FBIS+kzl9C)pL=3Ad#k_gj-)@FHEwM#Oipus;(W5l}(S-N)0bBOu_ zQh3bFIr<&0v+=M@XslN0Q;}i0NdDe)_38rN?iy)ajr*ks-;R*SDP}=vF*VkU<;G-* z+O)P#?$oK>qCkyeZi`|Lg4~5i%f-y*gM?N6rA33va=`e6bDJu^wYZsX^=_X&HqCOg zcA4&7yrakYF5y^jwIW?huGzNSgr!5j8_}MDzFNSO&2=N9S}XgY=x&k9NTW{aton-C ziv^WQ3_^AGi`zQFh>*IqD&Jz)U7o?uOWT;-zTy0uL5m#478>=sa=JMP2-}u)^?ast z`J-@Uj$d=JXC+nYqv&?$VVZ^-MKNOrL}QoQ8+JB#iBV?;@9@o`2Gx_;%Cl$KtkyK6FS+J&*4$XHdKf_lYnE7$pnlO-OfVvv$_%YxkN zq*dd0-o)~!Dn6RZ%kPMq<^12zVk{lLvrCXY&g6h@-K^<1g(bDk@0`Hk!A)Bt{=GiS zY20gR5{WSjTAe5~@fmyc1<{qVNH}fQ#_-|79Mf5qATnDPs&I@Ju@CkRyH2jda3kHP z3TJY(r!Yu65Z5oaTKU|y7Q`t;W#~F81pNg$zc?iIRQ1H?VI#*fkLFdKWbHB%ov)|9 zEYIXL7%ix>W1DQee8(G+vSvL^zI1w{qFm6_%9pb*y)tx^OljM7&^%JX+Z3;Op*sBX zG|Tr<)#SfmDk?xd4>kbKn#59j+&d}01Too?f!8hDG|a8%;;)|;d|iI+68hhoW|!qr zj|`Aa#WG+N+8;kB%^nL~&n*pCnAA9$VUM3+Ofi$!DdEMjMC#R&;*1e;p4F9C);==r z<~zPXS{1%-@ubP7h95HMEE&~QP7Kz4k=3Lp)%=jy^z$O>aQ;jw%BJyhn_JH6STBY) z&bizlF(lurCR<2NTY9zYrC2!RHq+QvMaP<0`}fsQCeknEq4Glgrhd+pGFnxzQ(OA_ z&`Xp<2Zrqfpf zv{gZq>7`^xXE~lyt_}+Vw^G}D&GX&RAc-n2Q|qy+2ClIW)r03eC3x6$$e-H`)JI+8Tsh2TDBW&U#q=P zucN2&=_JN@f34$ZbAw2wcbuD&gca`>?c$Vn2F3&@Gm6ZUD`nEIx_RBWuek$W7!hj; zL>D=?9?LCg@cbF4Zl4-&ir&YmRmm1<7&OHim-IMRz}(X z`!^eOsbZkaLd$Otp8dqjpZ-6Lf1J1t(UctX@|^ibL5xW5t{vS`^oW}_m~m>Jj>nXb ztV$KC%Nw6c>IFO5GA^ka@tv;S(fF&4$OUeAv{qp6Zk8-+EjoHpSpQ}pR(MCZ zKL@^42BnX^+L0WZ>%P9tUuO#FtCtp!FgRH~9?!LA&OboC#1e5ok2tn<%q!ZwOhou{ z|1WBV>R~M5^bm|%Df|ny@`g>n{MNt=hew6@p3o)?b3f$+-ZaSA_npzZ+7O)gWxyju z0;u4-Y=7DyYBwECEkNNn3GYB!pV3@IO?MtRC7B-y-R4LEHS^Gz zg8Vip==6RVGdhxcICj>DRNuLg>}P1sqbY=!Y>0W-+$8`-^`z6ZbU)CK=n0@80=bHq zMlf$T9g%7{5AF(YG4h!3;bCZ4K<=IE(~2mj$dGw!Ea*z^3h!lXaSU6D45!2RIpad=s}S{_FXnZL3!t9a3|pr{a2 zh|BeV_{inLNrHXkp#D#Vwsk|UAAilz-wiSTJ+wcCE~+FRxPl1ZzfOP;y(9^syeo5w zUw2`^K5|h1gh~J}P4Hqgqr;TFf%!1M|$iRc;wk!%236w3KToV6?&?n-kaY z@1N>^-1mRbi^`2clg9*-efQblB_XKYLKFh(6GNuC=7>o7M`@B|fhpbFxRew3?*TnH z-r^`zn%}Asf4}24K&>Icrm19ucawIf7v4j#{Q1oq27s39pa4Sizx+e#=98brelK|x z4y}bV8lH7TAk+&MKZGLAK|wP;L;Tha2j+B7%#=djD!C8EZ{af+yg>qgzekDw5dOpc ziw6zaJ0#(A3@D*4;vI;EaWj$Z>T?)?*J6?@1XyIOy5k-2hJ=;nRARDukZU9i-VB@8 zyqH4R94>t~4B`ze(S|k}lydIBm7y0|hImVjU{6Qa^Mh7=GFC?w+)SWNybl8v7T)VX zijTq<)9(VULfHU~4c{f(8?av5pH)bl6D4DgW0Y?*YeqGXC)uDE0s_kHgbK~e{ZNp6 z?5L{pM^mzPitYMcNxAjTVF6Nl8R0SUVOdN^`EXFQ_z<@wtM6p<>NA4u`qoi^kT|I0 z>G3>+%s=S#(d+|I5O@%mC*1^d>mOKmZ=tkeh31tJ*s+%a6d9TG5k8SMUBpdMq7x{g z?4A5O?5#hTF_h>i1rZ4GD1P=Txb4qu6aW=1OgFT$?1mqL;_K+Z8y!4aRA5wT)CxH# z!c~?PaI$Ygo|Jt?D@f%gff#V}?sD=PH4C$M(4PkLs!do93hJDgauyyV;*ejcS0NgN z5Fmw$Q}R&+^21JB{=ECBuo{}(=X{t)@G{O$9HpW_M4iQX4*|7Vk39;zmF}?U`VJQ2 z0z5Hc$%2!CD(H9uCVu!)y4V9Sb`hHNJGJQmd@y?G;YvC255(>rss_wljft$?N@>(| z6Vi2D@?<#g7(V7CaY2@sG#K+#$WTbUHSvEiy6OI5bmxE>-Hm@3-B$m>=$80TMz=4R z(XEhCB?4pcjX5z}P_e8r3e5$fr!HRZ`17XT4IB!X)9nrB^y+nWKmun^4)3Kf1*P;) zl6>Gk8sYZ(cCUO37QsF%cm2~HQ!vWNI|5AU&LsLTN;h(=nBFWG(gRrYP+{q9V$I!= zH^gu?$2)%=_hoaHvmZPul$-uRiM&;4VlMu_Z$F-vl`zD5Hfxy}?FN+0Vo|}ohXL%R zlr>>ViIAlA9z=jdSi*7$P)NBFzD-$yNO7w_`R<&9lrG5qLA-Dy!jR#`7UyiHG0`ws z1|-7hTtfXjUi*rmoUL`ePA^yzeV8^x^v1aYqYqW)OW9Q z)o%)v3B(mGz>Vk`BmwgIgFYYLsKCYAz$jz*bgbhk8;Wg{WGKPP337!Sk_8IgoWpcE zz@WaR^b-m9zQI@=-~gO)(z0xX^B;6LD_w^i* zwFqeOjgNFI*{2ZWe$SDhWVXStG0? zxRPK_A2%!uhY;-vK`M$m%z!K=>M3|*N{tHmyj8vY3jFCzG?Tl!ly^sx;WfgClH_LZ zO!o7utvdcDYy=urxJQ665RH^2Zk&bd*OS-{1x_>*X;oY0j7pfO29}Pg5~DC^TUPEaXmMNco2X zYh+J|f=oOCYhL;eNxdp5gt(}0gvrCNPg?~I6TJQui%8#;ASosJ)=({B`e4IJnbM4S zJB99VZ_F_OXQW(Eg>f4N?(&4;wHT0&Bto z+oedhg6xnZ1%~QGxPqx5#RrpC#sp~ci!bPvdrj#c!OPNAN(?nb5l47S1wUvd@SJAX9LzYAVm8QHQf5g;5@3rp>{kF zdm93up}~2+)O(fx5+@2tP=F%V;~)hDiV@plC_x4SZDe9lF^y5<6?F4anEha4)`BqE zWOAdzZ0G`q+S-^Q3@*Hoqx>blL;7~ek!(GsoS!0YCWTaobvk``I_*dU=*bvzNh**? zeq%%Q2JaP|(7}cV;-8tqpcz!cz7~G88AA}c`&;&4*dS93#h+ng6YWKYfz}1ZdKm>q z-{|qY!8@a*-y6JDW5{vePfTl3(@U<6VsVnzSG?zaa)E`~rp z@Kh80nd-y}@aca~fma2%b+^qyaC~to(6!#v#W70Yp55!FYstUsz4;l1qSWS7W^0J3xw?Pt|z|C zJt2|cgAD$>x{E?a=-XQv#={R897KHi*DH~z2VE<7PzP(gXH>Y&`!14neJX-LE}=pj z&?E`MslEA~s<9Y?Ekch00!5YQSspr@EL9B5CEKrPAuLgA&P|-|?+4}>WB?3^b3}0% z7!r|#bOZx$Lsq(FSqsRZdy>yR%rJ!qm8`6QpADYY_W~t(FhAvii|%ET!H@;pFUtZW zIp?F1|MsRsJqpKQ)9Ncw0WbRr2exf2FU(^E?J|iX22vP+<0dZmMgtYxi=?3h=$7OP zab8L7=e-e@@W`xE3JEjC3$4WvM>{ITAnV2z>T*dF))N*%!7YRZPLoIyyWeMTcoya9 z?a)g(KxjOZbz+5SlP~!k{4wZGA=(+R|l^AanZ4 z7@~;p#*5I2BWpyKAjB4ksu8HqESg0nGev!+3Wqj>FjR9u&qyg8w}i?Up?LTziE1c( zdQ29v0dvEflj8v^wB=oXsVf}ykje;;*&ASNmIf|Je z1_(*%-9~n)eHTSW7}J}1b`?Mt{00qqI$?XWMvw=OlKhLc=KUbUpM z89wd73|};JH=e0L$M|bXg@vBji|Tfl-BVc{)cV|b>D$&3Z11(YRAt@@nN3>|IDm%Kt3dCiZVzcUPQP~%n{kdvzHto;jUrg zN07(Yhcbn?7FiWFI*6K(4SlCX_z)P46~|K5TrvHsZ#tzA?m3GqGUc#|LlQ#d?hy?% z#xFY@6Ct~;<1e!%MK=nIqYqa6F*zp0M0jQiT? zFieME*jvDQ%p5!(9@8lb+Z8=9j>aL>#o;H<=Pox)rfZE#sKJY<*oBXluXX>zc1V;A&cR+YON3Uy+Wi}nj0CHh0@D6n){rTsx9)xwBB(DB(&1(AM-x&-?)Pr=z-n^Nlwl?&66xb69X8(4VK z^ASEi{lhp&X&pw_s8QyWQeYY^UM{tm^qjLT+TWsz1WPU=8 zLAr@7StEoWPN7>+!MU}`72-!UzfRT+qVqbB1OK0pqjUVQF*J~HS=~46Z3Rc>p1$}%rBfHU#cvdpq@6qolNLcv>9WZd|S8^1pnG8 z1rc1yXmnt~k(#SS`qjkDjF;m_(^WKsXuKf2m!h5V6(k z%NNb5(m6RRMhHSTE{We`X7RE%J@>N zi%5}*E#u3s4L%z`2E>c%bR;=A_AT3Y5+jJHQx)`uw~OlUaFH?>OH1>SB!z5~H8Gi2 zNnQ`%2`Hs*xXNm=wn_eAtSl-li%F9<_#27HwNpq!qt=L6-MyWdEFgQc9KZl;236-{{)xbP^Ku{=< zgZ{Gw>QKxj0?xKTaMq9ux+F)!mgtbF6;73ie`TO;c)Y(m2C+N-Bq24-*VMu@Sr=rE zHVKMULBrKNT6i`N$vr7%R=IlNXIG!+b%)90+@4gk1*N@%-`d~U*{;de$==<~<-Es) z!3i=*Q$JdcU-X@6Lku{@cRpKu7j?@Vxh+nW?s~?#ta^nE%5ZfAH zi9)M9vfDi$+v6#LVo_%*^l=+DkIJO3VcN#)$g!l8@Mgz6Obu#?(%u0P@l;nQQ(re- zg%_dbIfj`Z&~#AZ;r89Dog3ub!_g6RwMt8QICGb&xIxb*r(=B{iU9;SOUwZHHXew(AzuJYPB;lo{Bj= zUNzQ-1q;>F$oo0AJ$Qr4VWvX$lRoY@kyxJE5V}vuA8b<+2l5*Q1IAAsYsh4-o|0Hl zRcZD3s}s7CKXR*+e$1yUVtsXcFJ`Yne*^`~hZ3+yeHw+W9|S^~+|u|-V3oRGf-;AX z_pN$%r+o5u2x@}>;X6r;yv>@psy}=rxdFYK{viCf^SeFS89^U(=H_+**y%6~tjwHo zi;#+2q9@W(s8&+CAe1 zp0=TMZ`?D}qnjzajR+n+8Tf3guD(}Q^_Gf1<;>+!6-ye7CN=3&&2{YUZ>W&3jWu(B zxNSE!zks3UkG(MBXXK(ZT^)!LSLSvZ|FH&Zm49b(&Bjl8h5k{K+mj3Bi6Y}dqPeH$ zaAl6xtSZ2z;vIS*)pY?R14(gk!8bC8h}|FPcy~L;OkCl+V~HFp#*3ERB+$!0-T30R zb}FU?_`ja`-l`pv?V5aHI&)@N&eXbYm8wilIIjf>t|<9Yst-z$X%CwQHloE~)YFZF z>tscf>BB|`D>q{=%<=lBdH0^{Q z9s`A259fITZ}bxviB};V%y1*ReYC2Ft= z7y(LeWVSIB-Be48{8!TY6y#JeaIkKlVG*7vu?!|Zi}>ce!Uj=^=CT2|FfO_E3%=** zE?p=l@ST55eW0%jcK-~_ehVnt;N<_+5iA)-isxpc|7s%EyIA-Muiv53nHD5h3j#6m zJOX64DoqeNT4;9Oh#@aCaeACJo`Y&W8a#Z&>Cxp}eEyVyBJ}pLAX`@!qoDToD)>yGp{Q5w6? zC;>6qbWVf8&%e793RHSggB~+}+v0D!D_!)zoN%oqH?2Q7Cx>2AAbyUhCVHYYl4}7Q zqW0f*)imtx!IYiD!b@;I!BU@lhP`;;cnYA?Ums&7F!ekK1H{(JQR~ne5m6&o@JP$H zbBytQ((*WwcjN|_8frXkXUEYouqo;mq5U>9B!cA@>)u84YIV%1(vciu3jIL%$Wf)y z&*WJT#XqPgIS-u(kZU3z$ItvC`fI1a^*z|Pa%`~&G;L|?f9w*u&rXO*yUR}rajI5Q z+&9rk z0~vJ@i}kjd3vaD=9(x>U54cAn+-{_gj&g&TTfKf{W3&bA&D(-2AcJzUtF zmM^3 zdszZu4Yh%@40ZkjuQoA1CikXRma@XOgp;7L8B%I2?#&lD;*mA+zM^z$=JLRnG2&h8mXl8WdPBu#b*%QGC@WT2=@wYS& zP+^&;0Vla_%-{I7Z!JqckVr}img5Vyo!r^Il|7&iq8y1{$9m(Q=5vV(YuAme*lRMU z)dfM_!mDl_t%;MJsWNxRgB0_SN|E_IPUUo3+ z2~N`=l*SPlyZheFk+^rH z+t012IUfhPveX3k9NI%0ab&g~vj*skEwV(?Pis8z65(H4U+mUD*MPiXoR!uw&Yr{3 zDnB?AHePq8e>~%}+4tc^Uisb7mHzzk>=_@6W{qoV59jO}VbxuCk9Bue9Uk7-;djd=;fw5M+jrW5Qj|qHLJ3i#dbgtiqK1dU)U!C_5jF0Yrz+w97@`%y7afhnq^9y3@8J#fN=xq2& z6X(Q(vnk%h@_f6k?nSy|pXf6Dq@ypHq-T`qyss%$pUEVIq#Y%HFky=om=!3bo*t2ADemrmG4sx(}ZlGbC!NgH92ddP7dWDsVNHqWFXN%#zMm-V+Dt!)X_QE z@#e|)-dj28iqn`>fN9r<8d=8m+wccv!lbLtIBy~^yVtdW%YCkk4~!wBFMhfBce?VK z4o<`JuEWk3bP3x{L08}4GvZT_)6+LokEoFXcI9_zZ!4ZvaW3iJ#hQPYHv;apvajOR zP7=~VvR|_x2g>GTmv8O1S42aNTRf?3HRcS$mF1LX3T@8HkMH=x;b;WbM%7@rH`PW2 z6Z9PqNBXS^&tNG2;`+(r8tGrlxznp@|4@7UgS}m+lvbB-iuS&2px0ebLADY}tjm1Y zOvPI^Ci9BC3^-q9`flkV-DU7yh{op1phY>r_<^zKYC-{E|o`BiVM|!jJ*aO-mUh^%a%eleH8~&Gg4S_}U*x zl90Q@p1Y`+8g~`)vHwKXR;C9)McY$Ns8zXx74%Ut$^g6@le>FuEaC&LDs7 z%89+aL^oTlZc1Vp8)dayl2VM?H^7QNMgu~JO)Q2f30_p%<>&b#!=`6RC(KmOtIw62 za;K%;DsAyQNc^i$+Z&5{=C|MLwRc;2gbn9lJoB4IBrhza15fYmHR{nkO)SQ`kM!-H zf*RU^C&?~!mQRf(KqO?jIZ;lQzy&3ab!*;}o&}XY>^#51kc3c4Iv8ZUr-143g1uVt zV}2V2ApZ*g#~<97`PFBq z#2%+el8Y?&xg*Op!F_DxYi7u1v0Wem7s*@bL`cdCYc%X(zK&UVCZIIRf4~2{?7{aI z?F@a;a6A6vpNhMZzwb0#=%s(RBxJ5KmGp%_Qf!h`Bo|GPr}Luo{|4_FE*iM&+8_OI zHRtV-x!qp9irk)%+8N}R1I%(szl|k0msLjL&DWoAt*AJxOCq@zC-|PKHW$53dORQ4 z-ddayI+*1`zArF+nytKp@~e6}65L<_DS|1)$_n!TTKCIrCZGB_i0F@KrC zFkhu;8YHre#NNMJCyW;pBHZv^X4Dxy6Yb|n!S{E%1&z=lx03|rJPeCqaIJvI|Lv`J~2^Xlb$hIYt!5Hpv4oe(NY z!Nu-Pr8eA9IYwHgoY}WAa@xGPA*z~2p;n6OiG|a~m*lBgtJD`jD|9t~0m9nZ-zuMI zt=hp2qWCYKMMMXv8f^p<2Sscc?$+>cqNHvmR_AavTT$4;velGZT(R_Rn+XWzAJVb* zj4e+9B-yY~S5PU7(kOM4Rw*`o>}cZ-Ml?HwIlF3NZKQuzTmHUYDz ziW=UD@;)=zE+~j!4ReFKl?B1cYf-O>{244L2ws(*KAy^VqDe7K{uDTpg4DjTWQ6D6 z$^5tc`%!VmYdq(L?{9nBxlTg=3UA+aqdL0Uxma58@A~v~J(%C_CezP5NzPfH?J)UcWw&r3X(~_z#VLRYMl~VuBNtaa25yFM>13 zQg{1q0h6wyn^bFf!(wdEJTQ`hHiOYmGJN*Bg-E+UX#wOu^lMiUkT?sK#*0lmA*wSq zhdjGdsslFqaefkcz1rd2W0Y662_aR7Lxl_$!WAPm#UG%)%OJXh0j$}s3Ey-UeB|J- zI`ol6ws8A4^C9)s(g5_3+MvD`lg+zj3@U=;! z?UL1%v+yU1#ItGS^@{pH7~&KTo2}B;V>!t@>xS504lwtf9v-j(iZNuJ8e|D=62f>E zwu^pYC*PMJ@bkw{m&`w00+qowlFAUQjRx$~4NW7c!%?Z9*H zyONG7>We<~8gBz5Ofuu)R}km{;F1YRX-r!kbofv+NPN1@NJrekQ1U}}`8Kl7m)Y33lA1?uDXR<* zPD{6gmOJ)=sU7Vlix@$raI+jUYC>C;Uby^(#;@?v^+n@3UQLYcqNK_mBVl<)$+r&F zmKy3<0o*O5J^qhY9+GOG^o1%8j5=w$7FNxNY&yK!@cfa0&T0m6%N4;sb;azP)nkC= zR16oR1@_z|95$_eEX@zkzD5flLZDG%tz0o2!*9R)$K@9MOjM`@fJiFJp#$npNAngA zu~^((5ySUlTFm4TecWy#;5~ywhIN3ELR6iN86SE-!6+eCE^aMi-X-l6GD>gy(>GU= z;blVtaz=JA&CSTsvjWajRY|ophu*tO&q*thdHCyI&x*!>)9OntOom3%-*zoMY(dOW zg)fNN%Et&TM-nm0nWddp3r`=Xg{Pk-hzxRX@S_=L3iEdKId5;0n&^d#U-y|_9v+4H zlD-iB*!;OM*w@g>Bf*UiZf0uzqSn71gJ#}8HbP4gLb_7MmVbZYdQvb@FI{UY2NQor z|628nqHF}U)4BMNvvsHv-?OUnxlAJlvwa3GF=In>H6xf&7XB#OBK+&{s|`WuB;r`C zDMzF)?f5;QAn8Tpi0Tnvw>zzdm4U)R=S$+AoLtkCfxYpZ^AE>pQ@(&s$(dS+>M(l2 z3rh)6A`6`woW>q=(vdm+n%GD@MYCRtM>xS;)Em8n@Ih5x$jEVc-=(^}0-hf|ZnRtIls9Of03o6mi&_(y*4xs|bZOH(Ra=hJe1eJLj^&suS2- z%}QFDH>#Ky1Ti-L;Cv8hkc4dGv(SJ2Ew49Y}*<3Kv ze#U3ICM)MeuZgXd{9TnB5Gn42ivtZYcMGr*ZPE25h`aanptn|*h#pj?@w>K=J|3f@l*87S_tkp+Lu|pML9V|+2f>w zJUM+Bl3j(H&>@?*g5KGHJz6;h4{4hcXKaHB`z%@t1w*~DgjoPO%_@IK)0j7C{r6A( zVhWtFXe!}>7E2T{mAHJHv}^^u96f|Cg}MnxNYwtFu5^wi9cWRLO&67Y*#^lrHJWGoi=OXV_Pv$6Ac(3@36*8tiA8YNI)Lb zeEzNedVV)bgs*V7dF|5ja^tyA*_PI|%QsR!y=9U)(91=mJEbP|9Lf%CPXTzi9 zdnCvBO-khVPoJ(;%3iqTD{eM2tfDXlRHx^XI)@#f$cPzrF)NwW+X;8Qem2qdm?_E) zk9EFJb*WO5g8!fqTRzX({8Ey|lDhdTRG%kG-=-bv(>t~ORVxc?oV1>WaQM`@)-e%I zT@L^?-(~v^a%O2lXnS<56o&SZf=iU;9(is32!i++Df`)eA>mOIFY{3;62MshWxybg z7M>w-o$8Y`tT2W>K>(9NV^ScWiW=bZpzUZQGr6Y}>YN`-zR4^L^*N8uw3) zwb$6YcCCMN)T%jWN5UF}D8&M=Aq=p;w0WXVJ6Z79%FrLSEEJCIVU&%X=pwIaHr;0S zHB!ao4wIO47XJbl)nyU?5mx$yMw!lVZ8F;MCol3xDwhsk5-F{AN(<~7tZf`_PfrwA zi2<$9oqiHf4kph4H3bMKY|t2Rwg43KTp)r3(Iw*xUjbZwT7AS@2)8p-CJN;OSC%mE z9CH+9B@>u>>=Y&6bV-hA2zB*lP5NEx`-@GfR9N-8ifVwXuWjYrwvI(4(f!`#-ZGxU zILtq8?T91+98gRrAR+lUaR!omvPEB9u<8If`yX@U3 zIj0oZN@VQvL;jJ{{)$r3eh8;n%3ScBXS#>4L){>5S2|bC7n9-MGKtUa?lp`#c-cGU z@FMyHilMPgBV`MHx#ED-49g&N*+$5rI9Q{Y^O)NcIJ0THiy^_gUUOw^GwR~e`-*Ujb7ZNNMr9> zQN0Ds5ZO{YFlHaJX;CMDoIayO>|lBjmQ$4?`^5f-G}tFa_dk zMepn=WjUCWO~`e;6H?sTXvq)LMb{fX|LHnEv=N;&*gFBvt$c>i1YNZFv>tgMM1GGC zj`L6V8YTg!$9z@{>`P|%HX$3IEQ7BsPPV-`$UiupgT&C(oqR>BJ1Dx8bZ6EW&^Sh2 z@lv{3CuHQ^16L1!PMWiXtf)P$*5(eP!>de6s9ksmML3n7w_ovF#9yOmhX+{W1%?T0 z>Bb+;3^hVfxr43bw;lc^XhP+fGbvptfqRVa5~W-<*~qEkZ+CpJZ6`yij|*D|FC6WP znofN7o>baFka8`2CqYHx>NFF9fsLM&Nus7Ougt*J3;A=E4_!l-ot6r*BPj6CfGg-| zqQJ*5b`j0@FNNHZ8E5q8Mj3(>$@4K(OM;*kjT8``e0z&PWvP5chw){Fqv~0vcaNb@ zPa3MfNT4X6&HIsNV9*8BvZlru6lnE@>{wDHI5RitGPBNM z2KK4L)IR@(&k0t4Ap19PR^n{UNHL)PzrlNt4BTj(_G-^f(($(Me4e3Ty`A&;wfe*4 zW>^cL@ne98@wfvkp396Hz{?(MITD}`UuZ5B(zvDVdoYE|Qi(HB&0#6PD5l*}LheY41|*Xx#ppQZ|x& zIzouGwDL-U&l4xBSi0Odnb8?nX=xU&#-V*xmA1`N;-#hpsazI4Q+e~I5m?&N{b;dw zU6oJYMHd8(EF@|zxO9Zm;{v_3)@BSw4s%+m4xI)?NEl(7u7^Bgj|-`)o=e5u6;b=7 z#EV)c356W?P*g32)IAdzlEaomERjw{eA_9sc;gJ=^Mig^axSfw0+4(OHVhhX!-PR- zz?ehjtsA*Z!at3vc#kQ{La^bKDDN4sC>@VsyZ%du6Yb=}xt+poN{v>pGs@1+ra3pk zY(uPI9;NLHvG)P4rrS%C)Xpv?!I{u#hJuT*rPeT zJL0OE*pp+))CHp4N$pYnfWaI;Xm8CI1n~hH(?0G=$Ftxq${bFLSRfMb?k^oIoNR53 zXn($xUn-lJt~i@y9_sY|0@;3z#9mmk1`xrnWDTzCkZ39rhHDGDJ}{_ici-E>(a(@+ zwO&RP78W!GQvOH#{xinKpRx29V#dGX3c`E2n{p-BCve|EW3}U>mKaVvf5AjUl=bh* zVu-O#1od7R`{R*QXkO>#9BYqs`P()9)|UOc25QBsWX@!9GE;V2B#vEjO*uG8Brr4vGkekQ@Sl(YxRBuI`?4Aj^ zeV+?Y0+?UgN#o6erUdm)+;+;RtDal+?AxwwiBCdJcG^VDGZ0Gj=V0q#XUKo^D^~jJ_IiR^eG2Ptu)$QML{-hrl#Q1=KXt9VZQgqq}f_C}l|D zUADl;sxDnWl9Pgya8PQwXdau-lPK6mk07@-W>0t=Lsvzs=5mn*;*~aHl&Gl_fwF%F z3-ko_$WOJkgB!kYB>SU}yP*}y&;aItmyLdqWCPif4W^Pg<_nvu?~aC6Y;W#bvjU6YeG8O{W^O8S?<@7UDd;$j0gbvx86 z5D7eKI=|+?i<|cMaFPI?3TOm{A@kgn* z@)GZFF-Rz35i5YKuTaBGB$7oZzU(p}T<`zy;meh4?t1d1X*v3C@13^J&Nq-N&v&ol z9eHK(P=AR5bjx4Y&k|2b;;jiV6!|WG_(>(w4Z?}?PbXdM79EZ8{K3s^Dp$>ZZVk@H(b-Q%+ zVFg$t>|GsfNg+j`Sl9xhq~aXhO%EkT=~FP4$U}-OSc@fBgc4DF8%1{0zXUZR#CjJn zw0`-eZ~fA{3OUTr^rV;Rrfwi`gm1g;5w^n>%E$w;?eK^QzWoZud0k-5VQp=aU!_2T zomiG@EwBH*xM{EXe2}Oe+{;@2uzHSDGs-h^QSmdiS)BCdmtrJVsO#Q{UZ4P66p`xR zF+nc;r0QlY-eAB5jShB!t00ICZ7?fbaB?@Xi2HT>?nZCIIe@-Y<&07c^7syNPO850 zWcj1Pb2`xg0$WS53b?z$uFxwQ1@j8NOP;MG%z3`u2lQa3X_Jf0k8Q)h@4X|wUrxO& zJf$t4slxKI1i!#?Trts_+cVxs+x4Py4xhV%x5B*bVR%<(3;NNNC?CyCMED7c1o~>- zGW?KLgrTJJk%~#l3KQa2B^|X)~5b=C>Z@z;E5_bFt5#v4v<^?ss#vc@X3BbFb%oy;X7_dSpQ?=OCN(qB`eMym>Jgx+hU(D zvqz8=YxOkm3RJ^Y7|6sw{V6eQ~IV zjxQ<)yS-XPzu|n48hr5>vi5u*m?v4jsC%4eb%Kt^Vv`vpxBbO`lF`9>*lje|B}A`|O;c{90pT8$n4YRpIsz6DD#sRb~BF{!^yKqH=Xr7KYz;6JUW4qd?S zp%myTead5-`2Qh@-oaKTEnGB;;^dJk5C2NX9WvPX3j>U@V@AQySvo+^cwQ$9pmb@Y zt5^;*hTBcZ*~-{|SAiQ4Gm|N%Bk0~77VrZ84tIroAHI9$R>vEcr+m33L z+oaqcuk|_UaNGW$@q5bd?Dwx#0TF|F-x%~%pLg#(se_o1jEto8j{YyQ)O(#h_gvQ2 zuzji<6AKdCwyn#shd1p_)78m;?^=7bM=1A&H8F8015i?DkyL;eIJ8 zh9(-2uWbfLoe?V@kq?`?|B~EQ_rHP>d&OS61^*pR9)t-mBZ60c3)A;;jm@YFVGDC2 z%4i{+pN*J>4LCsNz=Rhos3Z{_jAye)>vNM+Gb>+p{z$(sN=^&?-jQ`~n9o441)X!* zzYFXagvVP@FT7vNr;aoahi{p)xro%Q*;GF_SFSRXfcP7W0fRDP09w8Bq_PrwYPtf< zdN{TKAPC_Ort$45OgYmmf%iw9Y{QhtzV9y~fO!PCd4X6MQUWj8AlmjD`A;-QI-y-^qof&JM)mYo^n^W!CG;qx$*RB9uvZ-b6ho#=AU{<zsKxzPetYR8wB#U&ic$fn)8V6M}jVafhB>5s)p)kCE2tXol zcYipS>I_7kaA~Ge%<;pxs|Ud9@Y3KGP#uNptZhoX;UlM zH)^n`s2kWl+fu-7IP6d{?*3I3NI`q2OA*QZ%4S9Zz^$HL@4a8nnQ_5Q?)BVr3J3}d zPUhf+f{m^JV5lA~_1#KD)Ef88K|@ctC=teC7)BKF_Dds=X*#87317>ul408Q-`cp=AhB5BDVb59 zzXMLV7lL0EFKJQ8dI^!6ilK`lWOlm+HM%hsMO8jkSMa|JP9C@AO2~%Z5Z)3sf_lj= z2Gwj(DWo46=zZ&APr|g`5HCHQBT5f)PI)oBQ?{ZB+hIYX*K3Fc7XbEJMQtxN?S5J3 z6JtOq15&AY>0_CIpy20Lx&sZ#8ZHLne#Mzd$;fb- zGEMB!KR;DmkzA*OT^w^MaxhL8-kFI@a10u+ls2<;5(`_4n?ptuSCn*?aQMD%GgMZ- zXxBSkF!eMzav@GDLs=S-M#oR9l8_G z7xvii-fFLiZ)v4B5XN&WY4JjXzkF4UeZ{#eCTm4epymS1u7!ja>O1b?(&(}``+E(hSMNeaD{haOU{I)g> zAT|B~3*3*kw@c9|gz{Koxbch;a3ER1>39`yzH`E;nAiW^*zStaaOX^8z@Nl$7D~(!@K*!u9Guo`6v8c?K(n~`94l~n_$tPaVk@$ zHPlr1fOEgcBE>Rp1Y{z7Th{NQ)nVQf_lF$1{8 z=CmPdSMu^Rv5ahz{hRJ{gn1n~&oS5EwXxFmt5+Zrv(6Vr7=>h}6pCc-P_wi#gRe*X8Nf zy^CVzRRhVQ2*YczMOsnX-wenafwgiTgSHC{u$?Y~z0AG+oEA^pqi&%-`|O1AEB|_f z3HM2>=n!}O_57Y{`R$g;Zoy#e|33RuMN=1e*bX8=g%59&D+_?eBgf&J*~g| z-Nlt)WFod%UOCy`Lh>HG20apjG?CEQ)ENIXJQ_-aeDe4Us*p!6a1_4NWQYeG8#G1m z(d_gN{PXd)PJjLM;oTiZ)KEpkb zNAL_7JyZa_seM*$&fu*ptbFiiXu;=IP`Oqo@5Kfsd(Ec%mfQ}z`Z5fO4Kdly;SuGC zbQAnI!-8dYy8r4aW80zDT@~v&NE0}_^v7o=h!PAIbJP&U)S(gO-A+#0k|V)ncdLBD zecU2jK%#EcAW~Asg6(@O)U%6CScW(ycgHKa5@1*NRh2zx^AU`7-a$t6=fXn5gr(q zs1yLNc?mQh;%3Ri$?;M7E^&=JE`;S|X5*9u z%Hu?`y&BQ4eMhsN&uy=_!XTH|UD37mId|iA4Gl08ne{xgR-xn8CLIddkV2^g!H!vh zsj#nJkqoFAs77=`6lK@78Q!eSIzZFv=VSmu3Mv4(mTRyhHt;q63?A!?p?^$e`vlwnPz;Qa==2R14eV672b&d^{je+ zlDuwvI;cYZ52g;=Xl6Dmfn0@l3Z>>Xw*Q=dHg6Z^?&s>x$cX&bJ1xFnA@IKc zzyR52HTGx+q7qUU9A^b|wzWa~BuiElOO}x_7Bp}3K20R+R;5nCKb+s#?ry<=W-C_S zxGQ;tCH!OXJfc7K6JH;@_RbRY3U@F>pIG(wsHv+i$o^kTAL2iZ zMo%2EVizkzI}|GXoY&44(pJt-`ToKSscPZR+{Mi^s6xgSEXByqVC%p9BRgImuln#GsQaSZ&dezt-$8?B6%uJu5HFmovS0;IdSs-jVv+W zSXBJc_xA>+E4Mf0p^Q7WGZ)MhcLG_i-)TE4iGFo1mI~s#U^2*PVkySL22`P#*viIe zLMJAPicC_dSQ);SGS=8*oNA5i2ufw#T^{0We4$T+6b4MZ9aZ&$LX2`5Ux*H`{|q* z;1TR;Y$smSoYBxXz+hJH+~&2S9K_xy5^f%OwrI_f?1H)7M?8_9_r)YFYOY@F8oMa+ zi^jCoYOLBJaNQd=qX%Nh_QqLNFb<`c>*BYJNv7RRq`XQHClYBgUm!xr~2XVl<+ zlM6t7p-4Y02%PhxBR-bCzKDZ2=~_NSkHSVb(>iLuN5s_eYk3UQ`gDYVe-nF5YO(7k z3Jl}M*XMxiq}KEIGDEzIw+J$RGB$E$}t#g@+67RcL>5l9B{ ztvoUp037W{MYhlo%LiBAz>qg^=M$I4df0b{#C0TZ|9fPh%tWM^5_!l1cb0#XWlluA ze(;@#c5zslzIMtwg;0+T6t$ntN&@mf&)*iMiQMW9gw;E-VT=ZYSie%AEhM6k@%07C zr>hynHg$^ZOfwF;&Tc!GL@111xAN=|@LK7k7<_gAXM&efemZeBXWVO(HkpCZa3PVD!|~Ry)OTCxz!1Af7bb% z8KMb=B#TqYVv58b`1o*}I1oe9aY$&o!os^C) z{qk|Hq71+rUhl*BjLjS5nn??TqKC?W#xyqaHzs@&ga4^+dkGUg*>Lf?Afp9R#f4Fz z`o4_b6@^%U;Ju~NboLp_#v+<`f zc*UNR>Fe#9>@ z9x=^TJQmstDT>^!ZdX>*NdG%44-cLW%Ah*MgO2b#)NdQPPAG^9-asJ?l0pT3`NBn; zG@TUhTNh3P?5jQ~{5GR{TT9NV2lp(dC4od4RKqnky|@8thUTZ}*Ti_FMcz#x4Hgmx zO1>saJPe-jRwWtuaEY)^JW55BKj7X@eyJ}aC4}qsZ#?m2ahRg*_UZ^xX}cOL1Qqlq z_=NX$=h~meS^%-*XzhFi5aYROoQo|w4aadyMQctutelQEY2>nTW9DSl`Ck+vFGu;( z@>~~0MO%8?`}Sec%^$D`0H(hJ{%U^xLs=c%Kljg`M$5juhN%-oY=q*#ZP9_R255AD zm(QaIW%O*RyzSdsGCigiP8F>-$M#}33lk53^*lM}v^mk}8nMLOwE5HzNyF^LqCSsv z{S}?j-IUU6!#<;44fbE%ko7=e6k#7f#@qGT_ zkH9?<5F}b2+C$BN=bjy(Czh^ANCy$^=bXjWEm}7@_G7X^B|pG%Suw|Q0fO0A^V*#? zd>nAiPtJ&A-PYhj+~{}|c5z&j!a^%KXo#x_lgHWREoS=o7A#t898z%AkfOku z{}JoRYq&6-&ANNc^IqkwcI7KV#Gn&y8}btWsXIY&+jhcQO0tWTp107QFQfSKPMB#; zV3@G|QdVjikym+oPVvxq)^cWDya@ioJdpBWY&#PtdYmWw4%(gi`>qBZ_E)RRff$ib z8Eozy4g1Jl8^w{5!0V#?SAuUn8t99(!}pAG^0!~ILhTN5fqov!cuDY`0BymC&G$!D zbYj!Jo$qsv*22hw{PCN#*YrYQKU1YYF`I}~CQ-rkkZ@xv*V8XuL`NzTA zzTy%gUtp-OQ!(v$+`|q9W6d6I*#@Z|zr8m4cLnM<;QLV?sHBW#fVq)u9B)VpONg15 zcpi^<6l9ngWo+ysZ1)TR;Vbm|OGJV?`^$YH2@_2&l}sjIGSP@F(}2xGMJN|lEK~8P zUs_N&+JnoTF>4>GHyG59k|`XI5>R$Yf<-1yiJkh-J+=@`iuC6N@z0F{BhiR?6X+AQ zjo;Fdbn!)GwG~wh%Ax;EG7K%`K452|FFS0QFnKad%5DF~Bltz%@kMDU4|9hj?i5Xy57H-zGQ-fHDX1ZL00ozJBxXH(;dNVr?fN#u z7RLsT$r*_LU-BHvhHQU~HB|Kp(Fo*;_l;y^X9cF}zav(i;D&#r6*NR~P*A6&f@h~t zJ=9?^1nE~Gyyu1IgyzEuEzotz7<;n6TLpK0&D4>!X(A@-HYbunV~KyW zZG7=PsARsxUcp6s_w4q>;uUury?>bnGD4#W!@l+FP;iAHNy3PDNF9*pCU9xyvw@|F zsm}Uk27I&jDTzwyV+RUqDDHD5=$Oxeh*uMz{WX3aI-U5a&-HjJ(RcxXe6+}XnH+z$w$VT25&w2M{gJ}Y zdKEzZ07TP$j!gZe_;L&FeLpb$>HzwtXZ4>-C$M^uyfkF*)vmz1d;8_oH~(Ia7Lz%X$Uuk1gytg?$$Pyfy2G&mHPF@}HbK z{@1H-q(6mNhp^t7uzo_<@stfJli%J5yg`RRS*@(xIgY6?3 z{si{tlLdwIzOI_5E&RUAtJ{eG=wU|t+_ZUV+qmiB?_&S7`5EwCwRDFM{C-d!VDu1Z z!SS_a|9H&*KC3$0*{_mp^o^#wcT+Ur@6_?qBY~xPPYK)OO5L-&e7f-Fo={GuhbkW6 z{+RyNoMW*6(~1#AGww!KjDJO`D{k`Z1lZlzb`I=3)QDyQ57&e+MpLVmA=1U1kTHQK ziEdAEA6ontB9@7|jLtv^8H@>Y*N&!BrhXEE&? zlEh|0@!1zX>Y({dV0rrn<7!w z^#R+Ed3Y{xYn==WmX^aaTjE{d*ILUXsp)!X+$qtu_s-J_L1rG+T$t=PUed#IYG^dc z2v5J{9@g0$)j^^sUt?StA~Ux&NyO_azSnJ+JKNPfy|rIN2^09-y!z;N_BarRqhA@! zVE$qVLt@%@JYfywdEA{#NDn{rZ6A~2F##pztL}tV#>V@V0-OLzD zb`>L-GY{ej2R>iPyE41UxVRE4_{53M+%tmotw**ENz4Y4+u2=q8rNgKTL+Cl_maUt zOPlwk*O)uApX=4 zC%k7xZ%nqxxWcL+==zA!oy7%NPrlQK$0u?Sc==DPeKaFQm9VIRC_XALj9y!rVUadD0ciO|)4(o928AK7gg3MeSj55roTtzE_7ZX-d? zyt`Kaxi^Q4q^CL*9-nl8bq`bB`<9bxp5;mG!7b|2vIAb4OSZY&(B=7$C)Sw_c}aN+ zDqh&WU=i;E>fN<9yvB~Qfb#eHVJX2P;+i%GTH3zId#Dd2^>_P;^?A(^*Sk@0S1DS~ zJb88k8e9TZz{%_gOHZ^oG>&j$kznbEQ|<$o{987q&veq)9B;%2{yo{Eycs|XmXKFdL&TB60MVD`o}rs=%fzN5^ygyWGd$x}SYiZV*1)Zl4@#86 za$dCFq2)|@HPE%! z!4gjSymaM=D56fKi}cM7D3!#|4Z6lvZy34=&^{Ge7fe^Ed&vOhWd=#ENk*i=O1;I+ zb1$%5NTF+aVYGGaa57np7gBweP@;!|_voyry(ECiI1v3Y z6j94Ur5B29n0Q_$TZRn{y;nyPWOn2Er}d=CayZ-8(1+>^@}}j_^CoGo!E`HagJgi# zNvZXRz#Qk;+}VBwk3txyE(k{+79uPj!uWTwcVO|y6_4Pii^6j5YEjX=Y~9@gN{3I! znG3nt-Bt{|*=9rtZZR?Jfm(Pt_dKe+4s5k5ngNdR&94k z$(zG0ooR1V=dhMBu??8mCd_vGUZF-I`GODBd4Iy5`aCwj*$)G^3XT0RyEH6qYhRe&^ zvTlq1-|P9y!g((B$y^`bKGnl=Jw9B&3gK) zAS2DwQuZF49Y)Rvy+lPvUShbq@E&%^$mWeL%j3Dw;Wa&2WvFttv$kE%m{4jHWV}{> zfvgU7JV;|Pd!~?sfjhmsiXU-Lkld_*Zoq4IAq461!X?-~`{+2AC zOwTi8WsQD!|BYxLct?(zyYDw-KQ$jFPoQWoXL6FBinzR+y!7MW+PtIkH$NWtphVbE zueGzFo{+Ye^FcQbJcp3akZhRejQ`rme&-T#hIkK3_L`GP+h>l}p-_a=K4z8)NowwM zhWJ@B;jP%$!4#Zkh`e9=F;z`i|t+01Wz-(X~t;kad*q>wRBGvZM7fB{O_JHuSF>G zrbM?hN=s`QE9u5+*|UX>hAg=1{SqftjC+BBT`#GF^qZJhcvrLwp29|hQd7=sfp(nt zc!%dJQ;!mrofkX6MW3Vhp<9DzQr=&bY#vkvx~@uz)#oH*=LneTnH9@HB&EPvz1%J> z(CmePqR)^r1RHHzf7_@M0KA;{XUMHaFM&U|aP|48mDQ)I3xp^>ctg0o(!pphay(G^_{v7I?(M~oc~eLxrT}PJ3b6CrH@7OU9VSIvf4_f9zd2-mVBI# z@|~qNzAZHPCh&cSqx{A=bngTsKl|{v78{0~D?#CmUESCi0-Y-WgDRAa9Q;7}b?jci zFHj-Sn9S2_{lv@z);rLf2l(_z!PQ(43d|{k6ceYD zwH<)^D>9(AVjW*Rxp%50`)LG$Hk7ZF#{ETE-R_WX-I-9ngUR@1cUci|=*obt8*p{v z!)4o)>GUk>q6b;{EH^u3${elO8U{iI|3&NB8Wekq^#_oNF(#MhesJX;GdYDq!dj9g zy>>?Ib)|R9OK1^P?wqGMsRs76wP0hdfM!>kyV0yQEn22lL ziT1jDOOI-YZPuPP#nUfHI}gHc$qvQc=MXIPh+bm@-b7=Wioe9+#dfz?NH@-?6~VXi zDqbbEoK9Jd4Z!=e8K7*)!HjKmp|L#JQYHh@LJ**$<*6%RUgEQvv4pFexk96TP61Hg zoV|+0_4XgHae$tMEI6I{bP=X#cEEuT(*zqkDmIEeQpIYQy>#ELxhT0R+^qw$ml26g zI?GvE8i+lfR(iBONxVMQqTsrlH_{Ac2d1Swt`J6!&*}7JExpejQ!Jb?eCK!%r^Y(8 zSDfD6>TTBgc2*dNe(u)2nOoWCxJ2;GmV}qL&9#=>KRjItCao1>4kC?VRRw&xw<@-u za*D;wC|h<2Nn<$qdOU*jq^P@w!7*|Y#En=f^!@QBWeVr@s-fMH{Y^o1)=tXGpdo2+ z=sM00f@sfS8f054{~PLnn3dzkvp8)(YJ&hQHg0C)<7bw_BK3==h^#Hmyx3=lG-4hUtFt zlnn{=slf4Y<9hO${jsJRY(->$b@N{vHa0h#F16?(>@Jt02CQTI_#x^_N1pE4uVw_m z^`|}cVOnXCzME8)IQeJdJi-k;VzL_5X_Q3?B}}Uiae|O`dp1YVmsgD(FfOJZcc`L? z2_6d;K}4Fv&9pZNEW^_13Pd~wX8nOA_Br1+8jw>NM<1F;y~_TS0)&JyXH{}jKD|V0Y2Bmy~OE1&sj(caIzze z3w1CS>(gY#x@cwS=q6F%j_gsMx93^2l`$qUYOrdhk^KU8U=SF)>Zeo-<0&(U10Me-J7}6EWRhz^T?s&othk zkKlI6jMwJ)L-VHQatX@ZNVnf{is>Ktw8c6bet8d)L?ArsXbn%{gx*O* ze{-3vHQP`VNAv;RK@f$T`>}6oHiz+N`$XZx2~&YF8+Y6iX*QjcRlKCL+o*%2Ijbra zwD3{Rca+eER3(lC-2C7d!y%srYlWM!Fvjs(vkmK+#^!P&b&g+n|S8%4dHC9g?xi?%7|+CzW8iT%t{ zW{>DpDyJezc=lXqXfEO|lT4ST)SmZxTN_}xKE9}OB6mlMWlPa`6l@PSCua{3Hm&B2 ze<&mqnX3#56?q1aN`bRuC=Mn*lhFpH1`y~!BR}LG6DP6L61efPMEq+l@Ao&qMDkw} zuI<`;k+PA}wwo}4k|Q#LP<{%iJu4BgG4wNyTT&g_7bOIIsUu-vKkfw9SOKO_B0ByV%Wj3p zNbIR^g0#e>ouv=d$d5-GmcYKZm>Eme`$sr9QPMLvZC5LJg$ zL)UQraC>UoJqWQpWx$+x)|b|LoLV%yx~|umxd#Q;e)^m7@mBUB_;AnKwyG9krKi9b z)x~}7&*JH&N_;&VZe7j_jmahZf%`MXIbE;mU*Lp{fvm#4d_W=O`PEFX5tjoK zU;hc?pX~#irJBGiLOrU6CnrLNAP?ca2wkZ+_94ar2c>8no$4+|9hfx-jQ2vgvhsze z1utLrBR+LJ5`{KxnArkFH8O)7Pr}{BHO=*bD$Jc-UWgY2y4qjK6?shK{hD5hT>_3gjQbqxXW<5eOlb@>P~7L{2oICk_Zc-1N&LZMt@YxTug*%C|UB6RNx z1V=Ms_q9%BF+KqcMOB~!-0Ks$OkuGZm)QzW?3Y8{niDZTO)YOW+$?)BzFkE#EqX&< zV!amAPsk&enJ2de+%4sKd`5SRUV18(8Kgy`NUsg=uVSI%>ytxm`9V zI|!Yz@VR>~hR*$Ri#Fsps~p-pTeePgjkmLLimv?K(cN307Sly9La-8A%2=K)mC0a# zD8htM&k45NKFxUD(US*eOWW`?{8W0JI3m{$eZ}uSG-1T(pQh=G8``Eg7@g!K2dT!# z*XRhURKFrk>pt7pE+_cV3!*LQ!tbzHwX6;Vc28c|wA*}+qN{w; zBC5l_&1$ar6G^|`UtsXx|D0dZrcOKkXWi7@6>dCW^?+xykEL`jHa*>*RI5 zl+wNzj|5y^|Fi6o&+CSJe&%kQ*6jgAQPivxS~;gjl`{Vtp7%Ryi0)zTu)6QwZw{Jn zKwzxz_?Xoyoc_}OHmSXr=OKpWuEufXn$PyUUsg3+22h^yQ80peqM$kqYwjQO@MI}gP~sO(E6ZT<%vd*QrFJ9zU_ z|E4OaO$?og_J;~4TOebkhGS5K8H?iWt7YpelaKu#yA|O2q8#}?e96Q6PCEEr@Blq33?}KI^DU2z*r9@#CI67TrBU6Q$7^*+)dLGb`c7ajOWRgq4PLaVA zr#l4&Ozid9?`!+c7Eul~L#-UV{Eqk|@qUA4kV+0yXMLGbqJ(3IC_VdnGL|CyumUT# z0O$_@v^ROx<`*TA(|PbA-eZ{9cT0<3E?wC__m{6x4NBB8C&rNjWSK^i8bwUOB^tmc zNZH3=ipLhfe#6)Ahj)3}Ur7^wj<50z-(yET@&dgAFGa6ag!+5?cf49AdM}+C-=~nR z`%8#DY?H^IIfVMn%RXDbK3rRQW{F8tXT08&{Eu~wwIuBHa(ix@JGHtMdW6qtHwj${990BJ&+%yu1^FEkHW02oVR$3cM}h+Vr@L4_K8EtM zULQHGgWN_PzZNiCqJOLK==84;{yHmR-2OI1CajKO%r{$x%6e%jopY~ zM0|>;ddqYX0-Ag;bZePIg9#2kw{Kk=!G6DXx&X}w;R}qNvjny0&rRSfr}Vos>W~a&N;fcZUQ*#fC@Iv$-{K)~b_0B6 zu~&lSh}OpWug|ipHM*^SlqTs@9Hzu)Uy-!Mqs}RF(s}|5w{ZxpXLCG7Ha$>n!qPnVQk+$m`?0qTw$I3(cb_Iy=XpyTnHduDc)2N^pLWq zhz1pm!DUjDl7_?e-j=&KKSm^S;f$M9n%mDID)_iCJ z5{bQ#69md?feJ-+F{Ww+-8RCuO%01+XGu*1fkho)UY_+NWzUiQWANZ@@Q1o)dRCa< zQS`D&isc?YiK*jgq$=CF#;19I%7?@tdfnx@6TBFH^puA3W|(5l@lR@_iav&tGxS!M zJ!!QWm-Gp~Z|6k$Tzs*_CFBkZ=Wr5A*px^3%jYTOVQW4;c2FfO=G;EswJ)cuL>ohi zEid+6Vq(F;TIuFp?^TgZd$(uKOg>YwNIP|Yo#k^<8$j9=opoX7Mr;H>%4$Z)8<|lG zu)`nPfI_?P9yCtAA9Mnnoqs>|toR$+Ruu$ee$RxWG}9{H>1S8U{guV=c6jk$Ag|^G9@6>E1bw0p?zul8( z>wmEIjzOXX-Ii$Ewr$(C`?PJ_w(ZllPusR_+qT{PdcK+a=H7^j$XKy+XI1XZs;D28 zxpS>Tdy9o^69Ip9biG!$>)IH}g13py82i~tQjN_{Obt@B!E6Dw>GQ4uq$}YupR~n4 z0qACs8xgrtL>9R#FzlVw8qcaSEgOtH){L5gBiBvMTq5m^rh`utf++MR^ySTh3ABE|-3QM>b z43V|_l7(_=w#gVd7L3RCX`_!1*jeGYLZ_PLHN5pBV!o>8b~8h&cPK%{%1g*oC|Dj( zWF-oU5Q`Xc`!XrS_CQN@nYwZEuw`yVHZtA=X*u2&Enn_}GeZbPUX`Um9A=zH*QF{z z1~aVQ3f)B#kp61V1uhT*fEJuPb>2afpNpBL)vPM(D+8hw53gdhMm5orm~?_UokB>* zoxXIrHws=6?Zz~SeDYux2Ba(Zm)$1CHcHG@egE)vRIeP;Nj92BMZ6L$PO5Up8900 zFr8yb9Vk~nbL~IQXYRx2PG0h}ij2a`x_fi0_x;_LUj|e{=fU%({<{CsppHNSTL-EjAHC1`R;T^;{~|eJWG) zIam2F^DD%g;^&{5&s~1Bo|(q8(TwVni2(-ChfesbsC+ zEV&~hO;sb_WSy|&!brbJ9n?C%nz%3Z?bI&4nmD%wnva*8Qu@%6)fJ_$NIBN+iEtd& zRZ@Nu!hJz6fX_mb#{u*p+vSyw9(sN$A%1TMzxH!-l{5aP@Upa~TDEB?;3GbSlBe6`WG?>%$iO?C81uGv@!U`}veF-5AW1nwqN{i{L| z!Hre6Fs6LBgLwHAla#>D2%5joHZ3M8g*#&aJys2NFcc&7#c|hr<_HW%>L%Z5=xJM2 zjK_31tm_EL8;yTNYnGukpw<=_ha?xrt_^8{g>zpaN}VpL_2miG7>L^XB+znY_0UzI zqoS0Dl=83q z4e%Yz6Ot@@G`kjboqFPVHvum;_TI#H*^3T9Z+9)5{e)xcpM}3=2!9*IKyIj#$VI)| zW*G75+-rl2Fb`k4LEDxK^RI7-E1W=UFLE9^05(CUDXjPH7l;Uh8@fR&;V*oC8^qyd!jEf%Fx`ylY?+vgWzz#bB2w2xiV4iEl>`ds9J44Xu z_NcxZ0 zeT4&k+##=qb=@cgrSg9sLKjOznC3zoDB^NQN`fw`bSZYE#&s}Zr=abQxw9&r{4$`H zvMRodG!KfJK8fzR+*ZnLcytE*b;yB&#{%s=^KRsfW*FE+u=RI4eKv1t>#7;?KW-Hk zK+SAvNGoODfI=R*eQlO~#bJ^8b!=oaJH&E9*HsTem@1iG*`JOt#~esr_9&^Q;;)n+ z*a#!2iFQLIUr3dQ6;H3}vZHld!KD!XAzSd4PB{>h1#ytIbNO^-9w`p#lkHA>af|N} zkqBG<(@*TTedS`0YZh>`Sr6si$bi|F?3R{93pn>;`XEr-?Z`&MO`y743VfCGhhFeY z-lyeiKBsMf>6iX8VJ-*ND}dCsR%e|vUZ>S7=1JUUIz3-k&N9d|I;x4`9dq>4ElIOq zwA9U=G3K@%M|lsTUFa~6U>17TIKrcucGew>QknmI%ZmXqY7#Ht1F%V;BBoYPrJt)9 z%$`>s#9KU78+P2MUn6Z=u-|db0kbx6iaBb2o@Qfg0OlXXPGc%N!B@$m;YwV-GpK{b zm7*KA2{QweFPcpwt_rix%B_L9X`7&#l@w<_b;aA-rUB4JR5e^g@D!`Y=+6+g5r6uc;OhvV)a5JNc`drrSZihJ-#N>~^omC=skk~bOqcawv+8iz zap)f>9E_We$W`m4oDt-!j#blA(3)Frq(i&U@#MveFIb2N{>aZadEBpyjNhZ-epUV; zBvJ+LIe$qL4NORK-LefYZG{+I#PTBz@g@5+l1QyovLz>|iXc&uEJq-}mEL<#Y#Py8=9cu|f z(p(*Q-w#nH9({dzWV&?qE}zmqh9ml{%qtm!lB#% zELV~8K=X*JU&wEU)+EAt|L1?acaBlv~$=Q@n@E-zWAT8SUlAb2sI`sZV8TP7UPcwmVHw zvlQ{0U4oO=XRc9L*eOKIp$>TSP|6Zaln~DW{IU?7J|(;xCYbkQ3{s*q$bPztY@!Kq zElQ|>e;t85DU*I+MVpis@9yw%E$1$PcIE70lq5D-43=Fx6~S2eW*BZCLEg1?N7wPR z*^rx~FxvN>*~#SsI-~#YJ*MD_k3@&hirD_`^1j7p=KABt0m{0dl^=in+jOXFHvTib ze_%Qn8VwVblLih7AW{|6ecZ_XzPF5}#EnU`9+3Pe=T5ZNy*H|t*O<1CC0vxR-1hMv zyB@Oy(?*&?`r%b%eGPkdke}hrVc1Fkp&FxC`)4s#iyLfB0%19C^WSA{CR@;4cB2HG z$K>_pzZw9Bc|ON;_Jb_10?js`PAM9+D?EWAOW2a6sjWul?WQEvQ1qp9))=%Mw<;!M zChE*>`g=yMdq%c)eYH6;R3EfsCwH}P{^xBg4O^TIpwDEFnTUKh_y>WTt|{;IJo)w_ z`9w^{A3mP)#Bs?jotDKdHC7Ly$ROdsT_8y7>Lx(zSM&HBJ)E^-3tQ#qt#+39R0*v= zG(zX_-@IOQRE>JLr8LN+)BWIouxNl^`)n%pd>}q_J_{C>hsk1<=1H3H%Pis9htS=- zO<%UGB$)N96>0iUF2n&eYr}~N$PyqtixwP+*(W85rcf~Yt-ztIq8P#mj`oCT9R_y7 zI-l`HoMvD}!KQ2E7A@C05$7WZfGJE%b=HZ9Z9|i3*?*}_u)4@7jb)Nt+E((B24i#< zDBu?POzg=lWSuO-1e)hyUo!f@GcQY2m698#FN~N7Tly(^R3IvoCT8C@YTaw2A~3CL zg{Z-kr1()X0LOe%C5m^@$O+GG$w%lpE-5BT{^Zmxx)yxpYgY_Wr8Da6#b4DtL|RQI#;dl5c5>w^zc_O+5^3 z&_p08mP)Fi%}k4p7nc|4g5mpEM2n3$gwtWav^KcLXN>v~8+t3=^ATT*0tjy}78Rb$ z@73`HWkj=gm1NoU{|WR9E_Aw3kniSQx0cu8EIR^TL3?l~qzC?@Ks5$3)C( zYqF0Y9^H41Atp3D#J9ar939JU9n_58P<>6jPa`$d);est^U|4Bb>A>WmmN;7P zwnxTBO};LDYBy{$u~JrKmK*^lUHAGR?K%!#?Wf9kiLd)!X|Q3o#m(XqtBhs7tr?6L&;EXLQ&?rc{Uj#zY>DsT}PKM-9&i&1h4 z^g}?KBtg}wB7~~*7HAhbUK8*@<>;pVl$2YgKQ$anj}_CIR>=#1HWsQ9 zj{~ZJ!+qz44F|1-Qp>c)#D7g&>GpHm-ijJ9Z`RiQw6hYYwlb!4RR^WTY}4NeW6l-s z(ioLAw4}Um*{7?|km8=5Ef`zui4mq)!8Px2><{Oi;Lry_N!)aB>Kyu7Sg{cj7Ysi6 zg-V*Vh(@Dz&y6sWSB&ByofLXaeK1S@JnmfXOKp%aez-xavi7l})TRGNC*DLyy!ly` zj%HnMuZe!Pf&vN)HVSCkXEhDh>7_utC)1w~Z|eGJl_(k0!tOqL%DO&aTa=GWuqw+; z@|t9MXx;aLmoNCwWPdt@*O$m>I3k3=d}Xmg^#C%^Uv)&8Kujnh3=u_4j1dp9pYGh8(i`` zCIN0h^sQxht)~T-;~iHRZUIekex3VCX8Z=P7DJ`C$q#TlP%quDM#iGOGoxIw5qCuV znS)XF=Il}>bjGb(cV@)~L=c34XF6xpju-=ta^@}$Xv9HKcqO6-eoI4OgJ2!s8MexA zq%Lk~2H|%XVKw7gmuyeYICRy+sef}h?&Rfoa&D8u{n96ZkGqRN5Me5EI}MK6BX9if zgTF?LxhWh`E@gY1_obb{y>Sgy(!^hUVU{Nz`Wb^4r-$Axy#qr(IXK`ZP#3z9Cf;zT z0{w9>2(0qsG_-6p!4F;ua0qW>H4Kk`r94QqtW%qP?4e4VD9MI|a^Xt9ar@5=_DfM# zBxm>Z5c$wrxX12lm()|im8=2K%SOYS&hw=VQKD;VwT729!x(v*u8LKl?WgRBK&XwI zaM`K<2Ae{RrFixS?2F!Efk^X^2tEwP5>(+=`le9s61OVM!j*M%8yZAcFk%!jSH~9K zz;V?w3cHQ)JPHoKg~mNrAd$)|ij4VtAA~!b$z0^M(p4S@MnN0xa+F`WQ}6auKZz{H z6accRwtu{2T)f}0;N(B@=;OcuOza-aMaWyZck&eQ@(A1UqzBE_fSF=xF~blq!i9;y z4Dy^i6fYpy(`13xg~|B!;9t{U8iNM-)iAu+EBccJqhjU^YY38p;35B!DFMwQHq$|b z8RIAVD6;E&Mzj7OFaXJxVcN~tu`gWfgJcCN?>AW;U%Yc-V7^t{1HpQ+!>R9&G0S6|3Dh1I!Dbqhx(E+0q zsg;-8%0I(!dt4>Ug9{w>?W(qS{d^8MqS2UpLXoyZ@!3H4&Nz``&6nSEA%BNnV*S%) zcv(o27AWZ%+3`rys~<^70)&bnV+`TKq#s!DkAyh?C44e-w+Hh3|3Vy=(GN8-89vC) zw?)nRS7}$JdB-7NOqMs{?XyNK&aB(sgE>r=#l4Qca7hH|iC%1vN6N^jfyim;YbJgX zze^9^O0RP>Hy(v0x*cmpS7;G2iXzCLDov!UUJnwf1HrR7zeL}A07wc5&RcaRR zIwd@VobFI*9pck-xb5PX^z_!6%gni8Q3u5>y7FdfmWqx}NB{4umKUbi8>rFyCj4yr z0qt>%9s{Q;x{n@zNbKv#@qTd0-h|6Yn)I(17n~;nP$V>6B)A3HgKJfH?-cjP6p+6> zR-oOt`+WRZPVwY*tc66qNwuiNn{L=c#3?VMxZU@a*L@YB-n*d}^2?TIke@5z$(QV3 zsSC=2@(`fA`u3&QG5vAjV@0NKR+GsGH3?u`?q9&dZtwc`)z5!d;oBL3t-tiQrxWv* z0aBQV5-?Rhq>tgt^Q)N);~Bb7YAvFW)F;QD$xp&D;E2uYCn3|v>04k4a(bkd+ulF? z59Ed;Lw&9cjfro!aCk%|wQ#)A+ty!9@XH>(cWysYV+VV%+xvb~3oc=ulum(mJ9eX` zHNKvUbyzzFtvw&c5Dpjya|@=Qs>L@Th!kD>I}qktPwfgPa8&(q8Z2GUIP?-V`BYW@d)FfBs9K`cVH z|Br`XP?P-~J9xotRi1m2?U@~de8fIivf>AJU;3vpG+?iXkv44SY(%Z|==k*`Yek$P zl^Ra!Tm35$M-x1Qvgfl9_~|C6=aqU}<@AQN1v}UXLDDd>cL8YT;qg;wA0cvIBBzf7 zF9^XINsVP}TjWVrdFQ_axK8wkAW<8?$OW~{@d+(md~S0q$5`1_6ihagUS}b{jl&*P zqu&eT(t-~m0Qy#v)leH5&ndr5Xxm5pojp9mdn(ZrY3En^>UXRFcc=9Hy{(^(gKge8 zs@WuHf2&!@0do}6#@-c}%wMo@+*(GT4J7~!?VM9NAZaqSa>R+`&$w@S?t*R>?lJ}; zMC8@>5*i2+bi&4F1f1<LC^dSO4o4oE+GQMp}FVcki2OcWl=)1l=+-nVr7S$KPUb81vQl7xg4NG(` zO=j;$s^$PyJHgW62-35+da-_zB5(s1YR<7d6%TymK(o#8tBWAFA*{K`Hp4&QH7B?^ zaR8cpEaL$2?utzfygp^&AS(XDXy#2E;q{M|Ob>EC(9DI#I{tPz`Ug@(r+a5bc=)HjG}KFnjQv$vPkoD% zHKQ)p4N^lM2UN}qiohFm^U;51TD>VJg3VbjDQ!JK4M39^J^nZcA>_&^wvZ9Rlz7+C zb9M(VE}ZHdUlZDSW^`$Xt-6J_DwW}_Mx@~@Des(k2_JPhc8#gc6V_H5 zB2umtlX;aCieofz(ocZBP~;p4EcAFB(IadauqW3ccR^qLT`!l2}SgYEM`b`?XW-0p4_=ciuthifIIs1?&wLM zTsNIe4yWMP&ZiUgBEO7}dWk}`j#Q2hr-J{8*>c&rwDhO&8RV>xY<%e_aE3EpXG~wmv zcYpU=_d^9qQkwvuut^byiXfaQk7Y(KU#piP5eYMzhy;%K=8I~_@SFDd7jh##Ugg6* zUjI{}J^raElxx3T__vULW&}HO{Ns?ZVEVyV($OdDB)F@W&ZNSXn+2w~6{fh+T`%Ef zZ7x=5@m$$2v!Extg(fDtujNDRwxP3o4Ta979=c1{R+9#+dP~|}TW*~sx^psNlQ&?+ z>-|L}>(-0;o%OU%O}KX!u{6{?JLaC|J$C+}#O6GFU^03cHN$cst@`wU+Y#EPV!9EK z+W?h&^Xho^))Djo+tr`XmBIfLi&6JBz?ku)=Xr|%wHhR|Ra*b~mRkE&!+SeL#n7vV zg!Z~)^*0{K07%5(9|2`SI5Us96GV_>G{G$l(A}%@JElZ}oxvR%+W&wT4FX>%=yxg~ zO#QIYX)|=}NGW() zduCq*nF})4ye?mfN6b|}+N0l`6K0gRO&^~m__i>AdAWo$US~A;q+<^re3E~SiN80H zQ($T?FtXCI#DudXWt^yC)XiSI*WANxrySY}^+IPK@nGLs$9nfQkEzlyRahJCe2s}; z^xAK6B4L-y9&6LZ`#d)2&1cC+UPWq1=}*Y-X z`c4uSM@Wij?W~sWl0&JCsZtzTZ$j;9-VV$KL7iwA^4k(1xr!itMI%Q?`XPJ5y3)T{ zMcB+_ICi}M{U3=c6)SRbX+ ze=Xq=N1j79M|C(d3k)nN>!CgBU6J-pGA%NIZ9cBS3O z97P(Q-9_HSGI$j24yG3JIzw6_j33giD1M|!9n>rr@!TmBx#hoafmQ;LQSh_69`Z=L zx$yl}Cey`0J>bvfEMA-;PtTpndcW9lTN@)l>lxRrLJq2?kV1*AZf*R`lHIcQYS^o| z+`0L;xjygh+R5S44<4t$aNJ+l^vtS2Ugdg0?`~2>slOF>+4ppAIG&_dj824$BrN0| zww_v;-ACj^B2q8a|MzkiKg9$MCM)9zqF~98ihe(JLR17+4-WKBG()!)b1F@LRbGD^ z`0dEi=qa_DOzbVtu*iAC{&}!4@N_`auZ`yobPJkQ&+QM#T~8Nx6ILH z0gC~TJ7(@RXjXHNlWsW{H$CRnoUi?o!%Dw{W&)b~A52IOL{$-36nDa@=vzI_5%3^C z>N}}?WkG^cw4nIFJP3?FMiBW3kiJ``%3?^kBNEs1^%i%ZsFnYKADM0fKcQ~I|M7gu zN36tAnEPRf9+tAoiCN56*H}Zw`KkVRzM=-CV)f$INo*xtRy8*K=BWEjtqIX1nkQ6PHXx9hEKJp@)_pZ61LLq1`2` za=Ivmw@Rj<+5^55A&0?)p>b~l%3ef5DedI~yWt$`I2pf)Su_sXvywp$-@w@ym2K2gm&BRfH8jYtzZefQwxjfG&}!Fc_0>P$50 z7FHsD8BVS67ZScxd+P6?X$_RWH9td*mT61C956_0a9f&9)_ z+`}vBn*Gc->Do`KeU4wSeeORM_OtJ}1lunw?zO1NXGU1LZFKc=1`B`-M@38E+G+&C~?q zl4!os_esCQI+K*9s~xcnC%JlfNVdvURD(F*r(4z)5Ug%B%0yeCzVI*bK%75Kf} zxLhW_HdYgBK+vMH;`I1g|nq zrdZvA+13_$<*8m{_5;kU_kc$6b?jVLwYgd2u;aH&1hgomy>5<|CQVr%q?q<8`nZOC zb;P83MP%INLebrSky^w^OnHO4>WYs3GS^CleB-BM0A^gY)|F^G?QD4uJLv;<=;Oc0v{QF=kF&r`R*q5#ul< zg8>Bsfc3(8$QA=JD@JncR~9MHKHP(F(}L7&XFNSXxzij8g&}cR&f}ydN8`~24Qgr| zohz1fe8(yPAws(LEE}X*e8ZeS`QXlV#dqJcPD479C^W_X?M}w4C{J4Dfbhn@VL~@0@`Fg`wmqeVOi}rxk2S35woy7@ zh}J#+MTKZ?q+lfooDoObqRBCeyBKEKZl--3O1ECr-q#^?&yf$Kl`N2~#fxY65_Y$mHEnKIE2u3$}k=8WdGMo)9d{=$#F(QzOk!GdqIunP{kFVCu{KKn`V^jN-JrnqB6HZ4%3h0$^$?mh~_!6JV?qL&^Dse&-!4e7FB=wZ(fj;6pq(D7F zY)BE&Rx&pyCaPIwYYT;ga*kvQfqCKb5Dlv9u%#CeamyrIJw~a$5+BBqe?B1-$Pp-- zgj%>q)}ymPk7u6Dykh2LQOs&$Jc-(NJfV_O-@9zd_U&;Jh?RrXMq+$OBi4%MS_|L{ zrC4ZC=+AgUzQ`a(?B=7ztAX}7wR5;o@bgJ{7lUeML@quN?Jo8mWKJ|cnvwC-A2{m+ zUYjJ!So0$p&Z)b;uX~B~Y?gyj>vZ3|%@ULy=Aw|n%vj3+4~WAcO?hMI_(@{^!^Y@) z0)V6(w{mc%)^mtg=T$S}FfwY2g>W8Icy7y&>)Ble7Sjmw@c@i4z)Z6Wc!i1}O-n?W zwT!D(^6XdMp;wW2w$3a%gB>Lyo&Fndo+76Q=H^x)0#5Wtj*N^kX=SjpQtgV3k#*{( z;7rt>NHI=WJEx@Chbqtsdsu+MASczEf7pwC&P>F+@Z!ir$C9Hdcu=8&8ciVDcb_$+ zNvr`v%R4;Wu8?Z15Lpzu;nbiF4T1`RqBx>YG|o;XBV)8J?6*3Y`$klKvHC-~67>?9 z7bl(EY0^fj7K`}O87g4TB&8P;jE3pQ82E&4&=PqQg>97Oj_H;q#st%nU3*B#4@xK> z{(g)x^OexLYg>h_Vq|$&ZqtwDWp04M<&+1|v;e5+H7qDm$R6Ppd?qUE<$IA(%}0Xp z#1+{GnJ|!_eU>3i(T_c|QL-1x#&Qw&6PW!7vUW72Oy`ytwdz=+_@m-@SMnJI1(*cU zYq%t@y<}A+SkLM23N4>z!KTN|C zX#~s#06#3wAm0Z0g@^FaxZ*!svk{FF`jMmjHY}(k#!?BK3CEZ~1FLJ>3K-+_EaZFq zuwTYuzkT9Ae}394L8=H)3JRI|4b@$Ge`+KsLdXT!rK{hxNxUev)f9l43xr4}!c48JqHWwHei30BM`uXp7 zmAl|iK)iiQuAVXrXVsxw!zDk^vt9Z#KHx`-^@rPFjxZ!p35SX?g+#wc018Pg(x`>! zY(rFuMt~u?cl4|6aP~VO%^`ag_tQXzTllB%>`b#D8A=KwBpBi-^^?E-@u2<@&imnVPJ4}& z<&&@bysV^@LQ;=b;{2PVia(OjFf<4f{i}3iY9H&%&iotrXCvpMW%3gq@LS9DJM2Hi zNy7gjIm+4nIW#@?+nW4AkpIs7Z4>xkluO^Z7Hhd18jm%mocvxD@O?3>a4GJ7`w9>9|A=N>o7`i2+(Z7#-)8>w+duxm zx&Xgp+!vQK%vZqw4D!E6Ncg`foPH2?%kbL1DDH?2Sh{l zIk5&PB8WM`h%kctcg7|UM&SXz{{M;c_6aj)W&dk{A?bseG{6XwBTDawOipl!F0zRB zmdW(*&P@Nw*=1%JV#Ikrtv7aHl0p%z;{kp6`xk$-Ff*!@fN$dfW9%#eF}0BSz#W^vcDxc<*cI z>t5eB)l!u{)1N%?sl9#uUaYhE=wDyAqCOkR&NA)3q;czNgiK3$bnkzNbEd--YYE2{9%YLZFVUj&QiaC^3QvfvOMwaI0eP|DeYv z-7JOvmY)%Bl0`f0%b&#JP>Q!2de+a;4_(XG-*RF1{oB-IRyru38J;!r(+Y_@GW#U1 zUXoGz7H9tlNcN%MuKq922zTg#Q6lk1vz5g8orTs5zxDUs+sVRp1m91X4yNDx+j|MY zPYQllR8ry@SLMZ@cectCe$D74JN&ORrdcfVbJWH$NA1{>e<`beAv0Z7c_4L_bM5H< z&!6GY(9SBm!Ah|Pe}%CKpFH zZm6a5gM@J&eg%y}W%X-cF-g?$WR&d_vP61`D!a)w!QW%~_-Nhb?ic&^58dvB*Nr!H z5_{E?+vpLt|J_kzqE>SzO}$V3`_5p1C~%KAULA@MYL%i+pXu^}czkn;x?$vyVDg4^ z$uvMwpSGiG!IKTb!k{PJGHS#DX9*AGuIILO$|Hh%(D@#am>DEx1jz(a${A>n<`e;k zrd4ZV=*$NEnV3S7l^TAQe#aowe_TJK84kfEYdhj(wiRdtNX+5NhlC<~O+kUBmKYo$J`SvJ{1-{8nkbo)}mt=f^Ty!Op_pcXSBJw7E{b3(% z!V}*kB9Z*AB1%WUeqF*CY8~7o8B^0_+jcQLRe6x}zY5k7zh|Rune$cUsZo2H%A-@F zGt%t9M8D>!Z}zDJ;*fR$HX2xnXybp1!~d|9c5sJ_M#FIK{AsTQ>U8W4BW6}Gh~_l) zf|K`o{vj#}ou$R}U2LOwpz2Z#!L8KdLlUWqoeVWS!Mb)lgxGpV?KZx9+Y*c>4FfTr zjZ@%xR~mscd_)%OcnMxKY+;T8+ktCmju>AHj0ByE!vbw*I9*gzM0WCqlZsAAi?-4% z+~#bCiRtKAI7G<1B+sep!R62 zWk*Im6M0E00Sp2KEu&-iOHiO&2H>?^OYrE`V7Mw!reU$v#BS9N_e0Yr8wqnDABt=y zzWhZMZqsayC!l;H?v7aH(aAexy0v0Ss5LU5C>O?9y@D4h17z$zqMIs9 zd%fkaH;H?OCj$iomJh7nEnB*aE6t152e?7rhBhk132zlY!_2iepvnML{% zxoL!iQS$V9s%L}qW}2#AIK~3lMx)BHGwPK@pp%k8WYtGSRFFOs-aYs+KXq17eqi`(PNx+n)zBFO%SU2{8waeivmN^vp-_q zh&jQqc4!I|qv?q!1W^o^y}0q(bDmz_Lj}ng%rib|m*F&3GC0w>tx$yNIF6D}B%glI zf4UiJnk=}p2E-ttae3CC9}?Qw1Ht(~eO7L9Uc@c}5)mDH?B6sApGm+Ts&|04Iq>Hu zj3ef?SE)nhQbVwbBuAm>olVixhiDsRwJ`nDQ@bukWUMU))M`2K$#HV+;8DuZ0T*H@ zSzW6-e>)G$+ftIAo3t2=WQl@Uxx!Vba~iagiEsF1)RLBc-BEqo6Hk;27y6d6LUTGXv7ku$14mf(f6^6nhQ4lTMx5Q&Z0TRi@stGnO8n&*SyCz30W-;`) zkmGtO#f#1t^+Y5WM_>5OVTRFZfY|8OIw8TR$u4>#?@oQFO}8&Lexq#p?3F@*1Y}@G z=v>w3yit+^c&AQDLF-LFxw%-~xNP9H(xu+8W9#}IF2W2>zcO93^U2z5jmhzbI-4*wb{!y{LOX+Gz4jhJPpYNMm zf{6jJ{45PjJ6(U=^J~(9_dfr8@w3e)9dZEN&nYJ?rS3_2`_x%|B~%$A!a z_<79@_pk?JsBx5IQBT6LG9WP6p_ojcRFhWkHnjD=x=QwC;Wow}>zecOb;v-)7vs9l zHt@+8N6YO^nnk0!T?6_uvPD{@TcxY4MT?CcVeUyQYT(8VMp8PuH=mAu`qvd%m+B4J zgCN5_3SEL)%?26-{ScCTI3{}-3k>ynB0w(K;=Q4J;Kic(Me6s^$EDy#?hAQGPdit< zaJ`T;*Fq~9`Y|`Fj{6Qwq$o$UrOD@-J_#WW!q8l+>Nsk1^mu~rf3}>c8mZ>jaXel>vNQb60E<<+Fip>JCd5}bJ z$NpI0Q!I1|S(Bi|?;LK@6^%O1K?+#CXjfB(d?ZnO*h(ebisd!l&*gt0I*u(>u6msa9^7NP%1e(7dAGmQ$yoJvhps*keiV9K7N3uMY&Qhmv4f%c4aN1TP}&Ew#kc z=*fmP)X6;hpf>S~v<%!;dH|BSu*JZCalJHb0zT` zC}2`v0B0j&y+Yc)nocIbHF=x4jlp9ZGK@$-qlsEM{LH3v0{6Q_l45rKj`|xs+{Ana zs9nl$nyRgU>pZN3!x==9CYyxj8U=u$R0(x==?{veTGqV>*(H;gaW*Wbm(1HOm}J(2 zz%6(;HiHS}+H`fRdx(Ij2i#Fk>TlQOr@J&AG4#>lxD@!naeRpkEPCVG_6tZsuJ-kK10B zlVIeExM;M%BF>a3MJ`67hL!tU!CMxtT?SAh2fB{TzAmd`=$I23iX=7I6l4C3IBIha zA$&eD+TJ-3^Fn1dIBsC)pCCC3(Oi^GV~iOUEt|SHQ|Hu=Y6b7o!uLu|*Pw92sPG;bnH4Rd&-#_h0L`I|CG*~&Wkiywrhg7~y8wg`TjH_(isJoDy}C1v zbzjRt=f=8LUsC!mU{LLm;=uiV(&1b=k1GZl0zJWgs)}#_a@&>&5YljSq0Ag zBsz#5r(YF*Y*fQiPF3sQnFoBElrJ-Kj)1g;cW)S8p;c^UL`yQ57G7jBR}2E;<;oB@gq6bi^=k61H= zC9-4>^Vt4()q_U-8yzvIok?_j4O&;QhZ8~E8oKdj`y(v2>E-cf8uzqXSa ze)dEzYjD2W@XnasOAd>B!ue-nU6(B1JOZ6N5#qo}u9!Z$;sjEkBQWyo46y<_&{@-X z2hOpYCPb>GVB?i@bJ>i%GQ4E6eDclsXsB(1P}{KC)dXx29>5VhZ8k`dc?X+qnF zJh9WGX;h`n!sK&D0;hQQLr2%fbJM+W9ncjE0u}Bfe~IbJJ#`mUB{79clEj7^S!?QD zYF&U9)FovtBU^8UrBy=XEs(dBSw&rCqfs<o_pbnWyfHG+TJYdcy-hqVs_I2+i`bxbJ6tCiD}c(1w|7jy}3HV;19C)>aJ$>!AN z?gDX_%<2S6e!_?vh?`m?wd^J7s-ZQuXPQmyu8P^%43XE{U{x^Ln{+p5?|K&&JHM;r z!UAT|zm`5p*A|VI46P~3$l(bBSTA^R^D58Cti6XKJ)0%*^w@VQr1M|ux$G1UF@nLJ z4ns2qMD~m*J*vpaa*w>Q(g&n2a4^~2{^1oFDMh8i=Ptb0Y+qMc>mvsJEuUm| zB&zuw-}d>gu+f>&Lc)m$PO!Po(o4{WAx4G9N&EJ#Bp-67xXf>tCx@ zO>g>A<$1wyR|DSe8s|3YbhG;sg9B7NR5ItjKB6`Nf&DSVLub1D*J~!`WZ0QG7)I!1 zu?3)rCil#vkjsJQ1Z1jpoe6`%vJN2b5R}>iga>zMD>aOh4u<-6b($z=;$bx!=2GN{ z28xXllXxKOFnibejhO4KXqjB;W$(-8a@9!&mQrUA6swEgo8Zp9Yb9l&vYW?&psAYe zfrQ_S*OD5#`&M8_q$r|%Yen=r>*(V~4t&uyw}?h^P$PR8>+6MJ`{MSrfNX&@?28W0 zK_^~W@d<*^?PB>&btpy10snH#?qOYcxDgjrav$$Gf^EdqB7W&Puc~0X*@bI{ZFzaT z6C)f{wquO!a$CI$u!s))3 zrPq0Mz;&}41I{985U({Ozr@E{k|%XTJYlRa2^fS{GeC|D5J*fU5N0o!fXArV@Vs`% zWVfzOL~Oft1LXmO%bZU6;&s~w&oGUlrXu11D2ggPPY;YxYR{`pbb2rmiOeBZlo}F~ z{@2G%OFE!084!+5e-g%Y=`RuJ{UV5&l5yBiyN{R`07W>+xDI6P{p%7WvFFJ8u?{)# zejYvHK7;eMq!I*Hy7W-A$U0IIJR9^>+zL`kL>TYwv12D)4O|uTqsTC?1UPzBzy{K) zlFtdyO2^{z+!ZO9_&vpkui+?(`ciWcL?EfoPwQCyGZ#(yDS`H!Rz9b4s{r>!rdkAQdSdGy%Q7Ys`<=Q4`1OxH;{E&u4_`C(0C2R>b!T_>)k zUX0)ggcD-LLp6^yeE9_X@k=m%p*_VzQm|exN!}=}Y|1s3B!>G)JEznE!hWLVz~Vn% zQ}%8!sWC~;{B23UoR@VuJx8j(J7y%oH#0igs#KZFL;dW=?Oc$~f+=m@29nPD6^seA z6ArCRTeAq(vL4oM+4}AxN;)pEE}kSr(Tf7>i=Q?U~~DXIRpB{L4l1mE6n z4!Y*PlfIkz=i+H{T-YZg_ORBx+AMI&2MdM)#z8^)_lfp{yU6Bg=-Lwm7<`D)A*T=# zIXkpjvhmvoQz*HW*Xh)AEh)t><<9>KZ5T^ggH#EV+#E_XH-E|4r4Z;qXKN<2ny^c2 zN+V2{42u7m7A0h8Ut8&CM2ZT@G(i7U@z6ek>u8%^guGDXA?|NiBHB)IIeuTY&_A1+ zO&Z)Im0zkqoe#%x*m6ANys>H!vd3{~^JPPfb>tZ;ou$6~<}*Guizer2<##2Zn(uQ6c`BE-&_yZ^lK%B%D3y7_pOO4AM)O z-Z^2t%Yd&qBt%m03rXlGdyerZt>@Hz;9E78ooApPN1g#lN)dUW zf`!%*W_q;c;OffVi+g)Jw&lM}-#;T8YWoPQKuY619NTF#O8fjgrtLC*5plPO0MXy} zop1F%PmeP8-ZE7nY6jKSzD2HBXYq>3?040-RMNf~a5sLuZ2b{ONlLMAyeYYUGz5GB zj`-7j+sn8a#1_3%ie%(Y#Nsa2`rVmPj#08 z%Uvm4^dWXHYA3D*8?n6MnUWMtP(l?2o$)58-~{+A8Z4sQFE&9qHo+`U z@WD7LOD6LST_>1Gc4hc z=LxP3*W#tsC?{E0%%Ya1u%=p~yykYhm#CPsg3n^q6}eMWgsk zE{`H^@%qU1hFd?_Jk66IwKoLQHiGzdp{_lR!`i=NbuFWWU!WEH(5oRz|2Vo>EoY>K zC&+FZ4x4>)5hg+=AVz6=WK6Yv_}$8d0#T$1XNBM@LA@PIO+U~BPp`0M#ILgYZmI06 zlkx(TcD>S(ItF&Qml>-ebUoRFIHf(ne5`3nMpF#m=i14lc*eM^i`v+=BD$?rDeqv8 zd_wlvWn@zx)eYPCI_iZTbpRs*Yr!a}gvi!xA4z1_l2UBSVwA2p+(!3vB6{{%Ewi6h zq!ha=wa(*Y89g>#hSWIbDxtr%bOf^=-@>|uB7C5WfAhA)EYG2Odc>7@L2+iqZ^4?P z)2LRx2{Oxb3VOT1xG+7=28QZ3OFBPUG1SIxYRT(#QfDT@_xN=xA)O0u~q#X z7`Ureqc*za$$|S>*mMWOgT>6I{&n(w7|mOfLuK>h){d_Yd%t1i*qC3iWHJ+#twiIg z>2DcH4e;i-!Msp4OLm?QNj_|DwvDAAYMOe0IE3|N(3Y5M&Oi&4PL?VN_up z+0u1)3bt8TY%dqS>&Y{N$d0~0{p`UyXbcfk#7h@e?yi9`p91+6oi({h@5tWtu>D@R8i*;!d&QL3spT+VobU6fs;sUDbZt@Ax<$2jo6dU~-ED*HzUE#NlhE4p&ntq1-&f(keAP^^ zbggTvKGl}4WB$ovh42c_&CG~8SIi=!B>*Y@E82Ao?W0^de-fUfIPcw>=kB)I3S2^J zk-25;8lHO?+CxDVBl05=$>uLM5? zi5Mp8LAz;05N#Swi5U%G3^`9#te{q{u

  2. e(3`Qx~%v91< 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�nF#vdq8D6TSo9A+$geo{bay(LF> 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≤b>jXG(LtYzs6)NI|Bs&-H5tjrEQT}U8X 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

  3. U&(R)i48x95P45FU`=#w^;y0DmFT(r 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

    $<E2FgvJS<;zX+U31hmWM1} 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 { require('./journeys'); diff --git a/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts b/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts index caa3ccac4118c..2d28655da4e1b 100644 --- a/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts +++ b/x-pack/plugins/synthetics/e2e/tasks/import_monitors.ts @@ -6,9 +6,6 @@ */ import axios from 'axios'; -import path from 'path'; -import FormData from 'form-data'; -import * as fs from 'fs'; export const importMonitors = async ({ kibanaUrl, @@ -22,29 +19,67 @@ export const importMonitors = async ({ // eslint-disable-next-line no-console console.log('Loading sample monitors'); - const form = new FormData(); + const jsonData = { + type: 'browser', + enabled: true, + schedule: { unit: 'm', number: '10' }, + 'service.name': '', + tags: [], + timeout: null, + name: 'Browser monitor', + locations: [{ id: 'us_central', isServiceManaged: true }], + namespace: 'default', + origin: 'ui', + journey_id: '', + project_id: '', + playwright_options: '', + __ui: { + script_source: { is_generated_script: false, file_name: '' }, + is_zip_url_tls_enabled: false, + is_tls_enabled: false, + }, + params: '', + 'url.port': null, + 'source.inline.script': + "const username = 'diawar.khan.shewani+conduit@gmail.com';\nconst password = 'aNL2sTGRbNYauc8';\n// Goto https://demo.realworld.io/ and sign up for username and password\n\nconst articleTitle = 'Artile No. ' + Math.ceil(Math.random() * 1000);\n\nstep(\"Goto home page\", async () => {\n await page.goto('https://demo.realworld.io/');\n});\n\nstep(\"Goto login page\", async () => {\n await page.click('text=Sign in');\n});\n\nstep(\"Enter login credentials\", async () => {\n await page.fill('[placeholder=\"Email\"]', username);\n await page.fill('[placeholder=\"Password\"]', password);\n});\n\nstep(\"Sign in\", async () => {\n await page.click('button[type=submit]');\n});\n\nstep(\"Create article\", async () => {\n const articleSubject = 'Test article subject';\n const articleBody = 'This ariticle is created with **synthetics** for purely testing purposes.';\n\n await page.click('text=New Article');\n await page.fill('[placeholder=\"Article Title\"]', articleTitle);\n await page.fill('[placeholder=\"What\\'s this article about?\"]', articleSubject);\n await page.fill('textarea', articleBody);\n});\n\nstep(\"Publish article\", async () => {\n await page.click('text=Publish Article');\n await page.waitForNavigation();\n\n // Fail about 30% of random times\n const passFailText = Math.random() * 10 > 7 ? 'non-existent-text' : articleTitle ;\n await page.waitForSelector('text=' + articleTitle);\n});\n\nstep(\"Post 1st comment\", async() => {\n const firstCommentText = 'First comment!';\n await page.fill('[placeholder=\"Write a comment...\"]', firstCommentText);\n await page.click('text=Post Comment');\n await page.waitForSelector('text=' + firstCommentText);\n});", + 'source.project.content': '', + 'source.zip_url.url': '', + 'source.zip_url.username': '', + 'source.zip_url.password': '', + 'source.zip_url.folder': '', + 'source.zip_url.proxy_url': '', + urls: '', + screenshots: 'on', + synthetics_args: [], + 'filter_journeys.match': '', + 'filter_journeys.tags': [], + ignore_https_errors: false, + 'throttling.is_enabled': true, + 'throttling.download_speed': '5', + 'throttling.upload_speed': '3', + 'throttling.latency': '20', + 'throttling.config': '5d/3u/20l', + }; - const file = fs.readFileSync(path.join(__dirname, './uptime_monitor.ndjson')); - - form.append('file', file, 'uptime_monitor.ndjson'); + const id = '1c215bd0-f580-11ec-89e5-694db461b7a5'; try { axios .request({ method: 'post', - url: kibanaUrl + '/api/saved_objects/_import?overwrite=true', + url: kibanaUrl + '/internal/uptime/service/monitors?id=' + id, auth: { username: username ?? 'elastic', password: password ?? 'changeme' }, - headers: { 'kbn-xsrf': 'true', ...form.getHeaders() }, - data: form, + headers: { 'kbn-xsrf': 'true' }, + data: jsonData, }) .then(({ data }) => { - if (data.successCount === 2) { + if (data.id === id) { // eslint-disable-next-line no-console - console.log('Successfully imported 2 monitors'); + console.info('Successfully imported 1 monitor'); } }); } catch (e) { // eslint-disable-next-line no-console - console.log(e); + console.error(e); } }; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_summary/tabs_content/availability_panel.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_summary/tabs_content/availability_panel.tsx new file mode 100644 index 0000000000000..29526a3fd73df --- /dev/null +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_summary/tabs_content/availability_panel.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { ReportTypes } from '@kbn/observability-plugin/public'; +import { useParams } from 'react-router-dom'; +import { ClientPluginsStart } from '../../../../../plugin'; + +export const AvailabilityPanel = () => { + const { + services: { + observability: { ExploratoryViewEmbeddable }, + }, + } = useKibana(); + const { monitorId } = useParams<{ monitorId: string }>(); + + return ( + <> + + + ); +}; diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_summary/tabs_content/monitor_details_panel.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_summary/tabs_content/monitor_details_panel.tsx index 18f74a61f2d7b..6209ddb7c5d2e 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_summary/tabs_content/monitor_details_panel.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_summary/tabs_content/monitor_details_panel.tsx @@ -71,7 +71,7 @@ export const MonitorDetailsPanel = () => { {URL_LABEL} - + {data.url?.full} diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_summary/tabs_content/summary_tab_content.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_summary/tabs_content/summary_tab_content.tsx index 25e71dc6fb9ab..402967e2e8d61 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_summary/tabs_content/summary_tab_content.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_summary/tabs_content/summary_tab_content.tsx @@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n'; import { MonitorDurationTrend } from './duration_trend'; import { StepDurationPanel } from './step_duration_panel'; +import { AvailabilityPanel } from './availability_panel'; import { MonitorDetailsPanel } from './monitor_details_panel'; export const SummaryTabContent = () => { @@ -26,6 +27,20 @@ export const SummaryTabContent = () => { + + +

    {LAST_30DAYS_LABEL}

    + + + + + + {/* TODO: Add availability sparkline*/} + {/* TODO: Add duration metric*/} + {/* TODO: Add duration metric sparkline*/} + + + @@ -66,6 +81,10 @@ const MONITOR_DETAILS_LABEL = i18n.translate('xpack.synthetics.detailsPanel.moni defaultMessage: 'Monitor details', }); +const LAST_30DAYS_LABEL = i18n.translate('xpack.synthetics.detailsPanel.last30Days', { + defaultMessage: 'Last 30 days', +}); + const DURATION_TREND_LABEL = i18n.translate('xpack.synthetics.detailsPanel.durationTrends', { defaultMessage: 'Duration trends', }); diff --git a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/empty_state/use_has_data.tsx b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/empty_state/use_has_data.tsx index 671c11da398a9..33bf8b23e4eac 100644 --- a/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/empty_state/use_has_data.tsx +++ b/x-pack/plugins/synthetics/public/legacy_uptime/components/overview/empty_state/use_has_data.tsx @@ -12,7 +12,11 @@ import { indexStatusAction } from '../../../state/actions'; import { indexStatusSelector, selectDynamicSettings } from '../../../state/selectors'; import { UptimeRefreshContext } from '../../../contexts'; import { getDynamicSettings } from '../../../state/actions/dynamic_settings'; -import { MONITOR_ADD_ROUTE, MONITOR_EDIT_ROUTE } from '../../../../../common/constants'; +import { + MONITOR_ADD_ROUTE, + MONITOR_EDIT_ROUTE, + MONITOR_ROUTE, +} from '../../../../../common/constants'; export const useHasData = () => { const { loading, error, data } = useSelector(indexStatusSelector); @@ -24,8 +28,9 @@ export const useHasData = () => { const isAddRoute = useRouteMatch(MONITOR_ADD_ROUTE); const isEditRoute = useRouteMatch(MONITOR_EDIT_ROUTE); + const isMonitorRoute = useRouteMatch(MONITOR_ROUTE); - const skippedRoute = isAddRoute?.isExact || isEditRoute?.isExact; + const skippedRoute = isAddRoute?.isExact || isEditRoute?.isExact || isMonitorRoute?.isExact; useEffect(() => { if (!skippedRoute) { diff --git a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts index 7cbd2468811c3..23825097577a1 100644 --- a/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts +++ b/x-pack/plugins/synthetics/server/routes/monitor_cruds/add_monitor.ts @@ -27,8 +27,14 @@ export const addSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ path: API_URLS.SYNTHETICS_MONITORS, validate: { body: schema.any(), + query: schema.object({ + id: schema.maybe(schema.string()), + }), }, handler: async ({ request, response, savedObjectsClient, server }): Promise => { + // usually id is auto generated, but this is useful for testing + const { id } = request.query; + const monitor: SyntheticsMonitor = request.body as SyntheticsMonitor; const monitorType = monitor[ConfigKey.MONITOR_TYPE]; const monitorWithDefaults = { @@ -51,7 +57,13 @@ export const addSyntheticsMonitorRoute: UMRestApiRouteFactory = () => ({ formatSecrets({ ...monitorWithDefaults, revision: 1, - }) + }), + id + ? { + id, + overwrite: true, + } + : undefined ); } catch (getErr) { if (SavedObjectsErrorHelpers.isForbiddenError(getErr)) { From d7ef0ee8736fa0dd1f4e6922ec7ac8ee2d9b1162 Mon Sep 17 00:00:00 2001 From: Tre Date: Tue, 12 Jul 2022 16:48:11 +0100 Subject: [PATCH 12/42] [Archive Migrations] Follow pr of https://github.com/elastic/kibana/pull/135200 (#136205) Drop unused archive --- .../kbn_archiver/spaces/enter_space.json | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 x-pack/test/functional/fixtures/kbn_archiver/spaces/enter_space.json diff --git a/x-pack/test/functional/fixtures/kbn_archiver/spaces/enter_space.json b/x-pack/test/functional/fixtures/kbn_archiver/spaces/enter_space.json deleted file mode 100644 index ba375c51d97fe..0000000000000 --- a/x-pack/test/functional/fixtures/kbn_archiver/spaces/enter_space.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "attributes": { - "buildNum": 8467, - "dateFormat:tz": "UTC", - "defaultRoute": "http://example.com/evil" - }, - "coreMigrationVersion": "8.4.0", - "id": "6.0.0", - "migrationVersion": { - "config": "8.1.0" - }, - "references": [], - "type": "config", - "version": "WzYsMl0=" -} - -{ - "attributes": { - "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]", - "timeFieldName": "@timestamp", - "title": "logstash-*" - }, - "coreMigrationVersion": "8.4.0", - "id": "d1bd6c84-d9d0-56fb-8a72-63fe60020920", - "migrationVersion": { - "index-pattern": "8.0.0" - }, - "originId": "logstash-*", - "references": [], - "type": "index-pattern", - "updated_at": "2018-12-21T00:43:07.096Z", - "version": "WzgsMl0=" -} From 9c48dae12f8ce5886a27059708d7b6159f9003a8 Mon Sep 17 00:00:00 2001 From: Ioana Tagirta Date: Tue, 12 Jul 2022 18:13:14 +0200 Subject: [PATCH 13/42] Clear fallback flag when snippet is disabled (#136180) --- .../components/result_settings/result_settings_logic.test.ts | 5 +++-- .../components/result_settings/result_settings_logic.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings_logic.test.ts index f498d53c509d1..1d8873da7f816 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings_logic.test.ts @@ -680,10 +680,10 @@ describe('ResultSettingsLogic', () => { ); }); - it('should remove rawSize value when toggling off', () => { + it('should remove rawSize and snippetFallback value when toggling off', () => { mount({ resultFields: { - bar: { raw: false, snippet: true, snippetSize: 5 }, + bar: { raw: false, snippet: true, snippetSize: 5, snippetFallback: true }, }, }); jest.spyOn(ResultSettingsLogic.actions, 'updateField'); @@ -696,6 +696,7 @@ describe('ResultSettingsLogic', () => { { raw: false, snippet: false, + snippetFallback: false, } ); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings_logic.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings_logic.ts index 99b3e6157e227..6b4de636b9c25 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/result_settings/result_settings_logic.ts @@ -272,7 +272,7 @@ export const ResultSettingsLogic = kea { From 068e08516dfb556bf269e1ac0f663e2d9f19dc9f Mon Sep 17 00:00:00 2001 From: Ievgen Sorokopud Date: Tue, 12 Jul 2022 18:14:05 +0200 Subject: [PATCH 14/42] =?UTF-8?q?[Security=20Solution][Detections]=20Field?= =?UTF-8?q?=20overrides=20cannot=20be=20set=20for=20ML=20=E2=80=A6=20(#133?= =?UTF-8?q?494)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Security Solution][Detections] Field overrides cannot be set for ML Rules via UI (#84836) * Fix integration tests * Unit tests * CI fixes * Review comments * Fix types * Review feedback Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../exceptions/add_exception_flyout/index.tsx | 16 +- .../edit_exception_flyout/index.tsx | 16 +- .../rules/step_about_rule/index.test.tsx | 206 +++++++++++------- .../rules/step_about_rule/index.tsx | 10 +- .../rules/use_rule_indices.test.tsx | 61 ++++++ .../rules/use_rule_indices.tsx | 24 ++ 6 files changed, 229 insertions(+), 104 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_indices.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_indices.tsx diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.tsx index 3ea72e1bc8426..28832af6cbf31 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_flyout/index.tsx @@ -36,6 +36,7 @@ import type { import type { ExceptionsBuilderExceptionItem } from '@kbn/securitysolution-list-utils'; import { getExceptionBuilderComponentLazy } from '@kbn/lists-plugin/public'; import type { DataViewBase } from '@kbn/es-query'; +import { useRuleIndices } from '../../../../detections/containers/detection_engine/rules/use_rule_indices'; import { hasEqlSequenceQuery, isEqlRule, @@ -67,7 +68,6 @@ import type { ErrorInfo } from '../error_callout'; import { ErrorCallout } from '../error_callout'; import type { AlertData } from '../types'; import { useFetchIndex } from '../../../containers/source'; -import { useGetInstalledJob } from '../../ml/hooks/use_get_jobs'; export interface AddExceptionFlyoutProps { ruleName: string; @@ -159,16 +159,10 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ const [isSignalIndexPatternLoading, { indexPatterns: signalIndexPatterns }] = useFetchIndex(memoSignalIndexName); - const memoMlJobIds = useMemo(() => maybeRule?.machine_learning_job_id ?? [], [maybeRule]); - const { loading: mlJobLoading, jobs } = useGetInstalledJob(memoMlJobIds); - - const memoRuleIndices = useMemo(() => { - if (jobs.length > 0) { - return jobs[0].results_index_name ? [`.ml-anomalies-${jobs[0].results_index_name}`] : []; - } else { - return ruleIndices; - } - }, [jobs, ruleIndices]); + const { mlJobLoading, ruleIndices: memoRuleIndices } = useRuleIndices( + maybeRule?.machine_learning_job_id, + ruleIndices + ); const [isIndexPatternLoading, { indexPatterns: indexIndexPatterns }] = useFetchIndex(memoRuleIndices); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx index 60c7af78bd4b7..0b6a39530b80b 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_flyout/index.tsx @@ -34,6 +34,7 @@ import type { import { getExceptionBuilderComponentLazy } from '@kbn/lists-plugin/public'; import type { DataViewBase } from '@kbn/es-query'; +import { useRuleIndices } from '../../../../detections/containers/detection_engine/rules/use_rule_indices'; import { hasEqlSequenceQuery, isEqlRule, @@ -60,7 +61,6 @@ import { import { Loader } from '../../loader'; import type { ErrorInfo } from '../error_callout'; import { ErrorCallout } from '../error_callout'; -import { useGetInstalledJob } from '../../ml/hooks/use_get_jobs'; interface EditExceptionFlyoutProps { ruleName: string; @@ -137,16 +137,10 @@ export const EditExceptionFlyout = memo(function EditExceptionFlyout({ const [isSignalIndexPatternLoading, { indexPatterns: signalIndexPatterns }] = useFetchIndex(memoSignalIndexName); - const memoMlJobIds = useMemo(() => maybeRule?.machine_learning_job_id ?? [], [maybeRule]); - const { loading: mlJobLoading, jobs } = useGetInstalledJob(memoMlJobIds); - - const memoRuleIndices = useMemo(() => { - if (jobs.length > 0) { - return jobs[0].results_index_name ? [`.ml-anomalies-${jobs[0].results_index_name}`] : []; - } else { - return ruleIndices; - } - }, [jobs, ruleIndices]); + const { mlJobLoading, ruleIndices: memoRuleIndices } = useRuleIndices( + maybeRule?.machine_learning_job_id, + ruleIndices + ); const [isIndexPatternLoading, { indexPatterns: indexIndexPatterns }] = useFetchIndex(memoRuleIndices); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx index d8d9de4cf85c2..40ffc91be47aa 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.test.tsx @@ -7,12 +7,12 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; -import { ThemeProvider } from 'styled-components'; import { act } from '@testing-library/react'; import { stubIndexPattern } from '@kbn/data-plugin/common/stubs'; import { StepAboutRule } from '.'; import { useFetchIndex } from '../../../../common/containers/source'; +import { useGetInstalledJob } from '../../../../common/components/ml/hooks/use_get_jobs'; import { mockAboutStepRule } from '../../../pages/detection_engine/rules/all/__mocks__/mock'; import { StepRuleDescription } from '../description_step'; import { stepAboutDefaultValue } from './default_value'; @@ -23,16 +23,11 @@ import type { DefineStepRule, } from '../../../pages/detection_engine/rules/types'; import { fillEmptySeverityMappings } from '../../../pages/detection_engine/rules/helpers'; -import { getMockTheme } from '../../../../common/lib/kibana/kibana_react.mock'; - -const mockTheme = getMockTheme({ - eui: { - euiColorLightestShade: '#ece', - }, -}); +import { TestProviders } from '../../../../common/mock'; jest.mock('../../../../common/lib/kibana'); jest.mock('../../../../common/containers/source'); +jest.mock('../../../../common/components/ml/hooks/use_get_jobs'); jest.mock('@elastic/eui', () => { const original = jest.requireActual('@elastic/eui'); return { @@ -45,7 +40,24 @@ jest.mock('@elastic/eui', () => { }; }); +export const stepDefineStepMLRule: DefineStepRule = { + ruleType: 'machine_learning', + index: [], + queryBar: { query: { query: '', language: '' }, filters: [], saved_id: null }, + machineLearningJobId: ['auth_high_count_logon_events_for_a_source_ip'], + anomalyThreshold: 50, + threshold: { cardinality: { value: '', field: [] }, value: '100', field: [] }, + threatIndex: [], + threatQueryBar: { query: { query: '', language: '' }, filters: [], saved_id: null }, + requiredFields: [], + relatedIntegrations: [], + threatMapping: [], + timeline: { id: null, title: null }, + eqlOptions: {}, +}; + describe('StepAboutRuleComponent', () => { + let useGetInstalledJobMock: jest.Mock; let formHook: RuleStepsFormHooks[RuleStep.aboutRule] | null = null; const setFormHook = ( step: K, @@ -62,6 +74,9 @@ describe('StepAboutRuleComponent', () => { indexPatterns: stubIndexPattern, }, ]); + useGetInstalledJobMock = (useGetInstalledJob as jest.Mock).mockImplementation(() => ({ + jobs: [], + })); }); it('it renders StepRuleDescription if isReadOnlyView is true and "name" property exists', () => { @@ -80,16 +95,17 @@ describe('StepAboutRuleComponent', () => { it('is invalid if description is not present', async () => { const wrapper = mount( - - - + , + { + wrappingComponent: TestProviders, + } ); await act(async () => { @@ -108,17 +124,18 @@ describe('StepAboutRuleComponent', () => { it('is invalid if threat match rule and threat_indicator_path is not present', async () => { const wrapper = mount( - - - + , + { + wrappingComponent: TestProviders, + } ); await act(async () => { @@ -137,16 +154,17 @@ describe('StepAboutRuleComponent', () => { it('is valid if is not a threat match rule and threat_indicator_path is not present', async () => { const wrapper = mount( - - - + , + { + wrappingComponent: TestProviders, + } ); wrapper @@ -170,16 +188,17 @@ describe('StepAboutRuleComponent', () => { it('is invalid if no "name" is present', async () => { const wrapper = mount( - - - + , + { + wrappingComponent: TestProviders, + } ); await act(async () => { @@ -198,16 +217,17 @@ describe('StepAboutRuleComponent', () => { it('is valid if both "name" and "description" are present', async () => { const wrapper = mount( - - - + , + { + wrappingComponent: TestProviders, + } ); wrapper @@ -255,16 +275,17 @@ describe('StepAboutRuleComponent', () => { it('it allows user to set the risk score as a number (and not a string)', async () => { const wrapper = mount( - - - + , + { + wrappingComponent: TestProviders, + } ); wrapper @@ -318,16 +339,17 @@ describe('StepAboutRuleComponent', () => { it('does not modify the provided risk score until the user changes the severity', async () => { const wrapper = mount( - - - + , + { + wrappingComponent: TestProviders, + } ); wrapper @@ -359,4 +381,30 @@ describe('StepAboutRuleComponent', () => { expect(result2?.data?.riskScore.value).toEqual(47); }); }); + + it('should use index based on ML jobs when editing ML rule', async () => { + (useFetchIndex as jest.Mock).mockClear(); + useGetInstalledJobMock.mockImplementation((jobIds: string[]) => { + expect(jobIds).toEqual(['auth_high_count_logon_events_for_a_source_ip']); + return { jobs: [{ results_index_name: 'shared' }] }; + }); + + mount( + , + { + wrappingComponent: TestProviders, + } + ); + + const indexNames = ['.ml-anomalies-shared']; + expect(useFetchIndex).lastCalledWith(indexNames); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx index d37adc147e6cf..5927a762a583a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx @@ -45,6 +45,7 @@ import { useFetchIndex } from '../../../../common/containers/source'; import { isThreatMatchRule } from '../../../../../common/detection_engine/utils'; import { DEFAULT_INDICATOR_SOURCE_PATH } from '../../../../../common/constants'; import { useKibana } from '../../../../common/lib/kibana'; +import { useRuleIndices } from '../../../containers/detection_engine/rules/use_rule_indices'; const CommonUseField = getUseField({ component: Field }); @@ -102,15 +103,18 @@ const StepAboutRuleComponent: FC = ({ const [severityValue, setSeverityValue] = useState(initialState.severity.value); + const { ruleIndices } = useRuleIndices( + defineRuleData?.machineLearningJobId, + defineRuleData?.index + ); + /** * 1. if not null, fetch data view from id saved on rule form * 2. Create a state to set the indexPattern to be used * 3. useEffect if indexIndexPattern is updated and dataView from rule form is empty */ - const [indexPatternLoading, { indexPatterns: indexIndexPattern }] = useFetchIndex( - defineRuleData?.index ?? [] - ); + const [indexPatternLoading, { indexPatterns: indexIndexPattern }] = useFetchIndex(ruleIndices); const [indexPattern, setIndexPattern] = useState(indexIndexPattern); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_indices.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_indices.test.tsx new file mode 100644 index 0000000000000..d22b9a8d83220 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_indices.test.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; + +import { useRuleIndices } from './use_rule_indices'; +import { useGetInstalledJob } from '../../../../common/components/ml/hooks/use_get_jobs'; + +jest.mock('../../../../common/components/ml/hooks/use_get_jobs'); + +describe('useRuleIndices', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return handle undefined parameters', async () => { + (useGetInstalledJob as jest.Mock).mockImplementation((jobIds: string[]) => { + expect(jobIds).toEqual([]); + return { loading: false, jobs: [] }; + }); + const { result } = renderHook(() => useRuleIndices()); + expect(result.current).toEqual({ + mlJobLoading: false, + ruleIndices: [], + }); + }); + + it('should return default indices if ML job is not specified', async () => { + (useGetInstalledJob as jest.Mock).mockImplementation((jobIds: string[]) => { + expect(jobIds).toEqual([]); + return { loading: false, jobs: [] }; + }); + const defaultIndices = ['index1', 'index2']; + const { result } = renderHook(() => useRuleIndices(undefined, defaultIndices)); + expect(result.current).toEqual({ + mlJobLoading: false, + ruleIndices: defaultIndices, + }); + }); + + it('should return default indices if ML job is not specified 1', async () => { + const machineLearningJobId = ['ml-job-1', 'ml-job-2']; + (useGetInstalledJob as jest.Mock).mockImplementation((jobIds: string[]) => { + expect(jobIds).toEqual(machineLearningJobId); + return { + loading: false, + jobs: [{ results_index_name: 'index1' }, { results_index_name: 'index2' }], + }; + }); + const defaultIndices = ['index1', 'index2']; + const { result } = renderHook(() => useRuleIndices(machineLearningJobId, defaultIndices)); + expect(result.current).toEqual({ + mlJobLoading: false, + ruleIndices: ['.ml-anomalies-index1'], + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_indices.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_indices.tsx new file mode 100644 index 0000000000000..77ffc49bb7413 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_indices.tsx @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; +import { useGetInstalledJob } from '../../../../common/components/ml/hooks/use_get_jobs'; + +export const useRuleIndices = (machineLearningJobId?: string[], defaultRuleIndices?: string[]) => { + const memoMlJobIds = useMemo(() => machineLearningJobId ?? [], [machineLearningJobId]); + const { loading: mlJobLoading, jobs } = useGetInstalledJob(memoMlJobIds); + + const memoRuleIndices = useMemo(() => { + if (jobs.length > 0) { + return jobs[0].results_index_name ? [`.ml-anomalies-${jobs[0].results_index_name}`] : []; + } else { + return defaultRuleIndices ?? []; + } + }, [jobs, defaultRuleIndices]); + + return { mlJobLoading, ruleIndices: memoRuleIndices }; +}; From d83f8020999df0828d3cbead814e8def6005a0a7 Mon Sep 17 00:00:00 2001 From: Baturalp Gurdin <9674241+suchcodemuchwow@users.noreply.github.com> Date: Tue, 12 Jul 2022 18:14:18 +0200 Subject: [PATCH 15/42] stop scalability step in performance pipeline (#136211) Co-authored-by: Spencer --- .buildkite/pipelines/performance/daily.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.buildkite/pipelines/performance/daily.yml b/.buildkite/pipelines/performance/daily.yml index 28f58d6c814ef..fdc4ae17d69a2 100644 --- a/.buildkite/pipelines/performance/daily.yml +++ b/.buildkite/pipelines/performance/daily.yml @@ -19,11 +19,11 @@ steps: depends_on: build key: tests - - label: ':shipit: Performance Tests dataset extraction for scalability benchmarking' - command: .buildkite/scripts/steps/functional/scalability_dataset_extraction.sh - agents: - queue: n2-2 - depends_on: tests + # - label: ':shipit: Performance Tests dataset extraction for scalability benchmarking' + # command: .buildkite/scripts/steps/functional/scalability_dataset_extraction.sh + # agents: + # queue: n2-2 + # depends_on: tests - label: ':chart_with_upwards_trend: Report performance metrics to ci-stats' command: .buildkite/scripts/steps/functional/report_performance_metrics.sh From d533e8e789c581b24ce0a096e7396fdfc3072d31 Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Tue, 12 Jul 2022 18:18:35 +0200 Subject: [PATCH 16/42] [Fleet] Modify Agent policy and agent upgrades to handle custom source_uri (#135629) * [Fleet] Modify Agent policy compile logic to handle custom source uri * Add download_source_id to mapping and create new migration * Compile agent policy to get source_uri * Handle source_uri in agent upgrade * Revert 123464 * Add tests * Fix test failure * Fix preconfiguration test * Fix crud test * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Don't query source_uri in agent upgrades; Address code review comments * Fix agent upgrade integration tests * Remove unused function Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../fleet/common/constants/download_source.ts | 3 +- .../services/full_agent_policy_to_yaml.ts | 1 + x-pack/plugins/fleet/common/types/index.ts | 1 - .../fleet/common/types/models/agent.ts | 1 + .../fleet/common/types/models/agent_policy.ts | 1 + .../cloud_preconfiguration.test.ts.snap | 3 + .../routes/agent/source_uri_utils.test.ts | 130 ++++++++++++++++++ .../server/routes/agent/source_uri_utils.ts | 28 ++++ .../server/routes/agent/upgrade_handler.ts | 11 +- .../fleet/server/saved_objects/index.ts | 4 +- .../saved_objects/migrations/to_v8_4_0.ts | 15 ++ .../full_agent_policy.test.ts.snap | 9 ++ .../agent_policies/full_agent_policy.test.ts | 90 +++++++++++- .../agent_policies/full_agent_policy.ts | 27 ++-- .../fleet/server/types/models/agent_policy.ts | 1 + .../apis/agents/upgrade.ts | 78 +++++++---- .../apis/download_sources/crud.ts | 4 +- 17 files changed, 348 insertions(+), 59 deletions(-) create mode 100644 x-pack/plugins/fleet/server/routes/agent/source_uri_utils.test.ts create mode 100644 x-pack/plugins/fleet/server/routes/agent/source_uri_utils.ts diff --git a/x-pack/plugins/fleet/common/constants/download_source.ts b/x-pack/plugins/fleet/common/constants/download_source.ts index 1b74c627e93f7..22959ed7023bb 100644 --- a/x-pack/plugins/fleet/common/constants/download_source.ts +++ b/x-pack/plugins/fleet/common/constants/download_source.ts @@ -5,7 +5,8 @@ * 2.0. */ -export const DEFAULT_DOWNLOAD_SOURCE = 'artifactory.elastic.co'; +// Default URL used to download Elastic Agent +export const DEFAULT_DOWNLOAD_SOURCE = 'https://artifacts.elastic.co'; export const DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE = 'ingest-download-sources'; diff --git a/x-pack/plugins/fleet/common/services/full_agent_policy_to_yaml.ts b/x-pack/plugins/fleet/common/services/full_agent_policy_to_yaml.ts index f4193d619e168..11025e576b393 100644 --- a/x-pack/plugins/fleet/common/services/full_agent_policy_to_yaml.ts +++ b/x-pack/plugins/fleet/common/services/full_agent_policy_to_yaml.ts @@ -23,6 +23,7 @@ const POLICY_KEYS_ORDER = [ 'use_output', 'meta', 'input', + 'download', ]; export const fullAgentPolicyToYaml = (policy: FullAgentPolicy, toYaml: typeof safeDump): string => { diff --git a/x-pack/plugins/fleet/common/types/index.ts b/x-pack/plugins/fleet/common/types/index.ts index 9f88d9b231854..f819705b02230 100644 --- a/x-pack/plugins/fleet/common/types/index.ts +++ b/x-pack/plugins/fleet/common/types/index.ts @@ -38,7 +38,6 @@ export interface FleetConfigType { }; developer?: { disableRegistryVersionCheck?: boolean; - allowAgentUpgradeSourceUri?: boolean; bundledPackageLocation?: string; }; } diff --git a/x-pack/plugins/fleet/common/types/models/agent.ts b/x-pack/plugins/fleet/common/types/models/agent.ts index c8b175ffb9dc1..e0eecb99c996e 100644 --- a/x-pack/plugins/fleet/common/types/models/agent.ts +++ b/x-pack/plugins/fleet/common/types/models/agent.ts @@ -48,6 +48,7 @@ export interface NewAgentAction { expiration?: string; start_time?: string; minimum_execution_duration?: number; + source_uri?: string; } export interface AgentAction extends NewAgentAction { diff --git a/x-pack/plugins/fleet/common/types/models/agent_policy.ts b/x-pack/plugins/fleet/common/types/models/agent_policy.ts index 269a0d606b0c8..a7b24911d8306 100644 --- a/x-pack/plugins/fleet/common/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/common/types/models/agent_policy.ts @@ -106,6 +106,7 @@ export interface FullAgentPolicy { metrics: boolean; logs: boolean; }; + download: { source_uri: string }; }; } diff --git a/x-pack/plugins/fleet/server/integration_tests/__snapshots__/cloud_preconfiguration.test.ts.snap b/x-pack/plugins/fleet/server/integration_tests/__snapshots__/cloud_preconfiguration.test.ts.snap index c82eb5333fc9f..fc2ce4eb7145e 100644 --- a/x-pack/plugins/fleet/server/integration_tests/__snapshots__/cloud_preconfiguration.test.ts.snap +++ b/x-pack/plugins/fleet/server/integration_tests/__snapshots__/cloud_preconfiguration.test.ts.snap @@ -3,6 +3,9 @@ exports[`Fleet preconfiguration reset Preconfigured cloud policy With a full preconfigured cloud policy Create correct .fleet-policies 1`] = ` Object { "agent": Object { + "download": Object { + "source_uri": "https://artifacts.elastic.co", + }, "monitoring": Object { "enabled": false, "logs": false, diff --git a/x-pack/plugins/fleet/server/routes/agent/source_uri_utils.test.ts b/x-pack/plugins/fleet/server/routes/agent/source_uri_utils.test.ts new file mode 100644 index 0000000000000..a748a6b0895dd --- /dev/null +++ b/x-pack/plugins/fleet/server/routes/agent/source_uri_utils.test.ts @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; + +import { DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE } from '../../constants'; + +import type { AgentPolicy, DownloadSource } from '../../types'; + +import { getSourceUriForAgentPolicy } from './source_uri_utils'; + +const soClientMock = savedObjectsClientMock.create(); + +jest.mock('../download_source', () => { + return { + downloadSourceService: { + getDefaultDownloadSourceId: async () => 'default-download-source-id', + get: async (soClient: any, id: string): Promise => { + if (id === 'test-ds-1') { + return { + id: 'test-ds-1', + is_default: false, + name: 'Test', + host: 'http://custom-registry-test', + }; + } + return { + id: 'default-download-source-id', + is_default: true, + name: 'Default host', + host: 'http://default-registry.co', + }; + }, + }, + }; +}); + +function mockDownloadSourceSO(id: string, attributes: any = {}) { + return { + id, + type: DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE, + references: [], + attributes: { + source_id: id, + ...attributes, + }, + }; +} +describe('helpers', () => { + beforeEach(() => { + soClientMock.get.mockImplementation(async (type: string, id: string) => { + switch (id) { + case 'test-ds-1': { + return mockDownloadSourceSO('test-ds-1', { + is_default: false, + name: 'Test', + host: 'http://custom-registry-test', + }); + } + case 'default-download-source-id': { + return mockDownloadSourceSO('default-download-source-id', { + is_default: true, + name: 'Default host', + host: 'http://default-registry.co', + }); + } + default: + throw new Error('not found: ' + id); + } + }); + soClientMock.find.mockResolvedValue({ + saved_objects: [ + { + id: 'default-download-source-id', + is_default: true, + attributes: { + download_source_id: 'test-source-id', + }, + }, + { + id: 'test-ds-1', + attributes: { + download_source_id: 'test-ds-1', + }, + }, + ], + } as any); + }); + describe('getSourceUriForAgentPolicy', () => { + it('should return the source_uri set on an agent policy ', async () => { + const agentPolicy: AgentPolicy = { + id: 'agent-policy-id', + status: 'active', + package_policies: [], + is_managed: false, + namespace: 'default', + revision: 1, + name: 'Policy', + updated_at: '2022-01-01', + updated_by: 'qwerty', + download_source_id: 'test-ds-1', + }; + + expect(await getSourceUriForAgentPolicy(soClientMock, agentPolicy)).toEqual( + 'http://custom-registry-test' + ); + }); + it('should return the default source_uri if there is none set on the agent policy ', async () => { + const agentPolicy: AgentPolicy = { + id: 'agent-policy-id', + status: 'active', + package_policies: [], + is_managed: false, + namespace: 'default', + revision: 1, + name: 'Policy', + updated_at: '2022-01-01', + updated_by: 'qwerty', + }; + + expect(await getSourceUriForAgentPolicy(soClientMock, agentPolicy)).toEqual( + 'http://default-registry.co' + ); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/routes/agent/source_uri_utils.ts b/x-pack/plugins/fleet/server/routes/agent/source_uri_utils.ts new file mode 100644 index 0000000000000..6c6edd7b401b7 --- /dev/null +++ b/x-pack/plugins/fleet/server/routes/agent/source_uri_utils.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectsClientContract } from '@kbn/core/server'; + +import { downloadSourceService } from '../../services'; +import type { AgentPolicy } from '../../types'; + +export const getSourceUriForAgentPolicy = async ( + soClient: SavedObjectsClientContract, + agentPolicy: AgentPolicy +) => { + const defaultDownloadSourceId = await downloadSourceService.getDefaultDownloadSourceId(soClient); + + if (!defaultDownloadSourceId) { + throw new Error('Default download source host is not setup'); + } + const downloadSourceId: string = agentPolicy.download_source_id || defaultDownloadSourceId; + const downloadSource = await downloadSourceService.get(soClient, downloadSourceId); + if (!downloadSource) { + throw new Error(`Download source host not found ${downloadSourceId}`); + } + return downloadSource.host; +}; diff --git a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts index a389f749c9b11..024158b5b8785 100644 --- a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts +++ b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts @@ -39,7 +39,6 @@ export const postAgentUpgradeHandler: RequestHandler< const kibanaVersion = appContextService.getKibanaVersion(); try { checkKibanaVersion(version, kibanaVersion); - checkSourceUriAllowed(sourceUri); } catch (err) { return response.customError({ statusCode: 400, @@ -49,6 +48,7 @@ export const postAgentUpgradeHandler: RequestHandler< }); } const agent = await getAgentById(esClient, request.params.agentId); + if (agent.unenrollment_started_at || agent.unenrolled_at) { return response.customError({ statusCode: 400, @@ -102,7 +102,6 @@ export const postBulkAgentsUpgradeHandler: RequestHandler< const kibanaVersion = appContextService.getKibanaVersion(); try { checkKibanaVersion(version, kibanaVersion); - checkSourceUriAllowed(sourceUri); const fleetServerAgents = await getAllFleetServerAgents(soClient, esClient); checkFleetServerVersion(version, fleetServerAgents); } catch (err) { @@ -167,14 +166,6 @@ export const checkKibanaVersion = (version: string, kibanaVersion: string) => { ); }; -const checkSourceUriAllowed = (sourceUri?: string) => { - if (sourceUri && !appContextService.getConfig()?.developer?.allowAgentUpgradeSourceUri) { - throw new Error( - `source_uri is not allowed or recommended in production. Set xpack.fleet.developer.allowAgentUpgradeSourceUri in kibana.yml to true.` - ); - } -}; - // Check the installed fleet server version const checkFleetServerVersion = (versionToUpgradeNumber: string, fleetServerAgents: Agent[]) => { const fleetServerVersions = fleetServerAgents.map( diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 3c85d35595f7f..55b57dc857a90 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -40,7 +40,7 @@ import { migrateInstallationToV7160, migratePackagePolicyToV7160 } from './migra import { migrateInstallationToV800, migrateOutputToV800 } from './migrations/to_v8_0_0'; import { migratePackagePolicyToV820 } from './migrations/to_v8_2_0'; import { migrateInstallationToV830, migratePackagePolicyToV830 } from './migrations/to_v8_3_0'; -import { migrateInstallationToV840 } from './migrations/to_v8_4_0'; +import { migrateInstallationToV840, migrateAgentPolicyToV840 } from './migrations/to_v8_4_0'; /* * Saved object types and mappings @@ -95,11 +95,13 @@ const getSavedObjectTypes = ( is_preconfigured: { type: 'keyword' }, data_output_id: { type: 'keyword' }, monitoring_output_id: { type: 'keyword' }, + download_source_id: { type: 'keyword' }, }, }, migrations: { '7.10.0': migrateAgentPolicyToV7100, '7.12.0': migrateAgentPolicyToV7120, + '8.4.0': migrateAgentPolicyToV840, }, }, [OUTPUT_SAVED_OBJECT_TYPE]: { diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_4_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_4_0.ts index 203e26c0a2e19..8cd14a22a81cb 100644 --- a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_4_0.ts +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v8_4_0.ts @@ -9,6 +9,8 @@ import type { SavedObjectMigrationFn } from '@kbn/core/server'; import type { Installation } from '../../../common'; +import type { AgentPolicy } from '../../types'; + export const migrateInstallationToV840: SavedObjectMigrationFn = ( installationDoc ) => { @@ -16,3 +18,16 @@ export const migrateInstallationToV840: SavedObjectMigrationFn & { + config_id: string; + }, + AgentPolicy +> = (agentPolicyDoc) => { + agentPolicyDoc.attributes.download_source_id = agentPolicyDoc.attributes.config_id; + // @ts-expect-error + delete agentPolicyDoc.attributes.config_id; + + return agentPolicyDoc; +}; diff --git a/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap b/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap index 0ef9efb2fa7e5..2a3afab285976 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap +++ b/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap @@ -3,6 +3,9 @@ exports[`getFullAgentPolicy should support a different data output 1`] = ` Object { "agent": Object { + "download": Object { + "source_uri": "http://default-registry.co", + }, "monitoring": Object { "enabled": true, "logs": false, @@ -65,6 +68,9 @@ Object { exports[`getFullAgentPolicy should support a different monitoring output 1`] = ` Object { "agent": Object { + "download": Object { + "source_uri": "http://default-registry.co", + }, "monitoring": Object { "enabled": true, "logs": false, @@ -127,6 +133,9 @@ Object { exports[`getFullAgentPolicy should support both different outputs for data and monitoring 1`] = ` Object { "agent": Object { + "download": Object { + "source_uri": "http://default-registry.co", + }, "monitoring": Object { "enabled": true, "logs": false, diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts index f283581466664..ed70e4dd4e42c 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -7,7 +7,7 @@ import { savedObjectsClientMock } from '@kbn/core/server/mocks'; -import type { AgentPolicy, Output } from '../../types'; +import type { AgentPolicy, Output, DownloadSource } from '../../types'; import { agentPolicyService } from '../agent_policy'; import { agentPolicyUpdateEventHandler } from '../agent_policy_update'; @@ -20,6 +20,8 @@ const mockedGetElasticAgentMonitoringPermissions = getMonitoringPermissions as j >; const mockedAgentPolicyService = agentPolicyService as jest.Mocked; +const soClientMock = savedObjectsClientMock.create(); + function mockAgentPolicy(data: Partial) { mockedAgentPolicyService.get.mockResolvedValue({ id: 'agent-policy', @@ -97,6 +99,30 @@ jest.mock('../package_policy'); jest.mock('./monitoring_permissions'); +jest.mock('../download_source', () => { + return { + downloadSourceService: { + getDefaultDownloadSourceId: async () => 'default-download-source-id', + get: async (soClient: any, id: string): Promise => { + if (id === 'test-ds-1') { + return { + id: 'test-ds-1', + is_default: false, + name: 'Test', + host: 'http://custom-registry-test', + }; + } + return { + id: 'default-download-source-id', + is_default: true, + name: 'Default host', + host: 'http://default-registry.co', + }; + }, + }, + }; +}); + function getAgentPolicyUpdateMock() { return agentPolicyUpdateEventHandler as unknown as jest.Mock< typeof agentPolicyUpdateEventHandler @@ -130,6 +156,23 @@ describe('getFullAgentPolicy', () => { }; } ); + soClientMock.find.mockResolvedValue({ + saved_objects: [ + { + id: 'default-download-source-id', + is_default: true, + attributes: { + download_source_id: 'test-source-id', + }, + }, + { + id: 'test-ds-1', + attributes: { + download_source_id: 'test-ds-1', + }, + }, + ], + } as any); }); it('should return a policy without monitoring if monitoring is not enabled', async () => { @@ -183,6 +226,9 @@ describe('getFullAgentPolicy', () => { hosts: ['http://fleetserver:8220'], }, agent: { + download: { + source_uri: 'http://default-registry.co', + }, monitoring: { namespace: 'default', use_output: 'default', @@ -216,6 +262,9 @@ describe('getFullAgentPolicy', () => { hosts: ['http://fleetserver:8220'], }, agent: { + download: { + source_uri: 'http://default-registry.co', + }, monitoring: { namespace: 'default', use_output: 'default', @@ -298,6 +347,43 @@ describe('getFullAgentPolicy', () => { expect(agentPolicy?.outputs.default).toBeDefined(); }); + + it('should return the source_uri from the agent policy', async () => { + mockAgentPolicy({ + namespace: 'default', + revision: 1, + monitoring_enabled: ['metrics'], + download_source_id: 'test-ds-1', + }); + const agentPolicy = await getFullAgentPolicy(savedObjectsClientMock.create(), 'agent-policy'); + + expect(agentPolicy).toMatchObject({ + id: 'agent-policy', + outputs: { + default: { + type: 'elasticsearch', + hosts: ['http://127.0.0.1:9201'], + }, + }, + inputs: [], + revision: 1, + fleet: { + hosts: ['http://fleetserver:8220'], + }, + agent: { + download: { + source_uri: 'http://custom-registry-test', + }, + monitoring: { + namespace: 'default', + use_output: 'default', + enabled: true, + logs: false, + metrics: true, + }, + }, + }); + }); }); describe('transformOutputToFullPolicyOutput', () => { @@ -330,7 +416,7 @@ describe('transformOutputToFullPolicyOutput', () => { type: 'elasticsearch', ca_trusted_fingerprint: 'fingerprint123', config_yaml: ` -test: 1234 +test: 1234 ssl.test: 123 `, }); diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index 6a9577b083f98..d76018066727d 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -22,6 +22,8 @@ import type { FullAgentPolicyOutputPermissions } from '../../../common'; import { getSettings } from '../settings'; import { DEFAULT_OUTPUT } from '../../constants'; +import { getSourceUriForAgentPolicy } from '../../routes/agent/source_uri_utils'; + import { getMonitoringPermissions } from './monitoring_permissions'; import { storedPackagePoliciesToAgentInputs } from '.'; import { @@ -75,6 +77,9 @@ export async function getFullAgentPolicy( if (!monitoringOutput) { throw new Error(`Monitoring output not found ${monitoringOutputId}`); } + + const sourceUri = await getSourceUriForAgentPolicy(soClient, agentPolicy); + const fullAgentPolicy: FullAgentPolicy = { id: agentPolicy.id, outputs: { @@ -93,23 +98,21 @@ export async function getFullAgentPolicy( getOutputIdForAgentPolicy(dataOutput) ), revision: agentPolicy.revision, - ...(agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.length > 0 - ? { - agent: { - monitoring: { + agent: { + download: { + source_uri: sourceUri, + }, + monitoring: + agentPolicy.monitoring_enabled && agentPolicy.monitoring_enabled.length > 0 + ? { namespace: agentPolicy.namespace, use_output: getOutputIdForAgentPolicy(monitoringOutput), enabled: true, logs: agentPolicy.monitoring_enabled.includes(dataTypes.Logs), metrics: agentPolicy.monitoring_enabled.includes(dataTypes.Metrics), - }, - }, - } - : { - agent: { - monitoring: { enabled: false, logs: false, metrics: false }, - }, - }), + } + : { enabled: false, logs: false, metrics: false }, + }, }; const dataPermissions = diff --git a/x-pack/plugins/fleet/server/types/models/agent_policy.ts b/x-pack/plugins/fleet/server/types/models/agent_policy.ts index 38d4b88722743..bbb96028f88ec 100644 --- a/x-pack/plugins/fleet/server/types/models/agent_policy.ts +++ b/x-pack/plugins/fleet/server/types/models/agent_policy.ts @@ -34,6 +34,7 @@ export const AgentPolicyBaseSchema = { ), data_output_id: schema.maybe(schema.nullable(schema.string())), monitoring_output_id: schema.maybe(schema.nullable(schema.string())), + download_source_id: schema.maybe(schema.nullable(schema.string())), }; export const NewAgentPolicySchema = schema.object({ diff --git a/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts b/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts index c52efca4e86af..0499843415e64 100644 --- a/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts +++ b/x-pack/test/fleet_api_integration/apis/agents/upgrade.ts @@ -27,8 +27,27 @@ export default function (providerContext: FtrProviderContext) { await esArchiver.load('x-pack/test/functional/es_archives/fleet/agents'); }); setupFleetAndAgents(providerContext); + beforeEach(async () => { await esArchiver.load('x-pack/test/functional/es_archives/fleet/agents'); + await supertest + .post(`/api/fleet/agent_download_sources`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'My download source', + host: 'http://custom-registry-test', + is_default: true, + }) + .expect(200); + const { body: response } = await supertest + .get(`/api/fleet/agent_download_sources`) + .expect(200); + + const defaultDownloadSource = response.items.find((item: any) => item.is_default); + + if (!defaultDownloadSource) { + throw new Error('default source_uri not set'); + } }); afterEach(async () => { await esArchiver.unload('x-pack/test/functional/es_archives/fleet/agents'); @@ -126,7 +145,7 @@ export default function (providerContext: FtrProviderContext) { }) .expect(200); }); - it('should respond 200 to upgrade agent and update the agent SO without source_uri', async () => { + it('should respond 200 if trying to upgrade with source_uri set', async () => { const kibanaVersion = await kibanaServer.version.get(); await es.update({ id: 'agent1', @@ -143,12 +162,18 @@ export default function (providerContext: FtrProviderContext) { .set('kbn-xsrf', 'xxx') .send({ version: kibanaVersion, + source_uri: 'http://path/to/download', }) .expect(200); - const res = await supertest.get(`/api/fleet/agents/agent1`).set('kbn-xsrf', 'xxx'); - expect(typeof res.body.item.upgrade_started_at).to.be('string'); + const actionsRes = await es.search({ + index: '.fleet-actions', + body: { + sort: [{ '@timestamp': { order: 'desc' } }], + }, + }); + const action: any = actionsRes.hits.hits[0]._source; + expect(action.data.source_uri).contain('http://path/to/download'); }); - it('should respond 400 if trying to upgrade to a version that does not match installed kibana version', async () => { const kibanaVersion = await kibanaServer.version.get(); const higherVersion = semver.inc(kibanaVersion, 'patch'); @@ -160,7 +185,6 @@ export default function (providerContext: FtrProviderContext) { }) .expect(400); }); - it('should respond 400 if trying to downgrade version', async () => { await es.update({ id: 'agent1', @@ -180,21 +204,6 @@ export default function (providerContext: FtrProviderContext) { }) .expect(400); }); - it('should respond 400 if trying to upgrade with source_uri set', async () => { - const kibanaVersion = await kibanaServer.version.get(); - const res = await supertest - .post(`/api/fleet/agents/agent1/upgrade`) - .set('kbn-xsrf', 'xxx') - .send({ - version: kibanaVersion, - source_uri: 'http://path/to/download', - }) - .expect(400); - - expect(res.body.message).to.eql( - `source_uri is not allowed or recommended in production. Set xpack.fleet.developer.allowAgentUpgradeSourceUri in kibana.yml to true.` - ); - }); it('should respond 400 if trying to upgrade an agent that is unenrolling', async () => { const kibanaVersion = await kibanaServer.version.get(); await supertest.post(`/api/fleet/agents/agent1/unenroll`).set('kbn-xsrf', 'xxx').send({ @@ -815,8 +824,7 @@ export default function (providerContext: FtrProviderContext) { expect(typeof agent2data.body.item.upgrade_started_at).to.be('undefined'); }); - it('should throw an error if source_uri parameter is passed', async () => { - const kibanaVersion = await kibanaServer.version.get(); + it('should respond 200 if source_uri parameter is passed', async () => { await es.update({ id: 'agent1', refresh: 'wait_for', @@ -833,23 +841,33 @@ export default function (providerContext: FtrProviderContext) { index: AGENTS_INDEX, body: { doc: { - local_metadata: { elastic: { agent: { upgradeable: true, version: '0.0.0' } } }, + local_metadata: { + elastic: { + agent: { upgradeable: false, version: '0.0.0' }, + }, + }, }, }, }); - const res = await supertest + await supertest .post(`/api/fleet/agents/bulk_upgrade`) .set('kbn-xsrf', 'xxx') .send({ + version: fleetServerVersion, agents: ['agent1', 'agent2'], - version: kibanaVersion, source_uri: 'http://path/to/download', - force: true, }) - .expect(400); - expect(res.body.message).to.eql( - `source_uri is not allowed or recommended in production. Set xpack.fleet.developer.allowAgentUpgradeSourceUri in kibana.yml to true.` - ); + .expect(200); + + const actionsRes = await es.search({ + index: '.fleet-actions', + body: { + sort: [{ '@timestamp': { order: 'desc' } }], + }, + }); + const action: any = actionsRes.hits.hits[0]._source; + + expect(action.data.source_uri).contain('http://path/to/download'); }); it('enrolled in a hosted agent policy bulk upgrade should respond with 200 and object of results. Should not update the hosted agent SOs', async () => { diff --git a/x-pack/test/fleet_api_integration/apis/download_sources/crud.ts b/x-pack/test/fleet_api_integration/apis/download_sources/crud.ts index 0d0916be10171..af9a109ed62ce 100644 --- a/x-pack/test/fleet_api_integration/apis/download_sources/crud.ts +++ b/x-pack/test/fleet_api_integration/apis/download_sources/crud.ts @@ -52,7 +52,7 @@ export default function (providerContext: FtrProviderContext) { id: 'fleet-default-download-source', name: 'default', is_default: true, - host: 'artifactory.elastic.co', + host: 'https://artifacts.elastic.co', }); }); }); @@ -68,7 +68,7 @@ export default function (providerContext: FtrProviderContext) { id: 'fleet-default-download-source', name: 'default', is_default: true, - host: 'artifactory.elastic.co', + host: 'https://artifacts.elastic.co', }, }); }); From cc119944b3587cc58944cd979b3fc7e8fc259c84 Mon Sep 17 00:00:00 2001 From: Rashmi Kulkarni Date: Tue, 12 Jul 2022 09:25:14 -0700 Subject: [PATCH 17/42] timepicker migration (#136173) --- .../test/functional/apps/discover/saved_searches.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/x-pack/test/functional/apps/discover/saved_searches.ts b/x-pack/test/functional/apps/discover/saved_searches.ts index 5d8c2aff3ed5f..414dc8395f184 100644 --- a/x-pack/test/functional/apps/discover/saved_searches.ts +++ b/x-pack/test/functional/apps/discover/saved_searches.ts @@ -24,11 +24,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'doc_table:legacy': false, }; - const setTimeRange = async () => { - const fromTime = 'Apr 27, 2019 @ 23:56:51.374'; - const toTime = 'Aug 23, 2019 @ 16:18:51.821'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - }; + const from = 'Apr 27, 2019 @ 23:56:51.374'; + const to = 'Aug 23, 2019 @ 16:18:51.821'; describe('Discover Saved Searches', () => { before('initialize tests', async () => { @@ -36,6 +33,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); await kibanaServer.importExport.load(ecommerceSOPath); await kibanaServer.uiSettings.update(defaultSettings); + await PageObjects.common.setTime({ from, to }); }); after('clean up archives', async () => { @@ -43,13 +41,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); await kibanaServer.importExport.unload(ecommerceSOPath); await kibanaServer.uiSettings.unset('doc_table:legacy'); + await PageObjects.common.unsetTime(); }); describe('Customize time range', () => { it('should be possible to customize time range for saved searches on dashboards', async () => { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.clickNewDashboard(); - await setTimeRange(); await dashboardAddPanel.clickOpenAddPanel(); await dashboardAddPanel.addSavedSearch('Ecommerce Data'); expect(await dataGrid.getDocCount()).to.be(500); @@ -66,7 +64,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it(`should unselect saved search when navigating to a 'new'`, async function () { await PageObjects.common.navigateToApp('discover'); await PageObjects.discover.selectIndexPattern('ecommerce'); - await setTimeRange(); await filterBar.addFilter('category', 'is', `Men's Shoes`); await queryBar.setQuery('customer_gender:MALE'); From 114f8eba9c0323d6152698ee45457d521e85aadb Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Tue, 12 Jul 2022 12:27:12 -0400 Subject: [PATCH 18/42] Update on-merge.yml --- .github/workflows/on-merge.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/on-merge.yml b/.github/workflows/on-merge.yml index 34e95749a9c5e..a778c2a74f9d9 100644 --- a/.github/workflows/on-merge.yml +++ b/.github/workflows/on-merge.yml @@ -1,7 +1,5 @@ on: pull_request_target: - branches: - - main types: - closed - labeled From 4fdbdd105fcf85552f09b44f826e3e1bdc98456b Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Tue, 12 Jul 2022 12:30:48 -0400 Subject: [PATCH 19/42] Update on-merge.yml --- .github/workflows/on-merge.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/on-merge.yml b/.github/workflows/on-merge.yml index a778c2a74f9d9..072767f0000eb 100644 --- a/.github/workflows/on-merge.yml +++ b/.github/workflows/on-merge.yml @@ -10,12 +10,7 @@ jobs: runs-on: ubuntu-latest if: | github.event.pull_request.merged == true - && ( - contains(github.event.pull_request.labels.*.name, 'backport:prev-minor') - || contains(github.event.pull_request.labels.*.name, 'backport:prev-major') - || contains(github.event.pull_request.labels.*.name, 'backport:all-open') - || contains(github.event.pull_request.labels.*.name, 'backport:auto-version') - ) + && !contains(github.event.pull_request.labels.*.name, 'auto-backport') && ( ( github.event.action == 'labeled' && ( From d23f189ccf52b1500fd2a3515f96683a4bff45b2 Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Tue, 12 Jul 2022 19:37:10 +0300 Subject: [PATCH 20/42] [Fleet] replace number with boolean for conditional rendering (#136210) --- .../components/steps/components/package_policy_input_stream.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx index 7886e294a2961..77734feb67e21 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_stream.tsx @@ -172,7 +172,7 @@ export const PackagePolicyInputStreamConfig: React.FunctionComponent<{ ); })} {/* Advanced section */} - {(isPackagePolicyEdit || advancedVars.length) && ( + {(isPackagePolicyEdit || !!advancedVars.length) && ( From 3fb87f55e380c40b4cc0a1cd243aca6b69ebfae5 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 12 Jul 2022 18:20:18 +0100 Subject: [PATCH 21/42] skip flaky suite (#136218) --- .../apps/maps/group2/embeddable/embeddable_library.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/maps/group2/embeddable/embeddable_library.js b/x-pack/test/functional/apps/maps/group2/embeddable/embeddable_library.js index aa54a54196f95..b03c2191cd13b 100644 --- a/x-pack/test/functional/apps/maps/group2/embeddable/embeddable_library.js +++ b/x-pack/test/functional/apps/maps/group2/embeddable/embeddable_library.js @@ -16,7 +16,8 @@ export default function ({ getPageObjects, getService }) { const dashboardAddPanel = getService('dashboardAddPanel'); const dashboardPanelActions = getService('dashboardPanelActions'); - describe('maps in embeddable library', () => { + // FLAKY: https://github.com/elastic/kibana/issues/136218 + describe.skip('maps in embeddable library', () => { before(async () => { await security.testUser.setRoles( [ From 68e2ff6cbf25b4e5d1c031d57b987c8f2f7a7c6c Mon Sep 17 00:00:00 2001 From: Ievgen Sorokopud Date: Tue, 12 Jul 2022 19:24:01 +0200 Subject: [PATCH 22/42] [Security Solution] Fallback to @timestamp should be configurable when timestamp override is defined (#135116) * [Security Solution] Fallback to @timestamp should be configurable when timestamp override is defined #112315 * Fix CI * Fix CI * Fix CI * Review feedback: more concise name * Review comments * Review feedback - create `primaryTimestamp` and `secondaryTimestamp` variables within `createSecurityRuleTypeWrapper` and pass it from there to all executors * CI fixes * Fix types * Fix threshold rules * Fix CI * Integration tests * Updated comment * Fix CI * Fix types * Review feedback: better naming Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../schemas/common/schemas.ts | 13 + .../schemas/request/rule_schemas.ts | 2 + .../rules/step_about_rule/index.tsx | 23 +- .../rules/step_about_rule/schema.tsx | 10 + .../detection_engine/rules/types.ts | 2 + .../rules/all/__mocks__/mock.ts | 1 + .../rules/create/helpers.test.ts | 28 +++ .../detection_engine/rules/create/helpers.ts | 2 + .../detection_engine/rules/helpers.test.tsx | 1 + .../pages/detection_engine/rules/helpers.tsx | 2 + .../pages/detection_engine/rules/types.ts | 2 + .../schedule_notification_actions.test.ts | 1 + ...dule_throttle_notification_actions.test.ts | 1 + .../create_security_rule_type_wrapper.ts | 29 ++- .../rule_types/eql/create_eql_alert_type.ts | 4 + .../rule_types/factories/utils/build_alert.ts | 2 +- .../create_indicator_match_alert_type.ts | 4 + .../query/create_query_alert_type.ts | 4 + .../create_saved_query_alert_type.ts | 4 + .../threshold/create_threshold_alert_type.ts | 4 + .../lib/detection_engine/rule_types/types.ts | 2 + .../rules/duplicate_rule.test.ts | 1 + .../detection_engine/rules/update_rules.ts | 1 + .../schemas/rule_converters.ts | 5 + .../schemas/rule_schemas.mock.ts | 1 + .../detection_engine/schemas/rule_schemas.ts | 2 + .../signals/build_events_query.test.ts | 237 +++++++++++++----- .../signals/build_events_query.ts | 123 +++++---- .../signals/executors/eql.test.ts | 1 + .../detection_engine/signals/executors/eql.ts | 31 ++- .../signals/executors/query.ts | 6 + .../signals/executors/threat_match.ts | 6 + .../signals/executors/threshold.test.ts | 2 + .../signals/executors/threshold.ts | 11 +- .../signals/search_after_bulk_create.test.ts | 12 + .../signals/search_after_bulk_create.ts | 8 +- .../signals/single_search_after.test.ts | 15 +- .../signals/single_search_after.ts | 19 +- .../threat_mapping/create_event_signal.ts | 4 + .../threat_mapping/create_threat_signal.ts | 4 + .../threat_mapping/create_threat_signals.ts | 10 + .../threat_mapping/get_event_count.test.ts | 41 ++- .../signals/threat_mapping/get_event_count.ts | 12 +- .../signals/threat_mapping/types.ts | 12 +- .../threshold/find_threshold_signals.test.ts | 16 +- .../threshold/find_threshold_signals.ts | 21 +- .../get_threshold_bucket_filters.test.ts | 3 +- .../threshold/get_threshold_bucket_filters.ts | 6 +- .../lib/detection_engine/signals/types.ts | 2 + .../detection_engine/signals/utils.test.ts | 56 ++--- .../lib/detection_engine/signals/utils.ts | 31 ++- .../security_and_spaces/group1/timestamps.ts | 62 +++++ .../utils/get_open_signals.ts | 6 +- 53 files changed, 697 insertions(+), 211 deletions(-) diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts index 74fc8bbbc9331..40eea74cb3db5 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/common/schemas.ts @@ -188,6 +188,19 @@ export type AnomalyThreshold = t.TypeOf; export const anomalyThresholdOrUndefined = t.union([anomaly_threshold, t.undefined]); export type AnomalyThresholdOrUndefined = t.TypeOf; +export const timestamp_override_fallback_disabled = t.boolean; +export type TimestampOverrideFallbackDisabled = t.TypeOf< + typeof timestamp_override_fallback_disabled +>; + +export const timestampOverrideFallbackDisabledOrUndefined = t.union([ + timestamp_override_fallback_disabled, + t.undefined, +]); +export type TimestampOverrideFallbackDisabledOrUndefined = t.TypeOf< + typeof timestampOverrideFallbackDisabledOrUndefined +>; + /** * Note that this is a non-exact io-ts type as we allow extra meta information * to be added to the meta object diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts index 8d72861047640..bd72a6788a568 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/rule_schemas.ts @@ -45,6 +45,7 @@ import { meta, rule_name_override, timestamp_override, + timestamp_override_fallback_disabled, author, description, false_positives, @@ -163,6 +164,7 @@ const baseParams = { meta, rule_name_override, timestamp_override, + timestamp_override_fallback_disabled, namespace, }, defaultable: { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx index 5927a762a583a..af610bc2a17be 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/index.tsx @@ -144,10 +144,11 @@ const StepAboutRuleComponent: FC = ({ schema, }); const { getFields, getFormData, submit } = form; - const [{ severity: formSeverity }] = useFormData({ - form, - watch: ['severity'], - }); + const [{ severity: formSeverity, timestampOverride: formTimestampOverride }] = + useFormData({ + form, + watch: ['severity', 'timestampOverride'], + }); useEffect(() => { const formSeverityValue = formSeverity?.value; @@ -403,6 +404,20 @@ const StepAboutRuleComponent: FC = ({ placeholder: '', }} /> + {!!formTimestampOverride && formTimestampOverride !== '@timestamp' && ( + <> + + + )} diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx index 6039e5e3e272d..328bf7697e44a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule/schema.tsx @@ -236,6 +236,16 @@ export const schema: FormSchema = { ), labelAppend: OptionalFieldLabel, }, + timestampOverrideFallbackDisabled: { + type: FIELD_TYPES.CHECKBOX, + label: i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fieldTimestampOverrideFallbackDisabledLabel', + { + defaultMessage: 'Do not use @timestamp as a fallback timestamp field', + } + ), + labelAppend: OptionalFieldLabel, + }, tags: { type: FIELD_TYPES.COMBO_BOX, label: i18n.translate( diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts index cab5f995027fb..a0e8585579402 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts @@ -35,6 +35,7 @@ import { rule_name_override, data_view_id, timestamp_override, + timestamp_override_fallback_disabled, timestamp_field, event_category_override, tiebreaker_field, @@ -155,6 +156,7 @@ export const RuleSchema = t.intersection([ timeline_id: t.string, timeline_title: t.string, timestamp_override, + timestamp_override_fallback_disabled, timestamp_field, event_category_override, tiebreaker_field, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts index 8c0520b11ffea..56ac425650d5a 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts @@ -158,6 +158,7 @@ export const mockRuleWithEverything = (id: string): Rule => ({ }, throttle: 'no_actions', timestamp_override: 'event.ingested', + timestamp_override_fallback_disabled: false, note: '# this is some markdown documentation', version: 1, }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.test.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.test.ts index f56f0e8630f94..a941fe57a3b5a 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.test.ts @@ -710,6 +710,34 @@ describe('helpers', () => { expect(result).toEqual(expected); }); + + test('returns formatted object with timestamp override', () => { + const mockStepData: AboutStepRule = { + ...mockData, + timestampOverride: 'event.ingest', + timestampOverrideFallbackDisabled: true, + }; + const result = formatAboutStepData(mockStepData); + const expected: AboutStepRuleJson = { + author: ['Elastic'], + description: '24/7', + false_positives: ['test'], + license: 'Elastic License', + name: 'Query with rule-id', + note: '# this is some markdown documentation', + references: ['www.test.co'], + risk_score: 21, + risk_score_mapping: [], + severity: 'low', + severity_mapping: [], + tags: ['tag1', 'tag2'], + threat: getThreatMock(), + timestamp_override: 'event.ingest', + timestamp_override_fallback_disabled: true, + }; + + expect(result).toEqual(expected); + }); }); describe('formatActionsStepData', () => { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts index 67d56f9d6025a..4c4a7087cb218 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts @@ -392,6 +392,7 @@ export const formatAboutStepData = ( ruleNameOverride, threatIndicatorPath, timestampOverride, + timestampOverrideFallbackDisabled, ...rest } = aboutStepData; @@ -435,6 +436,7 @@ export const formatAboutStepData = ( })), threat_indicator_path: threatIndicatorPath, timestamp_override: timestampOverride !== '' ? timestampOverride : undefined, + timestamp_override_fallback_disabled: timestampOverrideFallbackDisabled, ...(!isEmpty(note) ? { note } : {}), ...rest, }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx index ac56607e7a081..84801163674fe 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx @@ -128,6 +128,7 @@ describe('rule helpers', () => { tags: ['tag1', 'tag2'], threat: getThreatMock(), timestampOverride: 'event.ingested', + timestampOverrideFallbackDisabled: false, }; const scheduleRuleStepData = { from: '0s', interval: '5m' }; const ruleActionsStepData = { diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index fba185aeb6ea0..6a8207371e230 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -164,6 +164,7 @@ export const getAboutStepsData = (rule: Rule, detailsView: boolean): AboutStepRu rule_name_override: ruleNameOverride, severity_mapping: severityMapping, timestamp_override: timestampOverride, + timestamp_override_fallback_disabled: timestampOverrideFallbackDisabled, references, severity, false_positives: falsePositives, @@ -180,6 +181,7 @@ export const getAboutStepsData = (rule: Rule, detailsView: boolean): AboutStepRu license: license ?? '', ruleNameOverride: ruleNameOverride ?? '', timestampOverride: timestampOverride ?? '', + timestampOverrideFallbackDisabled, name, description, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts index f7338d874c19a..019e5ad4c1a94 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts @@ -109,6 +109,7 @@ export interface AboutStepRule { ruleNameOverride: string; tags: string[]; timestampOverride: string; + timestampOverrideFallbackDisabled?: boolean; threatIndicatorPath?: string; threat: Threats; note: string; @@ -215,6 +216,7 @@ export interface AboutStepRuleJson { threat: Threats; threat_indicator_path?: string; timestamp_override?: TimestampOverride; + timestamp_override_fallback_disabled?: boolean; note?: string; } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_notification_actions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_notification_actions.test.ts index 39364d766726d..e47d9ff732270 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_notification_actions.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_notification_actions.test.ts @@ -44,6 +44,7 @@ describe('schedule_notification_actions', () => { severityMapping: [], threat: [], timestampOverride: undefined, + timestampOverrideFallbackDisabled: undefined, to: 'now', type: 'query', references: ['http://www.example.com'], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.test.ts index 7857fb202fbd2..ac0a1bd07c5cd 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/schedule_throttle_notification_actions.test.ts @@ -50,6 +50,7 @@ describe('schedule_throttle_notification_actions', () => { severityMapping: [], threat: [], timestampOverride: undefined, + timestampOverrideFallbackDisabled: undefined, dataViewId: undefined, to: 'now', type: 'query', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index 2911cf3d9e28a..ac29541574243 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -10,6 +10,7 @@ import { isEmpty } from 'lodash'; import { parseScheduleDates } from '@kbn/securitysolution-io-ts-utils'; import agent from 'elastic-apm-node'; +import { TIMESTAMP } from '@kbn/rule-data-utils'; import { createPersistenceRuleTypeWrapper } from '@kbn/rule-registry-plugin/server'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; @@ -79,7 +80,15 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = let hasError = false; let inputIndex: string[] = []; let runtimeMappings: estypes.MappingRuntimeFields | undefined; - const { from, maxSignals, meta, ruleId, timestampOverride, to } = params; + const { + from, + maxSignals, + meta, + ruleId, + timestampOverride, + timestampOverrideFallbackDisabled, + to, + } = params; const { alertWithPersistence, savedObjectsClient, @@ -142,6 +151,12 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = id: alertId, }; + const primaryTimestamp = timestampOverride ?? TIMESTAMP; + const secondaryTimestamp = + primaryTimestamp !== TIMESTAMP && !timestampOverrideFallbackDisabled + ? TIMESTAMP + : undefined; + /** * Data Views Logic * Use of data views is supported for all rules other than ML. @@ -180,8 +195,6 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = // so that we can use it in create rules route, bulk, etc. try { if (!isMachineLearningParams(params)) { - const hasTimestampOverride = !!timestampOverride; - const privileges = await checkPrivilegesFromEsClient(esClient, inputIndex); wroteWarningStatus = await hasReadIndexPrivileges({ @@ -197,9 +210,9 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = services.scopedClusterClient.asCurrentUser.fieldCaps( { index: inputIndex, - fields: hasTimestampOverride - ? ['@timestamp', timestampOverride] - : ['@timestamp'], + fields: secondaryTimestamp + ? [primaryTimestamp, secondaryTimestamp] + : [primaryTimestamp], include_unmapped: true, runtime_mappings: runtimeMappings, }, @@ -208,7 +221,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = ); wroteWarningStatus = await hasTimestampFields({ - timestampField: hasTimestampOverride ? timestampOverride : '@timestamp', + timestampField: primaryTimestamp, timestampFieldCapsResponse: timestampFieldCaps, inputIndices: inputIndex, ruleExecutionLogger, @@ -309,6 +322,8 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = wrapHits, wrapSequences, ruleDataReader: ruleDataClient.getReader({ namespace: options.spaceId }), + primaryTimestamp, + secondaryTimestamp, }, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts index 922e106f1e0bf..22676f6d120c3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/eql/create_eql_alert_type.ts @@ -71,6 +71,8 @@ export const createEqlAlertType = ( tuple, wrapHits, wrapSequences, + primaryTimestamp, + secondaryTimestamp, }, services, state, @@ -89,6 +91,8 @@ export const createEqlAlertType = ( version, wrapHits, wrapSequences, + primaryTimestamp, + secondaryTimestamp, }); return { ...result, state }; }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts index 7460337170c7e..38115347a8241 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts @@ -178,7 +178,7 @@ export const buildAlert = ( const originalTime = getValidDateFromDoc({ doc: docs[0], - timestampOverride: undefined, + primaryTimestamp: TIMESTAMP, }); return { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts index 08da25f544e6c..70cf31d8ae7b5 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/indicator_match/create_indicator_match_alert_type.ts @@ -74,6 +74,8 @@ export const createIndicatorMatchAlertType = ( searchAfterSize, tuple, wrapHits, + primaryTimestamp, + secondaryTimestamp, }, services, state, @@ -95,6 +97,8 @@ export const createIndicatorMatchAlertType = ( tuple, version, wrapHits, + primaryTimestamp, + secondaryTimestamp, }); return { ...result, state }; }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts index 7f5b9301f316e..b9a6c17bfc261 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/query/create_query_alert_type.ts @@ -73,6 +73,8 @@ export const createQueryAlertType = ( searchAfterSize, tuple, wrapHits, + primaryTimestamp, + secondaryTimestamp, }, services, state, @@ -94,6 +96,8 @@ export const createQueryAlertType = ( wrapHits, inputIndex, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }); return { ...result, state }; }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_query/create_saved_query_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_query/create_saved_query_alert_type.ts index a970c94e53c79..fe9c377aa04f6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_query/create_saved_query_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/saved_query/create_saved_query_alert_type.ts @@ -73,6 +73,8 @@ export const createSavedQueryAlertType = ( searchAfterSize, tuple, wrapHits, + primaryTimestamp, + secondaryTimestamp, }, services, state, @@ -94,6 +96,8 @@ export const createSavedQueryAlertType = ( tuple, version, wrapHits, + primaryTimestamp, + secondaryTimestamp, }); return { ...result, state }; }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts index 27a6defac7192..43e57057bcbc1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/create_threshold_alert_type.ts @@ -74,6 +74,8 @@ export const createThresholdAlertType = ( ruleDataReader, inputIndex, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }, services, startedAt, @@ -96,6 +98,8 @@ export const createThresholdAlertType = ( ruleDataReader, inputIndex, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }); return result; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts index 38a0e2652387b..eef69fd4ebab7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/types.ts @@ -69,6 +69,8 @@ export interface RunOpts { ruleDataReader: IRuleDataReader; inputIndex: string[]; runtimeMappings: estypes.MappingRuntimeFields | undefined; + primaryTimestamp: string; + secondaryTimestamp?: string; } export type SecurityAlertType< diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.test.ts index 279bac5885e7d..6360fb863547e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/duplicate_rule.test.ts @@ -58,6 +58,7 @@ describe('duplicateRule', () => { timelineTitle: undefined, ruleNameOverride: undefined, timestampOverride: undefined, + timestampOverrideFallbackDisabled: undefined, dataViewId: undefined, }, schedule: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts index 8caf5c5f71468..c75e7534417e9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_rules.ts @@ -54,6 +54,7 @@ export const updateRules = async ({ severityMapping: ruleUpdate.severity_mapping ?? [], threat: ruleUpdate.threat ?? [], timestampOverride: ruleUpdate.timestamp_override, + timestampOverrideFallbackDisabled: ruleUpdate.timestamp_override_fallback_disabled, to: ruleUpdate.to ?? 'now', references: ruleUpdate.references ?? [], namespace: ruleUpdate.namespace, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_converters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_converters.ts index 1565c2c61255f..f7dbb2358aca8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_converters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_converters.ts @@ -383,6 +383,9 @@ export const convertPatchAPIToInternalSchema = ( severityMapping: params.severity_mapping ?? existingParams.severityMapping, threat: params.threat ?? existingParams.threat, timestampOverride: params.timestamp_override ?? existingParams.timestampOverride, + timestampOverrideFallbackDisabled: + params.timestamp_override_fallback_disabled ?? + existingParams.timestampOverrideFallbackDisabled, to: params.to ?? existingParams.to, references: params.references ?? existingParams.references, namespace: params.namespace ?? existingParams.namespace, @@ -442,6 +445,7 @@ export const convertCreateAPIToInternalSchema = ( severityMapping: input.severity_mapping ?? [], threat: input.threat ?? [], timestampOverride: input.timestamp_override, + timestampOverrideFallbackDisabled: input.timestamp_override_fallback_disabled, to: input.to ?? 'now', references: input.references ?? [], namespace: input.namespace, @@ -559,6 +563,7 @@ export const commonParamsCamelToSnake = (params: BaseRuleParams) => { meta: params.meta, rule_name_override: params.ruleNameOverride, timestamp_override: params.timestampOverride, + timestamp_override_fallback_disabled: params.timestampOverrideFallbackDisabled, author: params.author, false_positives: params.falsePositives, from: params.from, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.mock.ts index ba071176c5a1f..fec09f41097b9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.mock.ts @@ -46,6 +46,7 @@ const getBaseRuleParams = (): BaseRuleParams => { timelineId: 'some-timeline-id', timelineTitle: 'some-timeline-title', timestampOverride: undefined, + timestampOverrideFallbackDisabled: undefined, meta: { someMeta: 'someField', }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.ts index d6002c5a1c026..2c1bf1fb86780 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/schemas/rule_schemas.ts @@ -78,6 +78,7 @@ import { RelatedIntegrationArray, RequiredFieldArray, SetupGuide, + timestampOverrideFallbackDisabledOrUndefined, } from '../../../../common/detection_engine/schemas/common'; import { SERVER_APP_ID } from '../../../../common/constants'; @@ -107,6 +108,7 @@ export const baseRuleParams = t.exact( severity, severityMapping: severity_mapping, timestampOverride: timestampOverrideOrUndefined, + timestampOverrideFallbackDisabled: timestampOverrideFallbackDisabledOrUndefined, threat: threats, to, references, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts index 915d675d5644c..26e5ec80d1bb1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.test.ts @@ -26,7 +26,8 @@ describe('create_signals', () => { filter: {}, size: 100, searchAfterSortIds: undefined, - timestampOverride: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, runtimeMappings: undefined, }); expect(query).toEqual({ @@ -84,7 +85,8 @@ describe('create_signals', () => { filter: {}, size: 100, searchAfterSortIds: undefined, - timestampOverride: 'event.ingested', + primaryTimestamp: 'event.ingested', + secondaryTimestamp: '@timestamp', runtimeMappings: undefined, }); expect(query).toEqual({ @@ -175,6 +177,65 @@ describe('create_signals', () => { }); }); + test('it builds a filter without @timestamp fallback if `secondaryTimestamp` is undefined', () => { + const query = buildEventsSearchQuery({ + index: ['auditbeat-*'], + from: 'now-5m', + to: 'today', + filter: {}, + size: 100, + searchAfterSortIds: undefined, + primaryTimestamp: 'event.ingested', + secondaryTimestamp: undefined, + runtimeMappings: undefined, + }); + expect(query).toEqual({ + allow_no_indices: true, + index: ['auditbeat-*'], + size: 100, + ignore_unavailable: true, + body: { + query: { + bool: { + filter: [ + {}, + { + range: { + 'event.ingested': { + gte: 'now-5m', + lte: 'today', + format: 'strict_date_optional_time', + }, + }, + }, + { + match_all: {}, + }, + ], + }, + }, + fields: [ + { + field: '*', + include_unmapped: true, + }, + { + field: 'event.ingested', + format: 'strict_date_optional_time', + }, + ], + sort: [ + { + 'event.ingested': { + order: 'asc', + unmapped_type: 'date', + }, + }, + ], + }, + }); + }); + test('if searchAfterSortIds is a valid sortId string', () => { const fakeSortId = '123456789012'; const query = buildEventsSearchQuery({ @@ -184,7 +245,8 @@ describe('create_signals', () => { filter: {}, size: 100, searchAfterSortIds: [fakeSortId], - timestampOverride: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, runtimeMappings: undefined, }); expect(query).toEqual({ @@ -243,7 +305,8 @@ describe('create_signals', () => { filter: {}, size: 100, searchAfterSortIds: [fakeSortIdNumber], - timestampOverride: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, runtimeMappings: undefined, }); expect(query).toEqual({ @@ -301,7 +364,8 @@ describe('create_signals', () => { filter: {}, size: 100, searchAfterSortIds: undefined, - timestampOverride: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, runtimeMappings: undefined, }); expect(query).toEqual({ @@ -366,7 +430,8 @@ describe('create_signals', () => { filter: {}, size: 100, searchAfterSortIds: undefined, - timestampOverride: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, runtimeMappings: undefined, }); expect(query).toEqual({ @@ -431,7 +496,8 @@ describe('create_signals', () => { filter: {}, size: 100, searchAfterSortIds: undefined, - timestampOverride: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, trackTotalHits: false, runtimeMappings: undefined, }); @@ -446,7 +512,8 @@ describe('create_signals', () => { filter: {}, size: 100, searchAfterSortIds: undefined, - timestampOverride: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, sortOrder: 'desc', trackTotalHits: false, runtimeMappings: undefined, @@ -467,7 +534,8 @@ describe('create_signals', () => { filter: {}, size: 100, searchAfterSortIds: undefined, - timestampOverride: 'event.ingested', + primaryTimestamp: 'event.ingested', + secondaryTimestamp: '@timestamp', sortOrder: 'desc', runtimeMappings: undefined, }); @@ -487,18 +555,19 @@ describe('create_signals', () => { describe('buildEqlSearchRequest', () => { test('should build a basic request with time range', () => { - const request = buildEqlSearchRequest( - 'process where true', - ['testindex1', 'testindex2'], - 'now-5m', - 'now', - 100, - undefined, - undefined, - [], - undefined, - undefined - ); + const request = buildEqlSearchRequest({ + query: 'process where true', + index: ['testindex1', 'testindex2'], + from: 'now-5m', + to: 'now', + size: 100, + filters: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, + exceptionLists: [], + runtimeMappings: undefined, + eventCategoryOverride: undefined, + }); expect(request).toEqual({ allow_no_indices: true, index: ['testindex1', 'testindex2'], @@ -537,19 +606,20 @@ describe('create_signals', () => { }); test('should build a request with timestamp and event category overrides', () => { - const request = buildEqlSearchRequest( - 'process where true', - ['testindex1', 'testindex2'], - 'now-5m', - 'now', - 100, - undefined, - 'event.ingested', - [], - undefined, - 'event.other_category', - undefined - ); + const request = buildEqlSearchRequest({ + query: 'process where true', + index: ['testindex1', 'testindex2'], + from: 'now-5m', + to: 'now', + size: 100, + filters: undefined, + primaryTimestamp: 'event.ingested', + secondaryTimestamp: '@timestamp', + exceptionLists: [], + runtimeMappings: undefined, + eventCategoryOverride: 'event.other_category', + timestampField: undefined, + }); expect(request).toEqual({ allow_no_indices: true, index: ['testindex1', 'testindex2'], @@ -623,19 +693,73 @@ describe('create_signals', () => { }); }); + test('should build a request without @timestamp fallback if secondaryTimestamp is not specified', () => { + const request = buildEqlSearchRequest({ + query: 'process where true', + index: ['testindex1', 'testindex2'], + from: 'now-5m', + to: 'now', + size: 100, + filters: undefined, + primaryTimestamp: 'event.ingested', + secondaryTimestamp: undefined, + exceptionLists: [], + runtimeMappings: undefined, + eventCategoryOverride: 'event.other_category', + timestampField: undefined, + }); + expect(request).toEqual({ + allow_no_indices: true, + index: ['testindex1', 'testindex2'], + body: { + event_category_field: 'event.other_category', + size: 100, + query: 'process where true', + runtime_mappings: undefined, + filter: { + bool: { + filter: [ + { + range: { + 'event.ingested': { + lte: 'now', + gte: 'now-5m', + format: 'strict_date_optional_time', + }, + }, + }, + emptyFilter, + ], + }, + }, + fields: [ + { + field: '*', + include_unmapped: true, + }, + { + field: 'event.ingested', + format: 'strict_date_optional_time', + }, + ], + }, + }); + }); + test('should build a request with exceptions', () => { - const request = buildEqlSearchRequest( - 'process where true', - ['testindex1', 'testindex2'], - 'now-5m', - 'now', - 100, - undefined, - undefined, - [getExceptionListItemSchemaMock()], - undefined, - undefined - ); + const request = buildEqlSearchRequest({ + query: 'process where true', + index: ['testindex1', 'testindex2'], + from: 'now-5m', + to: 'now', + size: 100, + filters: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, + exceptionLists: [getExceptionListItemSchemaMock()], + runtimeMappings: undefined, + eventCategoryOverride: undefined, + }); expect(request).toEqual({ allow_no_indices: true, index: ['testindex1', 'testindex2'], @@ -758,17 +882,18 @@ describe('create_signals', () => { }, }, ]; - const request = buildEqlSearchRequest( - 'process where true', - ['testindex1', 'testindex2'], - 'now-5m', - 'now', - 100, + const request = buildEqlSearchRequest({ + query: 'process where true', + index: ['testindex1', 'testindex2'], + from: 'now-5m', + to: 'now', + size: 100, filters, - undefined, - [], - undefined - ); + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, + exceptionLists: [], + runtimeMappings: undefined, + }); expect(request).toEqual({ allow_no_indices: true, index: ['testindex1', 'testindex2'], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts index 3dd99219f5a20..1a6e8fe3a723c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/build_events_query.ts @@ -10,6 +10,7 @@ import { isEmpty } from 'lodash'; import type { FiltersOrUndefined, TimestampOverrideOrUndefined, + TimestampOverride, } from '../../../../common/detection_engine/schemas/common/schemas'; import { getQueryFilter } from '../../../../common/detection_engine/get_query_filter'; @@ -23,30 +24,56 @@ interface BuildEventsSearchQuery { size: number; sortOrder?: estypes.SortOrder; searchAfterSortIds: estypes.SortResults | undefined; - timestampOverride: TimestampOverrideOrUndefined; + primaryTimestamp: TimestampOverride; + secondaryTimestamp: TimestampOverrideOrUndefined; trackTotalHits?: boolean; } +interface BuildEqlSearchRequestParams { + query: string; + index: string[]; + from: string; + to: string; + size: number; + filters: FiltersOrUndefined; + primaryTimestamp: TimestampOverride; + secondaryTimestamp: TimestampOverrideOrUndefined; + exceptionLists: ExceptionListItemSchema[]; + runtimeMappings: estypes.MappingRuntimeFields | undefined; + eventCategoryOverride?: string; + timestampField?: string; + tiebreakerField?: string; +} + const buildTimeRangeFilter = ({ to, from, - timestampOverride, + primaryTimestamp, + secondaryTimestamp, }: { to: string; from: string; - timestampOverride?: string; + primaryTimestamp: TimestampOverride; + secondaryTimestamp: TimestampOverrideOrUndefined; }): estypes.QueryDslQueryContainer => { - // If the timestampOverride is provided, documents must either populate timestampOverride with a timestamp in the range - // or must NOT populate the timestampOverride field at all and `@timestamp` must fall in the range. - // If timestampOverride is not provided, we simply use `@timestamp` - return timestampOverride != null + // The primaryTimestamp is always provided and will contain either the timestamp override field or `@timestamp` otherwise. + // The secondaryTimestamp is `undefined` if + // 1. timestamp override field is not specified + // 2. timestamp override field is set and timestamp fallback is disabled + // 3. timestamp override field is set to `@timestamp` + // or `@timestamp` otherwise. + // + // If the secondaryTimestamp is provided, documents must either populate primaryTimestamp with a timestamp in the range + // or must NOT populate the primaryTimestamp field at all and secondaryTimestamp must fall in the range. + // If secondaryTimestamp is not provided, we simply use primaryTimestamp + return secondaryTimestamp != null ? { bool: { minimum_should_match: 1, should: [ { range: { - [timestampOverride]: { + [primaryTimestamp]: { lte: to, gte: from, format: 'strict_date_optional_time', @@ -58,7 +85,7 @@ const buildTimeRangeFilter = ({ filter: [ { range: { - '@timestamp': { + [secondaryTimestamp]: { lte: to, gte: from, format: 'strict_date_optional_time', @@ -69,7 +96,7 @@ const buildTimeRangeFilter = ({ bool: { must_not: { exists: { - field: timestampOverride, + field: primaryTimestamp, }, }, }, @@ -82,7 +109,7 @@ const buildTimeRangeFilter = ({ } : { range: { - '@timestamp': { + [primaryTimestamp]: { lte: to, gte: from, format: 'strict_date_optional_time', @@ -101,36 +128,42 @@ export const buildEventsSearchQuery = ({ runtimeMappings, searchAfterSortIds, sortOrder, - timestampOverride, + primaryTimestamp, + secondaryTimestamp, trackTotalHits, }: BuildEventsSearchQuery) => { - const defaultTimeFields = ['@timestamp']; - const timestamps = - timestampOverride != null ? [timestampOverride, ...defaultTimeFields] : defaultTimeFields; + const timestamps = secondaryTimestamp + ? [primaryTimestamp, secondaryTimestamp] + : [primaryTimestamp]; const docFields = timestamps.map((tstamp) => ({ field: tstamp, format: 'strict_date_optional_time', })); - const rangeFilter = buildTimeRangeFilter({ to, from, timestampOverride }); + const rangeFilter = buildTimeRangeFilter({ + to, + from, + primaryTimestamp, + secondaryTimestamp, + }); const filterWithTime: estypes.QueryDslQueryContainer[] = [filter, rangeFilter]; const sort: estypes.Sort = []; - if (timestampOverride) { + sort.push({ + [primaryTimestamp]: { + order: sortOrder ?? 'asc', + unmapped_type: 'date', + }, + }); + if (secondaryTimestamp) { sort.push({ - [timestampOverride]: { + [secondaryTimestamp]: { order: sortOrder ?? 'asc', unmapped_type: 'date', }, }); } - sort.push({ - '@timestamp': { - order: sortOrder ?? 'asc', - unmapped_type: 'date', - }, - }); const searchQuery = { allow_no_indices: true, @@ -174,23 +207,24 @@ export const buildEventsSearchQuery = ({ return searchQuery; }; -export const buildEqlSearchRequest = ( - query: string, - index: string[], - from: string, - to: string, - size: number, - filters: FiltersOrUndefined, - timestampOverride: TimestampOverrideOrUndefined, - exceptionLists: ExceptionListItemSchema[], - runtimeMappings: estypes.MappingRuntimeFields | undefined, - eventCategoryOverride?: string, - timestampField?: string, - tiebreakerField?: string -): estypes.EqlSearchRequest => { - const defaultTimeFields = ['@timestamp']; - const timestamps = - timestampOverride != null ? [timestampOverride, ...defaultTimeFields] : defaultTimeFields; +export const buildEqlSearchRequest = ({ + query, + index, + from, + to, + size, + filters, + primaryTimestamp, + secondaryTimestamp, + exceptionLists, + runtimeMappings, + eventCategoryOverride, + timestampField, + tiebreakerField, +}: BuildEqlSearchRequestParams): estypes.EqlSearchRequest => { + const timestamps = secondaryTimestamp + ? [primaryTimestamp, secondaryTimestamp] + : [primaryTimestamp]; const docFields = timestamps.map((tstamp) => ({ field: tstamp, format: 'strict_date_optional_time', @@ -198,7 +232,12 @@ export const buildEqlSearchRequest = ( const esFilter = getQueryFilter('', 'eql', filters || [], index, exceptionLists); - const rangeFilter = buildTimeRangeFilter({ to, from, timestampOverride }); + const rangeFilter = buildTimeRangeFilter({ + to, + from, + primaryTimestamp, + secondaryTimestamp, + }); const requestFilter: estypes.QueryDslQueryContainer[] = [rangeFilter, esFilter]; const fields = [ { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts index 6077ebe1ef1ad..3f52dd1fcd4e8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.test.ts @@ -61,6 +61,7 @@ describe('eql_executor', () => { bulkCreate: jest.fn(), wrapHits: jest.fn(), wrapSequences: jest.fn(), + primaryTimestamp: '@timestamp', }); expect(response.warningMessages.length).toEqual(1); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts index e61f68d0e5e8b..5e54515be1056 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/eql.ts @@ -49,6 +49,8 @@ export const eqlExecutor = async ({ bulkCreate, wrapHits, wrapSequences, + primaryTimestamp, + secondaryTimestamp, }: { inputIndex: string[]; runtimeMappings: estypes.MappingRuntimeFields | undefined; @@ -62,6 +64,8 @@ export const eqlExecutor = async ({ bulkCreate: BulkCreate; wrapHits: WrapHits; wrapSequences: WrapSequences; + primaryTimestamp: string; + secondaryTimestamp?: string; }): Promise => { const ruleParams = completeRule.ruleParams; @@ -74,20 +78,21 @@ export const eqlExecutor = async ({ result.warning = true; } - const request = buildEqlSearchRequest( - ruleParams.query, - inputIndex, - tuple.from.toISOString(), - tuple.to.toISOString(), - completeRule.ruleParams.maxSignals, - ruleParams.filters, - ruleParams.timestampOverride, - exceptionItems, + const request = buildEqlSearchRequest({ + query: ruleParams.query, + index: inputIndex, + from: tuple.from.toISOString(), + to: tuple.to.toISOString(), + size: completeRule.ruleParams.maxSignals, + filters: ruleParams.filters, + primaryTimestamp, + secondaryTimestamp, + exceptionLists: exceptionItems, runtimeMappings, - ruleParams.eventCategoryOverride, - ruleParams.timestampField, - ruleParams.tiebreakerField - ); + eventCategoryOverride: ruleParams.eventCategoryOverride, + timestampField: ruleParams.timestampField, + tiebreakerField: ruleParams.tiebreakerField, + }); const eqlSignalSearchStart = performance.now(); logger.debug(`EQL query request: ${JSON.stringify(request)}`); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts index 43b8e2a788e70..fcfa61dcf3624 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/query.ts @@ -45,6 +45,8 @@ export const queryExecutor = async ({ buildRuleMessage, bulkCreate, wrapHits, + primaryTimestamp, + secondaryTimestamp, }: { inputIndex: string[]; runtimeMappings: estypes.MappingRuntimeFields | undefined; @@ -61,6 +63,8 @@ export const queryExecutor = async ({ buildRuleMessage: BuildRuleMessage; bulkCreate: BulkCreate; wrapHits: WrapHits; + primaryTimestamp: string; + secondaryTimestamp?: string; }) => { const ruleParams = completeRule.ruleParams; @@ -93,6 +97,8 @@ export const queryExecutor = async ({ bulkCreate, wrapHits, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }); }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts index bcd90e45defc8..a25561c52e271 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threat_match.ts @@ -40,6 +40,8 @@ export const threatMatchExecutor = async ({ buildRuleMessage, bulkCreate, wrapHits, + primaryTimestamp, + secondaryTimestamp, }: { inputIndex: string[]; runtimeMappings: estypes.MappingRuntimeFields | undefined; @@ -56,6 +58,8 @@ export const threatMatchExecutor = async ({ buildRuleMessage: BuildRuleMessage; bulkCreate: BulkCreate; wrapHits: WrapHits; + primaryTimestamp: string; + secondaryTimestamp?: string; }) => { const ruleParams = completeRule.ruleParams; @@ -89,6 +93,8 @@ export const threatMatchExecutor = async ({ type: ruleParams.type, wrapHits, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }); }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts index c1954edd7e5f0..e21e8ae21d91f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.test.ts @@ -20,6 +20,7 @@ import { sampleEmptyDocSearchResults } from '../__mocks__/es_results'; import { allowedExperimentalValues } from '../../../../../common/experimental_features'; import type { ThresholdRuleParams } from '../../schemas/rule_schemas'; import { createRuleDataClientMock } from '@kbn/rule-registry-plugin/server/rule_data_client/rule_data_client.mock'; +import { TIMESTAMP } from '@kbn/rule-data-utils'; describe('threshold_executor', () => { const version = '8.0.0'; @@ -75,6 +76,7 @@ describe('threshold_executor', () => { ruleDataReader: ruleDataClientMock.getReader({ namespace: 'default' }), runtimeMappings: {}, inputIndex: ['auditbeat-*'], + primaryTimestamp: TIMESTAMP, }); expect(response.warningMessages.length).toEqual(1); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts index b52cac1a2066f..cac4b2d4fa036 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/executors/threshold.ts @@ -59,6 +59,8 @@ export const thresholdExecutor = async ({ bulkCreate, wrapHits, ruleDataReader, + primaryTimestamp, + secondaryTimestamp, }: { inputIndex: string[]; runtimeMappings: estypes.MappingRuntimeFields | undefined; @@ -75,6 +77,8 @@ export const thresholdExecutor = async ({ bulkCreate: BulkCreate; wrapHits: WrapHits; ruleDataReader: IRuleDataReader; + primaryTimestamp: string; + secondaryTimestamp?: string; }): Promise => { let result = createSearchAfterReturnType(); const ruleParams = completeRule.ruleParams; @@ -114,7 +118,7 @@ export const thresholdExecutor = async ({ // Eliminate dupes const bucketFilters = await getThresholdBucketFilters({ signalHistory, - timestampOverride: ruleParams.timestampOverride, + primaryTimestamp, }); // Combine dupe filter with other filters @@ -142,9 +146,10 @@ export const thresholdExecutor = async ({ logger, filter: esFilter, threshold: ruleParams.threshold, - timestampOverride: ruleParams.timestampOverride, buildRuleMessage, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }); // Build and index new alerts @@ -168,7 +173,7 @@ export const thresholdExecutor = async ({ result, createSearchAfterReturnTypeFromResponse({ searchResult: thresholdResults, - timestampOverride: ruleParams.timestampOverride, + primaryTimestamp, }), createSearchAfterReturnType({ success, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index d8801190cc0ed..88f12f7157683 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -217,6 +217,7 @@ describe('searchAfterAndBulkCreate', () => { bulkCreate, wrapHits, runtimeMappings: undefined, + primaryTimestamp: '@timestamp', }); expect(success).toEqual(true); expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(5); @@ -310,6 +311,7 @@ describe('searchAfterAndBulkCreate', () => { bulkCreate, wrapHits, runtimeMappings: undefined, + primaryTimestamp: '@timestamp', }); expect(success).toEqual(true); expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(4); @@ -385,6 +387,7 @@ describe('searchAfterAndBulkCreate', () => { bulkCreate, wrapHits, runtimeMappings: undefined, + primaryTimestamp: '@timestamp', }); expect(success).toEqual(true); expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(2); @@ -445,6 +448,7 @@ describe('searchAfterAndBulkCreate', () => { bulkCreate, wrapHits, runtimeMappings: undefined, + primaryTimestamp: '@timestamp', }); expect(success).toEqual(true); expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(2); @@ -514,6 +518,7 @@ describe('searchAfterAndBulkCreate', () => { bulkCreate, wrapHits, runtimeMappings: undefined, + primaryTimestamp: '@timestamp', }); expect(success).toEqual(true); expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(2); @@ -570,6 +575,7 @@ describe('searchAfterAndBulkCreate', () => { bulkCreate, wrapHits, runtimeMappings: undefined, + primaryTimestamp: '@timestamp', }); expect(success).toEqual(true); expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(1); @@ -639,6 +645,7 @@ describe('searchAfterAndBulkCreate', () => { bulkCreate, wrapHits, runtimeMappings: undefined, + primaryTimestamp: '@timestamp', }); expect(success).toEqual(true); expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(1); @@ -710,6 +717,7 @@ describe('searchAfterAndBulkCreate', () => { bulkCreate, wrapHits, runtimeMappings: undefined, + primaryTimestamp: '@timestamp', }); expect(success).toEqual(true); expect(mockService.scopedClusterClient.asCurrentUser.search).toHaveBeenCalledTimes(2); @@ -758,6 +766,7 @@ describe('searchAfterAndBulkCreate', () => { bulkCreate, wrapHits, runtimeMappings: undefined, + primaryTimestamp: '@timestamp', }); expect(success).toEqual(true); expect(createdSignalsCount).toEqual(0); @@ -805,6 +814,7 @@ describe('searchAfterAndBulkCreate', () => { bulkCreate, wrapHits, runtimeMappings: undefined, + primaryTimestamp: '@timestamp', }); expect(success).toEqual(false); expect(createdSignalsCount).toEqual(0); // should not create signals if search threw error @@ -930,6 +940,7 @@ describe('searchAfterAndBulkCreate', () => { bulkCreate, wrapHits, runtimeMappings: undefined, + primaryTimestamp: '@timestamp', }); expect(success).toEqual(false); expect(errors).toEqual(['error on creation']); @@ -1015,6 +1026,7 @@ describe('searchAfterAndBulkCreate', () => { bulkCreate, wrapHits, runtimeMappings: undefined, + primaryTimestamp: '@timestamp', }); expect(mockEnrichment).toHaveBeenCalledWith( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts index 7f9e93920dbd6..afd2bb32ed8ae 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/search_after_bulk_create.ts @@ -42,9 +42,10 @@ export const searchAfterAndBulkCreate = async ({ tuple, wrapHits, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }: SearchAfterAndBulkCreateParams): Promise => { return withSecuritySpan('searchAfterAndBulkCreate', async () => { - const ruleParams = completeRule.ruleParams; let toReturn = createSearchAfterReturnType(); // sortId tells us where to start our next consecutive search_after query @@ -80,7 +81,8 @@ export const searchAfterAndBulkCreate = async ({ logger, filter, pageSize: Math.ceil(Math.min(tuple.maxSignals, pageSize)), - timestampOverride: ruleParams.timestampOverride, + primaryTimestamp, + secondaryTimestamp, trackTotalHits, sortOrder, }); @@ -89,7 +91,7 @@ export const searchAfterAndBulkCreate = async ({ toReturn, createSearchAfterReturnTypeFromResponse({ searchResult: mergedSearchResults, - timestampOverride: ruleParams.timestampOverride, + primaryTimestamp, }), createSearchAfterReturnType({ searchAfterTimes: [searchDuration], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts index d532e4923441f..f2411b8a981bc 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.test.ts @@ -43,7 +43,8 @@ describe('singleSearchAfter', () => { logger: mockLogger, pageSize: 1, filter: {}, - timestampOverride: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, buildRuleMessage, runtimeMappings: undefined, }); @@ -62,7 +63,8 @@ describe('singleSearchAfter', () => { logger: mockLogger, pageSize: 1, filter: {}, - timestampOverride: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, buildRuleMessage, runtimeMappings: undefined, }); @@ -113,7 +115,8 @@ describe('singleSearchAfter', () => { logger: mockLogger, pageSize: 1, filter: {}, - timestampOverride: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, buildRuleMessage, runtimeMappings: undefined, }); @@ -137,7 +140,8 @@ describe('singleSearchAfter', () => { logger: mockLogger, pageSize: 1, filter: {}, - timestampOverride: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, buildRuleMessage, runtimeMappings: undefined, }); @@ -158,7 +162,8 @@ describe('singleSearchAfter', () => { logger: mockLogger, pageSize: 1, filter: {}, - timestampOverride: undefined, + primaryTimestamp: '@timestamp', + secondaryTimestamp: undefined, buildRuleMessage, runtimeMappings: undefined, }) diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts index ac9a001257ca2..ff209e8ce2b73 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/single_search_after.ts @@ -16,7 +16,10 @@ import type { SignalSearchResponse, SignalSource } from './types'; import type { BuildRuleMessage } from './rule_messages'; import { buildEventsSearchQuery } from './build_events_query'; import { createErrorsFromShard, makeFloatString } from './utils'; -import type { TimestampOverrideOrUndefined } from '../../../../common/detection_engine/schemas/common/schemas'; +import type { + TimestampOverride, + TimestampOverrideOrUndefined, +} from '../../../../common/detection_engine/schemas/common/schemas'; import { withSecuritySpan } from '../../../utils/with_security_span'; interface SingleSearchAfterParams { @@ -30,7 +33,8 @@ interface SingleSearchAfterParams { pageSize: number; sortOrder?: estypes.SortOrder; filter: estypes.QueryDslQueryContainer; - timestampOverride: TimestampOverrideOrUndefined; + primaryTimestamp: TimestampOverride; + secondaryTimestamp: TimestampOverrideOrUndefined; buildRuleMessage: BuildRuleMessage; trackTotalHits?: boolean; runtimeMappings: estypes.MappingRuntimeFields | undefined; @@ -49,7 +53,8 @@ export const singleSearchAfter = async ({ logger, pageSize, sortOrder, - timestampOverride, + primaryTimestamp, + secondaryTimestamp, buildRuleMessage, trackTotalHits, }: SingleSearchAfterParams): Promise<{ @@ -69,7 +74,8 @@ export const singleSearchAfter = async ({ size: pageSize, sortOrder, searchAfterSortIds, - timestampOverride, + primaryTimestamp, + secondaryTimestamp, trackTotalHits, }); @@ -93,8 +99,9 @@ export const singleSearchAfter = async ({ } catch (exc) { logger.error(buildRuleMessage(`[-] nextSearchAfter threw an error ${exc}`)); if ( - exc.message.includes('No mapping found for [@timestamp] in order to sort on') || - exc.message.includes(`No mapping found for [${timestampOverride}] in order to sort on`) + exc.message.includes(`No mapping found for [${primaryTimestamp}] in order to sort on`) || + (secondaryTimestamp && + exc.message.includes(`No mapping found for [${secondaryTimestamp}] in order to sort on`)) ) { logger.error(buildRuleMessage(`[-] failure reason: ${exc.message}`)); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts index 1c9e2c5f6ca28..67e39e71421df 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_event_signal.ts @@ -48,6 +48,8 @@ export const createEventSignal = async ({ threatPitId, reassignThreatPitId, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }: CreateEventSignalOptions): Promise => { const threatFilter = buildThreatMappingFilter({ threatMapping, @@ -143,6 +145,8 @@ export const createEventSignal = async ({ tuple, wrapHits, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }); logger.debug( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts index 831ad0c14ce24..faf61c09ca79b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signal.ts @@ -38,6 +38,8 @@ export const createThreatSignal = async ({ type, wrapHits, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }: CreateThreatSignalOptions): Promise => { const threatFilter = buildThreatMappingFilter({ threatMapping, @@ -92,6 +94,8 @@ export const createThreatSignal = async ({ tuple, wrapHits, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }); logger.debug( diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts index 90fc8d2a3e4ec..579bf1ca8859c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/create_threat_signals.ts @@ -52,6 +52,8 @@ export const createThreatSignals = async ({ type, wrapHits, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }: CreateThreatSignalsOptions): Promise => { const params = completeRule.ruleParams; logger.debug(buildRuleMessage('Indicator matching rule starting')); @@ -84,6 +86,8 @@ export const createThreatSignals = async ({ query, language, filters: allEventFilters, + primaryTimestamp, + secondaryTimestamp, }); logger.debug(`Total event count: ${eventCount}`); @@ -198,6 +202,8 @@ export const createThreatSignals = async ({ perPage, tuple, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }), createSignal: (slicedChunk) => @@ -233,6 +239,8 @@ export const createThreatSignals = async ({ type, wrapHits, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }), }); } else { @@ -283,6 +291,8 @@ export const createThreatSignals = async ({ type, wrapHits, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }), }); } diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.test.ts index d53d935f1f239..15472b751537d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.test.ts @@ -24,6 +24,7 @@ describe('getEventCount', () => { exceptionItems: [], index: ['test-index'], tuple: { to: moment('2022-01-14'), from: moment('2022-01-13'), maxSignals: 1337 }, + primaryTimestamp: '@timestamp', }); expect(esClient.count).toHaveBeenCalledWith({ @@ -60,7 +61,8 @@ describe('getEventCount', () => { exceptionItems: [], index: ['test-index'], tuple: { to: moment('2022-01-14'), from: moment('2022-01-13'), maxSignals: 1337 }, - timestampOverride: 'event.ingested', + primaryTimestamp: 'event.ingested', + secondaryTimestamp: '@timestamp', }); expect(esClient.count).toHaveBeenCalledWith({ @@ -110,4 +112,41 @@ describe('getEventCount', () => { index: ['test-index'], }); }); + + it('can override timestamp without fallback to @timestamp', () => { + getEventCount({ + esClient, + query: '*:*', + language: 'kuery', + filters: [], + exceptionItems: [], + index: ['test-index'], + tuple: { to: moment('2022-01-14'), from: moment('2022-01-13'), maxSignals: 1337 }, + primaryTimestamp: 'event.ingested', + }); + + expect(esClient.count).toHaveBeenCalledWith({ + body: { + query: { + bool: { + filter: [ + { bool: { must: [], filter: [], should: [], must_not: [] } }, + { + range: { + 'event.ingested': { + lte: '2022-01-14T05:00:00.000Z', + gte: '2022-01-13T05:00:00.000Z', + format: 'strict_date_optional_time', + }, + }, + }, + { match_all: {} }, + ], + }, + }, + }, + ignore_unavailable: true, + index: ['test-index'], + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.ts index 58b84a49959d0..f01722612559a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_event_count.ts @@ -25,7 +25,8 @@ export const getEventList = async ({ buildRuleMessage, logger, tuple, - timestampOverride, + primaryTimestamp, + secondaryTimestamp, runtimeMappings, }: EventsOptions): Promise> => { const calculatedPerPage = perPage ?? MAX_PER_PAGE; @@ -51,7 +52,8 @@ export const getEventList = async ({ logger, filter, pageSize: Math.ceil(Math.min(tuple.maxSignals, calculatedPerPage)), - timestampOverride, + primaryTimestamp, + secondaryTimestamp, sortOrder: 'desc', trackTotalHits: false, runtimeMappings, @@ -71,7 +73,8 @@ export const getEventCount = async ({ index, exceptionItems, tuple, - timestampOverride, + primaryTimestamp, + secondaryTimestamp, }: EventCountOptions): Promise => { const filter = getQueryFilter(query, language ?? 'kuery', filters, index, exceptionItems); const eventSearchQueryBodyQuery = buildEventsSearchQuery({ @@ -80,7 +83,8 @@ export const getEventCount = async ({ to: tuple.to.toISOString(), filter, size: 0, - timestampOverride, + primaryTimestamp, + secondaryTimestamp, searchAfterSortIds: undefined, runtimeMappings: undefined, }).body.query; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts index e0add0da6dd17..9b9a36232fcde 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/types.ts @@ -68,6 +68,8 @@ export interface CreateThreatSignalsOptions { type: Type; wrapHits: WrapHits; runtimeMappings: estypes.MappingRuntimeFields | undefined; + primaryTimestamp: string; + secondaryTimestamp?: string; } export interface CreateThreatSignalOptions { @@ -95,6 +97,8 @@ export interface CreateThreatSignalOptions { type: Type; wrapHits: WrapHits; runtimeMappings: estypes.MappingRuntimeFields | undefined; + primaryTimestamp: string; + secondaryTimestamp?: string; } export interface CreateEventSignalOptions { @@ -130,6 +134,8 @@ export interface CreateEventSignalOptions { threatPitId: OpenPointInTimeResponse['id']; reassignThreatPitId: (newPitId: OpenPointInTimeResponse['id'] | undefined) => void; runtimeMappings: estypes.MappingRuntimeFields | undefined; + primaryTimestamp: string; + secondaryTimestamp?: string; } type EntryKey = 'field' | 'value'; @@ -261,7 +267,8 @@ export interface EventsOptions { perPage?: number; logger: Logger; filters: unknown[]; - timestampOverride?: string; + primaryTimestamp: string; + secondaryTimestamp?: string; tuple: RuleRangeTuple; runtimeMappings: estypes.MappingRuntimeFields | undefined; } @@ -279,7 +286,8 @@ export interface EventCountOptions { query: string; filters: unknown[]; tuple: RuleRangeTuple; - timestampOverride?: string; + primaryTimestamp: string; + secondaryTimestamp?: string; } export interface SignalMatch { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.test.ts index ab48d339c35a9..99553a24b36f8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.test.ts @@ -12,6 +12,7 @@ import { mockLogger } from '../__mocks__/es_results'; import { buildRuleMessageFactory } from '../rule_messages'; import * as single_search_after from '../single_search_after'; import { findThresholdSignals } from './find_threshold_signals'; +import { TIMESTAMP } from '@kbn/rule-data-utils'; const buildRuleMessage = buildRuleMessageFactory({ id: 'fake id', @@ -45,8 +46,9 @@ describe('findThresholdSignals', () => { value: 100, }, buildRuleMessage, - timestampOverride: undefined, runtimeMappings: undefined, + primaryTimestamp: TIMESTAMP, + secondaryTimestamp: undefined, }); expect(mockSingleSearchAfter).toHaveBeenCalledWith( expect.objectContaining({ @@ -90,8 +92,9 @@ describe('findThresholdSignals', () => { value: 100, }, buildRuleMessage, - timestampOverride: undefined, runtimeMappings: undefined, + primaryTimestamp: TIMESTAMP, + secondaryTimestamp: undefined, }); expect(mockSingleSearchAfter).toHaveBeenCalledWith( expect.objectContaining({ @@ -134,8 +137,9 @@ describe('findThresholdSignals', () => { cardinality: [], }, buildRuleMessage, - timestampOverride: undefined, runtimeMappings: undefined, + primaryTimestamp: TIMESTAMP, + secondaryTimestamp: undefined, }); expect(mockSingleSearchAfter).toHaveBeenCalledWith( expect.objectContaining({ @@ -192,8 +196,9 @@ describe('findThresholdSignals', () => { ], }, buildRuleMessage, - timestampOverride: undefined, runtimeMappings: undefined, + primaryTimestamp: TIMESTAMP, + secondaryTimestamp: undefined, }); expect(mockSingleSearchAfter).toHaveBeenCalledWith( expect.objectContaining({ @@ -264,8 +269,9 @@ describe('findThresholdSignals', () => { value: 200, }, buildRuleMessage, - timestampOverride: undefined, runtimeMappings: undefined, + primaryTimestamp: TIMESTAMP, + secondaryTimestamp: undefined, }); expect(mockSingleSearchAfter).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.ts index 5ff25209ea414..f0c70e1208511 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/find_threshold_signals.ts @@ -6,7 +6,6 @@ */ import { set } from '@elastic/safer-lodash-set'; -import { TIMESTAMP } from '@kbn/rule-data-utils'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { @@ -15,8 +14,10 @@ import type { RuleExecutorServices, } from '@kbn/alerting-plugin/server'; import type { Logger } from '@kbn/core/server'; +import type { ESBoolQuery } from '../../../../../common/typed_json'; import type { ThresholdNormalized, + TimestampOverride, TimestampOverrideOrUndefined, } from '../../../../../common/detection_engine/schemas/common/schemas'; import type { BuildRuleMessage } from '../rule_messages'; @@ -29,11 +30,12 @@ interface FindThresholdSignalsParams { inputIndexPattern: string[]; services: RuleExecutorServices; logger: Logger; - filter: unknown; + filter: ESBoolQuery; threshold: ThresholdNormalized; buildRuleMessage: BuildRuleMessage; - timestampOverride: TimestampOverrideOrUndefined; runtimeMappings: estypes.MappingRuntimeFields | undefined; + primaryTimestamp: TimestampOverride; + secondaryTimestamp: TimestampOverrideOrUndefined; } export const findThresholdSignals = async ({ @@ -45,8 +47,9 @@ export const findThresholdSignals = async ({ filter, threshold, buildRuleMessage, - timestampOverride, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }: FindThresholdSignalsParams): Promise<{ searchResult: SignalSearchResponse; searchDuration: string; @@ -56,12 +59,12 @@ export const findThresholdSignals = async ({ const leafAggs = { max_timestamp: { max: { - field: timestampOverride != null ? timestampOverride : TIMESTAMP, + field: primaryTimestamp, }, }, min_timestamp: { min: { - field: timestampOverride != null ? timestampOverride : TIMESTAMP, + field: primaryTimestamp, }, }, ...(threshold.cardinality?.length @@ -134,18 +137,18 @@ export const findThresholdSignals = async ({ return singleSearchAfter({ aggregations, - searchAfterSortId: undefined, - timestampOverride, + searchAfterSortIds: undefined, index: inputIndexPattern, from, to, services, logger, - // @ts-expect-error refactor to pass type explicitly instead of unknown filter, pageSize: 0, sortOrder: 'desc', buildRuleMessage, runtimeMappings, + primaryTimestamp, + secondaryTimestamp, }); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.test.ts index e67a6fa3dfa9c..8ae102ee75b99 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { TIMESTAMP } from '@kbn/rule-data-utils'; import { sampleThresholdSignalHistory } from '../__mocks__/threshold_signal_history.mock'; import { getThresholdBucketFilters } from './get_threshold_bucket_filters'; @@ -12,7 +13,7 @@ describe('getThresholdBucketFilters', () => { it('should generate filters for threshold signal detection with dupe mitigation', async () => { const result = await getThresholdBucketFilters({ signalHistory: sampleThresholdSignalHistory(), - timestampOverride: undefined, + primaryTimestamp: TIMESTAMP, }); expect(result).toEqual([ { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts index 61c15f1d2e4ad..8510641fdc3ee 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threshold/get_threshold_bucket_filters.ts @@ -15,10 +15,10 @@ import type { ThresholdSignalHistory, ThresholdSignalHistoryRecord } from '../ty */ export const getThresholdBucketFilters = async ({ signalHistory, - timestampOverride, + primaryTimestamp, }: { signalHistory: ThresholdSignalHistory; - timestampOverride: string | undefined; + primaryTimestamp: string; }): Promise => { const filters = Object.values(signalHistory).reduce( (acc: ESFilter[], bucket: ThresholdSignalHistoryRecord): ESFilter[] => { @@ -27,7 +27,7 @@ export const getThresholdBucketFilters = async ({ filter: [ { range: { - [timestampOverride ?? '@timestamp']: { + [primaryTimestamp]: { // Timestamp of last event signaled on for this set of terms. lte: new Date(bucket.lastSignalTimestamp).toISOString(), }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts index 4b779c27a1a89..96ec48c2ac55e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/types.ts @@ -328,6 +328,8 @@ export interface SearchAfterAndBulkCreateParams { trackTotalHits?: boolean; sortOrder?: estypes.SortOrder; runtimeMappings: estypes.MappingRuntimeFields | undefined; + primaryTimestamp: string; + secondaryTimestamp?: string; } export interface SearchAfterAndBulkCreateReturnType { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index c0e6c2cb903ca..0f56cc0238d0b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -8,7 +8,7 @@ import moment from 'moment'; import sinon from 'sinon'; import type { TransportResult } from '@elastic/elasticsearch'; -import { ALERT_REASON, ALERT_RULE_PARAMETERS, ALERT_UUID } from '@kbn/rule-data-utils'; +import { ALERT_REASON, ALERT_RULE_PARAMETERS, ALERT_UUID, TIMESTAMP } from '@kbn/rule-data-utils'; import type { RuleExecutorServicesMock } from '@kbn/alerting-plugin/server/mocks'; import { alertsMock } from '@kbn/alerting-plugin/server/mocks'; @@ -981,7 +981,7 @@ describe('utils', () => { const searchResult = sampleEmptyDocSearchResults(); const newSearchResult = createSearchAfterReturnTypeFromResponse({ searchResult, - timestampOverride: undefined, + primaryTimestamp: TIMESTAMP, }); const expected: SearchAfterAndBulkCreateReturnType = { bulkCreateTimes: [], @@ -1001,7 +1001,7 @@ describe('utils', () => { const searchResult = sampleDocSearchResultsWithSortId(); const newSearchResult = createSearchAfterReturnTypeFromResponse({ searchResult, - timestampOverride: undefined, + primaryTimestamp: TIMESTAMP, }); const expected: SearchAfterAndBulkCreateReturnType = { bulkCreateTimes: [], @@ -1024,7 +1024,7 @@ describe('utils', () => { searchResult._shards.failures = [{ reason: { reason: 'Not a sort failure' } }]; const { success } = createSearchAfterReturnTypeFromResponse({ searchResult, - timestampOverride: undefined, + primaryTimestamp: TIMESTAMP, }); expect(success).toEqual(false); }); @@ -1036,7 +1036,7 @@ describe('utils', () => { searchResult._shards.failures = [{ reason: { reason: 'Not a sort failure' } }]; const { success } = createSearchAfterReturnTypeFromResponse({ searchResult, - timestampOverride: undefined, + primaryTimestamp: TIMESTAMP, }); expect(success).toEqual(false); }); @@ -1052,7 +1052,7 @@ describe('utils', () => { ]; const { success } = createSearchAfterReturnTypeFromResponse({ searchResult, - timestampOverride: undefined, + primaryTimestamp: TIMESTAMP, }); expect(success).toEqual(false); }); @@ -1062,7 +1062,7 @@ describe('utils', () => { searchResult._shards.failed = 0; const { success } = createSearchAfterReturnTypeFromResponse({ searchResult, - timestampOverride: undefined, + primaryTimestamp: TIMESTAMP, }); expect(success).toEqual(true); }); @@ -1078,7 +1078,7 @@ describe('utils', () => { ]; const { success } = createSearchAfterReturnTypeFromResponse({ searchResult, - timestampOverride: 'event.ingested', + primaryTimestamp: 'event.ingested', }); expect(success).toEqual(true); }); @@ -1091,7 +1091,7 @@ describe('utils', () => { } const { lastLookBackDate } = createSearchAfterReturnTypeFromResponse({ searchResult, - timestampOverride: undefined, + primaryTimestamp: TIMESTAMP, }); expect(lastLookBackDate).toEqual(null); }); @@ -1104,7 +1104,7 @@ describe('utils', () => { } const { lastLookBackDate } = createSearchAfterReturnTypeFromResponse({ searchResult, - timestampOverride: undefined, + primaryTimestamp: TIMESTAMP, }); expect(lastLookBackDate).toEqual(null); }); @@ -1117,7 +1117,7 @@ describe('utils', () => { } const { lastLookBackDate } = createSearchAfterReturnTypeFromResponse({ searchResult, - timestampOverride: undefined, + primaryTimestamp: TIMESTAMP, }); expect(lastLookBackDate).toEqual(null); }); @@ -1130,7 +1130,7 @@ describe('utils', () => { if (searchResult.hits.hits[0].fields != null) { (searchResult.hits.hits[0].fields['@timestamp'] as unknown) = null; } - const date = lastValidDate({ searchResult, timestampOverride: undefined }); + const date = lastValidDate({ searchResult, primaryTimestamp: TIMESTAMP }); expect(date).toEqual(undefined); }); @@ -1140,7 +1140,7 @@ describe('utils', () => { if (searchResult.hits.hits[0].fields != null) { (searchResult.hits.hits[0].fields['@timestamp'] as unknown) = undefined; } - const date = lastValidDate({ searchResult, timestampOverride: undefined }); + const date = lastValidDate({ searchResult, primaryTimestamp: TIMESTAMP }); expect(date).toEqual(undefined); }); @@ -1150,13 +1150,13 @@ describe('utils', () => { if (searchResult.hits.hits[0].fields != null) { (searchResult.hits.hits[0].fields['@timestamp'] as unknown) = ['invalid value']; } - const date = lastValidDate({ searchResult, timestampOverride: undefined }); + const date = lastValidDate({ searchResult, primaryTimestamp: TIMESTAMP }); expect(date).toEqual(undefined); }); test('It returns normal date time if set', () => { const searchResult = sampleDocSearchResultsNoSortId(); - const date = lastValidDate({ searchResult, timestampOverride: undefined }); + const date = lastValidDate({ searchResult, primaryTimestamp: TIMESTAMP }); expect(date?.toISOString()).toEqual('2020-04-20T21:27:45.000Z'); }); @@ -1172,7 +1172,7 @@ describe('utils', () => { '@timestamp': [timestamp], }, }; - const date = lastValidDate({ searchResult, timestampOverride: undefined }); + const date = lastValidDate({ searchResult, primaryTimestamp: TIMESTAMP }); expect(date?.toISOString()).toEqual(timestamp); }); @@ -1180,7 +1180,7 @@ describe('utils', () => { const override = '2020-10-07T19:20:28.049Z'; const searchResult = sampleDocSearchResultsNoSortId(); searchResult.hits.hits[0]._source.different_timestamp = new Date(override).toISOString(); - const date = lastValidDate({ searchResult, timestampOverride: 'different_timestamp' }); + const date = lastValidDate({ searchResult, primaryTimestamp: 'different_timestamp' }); expect(date?.toISOString()).toEqual(override); }); @@ -1196,7 +1196,7 @@ describe('utils', () => { different_timestamp: [override], }, }; - const date = lastValidDate({ searchResult, timestampOverride: 'different_timestamp' }); + const date = lastValidDate({ searchResult, primaryTimestamp: 'different_timestamp' }); expect(date?.toISOString()).toEqual(override); }); }); @@ -1208,7 +1208,7 @@ describe('utils', () => { if (doc.fields != null) { (doc.fields['@timestamp'] as unknown) = null; } - const date = getValidDateFromDoc({ doc, timestampOverride: undefined }); + const date = getValidDateFromDoc({ doc, primaryTimestamp: TIMESTAMP }); expect(date).toEqual(undefined); }); @@ -1218,7 +1218,7 @@ describe('utils', () => { if (doc.fields != null) { (doc.fields['@timestamp'] as unknown) = undefined; } - const date = getValidDateFromDoc({ doc, timestampOverride: undefined }); + const date = getValidDateFromDoc({ doc, primaryTimestamp: TIMESTAMP }); expect(date).toEqual(undefined); }); @@ -1228,13 +1228,13 @@ describe('utils', () => { if (doc.fields != null) { (doc.fields['@timestamp'] as unknown) = ['invalid value']; } - const date = getValidDateFromDoc({ doc, timestampOverride: undefined }); + const date = getValidDateFromDoc({ doc, primaryTimestamp: TIMESTAMP }); expect(date).toEqual(undefined); }); test('It returns normal date time if set', () => { const doc = sampleDocNoSortId(); - const date = getValidDateFromDoc({ doc, timestampOverride: undefined }); + const date = getValidDateFromDoc({ doc, primaryTimestamp: TIMESTAMP }); expect(date?.toISOString()).toEqual('2020-04-20T21:27:45.000Z'); }); @@ -1250,7 +1250,7 @@ describe('utils', () => { '@timestamp': [timestamp], }, }; - const date = getValidDateFromDoc({ doc, timestampOverride: undefined }); + const date = getValidDateFromDoc({ doc, primaryTimestamp: TIMESTAMP }); expect(date?.toISOString()).toEqual(timestamp); }); @@ -1258,7 +1258,7 @@ describe('utils', () => { const override = '2020-10-07T19:20:28.049Z'; const doc = sampleDocNoSortId(); doc._source.different_timestamp = new Date(override).toISOString(); - const date = getValidDateFromDoc({ doc, timestampOverride: 'different_timestamp' }); + const date = getValidDateFromDoc({ doc, primaryTimestamp: 'different_timestamp' }); expect(date?.toISOString()).toEqual(override); }); @@ -1274,7 +1274,7 @@ describe('utils', () => { different_timestamp: [override], }, }; - const date = getValidDateFromDoc({ doc, timestampOverride: 'different_timestamp' }); + const date = getValidDateFromDoc({ doc, primaryTimestamp: 'different_timestamp' }); expect(date?.toISOString()).toEqual(override); }); @@ -1286,7 +1286,7 @@ describe('utils', () => { if (doc.fields != null) { doc.fields['@timestamp'] = [testDate]; } - const date = getValidDateFromDoc({ doc, timestampOverride: undefined }); + const date = getValidDateFromDoc({ doc, primaryTimestamp: TIMESTAMP }); expect(date?.toISOString()).toEqual(testDateString); }); @@ -1296,7 +1296,7 @@ describe('utils', () => { const testDate = `${new Date(testDateString).valueOf()}`; doc._source['@timestamp'] = testDate; doc.fields = undefined; - const date = getValidDateFromDoc({ doc, timestampOverride: undefined }); + const date = getValidDateFromDoc({ doc, primaryTimestamp: TIMESTAMP }); expect(date?.toISOString()).toEqual(testDateString); }); @@ -1313,7 +1313,7 @@ describe('utils', () => { different_timestamp: [testDate], }, }; - const date = getValidDateFromDoc({ doc, timestampOverride: 'different_timestamp' }); + const date = getValidDateFromDoc({ doc, primaryTimestamp: 'different_timestamp' }); expect(date?.toISOString()).toEqual(override); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts index a0ec3547093bb..ac0d9872dab90 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.ts @@ -34,7 +34,7 @@ import type { import { parseDuration } from '@kbn/alerting-plugin/server'; import type { ExceptionListClient, ListClient, ListPluginSetup } from '@kbn/lists-plugin/server'; import type { - TimestampOverrideOrUndefined, + TimestampOverride, Privilege, } from '../../../../common/detection_engine/schemas/common'; import { RuleExecutionStatus } from '../../../../common/detection_engine/schemas/common'; @@ -604,20 +604,20 @@ export const createErrorsFromShard = ({ errors }: { errors: ShardError[] }): str * it cannot it will resort to using the "_source" fields second which can be problematic if the date time * is not correctly ISO8601 or epoch milliseconds formatted. * @param searchResult The result to try and parse out the timestamp. - * @param timestampOverride The timestamp override to use its values if we have it. + * @param primaryTimestamp The primary timestamp to use. */ export const lastValidDate = ({ searchResult, - timestampOverride, + primaryTimestamp, }: { searchResult: SignalSearchResponse; - timestampOverride: TimestampOverrideOrUndefined; + primaryTimestamp: TimestampOverride; }): Date | undefined => { if (searchResult.hits.hits.length === 0) { return undefined; } else { const lastRecord = searchResult.hits.hits[searchResult.hits.hits.length - 1]; - return getValidDateFromDoc({ doc: lastRecord, timestampOverride }); + return getValidDateFromDoc({ doc: lastRecord, primaryTimestamp }); } }; @@ -627,21 +627,20 @@ export const lastValidDate = ({ * it cannot it will resort to using the "_source" fields second which can be problematic if the date time * is not correctly ISO8601 or epoch milliseconds formatted. * @param searchResult The result to try and parse out the timestamp. - * @param timestampOverride The timestamp override to use its values if we have it. + * @param primaryTimestamp The primary timestamp to use. */ export const getValidDateFromDoc = ({ doc, - timestampOverride, + primaryTimestamp, }: { doc: BaseSignalHit; - timestampOverride: TimestampOverrideOrUndefined; + primaryTimestamp: TimestampOverride; }): Date | undefined => { - const timestamp = timestampOverride ?? '@timestamp'; const timestampValue = - doc.fields != null && doc.fields[timestamp] != null - ? doc.fields[timestamp][0] + doc.fields != null && doc.fields[primaryTimestamp] != null + ? doc.fields[primaryTimestamp][0] : doc._source != null - ? (doc._source as { [key: string]: unknown })[timestamp] + ? (doc._source as { [key: string]: unknown })[primaryTimestamp] : undefined; const lastTimestamp = typeof timestampValue === 'string' || typeof timestampValue === 'number' @@ -668,10 +667,10 @@ export const getValidDateFromDoc = ({ export const createSearchAfterReturnTypeFromResponse = ({ searchResult, - timestampOverride, + primaryTimestamp, }: { searchResult: SignalSearchResponse; - timestampOverride: TimestampOverrideOrUndefined; + primaryTimestamp: TimestampOverride; }): SearchAfterAndBulkCreateReturnType => { return createSearchAfterReturnType({ success: @@ -682,11 +681,11 @@ export const createSearchAfterReturnTypeFromResponse = ({ 'No mapping found for [@timestamp] in order to sort on' ) || failure.reason?.reason?.includes( - `No mapping found for [${timestampOverride}] in order to sort on` + `No mapping found for [${primaryTimestamp}] in order to sort on` ) ); }), - lastLookBackDate: lastValidDate({ searchResult, timestampOverride }), + lastLookBackDate: lastValidDate({ searchResult, primaryTimestamp }), }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/timestamps.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/timestamps.ts index cdec24ae1cc14..0b055017524ff 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/timestamps.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/timestamps.ts @@ -22,6 +22,7 @@ import { createRule, waitForRuleSuccessOrStatus, waitForSignalsToBePresent, + getOpenSignals, getRuleForSignalTesting, getSignalsByIds, getEqlRuleForSignalTesting, @@ -31,6 +32,7 @@ import { export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); + const es = getService('es'); const log = getService('log'); /** @@ -184,6 +186,30 @@ export default ({ getService }: FtrProviderContext) => { expect(signalsOrderedByEventId.length).equal(3); }); + it('should generate 2 signals with event.ingested when timestamp fallback is disabled', async () => { + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['myfa*']), + rule_id: 'rule-without-timestamp-fallback', + timestamp_override: 'event.ingested', + timestamp_override_fallback_disabled: true, + }; + + const { id } = await createRule(supertest, log, rule); + + await waitForRuleSuccessOrStatus( + supertest, + log, + id, + RuleExecutionStatus['partial failure'] + ); + await waitForSignalsToBePresent(supertest, log, 2, [id]); + const signalsResponse = await getSignalsByIds(supertest, log, [id], 2); + const signals = signalsResponse.hits.hits.map((hit) => hit._source); + const signalsOrderedByEventId = orderBy(signals, 'signal.parent.id', 'asc'); + + expect(signalsOrderedByEventId.length).equal(2); + }); + it('should generate 2 signals with @timestamp', async () => { const rule: QueryCreateSchema = getRuleForSignalTesting(['myfa*']); @@ -224,6 +250,25 @@ export default ({ getService }: FtrProviderContext) => { expect(signalsOrderedByEventId.length).equal(2); }); + it('should not generate any signals when timestamp override does not exist and timestamp fallback is disabled', async () => { + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['myfa*']), + rule_id: 'rule-without-timestamp-fallback', + timestamp_override: 'event.fakeingestfield', + timestamp_override_fallback_disabled: true, + }; + + const createdRule = await createRule(supertest, log, rule); + const signalsOpen = await getOpenSignals( + supertest, + log, + es, + createdRule, + RuleExecutionStatus['partial failure'] + ); + expect(signalsOpen.hits.hits.length).eql(0); + }); + /** * We should not use the timestamp override as the "original_time" as that can cause * confusion if you have both a timestamp and an override in the source event. Instead the "original_time" @@ -287,6 +332,23 @@ export default ({ getService }: FtrProviderContext) => { expect(signalsOrderedByEventId.length).equal(2); }); + + it('should not generate any signals when timestamp override does not exist and timestamp fallback is disabled', async () => { + const rule: EqlCreateSchema = { + ...getEqlRuleForSignalTesting(['myfa*']), + timestamp_override: 'event.fakeingestfield', + timestamp_override_fallback_disabled: true, + }; + const createdRule = await createRule(supertest, log, rule); + const signalsOpen = await getOpenSignals( + supertest, + log, + es, + createdRule, + RuleExecutionStatus['partial failure'] + ); + expect(signalsOpen.hits.hits.length).eql(0); + }); }); }); diff --git a/x-pack/test/detection_engine_api_integration/utils/get_open_signals.ts b/x-pack/test/detection_engine_api_integration/utils/get_open_signals.ts index fcbfadec612d5..9f0504fcd4f02 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_open_signals.ts +++ b/x-pack/test/detection_engine_api_integration/utils/get_open_signals.ts @@ -8,6 +8,7 @@ import type { ToolingLog } from '@kbn/tooling-log'; import type SuperTest from 'supertest'; import type { Client } from '@elastic/elasticsearch'; +import { RuleExecutionStatus } from '@kbn/security-solution-plugin/common/detection_engine/schemas/common'; import type { FullResponseSchema } from '@kbn/security-solution-plugin/common/detection_engine/schemas/request'; import { waitForRuleSuccessOrStatus } from './wait_for_rule_success_or_status'; @@ -18,9 +19,10 @@ export const getOpenSignals = async ( supertest: SuperTest.SuperTest, log: ToolingLog, es: Client, - rule: FullResponseSchema + rule: FullResponseSchema, + status: RuleExecutionStatus = RuleExecutionStatus.succeeded ) => { - await waitForRuleSuccessOrStatus(supertest, log, rule.id); + await waitForRuleSuccessOrStatus(supertest, log, rule.id, status); // Critically important that we wait for rule success AND refresh the write index in that order before we // assert that no signals were created. Otherwise, signals could be written but not available to query yet // when we search, causing tests that check that signals are NOT created to pass when they should fail. From 735b69d1aca103a4f23990b22b3a63d90c4acbc3 Mon Sep 17 00:00:00 2001 From: Rickyanto Ang Date: Tue, 12 Jul 2022 10:25:25 -0700 Subject: [PATCH 23/42] [8.4][Session View]Cloud section on Metadata Tab (#136168) * added base functionality for Cloud section * added unit test for Cloud section on session details panel metadata tab * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../common/types/process_tree/index.ts | 15 ++++ .../detail_panel_metadata_tab/helpers.test.ts | 47 +++++++++++- .../detail_panel_metadata_tab/helpers.ts | 36 ++++++++- .../detail_panel_metadata_tab/index.test.tsx | 35 +++++++++ .../detail_panel_metadata_tab/index.tsx | 74 ++++++++++++++++++- .../session_view_detail_panel/index.tsx | 1 + x-pack/plugins/session_view/public/types.ts | 14 ++++ 7 files changed, 217 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/session_view/common/types/process_tree/index.ts b/x-pack/plugins/session_view/common/types/process_tree/index.ts index b59001b1fdfff..04c82e5e9c48d 100644 --- a/x-pack/plugins/session_view/common/types/process_tree/index.ts +++ b/x-pack/plugins/session_view/common/types/process_tree/index.ts @@ -151,6 +151,7 @@ export interface ProcessEvent { }; container?: ProcessEventContainer; orchestrator?: ProcessEventOrchestrator; + cloud?: ProcessEventCloud; } export interface ProcessEventsPage { @@ -218,3 +219,17 @@ export interface ProcessEventOrchestrator { type?: string; }; } + +export interface ProcessEventCloud { + instance?: { + name?: string; + }; + account?: { + id?: string; + }; + project?: { + id?: string; + }; + provider?: string; + region?: string; +} diff --git a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/helpers.test.ts b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/helpers.test.ts index 5689af3c950c0..61d9a7fb0f6b3 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/helpers.test.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/helpers.test.ts @@ -8,9 +8,10 @@ import { ProcessEventHost, ProcessEventContainer, ProcessEventOrchestrator, + ProcessEventCloud, } from '../../../common/types/process_tree'; import { DASH } from '../../constants'; -import { getHostData, getContainerData, getOrchestratorData } from './helpers'; +import { getHostData, getContainerData, getOrchestratorData, getCloudData } from './helpers'; const MOCK_HOST_DATA: ProcessEventHost = { architecture: 'x86_64', @@ -57,6 +58,20 @@ const MOCK_ORCHESTRATOR_DATA: ProcessEventOrchestrator = { }, }; +const MOCK_CLOUD_DATA: ProcessEventCloud = { + instance: { + name: 'gke-cluster-1-paulo-default-pool-f0fea4ab-lhx2', + }, + account: { + id: 'PLACEHOLDER_FOR_CLOUD_ACCOUNT_ID', + }, + project: { + id: 'elastic-security-dev', + }, + provider: 'gcp', + region: 'us-central1-c', +}; + describe('detail panel host tab helpers tests', () => { it('getHostData returns fields with a dash with undefined host', () => { const result = getHostData(undefined); @@ -178,4 +193,34 @@ describe('detail panel host tab helpers tests', () => { expect(result.cluster.id).toEqual(MOCK_ORCHESTRATOR_DATA?.cluster?.id); expect(result.parent.type).toEqual(MOCK_ORCHESTRATOR_DATA?.parent?.type); }); + + it('getCloudData returns dashes for missing fields', () => { + const result = getCloudData({ + instance: { + name: 'gke-cluster-1-paulo-default-pool-f0fea4ab-lhx2', + }, + account: { + id: undefined, + }, + project: { + id: 'elastic-security-dev', + }, + provider: undefined, + region: 'us-central1-c', + }); + expect(result.instance.name).toEqual(MOCK_CLOUD_DATA?.instance?.name); + expect(result.account.id).toEqual(DASH); + expect(result.project.id).toEqual(MOCK_CLOUD_DATA?.project?.id); + expect(result.provider).toEqual(DASH); + expect(result.region).toEqual(MOCK_CLOUD_DATA?.region); + }); + + it('getCloudData returns all data provided', () => { + const result = getCloudData(MOCK_CLOUD_DATA); + expect(result.instance.name).toEqual(MOCK_CLOUD_DATA?.instance?.name); + expect(result.account.id).toEqual(MOCK_CLOUD_DATA?.account?.id); + expect(result.project.id).toEqual(MOCK_CLOUD_DATA?.project?.id); + expect(result.provider).toEqual(MOCK_CLOUD_DATA?.provider); + expect(result.region).toEqual(MOCK_CLOUD_DATA?.region); + }); }); diff --git a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/helpers.ts b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/helpers.ts index 384e8698a37b2..7254f57cc93c6 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/helpers.ts +++ b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/helpers.ts @@ -9,9 +9,15 @@ import { ProcessEventHost, ProcessEventContainer, ProcessEventOrchestrator, + ProcessEventCloud, } from '../../../common/types/process_tree'; import { DASH } from '../../constants'; -import { DetailPanelHost, DetailPanelContainer, DetailPanelOrchestrator } from '../../types'; +import { + DetailPanelHost, + DetailPanelContainer, + DetailPanelOrchestrator, + DetailPanelCloud, +} from '../../types'; import { dataOrDash } from '../../utils/data_or_dash'; export const getHostData = (host: ProcessEventHost | undefined): DetailPanelHost => { @@ -113,3 +119,31 @@ export const getOrchestratorData = ( return detailPanelOrchestrator; }; + +export const getCloudData = (cloud: ProcessEventCloud | undefined): DetailPanelCloud => { + const detailPanelCloud: DetailPanelCloud = { + instance: { + name: DASH, + }, + account: { + id: DASH, + }, + project: { + id: DASH, + }, + provider: DASH, + region: DASH, + }; + + if (!cloud) { + return detailPanelCloud; + } + + detailPanelCloud.instance.name = dataOrDash(cloud?.instance?.name).toString(); + detailPanelCloud.account.id = dataOrDash(cloud?.account?.id).toString(); + detailPanelCloud.project.id = dataOrDash(cloud?.project?.id).toString(); + detailPanelCloud.provider = dataOrDash(cloud?.provider).toString(); + detailPanelCloud.region = dataOrDash(cloud?.region).toString(); + + return detailPanelCloud; +}; diff --git a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.test.tsx b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.test.tsx index 19fab27ee20a4..29a58fa9e052a 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.test.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.test.tsx @@ -11,6 +11,7 @@ import { ProcessEventHost, ProcessEventContainer, ProcessEventOrchestrator, + ProcessEventCloud, } from '../../../common/types/process_tree'; import { DetailPanelMetadataTab } from '.'; @@ -44,6 +45,13 @@ const TEST_ORCHESTRATOR_PARENT_TYPE = 'elastic-k8s-cluster'; const TEST_ORCHESTRATOR_CLUSTER_ID = 'PLACEHOLDER_FOR_CLUSTER.ID'; const TEST_ORCHESTRATOR_CLUSTER_NAME = 'PLACEHOLDER_FOR_PARENT.TYPE'; +// Cloud data +const TEST_CLOUD_INSTANCE_NAME = 'gke-cluster-1-paulo-default-pool-f0fea4ab-lhx2'; +const TEST_CLOUD_ACCOUNT_ID = 'PLACEHOLDER_FOR_CLOUD_ACCOUNT_ID'; +const TEST_CLOUD_PROJECT_ID = 'elastic-security-dev'; +const TEST_CLOUD_PROVIDER = 'gcp'; +const TEST_CLOUD_REGION = 'us-central1-c'; + const TEST_HOST: ProcessEventHost = { architecture: TEST_ARCHITECTURE, hostname: TEST_HOSTNAME, @@ -89,6 +97,20 @@ const TEST_ORCHESTRATOR: ProcessEventOrchestrator = { }, }; +const TEST_CLOUD: ProcessEventCloud = { + instance: { + name: TEST_CLOUD_INSTANCE_NAME, + }, + account: { + id: TEST_CLOUD_ACCOUNT_ID, + }, + project: { + id: TEST_CLOUD_PROJECT_ID, + }, + provider: TEST_CLOUD_PROVIDER, + region: TEST_CLOUD_REGION, +}; + describe('DetailPanelMetadataTab component', () => { let render: () => ReturnType; let renderResult: ReturnType; @@ -133,6 +155,7 @@ describe('DetailPanelMetadataTab component', () => { // Orchestrator and Container should be missing if session came from a Non-cloud env expect(renderResult.queryByText('Container')).toBeNull(); expect(renderResult.queryByText('Orchestrator')).toBeNull(); + expect(renderResult.queryByText('Cloud')).toBeNull(); }); it('renders DetailPanelMetadataTab correctly (cloud environment)', async () => { @@ -141,6 +164,7 @@ describe('DetailPanelMetadataTab component', () => { processHost={TEST_HOST} processContainer={TEST_CONTAINER} processOrchestrator={TEST_ORCHESTRATOR} + processCloud={TEST_CLOUD} /> ); @@ -201,6 +225,17 @@ describe('DetailPanelMetadataTab component', () => { expect(renderResult.queryByText(TEST_ORCHESTRATOR_PARENT_TYPE)).toBeVisible(); expect(renderResult.queryByText(TEST_ORCHESTRATOR_CLUSTER_ID)).toBeVisible(); expect(renderResult.queryByText(TEST_ORCHESTRATOR_CLUSTER_NAME)).toBeVisible(); + + // expand Cloud Accordion + renderResult.queryByText('Cloud')?.click(); + expect(renderResult.queryByText('provider')).toBeVisible(); + expect(renderResult.queryByText('region')).toBeVisible(); + expect(renderResult.queryByText('account.id')).toBeVisible(); + expect(renderResult.queryByText('project.id')).toBeVisible(); + expect(renderResult.queryByText(TEST_CLOUD_PROVIDER)).toBeVisible(); + expect(renderResult.queryByText(TEST_CLOUD_REGION)).toBeVisible(); + expect(renderResult.queryByText(TEST_CLOUD_ACCOUNT_ID)).toBeVisible(); + expect(renderResult.queryByText(TEST_CLOUD_PROJECT_ID)).toBeVisible(); }); }); }); diff --git a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.tsx b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.tsx index 457c98ede4258..d8bc9fa6fd46b 100644 --- a/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.tsx +++ b/x-pack/plugins/session_view/public/components/detail_panel_metadata_tab/index.tsx @@ -11,18 +11,20 @@ import { ProcessEventHost, ProcessEventContainer, ProcessEventOrchestrator, + ProcessEventCloud, } from '../../../common/types/process_tree'; import { DetailPanelAccordion } from '../detail_panel_accordion'; import { DetailPanelCopy } from '../detail_panel_copy'; import { DetailPanelListItem } from '../detail_panel_list_item'; import { useStyles } from '../detail_panel_process_tab/styles'; import { useStyles as useStylesChild } from './styles'; -import { getHostData, getContainerData, getOrchestratorData } from './helpers'; +import { getHostData, getContainerData, getOrchestratorData, getCloudData } from './helpers'; interface DetailPanelMetadataTabDeps { processHost?: ProcessEventHost; processContainer?: ProcessEventContainer; processOrchestrator?: ProcessEventOrchestrator; + processCloud?: ProcessEventCloud; } /** @@ -32,6 +34,7 @@ export const DetailPanelMetadataTab = ({ processHost, processContainer, processOrchestrator, + processCloud, }: DetailPanelMetadataTabDeps) => { const styles = useStyles(); const stylesChild = useStylesChild(); @@ -41,6 +44,7 @@ export const DetailPanelMetadataTab = ({ () => getOrchestratorData(processOrchestrator), [processOrchestrator] ); + const cloudData = useMemo(() => getCloudData(processCloud), [processCloud]); return ( <> @@ -120,8 +124,8 @@ export const DetailPanelMetadataTab = ({ > + {processCloud && ( + <> + provider, + description: ( + + + {cloudData.provider} + + + ), + }, + { + title: region, + description: ( + + + {cloudData.region} + + + ), + }, + { + title: account.id, + description: ( + + + {cloudData.account.id} + + + ), + }, + { + title: project.id, + description: ( + + + {cloudData.project.id} + + + ), + }, + ]} + /> + + )} {processContainer && ( <> ), }, diff --git a/x-pack/plugins/session_view/public/types.ts b/x-pack/plugins/session_view/public/types.ts index 8617a580bf522..f8a42c0c4660c 100644 --- a/x-pack/plugins/session_view/public/types.ts +++ b/x-pack/plugins/session_view/public/types.ts @@ -118,6 +118,20 @@ export interface DetailPanelOrchestrator { }; } +export interface DetailPanelCloud { + instance: { + name: string; + }; + account: { + id: string; + }; + project: { + id: string; + }; + provider: string; + region: string; +} + export interface SessionViewStart { getSessionView: (props: SessionViewDeps) => JSX.Element; } From 8acc1466f85ade1d85e21c9a0efbd5c7713cabd7 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 12 Jul 2022 12:30:49 -0500 Subject: [PATCH 24/42] [ci] Conditonal webpack bundle reports (#136215) * [ci] Conditonal webpack bundle reports * fix * spacing * \n * fix file path --- .buildkite/pipelines/pull_request/base.yml | 7 ------- .../pipelines/pull_request/webpack_bundle_analyzer.yml | 7 +++++++ .buildkite/scripts/pipelines/pull_request/pipeline.ts | 4 ++++ .../steps/webpack_bundle_analyzer/build_and_upload.sh | 2 ++ 4 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 .buildkite/pipelines/pull_request/webpack_bundle_analyzer.yml diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index eca8e8280d8e8..36989003428e2 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -75,10 +75,3 @@ steps: automatic: - exit_status: '-1' limit: 3 - - - command: .buildkite/scripts/steps/webpack_bundle_analyzer/build_and_upload.sh - label: 'Build Webpack Bundle Analyzer reports' - agents: - queue: c2-16 - key: webpack_bundle_analyzer - timeout_in_minutes: 60 diff --git a/.buildkite/pipelines/pull_request/webpack_bundle_analyzer.yml b/.buildkite/pipelines/pull_request/webpack_bundle_analyzer.yml new file mode 100644 index 0000000000000..f1e61875fa254 --- /dev/null +++ b/.buildkite/pipelines/pull_request/webpack_bundle_analyzer.yml @@ -0,0 +1,7 @@ +steps: + - command: .buildkite/scripts/steps/webpack_bundle_analyzer/build_and_upload.sh + label: 'Build Webpack Bundle Analyzer reports' + agents: + queue: n2-4-spot + key: webpack_bundle_analyzer + timeout_in_minutes: 60 diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.ts b/.buildkite/scripts/pipelines/pull_request/pipeline.ts index 0777eca88dca8..025a8b497c544 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.ts +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.ts @@ -126,6 +126,10 @@ const uploadPipeline = (pipelineContent: string | object) => { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/deploy_cloud.yml')); } + if (GITHUB_PR_LABELS.includes('ci:build-webpack-bundle-analyzer')) { + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/webpack_bundle_analyzer.yml')); + } + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/post_build.yml')); uploadPipeline(pipeline.join('\n')); diff --git a/.buildkite/scripts/steps/webpack_bundle_analyzer/build_and_upload.sh b/.buildkite/scripts/steps/webpack_bundle_analyzer/build_and_upload.sh index 4374cce2281f8..24d08f7d5dc0a 100755 --- a/.buildkite/scripts/steps/webpack_bundle_analyzer/build_and_upload.sh +++ b/.buildkite/scripts/steps/webpack_bundle_analyzer/build_and_upload.sh @@ -4,6 +4,8 @@ set -euo pipefail .buildkite/scripts/bootstrap.sh +export NODE_OPTIONS="--max-old-space-size=8192" + node scripts/build_kibana_platform_plugins.js --dist --profile mkdir -p built_assets/webpack_bundle_analyzer From 4ad614c578e4f0d768d884c87167d8bea0075175 Mon Sep 17 00:00:00 2001 From: Melissa Alvarez Date: Tue, 12 Jul 2022 14:35:50 -0400 Subject: [PATCH 25/42] [ML] Explain log rate spikes: UI Part 1 (#135948) * wip: create initial use full data and log spike table and histogram chart * wip: adds timepicker and use full data complete functionality * ensure time selectin persists in url * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * add impact and score bar to table * fix filename typo * add lazy component wrapper, fix translation naming * update type names * remove duplicate code * update error type * rename overall stats hook to doccountstats hook Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/aiops/common/error_utils.ts | 184 +++++++ x-pack/plugins/aiops/common/parse_interval.ts | 65 +++ x-pack/plugins/aiops/common/time_buckets.d.ts | 45 ++ x-pack/plugins/aiops/common/time_buckets.js | 516 ++++++++++++++++++ x-pack/plugins/aiops/kibana.json | 3 +- .../application/services/time_field_range.ts | 37 ++ .../services/timefilter_refresh_service.ts | 15 + .../date_picker_wrapper.tsx | 169 ++++++ .../components/date_picker_wrapper/index.ts | 8 + .../document_count_chart.tsx | 162 ++++++ .../document_count_chart/index.ts | 9 + .../document_count_content.tsx | 43 ++ .../document_count_content/index.ts | 8 + .../total_count_header/index.ts | 8 + .../total_count_header/total_count_header.tsx | 32 ++ .../explain_log_rate_spikes.tsx | 133 ++++- .../explain_log_rate_spikes_wrapper.tsx | 96 ++++ .../explain_log_rate_spikes/index.ts | 4 +- .../full_time_range_selector.tsx | 198 +++++++ .../full_time_range_selector_service.ts | 70 +++ .../full_time_range_selector/index.ts | 8 + ...d_transactions_correlation_impact_label.ts | 64 +++ .../spike_analysis_table/impact_bar.tsx | 31 ++ .../components/spike_analysis_table/index.ts | 8 + .../spike_analysis_table.tsx | 153 ++++++ .../aiops/public/get_document_stats.ts | 114 ++++ .../plugins/aiops/public/hooks/url_state.ts | 147 +++++ x-pack/plugins/aiops/public/hooks/use_data.ts | 102 ++++ .../public/hooks/use_document_count_stats.ts | 102 ++++ .../plugins/aiops/public/hooks/use_storage.ts | 44 ++ .../aiops/public/hooks/use_time_filter.ts | 38 ++ x-pack/plugins/aiops/public/kibana_context.ts | 18 + .../plugins/aiops/public/kibana_services.ts | 15 + x-pack/plugins/aiops/public/plugin.ts | 14 +- x-pack/plugins/aiops/public/query_utils.ts | 87 +++ .../aiops/public/shared_lazy_components.tsx | 6 +- x-pack/plugins/aiops/tsconfig.json | 1 + 37 files changed, 2732 insertions(+), 25 deletions(-) create mode 100644 x-pack/plugins/aiops/common/error_utils.ts create mode 100644 x-pack/plugins/aiops/common/parse_interval.ts create mode 100644 x-pack/plugins/aiops/common/time_buckets.d.ts create mode 100644 x-pack/plugins/aiops/common/time_buckets.js create mode 100644 x-pack/plugins/aiops/public/application/services/time_field_range.ts create mode 100644 x-pack/plugins/aiops/public/application/services/timefilter_refresh_service.ts create mode 100644 x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx create mode 100644 x-pack/plugins/aiops/public/components/date_picker_wrapper/index.ts create mode 100644 x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/document_count_chart.tsx create mode 100644 x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/index.ts create mode 100644 x-pack/plugins/aiops/public/components/document_count_content/document_count_content/document_count_content.tsx create mode 100644 x-pack/plugins/aiops/public/components/document_count_content/document_count_content/index.ts create mode 100644 x-pack/plugins/aiops/public/components/document_count_content/total_count_header/index.ts create mode 100644 x-pack/plugins/aiops/public/components/document_count_content/total_count_header/total_count_header.tsx create mode 100644 x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_wrapper.tsx create mode 100644 x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector.tsx create mode 100644 x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector_service.ts create mode 100644 x-pack/plugins/aiops/public/components/full_time_range_selector/index.ts create mode 100644 x-pack/plugins/aiops/public/components/spike_analysis_table/get_failed_transactions_correlation_impact_label.ts create mode 100644 x-pack/plugins/aiops/public/components/spike_analysis_table/impact_bar.tsx create mode 100644 x-pack/plugins/aiops/public/components/spike_analysis_table/index.ts create mode 100644 x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx create mode 100644 x-pack/plugins/aiops/public/get_document_stats.ts create mode 100644 x-pack/plugins/aiops/public/hooks/url_state.ts create mode 100644 x-pack/plugins/aiops/public/hooks/use_data.ts create mode 100644 x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts create mode 100644 x-pack/plugins/aiops/public/hooks/use_storage.ts create mode 100644 x-pack/plugins/aiops/public/hooks/use_time_filter.ts create mode 100644 x-pack/plugins/aiops/public/kibana_context.ts create mode 100644 x-pack/plugins/aiops/public/kibana_services.ts create mode 100644 x-pack/plugins/aiops/public/query_utils.ts diff --git a/x-pack/plugins/aiops/common/error_utils.ts b/x-pack/plugins/aiops/common/error_utils.ts new file mode 100644 index 0000000000000..e3320663cfbf8 --- /dev/null +++ b/x-pack/plugins/aiops/common/error_utils.ts @@ -0,0 +1,184 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { IHttpFetchError } from '@kbn/core-http-browser'; +import Boom from '@hapi/boom'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; + +export interface WrappedError { + body: { + attributes: { + body: EsErrorBody; + }; + message: Boom.Boom; + }; + statusCode: number; +} + +export interface EsErrorRootCause { + type: string; + reason: string; + caused_by?: EsErrorRootCause; + script?: string; +} + +export interface EsErrorBody { + error: { + root_cause?: EsErrorRootCause[]; + caused_by?: EsErrorRootCause; + type: string; + reason: string; + }; + status: number; +} + +export interface AiOpsResponseError { + statusCode: number; + error: string; + message: string; + attributes?: { + body: EsErrorBody; + }; +} + +export interface ErrorMessage { + message: string; +} + +export interface AiOpsErrorObject { + causedBy?: string; + message: string; + statusCode?: number; + fullError?: EsErrorBody; +} + +export interface AiOpsHttpFetchError extends IHttpFetchError { + body: T; +} + +export type ErrorType = + | WrappedError + | AiOpsHttpFetchError + | EsErrorBody + | Boom.Boom + | string + | undefined; + +export function isEsErrorBody(error: any): error is EsErrorBody { + return error && error.error?.reason !== undefined; +} + +export function isErrorString(error: any): error is string { + return typeof error === 'string'; +} + +export function isErrorMessage(error: any): error is ErrorMessage { + return error && error.message !== undefined && typeof error.message === 'string'; +} + +export function isAiOpsResponseError(error: any): error is AiOpsResponseError { + return typeof error.body === 'object' && 'message' in error.body; +} + +export function isBoomError(error: any): error is Boom.Boom { + return error?.isBoom === true; +} + +export function isWrappedError(error: any): error is WrappedError { + return error && isBoomError(error.body?.message) === true; +} + +export const extractErrorProperties = (error: ErrorType): AiOpsErrorObject => { + // extract properties of the error object from within the response error + // coming from Kibana, Elasticsearch, and our own AiOps messages + + // some responses contain raw es errors as part of a bulk response + // e.g. if some jobs fail the action in a bulk request + + if (isEsErrorBody(error)) { + return { + message: error.error.reason, + statusCode: error.status, + fullError: error, + }; + } + + if (isErrorString(error)) { + return { + message: error, + }; + } + if (isWrappedError(error)) { + return error.body.message?.output?.payload; + } + + if (isBoomError(error)) { + return { + message: error.output.payload.message, + statusCode: error.output.payload.statusCode, + }; + } + + if (error?.body === undefined && !error?.message) { + return { + message: '', + }; + } + + if (typeof error.body === 'string') { + return { + message: error.body, + }; + } + + if (isAiOpsResponseError(error)) { + if ( + typeof error.body.attributes === 'object' && + typeof error.body.attributes.body?.error?.reason === 'string' + ) { + const errObj: AiOpsErrorObject = { + message: error.body.attributes.body.error.reason, + statusCode: error.body.statusCode, + fullError: error.body.attributes.body, + }; + if ( + typeof error.body.attributes.body.error.caused_by === 'object' && + (typeof error.body.attributes.body.error.caused_by?.reason === 'string' || + typeof error.body.attributes.body.error.caused_by?.caused_by?.reason === 'string') + ) { + errObj.causedBy = + error.body.attributes.body.error.caused_by?.caused_by?.reason || + error.body.attributes.body.error.caused_by?.reason; + } + if ( + Array.isArray(error.body.attributes.body.error.root_cause) && + typeof error.body.attributes.body.error.root_cause[0] === 'object' && + isPopulatedObject(error.body.attributes.body.error.root_cause[0], ['script']) + ) { + errObj.causedBy = error.body.attributes.body.error.root_cause[0].script; + errObj.message += `: '${error.body.attributes.body.error.root_cause[0].script}'`; + } + return errObj; + } else { + return { + message: error.body.message, + statusCode: error.body.statusCode, + }; + } + } + + if (isErrorMessage(error)) { + return { + message: error.message, + }; + } + + // If all else fail return an empty message instead of JSON.stringify + return { + message: '', + }; +}; diff --git a/x-pack/plugins/aiops/common/parse_interval.ts b/x-pack/plugins/aiops/common/parse_interval.ts new file mode 100644 index 0000000000000..73a20974cde4f --- /dev/null +++ b/x-pack/plugins/aiops/common/parse_interval.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { duration, Duration, unitOfTime } from 'moment'; +import dateMath from '@kbn/datemath'; + +type SupportedUnits = unitOfTime.Base; + +// Assume interval is in the form (value)(unit), such as "1h" +const INTERVAL_STRING_RE = new RegExp('^([0-9]*)\\s*(' + dateMath.units.join('|') + ')$'); + +// moment.js is only designed to allow fractional values between 0 and 1 +// for units of hour or less. +const SUPPORT_ZERO_DURATION_UNITS: SupportedUnits[] = ['ms', 's', 'm', 'h']; + +// List of time units which are supported for use in Elasticsearch durations +// (such as anomaly detection job bucket spans) +// See https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units +const SUPPORT_ES_DURATION_UNITS: SupportedUnits[] = ['ms', 's', 'm', 'h', 'd']; + +// Parses an interval String, such as 7d, 1h or 30m to a moment duration. +// Optionally carries out an additional check that the interval is supported as a +// time unit by Elasticsearch, as units greater than 'd' for example cannot be used +// for anomaly detection job bucket spans. +// Differs from the Kibana ui/utils/parse_interval in the following ways: +// 1. A value-less interval such as 'm' is not allowed - in line with the ML back-end +// not accepting such interval Strings for the bucket span of a job. +// 2. Zero length durations 0ms, 0s, 0m and 0h are accepted as-is. +// Note that when adding or subtracting fractional durations, moment is only designed +// to work with units less than 'day'. +// 3. Fractional intervals e.g. 1.5h or 4.5d are not allowed, in line with the behaviour +// of the Elasticsearch date histogram aggregation. +export function parseInterval( + interval: string | number, + checkValidEsUnit = false +): Duration | null { + const matches = String(interval).trim().match(INTERVAL_STRING_RE); + if (!Array.isArray(matches) || matches.length < 3) { + return null; + } + + try { + const value = parseInt(matches[1], 10); + const unit = matches[2] as SupportedUnits; + + // In line with moment.js, only allow zero value intervals when the unit is less than 'day'. + // And check for isNaN as e.g. valueless 'm' will pass the regex test, + // plus an optional check that the unit is not w/M/y which are not fully supported by ES. + if ( + isNaN(value) || + (value < 1 && SUPPORT_ZERO_DURATION_UNITS.indexOf(unit) === -1) || + (checkValidEsUnit === true && SUPPORT_ES_DURATION_UNITS.indexOf(unit) === -1) + ) { + return null; + } + + return duration(value, unit); + } catch (e) { + return null; + } +} diff --git a/x-pack/plugins/aiops/common/time_buckets.d.ts b/x-pack/plugins/aiops/common/time_buckets.d.ts new file mode 100644 index 0000000000000..62a3187be47dc --- /dev/null +++ b/x-pack/plugins/aiops/common/time_buckets.d.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Moment } from 'moment'; + +export interface TimeRangeBounds { + min?: Moment; + max?: Moment; +} + +export declare interface TimeBucketsInterval { + asMilliseconds: () => number; + asSeconds: () => number; + expression: string; +} + +export interface TimeBucketsConfig { + 'histogram:maxBars': number; + 'histogram:barTarget': number; + dateFormat: string; + 'dateFormat:scaled': string[][]; +} + +export declare class TimeBuckets { + constructor(timeBucketsConfig: TimeBucketsConfig); + public setBarTarget(barTarget: number): void; + public setMaxBars(maxBars: number): void; + public setInterval(interval: string): void; + public setBounds(bounds: TimeRangeBounds): void; + public getBounds(): { min: Moment; max: Moment }; + public getInterval(): TimeBucketsInterval; + public getScaledDateFormat(): string; +} + +export declare function getTimeBucketsFromCache(): InstanceType; + +export declare function getBoundsRoundedToInterval( + bounds: TimeRangeBounds, + interval: TimeBucketsInterval, + inclusiveEnd?: boolean +): Required; diff --git a/x-pack/plugins/aiops/common/time_buckets.js b/x-pack/plugins/aiops/common/time_buckets.js new file mode 100644 index 0000000000000..8d38985d07703 --- /dev/null +++ b/x-pack/plugins/aiops/common/time_buckets.js @@ -0,0 +1,516 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; +import { UI_SETTINGS } from '@kbn/data-plugin/common'; +import { ary, assign, isPlainObject, isString, sortBy } from 'lodash'; +import moment from 'moment'; +import dateMath from '@kbn/datemath'; +import { parseInterval } from './parse_interval'; + +const { duration: d } = moment; + +export function timeBucketsCalcAutoIntervalProvider() { + // Note there is a current issue with Kibana (Kibana issue #9184) + // which means we can't round to, for example, 2 week or 3 week buckets, + // so there is a large gap between the 1 week and 1 month rule. + const roundingRules = [ + [d(500, 'ms'), d(100, 'ms')], + [d(5, 'second'), d(1, 'second')], + [d(10, 'second'), d(5, 'second')], + [d(15, 'second'), d(10, 'second')], + [d(30, 'second'), d(15, 'second')], + [d(1, 'minute'), d(30, 'second')], + [d(5, 'minute'), d(1, 'minute')], + [d(10, 'minute'), d(5, 'minute')], + [d(15, 'minute'), d(10, 'minute')], + [d(30, 'minute'), d(10, 'minute')], + [d(1, 'hour'), d(30, 'minute')], + [d(2, 'hour'), d(1, 'hour')], + [d(4, 'hour'), d(2, 'hour')], + [d(6, 'hour'), d(4, 'hour')], + [d(8, 'hour'), d(6, 'hour')], + [d(12, 'hour'), d(8, 'hour')], + [d(24, 'hour'), d(12, 'hour')], + [d(2, 'd'), d(1, 'd')], + [d(4, 'd'), d(2, 'd')], + [d(1, 'week'), d(4, 'd')], + // [ d(2, 'week'), d(1, 'week') ], + // [ d(1, 'month'), d(2, 'week') ], + [d(1, 'month'), d(1, 'week')], + [d(1, 'year'), d(1, 'month')], + [Infinity, d(1, 'year')], + ]; + + const revRoundingRules = roundingRules.slice(0).reverse(); + + function find(rules, check, last) { + function pick(buckets, duration) { + const target = duration / buckets; + let lastResp; + + for (let i = 0; i < rules.length; i++) { + const rule = rules[i]; + const resp = check(rule[0], rule[1], target); + + if (resp == null) { + if (!last) { + continue; + } + if (lastResp) { + return lastResp; + } + break; + } + + if (!last) { + return resp; + } + lastResp = resp; + } + + // fallback to just a number of milliseconds, ensure ms is >= 1 + const ms = Math.max(Math.floor(target), 1); + return moment.duration(ms, 'ms'); + } + + return function (buckets, duration) { + const interval = pick(buckets, duration); + if (interval) { + return moment.duration(interval._data); + } + }; + } + + return { + near: find( + revRoundingRules, + function near(upperBound, lowerBound, target) { + // upperBound - first duration in rule + // lowerBound - second duration in rule + // target - target interval in milliseconds. + if (upperBound > target) { + if (upperBound === Infinity) { + return lowerBound; + } + + const boundMs = upperBound.asMilliseconds(); + const intervalMs = lowerBound.asMilliseconds(); + const retInterval = + Math.abs(boundMs - target) <= Math.abs(intervalMs) ? upperBound : lowerBound; + return retInterval; + } + }, + true + ), + + lessThan: find(revRoundingRules, function (upperBound, lowerBound, target) { + // upperBound - first duration in rule + // lowerBound - second duration in rule + // target - target interval in milliseconds. Must not return intervals less than this duration. + if (lowerBound < target) { + return upperBound !== Infinity ? upperBound : lowerBound; + } + }), + + atLeast: find(revRoundingRules, function atLeast(upperBound, lowerBound, target) { + // Unmodified from Kibana ui/time_buckets/calc_auto_interval.js. + if (lowerBound <= target) { + return lowerBound; + } + }), + }; +} + +const unitsDesc = dateMath.unitsDesc; + +// Index of the list of time interval units at which larger units (i.e. weeks, months, years) need +// need to be converted to multiples of the largest unit supported in ES aggregation intervals (i.e. days). +// Note that similarly the largest interval supported for ML bucket spans is 'd'. +const timeUnitsMaxSupportedIndex = unitsDesc.indexOf('w'); + +const calcAuto = timeBucketsCalcAutoIntervalProvider(); + +/** + * Helper object for wrapping the concept of an "Interval", which + * describes a timespan that will separate buckets of time, + * for example the interval between points on a time series chart. + */ +export function TimeBuckets(timeBucketsConfig, fieldFormats) { + this._timeBucketsConfig = timeBucketsConfig; + this._fieldFormats = fieldFormats; + this.barTarget = this._timeBucketsConfig[UI_SETTINGS.HISTOGRAM_BAR_TARGET]; + this.maxBars = this._timeBucketsConfig[UI_SETTINGS.HISTOGRAM_MAX_BARS]; +} + +/** + * Set the target number of bars. + * + * @param {number} bt - target number of bars (buckets). + * + * @returns {undefined} + */ +TimeBuckets.prototype.setBarTarget = function (bt) { + this.barTarget = bt; +}; + +/** + * Set the maximum number of bars. + * + * @param {number} mb - maximum number of bars (buckets). + * + * @returns {undefined} + */ +TimeBuckets.prototype.setMaxBars = function (mb) { + this.maxBars = mb; +}; + +/** + * Set the bounds that these buckets are expected to cover. + * This is required to support interval "auto" as well + * as interval scaling. + * + * @param {object} input - an object with properties min and max, + * representing the edges for the time span + * we should cover + * + * @returns {undefined} + */ +TimeBuckets.prototype.setBounds = function (input) { + if (!input) return this.clearBounds(); + + let bounds; + if (isPlainObject(input)) { + // accept the response from timefilter.getActiveBounds() + bounds = [input.min, input.max]; + } else { + bounds = Array.isArray(input) ? input : []; + } + + const moments = sortBy(bounds.map(ary(moment, 1)), Number); + + const valid = moments.length === 2 && moments.every(isValidMoment); + if (!valid) { + this.clearBounds(); + throw new Error('invalid bounds set: ' + input); + } + + this._lb = moments.shift(); + this._ub = moments.pop(); + if (this.getDuration().asSeconds() < 0) { + throw new TypeError('Intervals must be positive'); + } +}; + +/** + * Clear the stored bounds + * + * @return {undefined} + */ +TimeBuckets.prototype.clearBounds = function () { + this._lb = this._ub = null; +}; + +/** + * Check to see if we have received bounds yet + * + * @return {Boolean} + */ +TimeBuckets.prototype.hasBounds = function () { + return isValidMoment(this._ub) && isValidMoment(this._lb); +}; + +/** + * Return the current bounds, if we have any. + * + * Note that this does not clone the bounds, so editing them may have unexpected side-effects. + * Always call bounds.min.clone() before editing. + * + * @return {object|undefined} - If bounds are not defined, this + * returns undefined, else it returns the bounds + * for these buckets. This object has two props, + * min and max. Each property will be a moment() + * object + */ +TimeBuckets.prototype.getBounds = function () { + if (!this.hasBounds()) return; + return { + min: this._lb, + max: this._ub, + }; +}; + +/** + * Get a moment duration object representing + * the distance between the bounds, if the bounds + * are set. + * + * @return {moment.duration|undefined} + */ +TimeBuckets.prototype.getDuration = function () { + if (!this.hasBounds()) return; + return moment.duration(this._ub - this._lb, 'ms'); +}; + +/** + * Update the interval at which buckets should be + * generated. + * + * Input can be one of the following: + * - "auto" + * - an interval String, such as 7d, 1h or 30m which can be parsed to a moment duration using ml/common/util/parse_interval + * - a moment.duration object. + * + * @param {string|moment.duration} input - see desc + */ +TimeBuckets.prototype.setInterval = function (input) { + // Preserve the original units because they're lost when the interval is converted to a + // moment duration object. + this.originalInterval = input; + + let interval = input; + + if (!interval || interval === 'auto') { + this._i = 'auto'; + return; + } + + if (isString(interval)) { + input = interval; + interval = parseInterval(interval); + if (+interval === 0) { + interval = null; + } + } + + // If the value wasn't converted to a duration, and isn't already a duration, we have a problem + if (!moment.isDuration(interval)) { + throw new TypeError('"' + input + '" is not a valid interval.'); + } + + this._i = interval; +}; + +/** + * Get the interval for the buckets. If the + * number of buckets created by the interval set + * is larger than config:histogram:maxBars then the + * interval will be scaled up. If the number of buckets + * created is less than one, the interval is scaled back. + * + * The interval object returned is a moment.duration + * object that has been decorated with the following + * properties. + * + * interval.description: a text description of the interval. + * designed to be used list "field per {{ desc }}". + * - "minute" + * - "10 days" + * - "3 years" + * + * interval.expr: the elasticsearch expression that creates this + * interval. If the interval does not properly form an elasticsearch + * expression it will be forced into one. + * + * interval.scaled: the interval was adjusted to + * accommodate the maxBars setting. + * + * interval.scale: the number that y-values should be + * multiplied by + * + * interval.scaleDescription: a description that reflects + * the values which will be produced by using the + * interval.scale. + * + * + * @return {[type]} [description] + */ +TimeBuckets.prototype.getInterval = function () { + const self = this; + const duration = self.getDuration(); + return decorateInterval(maybeScaleInterval(readInterval()), duration); + + // either pull the interval from state or calculate the auto-interval + function readInterval() { + const interval = self._i; + if (moment.isDuration(interval)) return interval; + return calcAuto.near(self.barTarget, duration); + } + + // check to see if the interval should be scaled, and scale it if so + function maybeScaleInterval(interval) { + if (!self.hasBounds()) return interval; + + const maxLength = self.maxBars; + const approxLen = duration / interval; + let scaled; + + // If the number of buckets we got back from using the barTarget is less than + // maxBars, than use the lessThan rule to try and get closer to maxBars. + if (approxLen > maxLength) { + scaled = calcAuto.lessThan(maxLength, duration); + } else { + return interval; + } + + if (+scaled === +interval) return interval; + + decorateInterval(interval, duration); + return assign(scaled, { + preScaled: interval, + scale: interval / scaled, + scaled: true, + }); + } +}; + +/** + * Returns an interval which in the last step of calculation is rounded to + * the closest multiple of the supplied divisor (in seconds). + * + * @return {moment.duration|undefined} + */ +TimeBuckets.prototype.getIntervalToNearestMultiple = function (divisorSecs) { + const interval = this.getInterval(); + const intervalSecs = interval.asSeconds(); + + const remainder = intervalSecs % divisorSecs; + if (remainder === 0) { + return interval; + } + + // Create a new interval which is a multiple of the supplied divisor (not zero). + let nearestMultiple = + remainder > divisorSecs / 2 ? intervalSecs + divisorSecs - remainder : intervalSecs - remainder; + nearestMultiple = nearestMultiple === 0 ? divisorSecs : nearestMultiple; + const nearestMultipleInt = moment.duration(nearestMultiple, 'seconds'); + decorateInterval(nearestMultipleInt, this.getDuration()); + + // Check to see if the new interval is scaled compared to the original. + const preScaled = interval.preScaled; + if (preScaled !== undefined && preScaled < nearestMultipleInt) { + nearestMultipleInt.preScaled = preScaled; + nearestMultipleInt.scale = preScaled / nearestMultipleInt; + nearestMultipleInt.scaled = true; + } + + return nearestMultipleInt; +}; + +/** + * Get a date format string that will represent dates that + * progress at our interval. + * + * Since our interval can be as small as 1ms, the default + * date format is usually way too much. with `dateFormat:scaled` + * users can modify how dates are formatted within series + * produced by TimeBuckets + * + * @return {string} + */ +TimeBuckets.prototype.getScaledDateFormat = function () { + const interval = this.getInterval(); + const rules = this._timeBucketsConfig['dateFormat:scaled']; + + for (let i = rules.length - 1; i >= 0; i--) { + const rule = rules[i]; + if (!rule[0] || interval >= moment.duration(rule[0])) { + return rule[1]; + } + } + + return this._timeBucketsConfig.dateFormat; +}; + +TimeBuckets.prototype.getScaledDateFormatter = function () { + const fieldFormats = this._fieldFormats; + const DateFieldFormat = fieldFormats.getType(FIELD_FORMAT_IDS.DATE); + return new DateFieldFormat( + { + pattern: this.getScaledDateFormat(), + }, + // getConfig + this._timeBucketsConfig + ); +}; + +// Appends some TimeBuckets specific properties to the moment.js duration interval. +// Uses the originalDuration from which the time bucket was created to calculate the overflow +// property (i.e. difference between the supplied duration and the calculated bucket interval). +function decorateInterval(interval, originalDuration) { + const esInterval = calcEsInterval(interval); + interval.esValue = esInterval.value; + interval.esUnit = esInterval.unit; + interval.expression = esInterval.expression; + interval.overflow = + originalDuration > interval ? moment.duration(interval - originalDuration) : false; + + const prettyUnits = moment.normalizeUnits(esInterval.unit); + if (esInterval.value === 1) { + interval.description = prettyUnits; + } else { + interval.description = `${esInterval.value} ${prettyUnits}s`; + } + + return interval; +} + +function isValidMoment(m) { + return m && 'isValid' in m && m.isValid(); +} + +export function getBoundsRoundedToInterval(bounds, interval, inclusiveEnd = false) { + // Returns new bounds, created by flooring the min of the provided bounds to the start of + // the specified interval (a moment duration), and rounded upwards (Math.ceil) to 1ms before + // the start of the next interval (Kibana dashboards search >= bounds min, and <= bounds max, + // so we subtract 1ms off the max to avoid querying start of the new Elasticsearch aggregation bucket). + const intervalMs = interval.asMilliseconds(); + const adjustedMinMs = Math.floor(bounds.min.valueOf() / intervalMs) * intervalMs; + let adjustedMaxMs = Math.ceil(bounds.max.valueOf() / intervalMs) * intervalMs; + + // Don't include the start ms of the next bucket unless specified.. + if (inclusiveEnd === false) { + adjustedMaxMs = adjustedMaxMs - 1; + } + return { min: moment(adjustedMinMs), max: moment(adjustedMaxMs) }; +} + +export function calcEsInterval(duration) { + // Converts a moment.duration into an Elasticsearch compatible interval expression, + // and provides associated metadata. + + // Note this was a copy of Kibana's original ui/time_buckets/calc_es_interval, + // but with the definition of a 'large' unit changed from 'M' to 'w', + // bringing it into line with the time units supported by Elasticsearch + for (let i = 0; i < unitsDesc.length; i++) { + const unit = unitsDesc[i]; + const val = duration.as(unit); + // find a unit that rounds neatly + if (val >= 1 && Math.floor(val) === val) { + // Apart from for date histograms, ES only supports time units up to 'd', + // meaning we can't for example use 'w' for job bucket spans. + // See https://www.elastic.co/guide/en/elasticsearch/reference/current/common-options.html#time-units + // So keep going until we get out of the "large" units. + if (i <= timeUnitsMaxSupportedIndex) { + continue; + } + + return { + value: val, + unit, + expression: val + unit, + }; + } + } + + const ms = duration.as('ms'); + return { + value: ms, + unit: 'ms', + expression: ms + 'ms', + }; +} diff --git a/x-pack/plugins/aiops/kibana.json b/x-pack/plugins/aiops/kibana.json index 558e5b475c452..0c26434b3e477 100755 --- a/x-pack/plugins/aiops/kibana.json +++ b/x-pack/plugins/aiops/kibana.json @@ -10,10 +10,11 @@ "server": true, "ui": true, "requiredPlugins": [ + "charts", "data", "licensing" ], "optionalPlugins": [], - "requiredBundles": ["kibanaReact"], + "requiredBundles": ["kibanaReact", "fieldFormats"], "extraPublicDirs": ["common"] } diff --git a/x-pack/plugins/aiops/public/application/services/time_field_range.ts b/x-pack/plugins/aiops/public/application/services/time_field_range.ts new file mode 100644 index 0000000000000..3a8ae33d62abe --- /dev/null +++ b/x-pack/plugins/aiops/public/application/services/time_field_range.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import { getCoreStart } from '../../kibana_services'; + +export interface GetTimeFieldRangeResponse { + success: boolean; + start: { epoch: number; string: string }; + end: { epoch: number; string: string }; +} + +export async function getTimeFieldRange({ + index, + timeFieldName, + query, + runtimeMappings, +}: { + index: string; + timeFieldName?: string; + query?: QueryDslQueryContainer; + runtimeMappings?: estypes.MappingRuntimeFields; +}) { + const body = JSON.stringify({ index, timeFieldName, query, runtimeMappings }); + const coreStart = getCoreStart(); + + return await coreStart.http.fetch({ + path: `/internal/file_upload/time_field_range`, + method: 'POST', + body, + }); +} diff --git a/x-pack/plugins/aiops/public/application/services/timefilter_refresh_service.ts b/x-pack/plugins/aiops/public/application/services/timefilter_refresh_service.ts new file mode 100644 index 0000000000000..20eab7968fefc --- /dev/null +++ b/x-pack/plugins/aiops/public/application/services/timefilter_refresh_service.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { Subject } from 'rxjs'; + +export interface Refresh { + lastRefresh: number; + timeRange?: { start: string; end: string }; +} + +export const aiOpsRefresh$ = new Subject(); diff --git a/x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx b/x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx new file mode 100644 index 0000000000000..00e74bc7db618 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/date_picker_wrapper/date_picker_wrapper.tsx @@ -0,0 +1,169 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import { Subscription } from 'rxjs'; +import { debounce } from 'lodash'; + +import { EuiSuperDatePicker, OnRefreshProps } from '@elastic/eui'; +import type { TimeRange } from '@kbn/es-query'; +import { TimeHistoryContract, UI_SETTINGS } from '@kbn/data-plugin/public'; + +import { useUrlState } from '../../hooks/url_state'; +import { useAiOpsKibana } from '../../kibana_context'; +import { aiOpsRefresh$ } from '../../application/services/timefilter_refresh_service'; + +interface TimePickerQuickRange { + from: string; + to: string; + display: string; +} + +interface Duration { + start: string; + end: string; +} + +interface RefreshInterval { + pause: boolean; + value: number; +} + +function getRecentlyUsedRangesFactory(timeHistory: TimeHistoryContract) { + return function (): Duration[] { + return ( + timeHistory.get()?.map(({ from, to }: TimeRange) => { + return { + start: from, + end: to, + }; + }) ?? [] + ); + }; +} + +function updateLastRefresh(timeRange: OnRefreshProps) { + aiOpsRefresh$.next({ lastRefresh: Date.now(), timeRange }); +} + +export const DatePickerWrapper: FC = () => { + const { services } = useAiOpsKibana(); + const config = services.uiSettings; + const { timefilter, history } = services.data.query.timefilter; + + const [globalState, setGlobalState] = useUrlState('_g'); + const getRecentlyUsedRanges = getRecentlyUsedRangesFactory(history); + + const refreshInterval: RefreshInterval = + globalState?.refreshInterval ?? timefilter.getRefreshInterval(); + + // eslint-disable-next-line react-hooks/exhaustive-deps + const setRefreshInterval = useCallback( + debounce((refreshIntervalUpdate: RefreshInterval) => { + setGlobalState('refreshInterval', refreshIntervalUpdate, true); + }, 200), + [setGlobalState] + ); + + const [time, setTime] = useState(timefilter.getTime()); + const [recentlyUsedRanges, setRecentlyUsedRanges] = useState(getRecentlyUsedRanges()); + const [isAutoRefreshSelectorEnabled, setIsAutoRefreshSelectorEnabled] = useState( + timefilter.isAutoRefreshSelectorEnabled() + ); + const [isTimeRangeSelectorEnabled, setIsTimeRangeSelectorEnabled] = useState( + timefilter.isTimeRangeSelectorEnabled() + ); + + const dateFormat = config.get('dateFormat'); + const timePickerQuickRanges = config.get( + UI_SETTINGS.TIMEPICKER_QUICK_RANGES + ); + + const commonlyUsedRanges = useMemo( + () => + timePickerQuickRanges.map(({ from, to, display }) => ({ + start: from, + end: to, + label: display, + })), + [timePickerQuickRanges] + ); + + useEffect(() => { + const subscriptions = new Subscription(); + const refreshIntervalUpdate$ = timefilter.getRefreshIntervalUpdate$(); + if (refreshIntervalUpdate$ !== undefined) { + subscriptions.add( + refreshIntervalUpdate$.subscribe((r) => { + setRefreshInterval(timefilter.getRefreshInterval()); + }) + ); + } + const timeUpdate$ = timefilter.getTimeUpdate$(); + if (timeUpdate$ !== undefined) { + subscriptions.add( + timeUpdate$.subscribe((v) => { + setTime(timefilter.getTime()); + }) + ); + } + const enabledUpdated$ = timefilter.getEnabledUpdated$(); + if (enabledUpdated$ !== undefined) { + subscriptions.add( + enabledUpdated$.subscribe((w) => { + setIsAutoRefreshSelectorEnabled(timefilter.isAutoRefreshSelectorEnabled()); + setIsTimeRangeSelectorEnabled(timefilter.isTimeRangeSelectorEnabled()); + }) + ); + } + + return function cleanup() { + subscriptions.unsubscribe(); + }; + }, [setRefreshInterval, timefilter]); + + function updateFilter({ start, end }: Duration) { + const newTime = { from: start, to: end }; + // Update timefilter for controllers listening for changes + timefilter.setTime(newTime); + setTime(newTime); + setRecentlyUsedRanges(getRecentlyUsedRanges()); + } + + function updateInterval({ + isPaused: pause, + refreshInterval: value, + }: { + isPaused: boolean; + refreshInterval: number; + }) { + setRefreshInterval({ pause, value }); + } + + /** + * Enforce pause when it's set to false with 0 refresh interval. + */ + const isPaused = refreshInterval.pause || (!refreshInterval.pause && !refreshInterval.value); + + return isAutoRefreshSelectorEnabled || isTimeRangeSelectorEnabled ? ( +
    + +
    + ) : null; +}; diff --git a/x-pack/plugins/aiops/public/components/date_picker_wrapper/index.ts b/x-pack/plugins/aiops/public/components/date_picker_wrapper/index.ts new file mode 100644 index 0000000000000..232f6c65d2b64 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/date_picker_wrapper/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { DatePickerWrapper } from './date_picker_wrapper'; diff --git a/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/document_count_chart.tsx b/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/document_count_chart.tsx new file mode 100644 index 0000000000000..f58730544896b --- /dev/null +++ b/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/document_count_chart.tsx @@ -0,0 +1,162 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useCallback, useMemo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + Axis, + BarSeries, + BrushEndListener, + Chart, + ElementClickListener, + Position, + ScaleType, + Settings, + XYChartElementEvent, + XYBrushEvent, +} from '@elastic/charts'; +import moment from 'moment'; +import { IUiSettingsClient } from '@kbn/core/public'; +import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common'; +import { useAiOpsKibana } from '../../../kibana_context'; + +export interface DocumentCountChartPoint { + time: number | string; + value: number; +} + +interface Props { + width?: number; + chartPoints: DocumentCountChartPoint[]; + timeRangeEarliest: number; + timeRangeLatest: number; + interval?: number; +} + +const SPEC_ID = 'document_count'; + +function getTimezone(uiSettings: IUiSettingsClient) { + if (uiSettings.isDefault('dateFormat:tz')) { + const detectedTimezone = moment.tz.guess(); + if (detectedTimezone) return detectedTimezone; + else return moment().format('Z'); + } else { + return uiSettings.get('dateFormat:tz', 'Browser'); + } +} + +export const DocumentCountChart: FC = ({ + width, + chartPoints, + timeRangeEarliest, + timeRangeLatest, + interval, +}) => { + const { + services: { data, uiSettings, fieldFormats, charts }, + } = useAiOpsKibana(); + + const chartTheme = charts.theme.useChartsTheme(); + const chartBaseTheme = charts.theme.useChartsBaseTheme(); + + const xAxisFormatter = fieldFormats.deserialize({ id: 'date' }); + const useLegacyTimeAxis = uiSettings.get('visualization:useLegacyTimeAxis', false); + + const seriesName = i18n.translate('xpack.aiops.dataGrid.field.documentCountChart.seriesLabel', { + defaultMessage: 'document count', + }); + + const xDomain = { + min: timeRangeEarliest, + max: timeRangeLatest, + }; + + const adjustedChartPoints = useMemo(() => { + // Display empty chart when no data in range + if (chartPoints.length < 1) return [{ time: timeRangeEarliest, value: 0 }]; + + // If chart has only one bucket + // it won't show up correctly unless we add an extra data point + if (chartPoints.length === 1) { + return [ + ...chartPoints, + { time: interval ? Number(chartPoints[0].time) + interval : timeRangeEarliest, value: 0 }, + ]; + } + return chartPoints; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [chartPoints, timeRangeEarliest, timeRangeLatest, interval]); + + const timefilterUpdateHandler = useCallback( + (ranges: { from: number; to: number }) => { + data.query.timefilter.timefilter.setTime({ + from: moment(ranges.from).toISOString(), + to: moment(ranges.to).toISOString(), + mode: 'absolute', + }); + }, + [data] + ); + + const onBrushEnd = ({ x }: XYBrushEvent) => { + if (!x) { + return; + } + const [from, to] = x; + timefilterUpdateHandler({ from, to }); + }; + + const onElementClick: ElementClickListener = ([elementData]) => { + const startRange = (elementData as XYChartElementEvent)[0].x; + + const range = { + from: startRange, + to: startRange + interval, + }; + timefilterUpdateHandler(range); + }; + + const timeZone = getTimezone(uiSettings); + + return ( +
    + + + xAxisFormatter.convert(value)} + timeAxisLayerCount={useLegacyTimeAxis ? 0 : 2} + style={useLegacyTimeAxis ? {} : MULTILAYER_TIME_AXIS_STYLE} + /> + + + +
    + ); +}; diff --git a/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/index.ts b/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/index.ts new file mode 100644 index 0000000000000..22f39e2ca5d1d --- /dev/null +++ b/x-pack/plugins/aiops/public/components/document_count_content/document_count_chart/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { DocumentCountChart } from './document_count_chart'; +export type { DocumentCountChartPoint } from './document_count_chart'; diff --git a/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/document_count_content.tsx b/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/document_count_content.tsx new file mode 100644 index 0000000000000..db55d6bd718ec --- /dev/null +++ b/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/document_count_content.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { FC } from 'react'; +import { DocumentCountChart, DocumentCountChartPoint } from '../document_count_chart'; +import { TotalCountHeader } from '../total_count_header'; +import { DocumentCountStats } from '../../../get_document_stats'; + +export interface Props { + documentCountStats?: DocumentCountStats; + totalCount: number; +} + +export const DocumentCountContent: FC = ({ documentCountStats, totalCount }) => { + if (documentCountStats === undefined) { + return totalCount !== undefined ? : null; + } + + const { timeRangeEarliest, timeRangeLatest } = documentCountStats; + if (timeRangeEarliest === undefined || timeRangeLatest === undefined) + return ; + + let chartPoints: DocumentCountChartPoint[] = []; + if (documentCountStats.buckets !== undefined) { + const buckets: Record = documentCountStats?.buckets; + chartPoints = Object.entries(buckets).map(([time, value]) => ({ time: +time, value })); + } + + return ( + <> + + + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/index.ts b/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/index.ts new file mode 100644 index 0000000000000..696bc71274441 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/document_count_content/document_count_content/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { DocumentCountContent } from './document_count_content'; diff --git a/x-pack/plugins/aiops/public/components/document_count_content/total_count_header/index.ts b/x-pack/plugins/aiops/public/components/document_count_content/total_count_header/index.ts new file mode 100644 index 0000000000000..167988782ba51 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/document_count_content/total_count_header/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { TotalCountHeader } from './total_count_header'; diff --git a/x-pack/plugins/aiops/public/components/document_count_content/total_count_header/total_count_header.tsx b/x-pack/plugins/aiops/public/components/document_count_content/total_count_header/total_count_header.tsx new file mode 100644 index 0000000000000..d605b9c55bc21 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/document_count_content/total_count_header/total_count_header.tsx @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexItem, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; + +export const TotalCountHeader = ({ totalCount }: { totalCount: number }) => ( + + + + +
    + ), + }} + /> + + +); diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes.tsx b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes.tsx index 982f085b15415..468d543ddeefd 100644 --- a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes.tsx +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes.tsx @@ -7,16 +7,32 @@ import React, { useEffect, FC } from 'react'; -import { EuiCodeBlock, EuiSpacer, EuiText } from '@elastic/eui'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiHorizontalRule, + EuiPageBody, + EuiPageContentBody, + EuiPageContentHeader, + EuiPageContentHeaderSection, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; import type { DataView } from '@kbn/data-views-plugin/public'; import { ProgressControls } from '@kbn/aiops-components'; import { useFetchStream } from '@kbn/aiops-utils'; import type { WindowParameters } from '@kbn/aiops-utils'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { useAiOpsKibana } from '../../kibana_context'; import { initialState, streamReducer } from '../../../common/api/stream_reducer'; import type { ApiExplainLogRateSpikes } from '../../../common/api'; +import { SpikeAnalysisTable } from '../spike_analysis_table'; +import { FullTimeRangeSelector } from '../full_time_range_selector'; +import { DocumentCountContent } from '../document_count_content/document_count_content'; +import { DatePickerWrapper } from '../date_picker_wrapper'; +import { useData } from '../../hooks/use_data'; +import { useUrlState } from '../../hooks/url_state'; /** * ExplainLogRateSpikes props require a data view. @@ -32,10 +48,14 @@ export const ExplainLogRateSpikes: FC = ({ dataView, windowParameters, }) => { - const kibana = useKibana(); - const basePath = kibana.services.http?.basePath.get() ?? ''; + const { services } = useAiOpsKibana(); + const basePath = services.http?.basePath.get() ?? ''; - const { cancel, start, data, isRunning } = useFetchStream< + const [globalState, setGlobalState] = useUrlState('_g'); + + const { docStats, timefilter } = useData(dataView, setGlobalState); + + const { cancel, start, data, isRunning, error } = useFetchStream< ApiExplainLogRateSpikes, typeof basePath >( @@ -56,25 +76,100 @@ export const ExplainLogRateSpikes: FC = ({ { reducer: streamReducer, initialState } ); + useEffect(() => { + if (globalState?.time !== undefined) { + timefilter.setTime({ + from: globalState.time.from, + to: globalState.time.to, + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [JSON.stringify(globalState?.time), timefilter]); + + useEffect(() => { + if (globalState?.refreshInterval !== undefined) { + timefilter.setRefreshInterval(globalState.refreshInterval); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [JSON.stringify(globalState?.refreshInterval), timefilter]); + useEffect(() => { start(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + if (!dataView || !timefilter) return null; + return ( - -

    {dataView.title}

    - - - - {JSON.stringify(data, null, 2)} - -
    + <> + + + + + +
    + +

    {dataView.title}

    +
    +
    +
    + + + {dataView.timeFieldName !== undefined && ( + + + + )} + + + + +
    +
    +
    + + + + + {docStats?.totalCount !== undefined && ( + + + + )} + + + + {data?.changePoints ? ( + + + + ) : null} + + +
    + ); }; diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_wrapper.tsx b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_wrapper.tsx new file mode 100644 index 0000000000000..1b72aac836c18 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/explain_log_rate_spikes_wrapper.tsx @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useCallback } from 'react'; +import { parse, stringify } from 'query-string'; +import { isEqual } from 'lodash'; +import { encode } from 'rison-node'; +import { useHistory, useLocation } from 'react-router-dom'; +import { + Accessor, + Dictionary, + parseUrlState, + Provider as UrlStateContextProvider, + isRisonSerializationRequired, + getNestedProperty, + SetUrlState, +} from '../../hooks/url_state'; + +import { ExplainLogRateSpikes, ExplainLogRateSpikesProps } from './explain_log_rate_spikes'; + +export const ExplainLogRateSpikesWrapper: FC = (props) => { + const history = useHistory(); + const { search: urlSearchString } = useLocation(); + + const setUrlState: SetUrlState = useCallback( + ( + accessor: Accessor, + attribute: string | Dictionary, + value?: any, + replaceState?: boolean + ) => { + const prevSearchString = urlSearchString; + const urlState = parseUrlState(prevSearchString); + const parsedQueryString = parse(prevSearchString, { sort: false }); + + if (!Object.prototype.hasOwnProperty.call(urlState, accessor)) { + urlState[accessor] = {}; + } + + if (typeof attribute === 'string') { + if (isEqual(getNestedProperty(urlState, `${accessor}.${attribute}`), value)) { + return prevSearchString; + } + + urlState[accessor][attribute] = value; + } else { + const attributes = attribute; + Object.keys(attributes).forEach((a) => { + urlState[accessor][a] = attributes[a]; + }); + } + + try { + const oldLocationSearchString = stringify(parsedQueryString, { + sort: false, + encode: false, + }); + + Object.keys(urlState).forEach((a) => { + if (isRisonSerializationRequired(a)) { + parsedQueryString[a] = encode(urlState[a]); + } else { + parsedQueryString[a] = urlState[a]; + } + }); + const newLocationSearchString = stringify(parsedQueryString, { + sort: false, + encode: false, + }); + + if (oldLocationSearchString !== newLocationSearchString) { + const newSearchString = stringify(parsedQueryString, { sort: false }); + if (replaceState) { + history.replace({ search: newSearchString }); + } else { + history.push({ search: newSearchString }); + } + } + } catch (error) { + // eslint-disable-next-line no-console + console.error('Could not save url state', error); + } + }, + [history, urlSearchString] + ); + + return ( + + {' '} + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/index.ts b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/index.ts index 3e48c6816dda9..4666380c7bd61 100644 --- a/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/index.ts +++ b/x-pack/plugins/aiops/public/components/explain_log_rate_spikes/index.ts @@ -6,8 +6,8 @@ */ export type { ExplainLogRateSpikesProps } from './explain_log_rate_spikes'; -import { ExplainLogRateSpikes } from './explain_log_rate_spikes'; +import { ExplainLogRateSpikesWrapper } from './explain_log_rate_spikes_wrapper'; // required for dynamic import using React.lazy() // eslint-disable-next-line import/no-default-export -export default ExplainLogRateSpikes; +export default ExplainLogRateSpikesWrapper; diff --git a/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector.tsx b/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector.tsx new file mode 100644 index 0000000000000..ed6688fc60f7b --- /dev/null +++ b/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector.tsx @@ -0,0 +1,198 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useCallback, useMemo, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import { TimefilterContract } from '@kbn/data-plugin/public'; +import { DataView } from '@kbn/data-plugin/common'; +import { + EuiButton, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiPopover, + EuiRadioGroup, + EuiRadioGroupOption, + EuiToolTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { useAiOpsKibana } from '../../kibana_context'; +import { setFullTimeRange } from './full_time_range_selector_service'; +import { AIOPS_FROZEN_TIER_PREFERENCE, useStorage } from '../../hooks/use_storage'; + +interface Props { + timefilter: TimefilterContract; + dataView: DataView; + disabled: boolean; + query?: QueryDslQueryContainer; + callback?: (a: any) => void; +} + +const FROZEN_TIER_PREFERENCE = { + EXCLUDE: 'exclude-frozen', + INCLUDE: 'include-frozen', +} as const; + +type FrozenTierPreference = typeof FROZEN_TIER_PREFERENCE[keyof typeof FROZEN_TIER_PREFERENCE]; + +export const FullTimeRangeSelector: FC = ({ + timefilter, + dataView, + query, + disabled, + callback, +}) => { + const { + services: { + notifications: { toasts }, + }, + } = useAiOpsKibana(); + + // wrapper around setFullTimeRange to allow for the calling of the optional callBack prop + const setRange = useCallback( + async (i: DataView, q?: QueryDslQueryContainer, excludeFrozenData?: boolean) => { + try { + const fullTimeRange = await setFullTimeRange(timefilter, i, q, excludeFrozenData, toasts); + if (typeof callback === 'function') { + callback(fullTimeRange); + } + } catch (e) { + toasts.addDanger( + i18n.translate( + 'xpack.aiops.index.fullTimeRangeSelector.errorSettingTimeRangeNotification', + { + defaultMessage: 'An error occurred setting the time range.', + } + ) + ); + } + }, + [callback, timefilter, toasts] + ); + + const [isPopoverOpen, setPopover] = useState(false); + + const [frozenDataPreference, setFrozenDataPreference] = useStorage( + AIOPS_FROZEN_TIER_PREFERENCE, + // By default we will exclude frozen data tier + FROZEN_TIER_PREFERENCE.EXCLUDE + ); + + const setPreference = useCallback( + (id: string) => { + setFrozenDataPreference(id as FrozenTierPreference); + setRange(dataView, query, id === FROZEN_TIER_PREFERENCE.EXCLUDE); + closePopover(); + }, + [dataView, query, setFrozenDataPreference, setRange] + ); + + const onButtonClick = () => { + setPopover(!isPopoverOpen); + }; + + const closePopover = () => { + setPopover(false); + }; + + const sortOptions: EuiRadioGroupOption[] = useMemo(() => { + return [ + { + id: FROZEN_TIER_PREFERENCE.EXCLUDE, + label: i18n.translate( + 'xpack.aiops.index.fullTimeRangeSelector.useFullDataExcludingFrozenMenuLabel', + { + defaultMessage: 'Exclude frozen data tier', + } + ), + }, + { + id: FROZEN_TIER_PREFERENCE.INCLUDE, + label: i18n.translate( + 'xpack.aiops.index.fullTimeRangeSelector.useFullDataIncludingFrozenMenuLabel', + { + defaultMessage: 'Include frozen data tier', + } + ), + }, + ]; + }, []); + + const popoverContent = useMemo( + () => ( + + + + ), + [sortOptions, frozenDataPreference, setPreference] + ); + + const buttonTooltip = useMemo( + () => + frozenDataPreference === FROZEN_TIER_PREFERENCE.EXCLUDE ? ( + + ) : ( + + ), + [frozenDataPreference] + ); + + return ( + + + setRange(dataView, query, true)} + data-test-subj="aiopsExplainLogRatesSpikeButtonUseFullData" + > + + + + + + } + isOpen={isPopoverOpen} + closePopover={closePopover} + panelPaddingSize="none" + anchorPosition="downRight" + > + {popoverContent} + + + + ); +}; diff --git a/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector_service.ts b/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector_service.ts new file mode 100644 index 0000000000000..013ddb9d90c71 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/full_time_range_selector/full_time_range_selector_service.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; +import { TimefilterContract } from '@kbn/data-plugin/public'; +import dateMath from '@kbn/datemath'; +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import { i18n } from '@kbn/i18n'; +import type { ToastsStart } from '@kbn/core/public'; +import { DataView } from '@kbn/data-views-plugin/public'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { getTimeFieldRange } from '../../application/services/time_field_range'; +import { addExcludeFrozenToQuery } from '../../query_utils'; + +export interface GetTimeFieldRangeResponse { + success: boolean; + start: { epoch: number; string: string }; + end: { epoch: number; string: string }; +} + +export interface TimeRange { + from: number; + to: number; +} + +export async function setFullTimeRange( + timefilter: TimefilterContract, + dataView: DataView, + query?: QueryDslQueryContainer, + excludeFrozenData?: boolean, + toasts?: ToastsStart +): Promise { + const runtimeMappings = dataView.getRuntimeMappings(); + const resp = await getTimeFieldRange({ + index: dataView.title, + timeFieldName: dataView.timeFieldName, + query: excludeFrozenData ? addExcludeFrozenToQuery(query) : query, + ...(isPopulatedObject(runtimeMappings) ? { runtimeMappings } : {}), + }); + + if (resp.start.epoch && resp.end.epoch) { + timefilter.setTime({ + from: moment(resp.start.epoch).toISOString(), + to: moment(resp.end.epoch).toISOString(), + }); + } else { + toasts?.addWarning({ + title: i18n.translate('xpack.aiops.index.fullTimeRangeSelector.noResults', { + defaultMessage: 'No results match your search criteria', + }), + }); + } + return resp; +} + +export function getTimeFilterRange(timefilter: TimefilterContract): TimeRange { + const fromMoment = dateMath.parse(timefilter.getTime().from); + const toMoment = dateMath.parse(timefilter.getTime().to); + const from = fromMoment !== undefined ? fromMoment.valueOf() : 0; + const to = toMoment !== undefined ? toMoment.valueOf() : 0; + + return { + to, + from, + }; +} diff --git a/x-pack/plugins/aiops/public/components/full_time_range_selector/index.ts b/x-pack/plugins/aiops/public/components/full_time_range_selector/index.ts new file mode 100644 index 0000000000000..842102e2395aa --- /dev/null +++ b/x-pack/plugins/aiops/public/components/full_time_range_selector/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { FullTimeRangeSelector } from './full_time_range_selector'; diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/get_failed_transactions_correlation_impact_label.ts b/x-pack/plugins/aiops/public/components/spike_analysis_table/get_failed_transactions_correlation_impact_label.ts new file mode 100644 index 0000000000000..43ab6cd5404bc --- /dev/null +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/get_failed_transactions_correlation_impact_label.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +// This is currently copied over from the APM plugin +const CORRELATIONS_IMPACT_THRESHOLD = { + HIGH: i18n.translate('xpack.aiops.correlations.highImpactText', { + defaultMessage: 'High', + }), + MEDIUM: i18n.translate('xpack.aiops.correlations.mediumImpactText', { + defaultMessage: 'Medium', + }), + LOW: i18n.translate('xpack.aiops.correlations.lowImpactText', { + defaultMessage: 'Low', + }), + VERY_LOW: i18n.translate('xpack.aiops.correlations.veryLowImpactText', { + defaultMessage: 'Very low', + }), +} as const; + +type FailedTransactionsCorrelationsImpactThreshold = + typeof CORRELATIONS_IMPACT_THRESHOLD[keyof typeof CORRELATIONS_IMPACT_THRESHOLD]; + +export function getFailedTransactionsCorrelationImpactLabel( + pValue: number | null, + isFallbackResult?: boolean +): { + impact: FailedTransactionsCorrelationsImpactThreshold; + color: string; +} | null { + if (pValue === null) { + return null; + } + + if (isFallbackResult) + return { + impact: CORRELATIONS_IMPACT_THRESHOLD.VERY_LOW, + color: 'default', + }; + + // The lower the p value, the higher the impact + if (pValue >= 0 && pValue < 1e-6) + return { + impact: CORRELATIONS_IMPACT_THRESHOLD.HIGH, + color: 'danger', + }; + if (pValue >= 1e-6 && pValue < 0.001) + return { + impact: CORRELATIONS_IMPACT_THRESHOLD.MEDIUM, + color: 'warning', + }; + if (pValue >= 0.001 && pValue < 0.02) + return { + impact: CORRELATIONS_IMPACT_THRESHOLD.LOW, + color: 'default', + }; + + return null; +} diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/impact_bar.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/impact_bar.tsx new file mode 100644 index 0000000000000..0f90264667035 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/impact_bar.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiProgress } from '@elastic/eui'; +import React from 'react'; + +export const unit = 16; + +// TODO: extend from EUI's EuiProgress prop interface +export interface ImpactBarProps extends Record { + value: number; + size?: 's' | 'l' | 'm'; + max?: number; + color?: string; +} + +const style = { width: `${unit * 6}px` }; + +export function ImpactBar({ + value, + size = 'm', + max = 100, + color = 'primary', + ...rest +}: ImpactBarProps) { + return ; +} diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/index.ts b/x-pack/plugins/aiops/public/components/spike_analysis_table/index.ts new file mode 100644 index 0000000000000..df8a39ac56c4a --- /dev/null +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { SpikeAnalysisTable } from './spike_analysis_table'; diff --git a/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx new file mode 100644 index 0000000000000..3910bd8cc5a02 --- /dev/null +++ b/x-pack/plugins/aiops/public/components/spike_analysis_table/spike_analysis_table.tsx @@ -0,0 +1,153 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useCallback, useMemo, useState } from 'react'; +import { EuiBadge, EuiBasicTable, EuiBasicTableColumn, RIGHT_ALIGNMENT } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { ChangePoint } from '../../../common/types'; +import { ImpactBar } from './impact_bar'; +import { getFailedTransactionsCorrelationImpactLabel } from './get_failed_transactions_correlation_impact_label'; + +const PAGINATION_SIZE_OPTIONS = [5, 10, 20, 50]; +const noDataText = i18n.translate('xpack.aiops.correlations.correlationsTable.noDataText', { + defaultMessage: 'No data', +}); + +interface Props { + changePointData: ChangePoint[]; + error?: string; + loading: boolean; +} + +export const SpikeAnalysisTable: FC = ({ changePointData, error, loading }) => { + const [pageIndex, setPageIndex] = useState(0); + const [pageSize, setPageSize] = useState(10); + + const columns: Array> = [ + { + field: 'score', + name: ( + <> + {i18n.translate( + 'xpack.aiops.correlations.failedTransactions.correlationsTable.pValueLabel', + { + defaultMessage: 'Score', + } + )} + + ), + align: RIGHT_ALIGNMENT, + render: (_, { score }) => { + return ( + <> + + + ); + }, + sortable: true, + }, + { + field: 'pValue', + name: ( + <> + {i18n.translate( + 'xpack.aiops.correlations.failedTransactions.correlationsTable.impactLabel', + { + defaultMessage: 'Impact', + } + )} + + ), + render: (_, { pValue }) => { + const label = getFailedTransactionsCorrelationImpactLabel(pValue); + return label ? {label.impact} : null; + }, + sortable: true, + }, + { + field: 'fieldName', + name: i18n.translate( + 'xpack.aiops.correlations.failedTransactions.correlationsTable.fieldNameLabel', + { defaultMessage: 'Field name' } + ), + sortable: true, + }, + { + field: 'fieldValue', + name: i18n.translate( + 'xpack.aiops.correlations.failedTransactions.correlationsTable.fieldValueLabel', + { defaultMessage: 'Field value' } + ), + render: (_, { fieldValue }) => String(fieldValue).slice(0, 50), + sortable: true, + }, + { + field: 'pValue', + name: 'p-value', + render: (pValue: number) => pValue.toPrecision(3), + sortable: true, + }, + ]; + + const onChange = useCallback((tableSettings) => { + const { index, size } = tableSettings.page; + + setPageIndex(index); + setPageSize(size); + }, []); + + const { pagination, pageOfItems } = useMemo(() => { + const pageStart = pageIndex * pageSize; + + const itemCount = changePointData?.length ?? 0; + return { + pageOfItems: changePointData?.slice(pageStart, pageStart + pageSize), + pagination: { + pageIndex, + pageSize, + totalItemCount: itemCount, + pageSizeOptions: PAGINATION_SIZE_OPTIONS, + }, + }; + }, [pageIndex, pageSize, changePointData]); + + return ( + { + // return { + // onClick: () => { + // // if (setPinnedSignificantTerm) { + // // setPinnedSignificantTerm(term); + // // } + // }, + // onMouseEnter: () => { + // // setSelectedSignificantTerm(term); + // }, + // onMouseLeave: () => { + // // setSelectedSignificantTerm(null); + // }, + // // style: + // // selectedTerm && + // // selectedTerm.fieldValue === term.fieldValue && + // // selectedTerm.fieldName === term.fieldName + // // ? { + // // backgroundColor: euiTheme.eui.euiColorLightestShade, + // // } + // // : null, + // }; + // }} + /> + ); +}; diff --git a/x-pack/plugins/aiops/public/get_document_stats.ts b/x-pack/plugins/aiops/public/get_document_stats.ts new file mode 100644 index 0000000000000..6ab871ae30705 --- /dev/null +++ b/x-pack/plugins/aiops/public/get_document_stats.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { each, get } from 'lodash'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { buildBaseFilterCriteria } from './query_utils'; + +export interface DocumentCountStats { + interval?: number; + buckets?: { [key: string]: number }; + timeRangeEarliest?: number; + timeRangeLatest?: number; + totalCount: number; +} + +export interface DocumentStatsSearchStrategyParams { + earliest?: number; + latest?: number; + intervalMs?: number; + index: string; + timeFieldName?: string; + runtimeFieldMap?: estypes.MappingRuntimeFields; + fieldsToFetch?: string[]; +} + +export const getDocumentCountStatsRequest = (params: DocumentStatsSearchStrategyParams) => { + const { + index, + timeFieldName, + earliest: earliestMs, + latest: latestMs, + runtimeFieldMap, + // searchQuery, + intervalMs, + fieldsToFetch, + } = params; + + const size = 0; + const filterCriteria = buildBaseFilterCriteria(timeFieldName, earliestMs, latestMs, { + match_all: {}, + }); + + // Don't use the sampler aggregation as this can lead to some potentially + // confusing date histogram results depending on the date range of data amongst shards. + const aggs = { + eventRate: { + date_histogram: { + field: timeFieldName, + fixed_interval: `${intervalMs}ms`, + min_doc_count: 1, + }, + }, + }; + + const searchBody = { + query: { + bool: { + filter: filterCriteria, + }, + }, + ...(!fieldsToFetch && timeFieldName !== undefined && intervalMs !== undefined && intervalMs > 0 + ? { aggs } + : {}), + ...(isPopulatedObject(runtimeFieldMap) ? { runtime_mappings: runtimeFieldMap } : {}), + track_total_hits: true, + size, + }; + return { + index, + body: searchBody, + }; +}; + +export const processDocumentCountStats = ( + body: estypes.SearchResponse | undefined, + params: DocumentStatsSearchStrategyParams +): DocumentCountStats | undefined => { + if (!body) return undefined; + + const totalCount = (body.hits.total as estypes.SearchTotalHits).value ?? body.hits.total ?? 0; + + if ( + params.intervalMs === undefined || + params.earliest === undefined || + params.latest === undefined + ) { + return { + totalCount, + }; + } + const buckets: { [key: string]: number } = {}; + const dataByTimeBucket: Array<{ key: string; doc_count: number }> = get( + body, + ['aggregations', 'eventRate', 'buckets'], + [] + ); + each(dataByTimeBucket, (dataForTime) => { + const time = dataForTime.key; + buckets[time] = dataForTime.doc_count; + }); + + return { + interval: params.intervalMs, + buckets, + timeRangeEarliest: params.earliest, + timeRangeLatest: params.latest, + totalCount, + }; +}; diff --git a/x-pack/plugins/aiops/public/hooks/url_state.ts b/x-pack/plugins/aiops/public/hooks/url_state.ts new file mode 100644 index 0000000000000..2afb37d5f6791 --- /dev/null +++ b/x-pack/plugins/aiops/public/hooks/url_state.ts @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { parse } from 'query-string'; +import { createContext, useCallback, useContext, useMemo } from 'react'; +import { decode } from 'rison-node'; + +export interface Dictionary { + [id: string]: TValue; +} + +// duplicate of ml/object_utils +export const getNestedProperty = ( + obj: Record, + accessor: string, + defaultValue?: any +) => { + const value = accessor.split('.').reduce((o, i) => o?.[i], obj); + + if (value === undefined) return defaultValue; + + return value; +}; + +export type Accessor = '_a' | '_g'; +export type SetUrlState = ( + accessor: Accessor, + attribute: string | Dictionary, + value?: any, + replaceState?: boolean +) => void; +export interface UrlState { + searchString: string; + setUrlState: SetUrlState; +} + +/** + * Set of URL query parameters that require the rison serialization. + */ +const risonSerializedParams = new Set(['_a', '_g']); + +/** + * Checks if the URL query parameter requires rison serialization. + * @param queryParam + */ +export function isRisonSerializationRequired(queryParam: string): boolean { + return risonSerializedParams.has(queryParam); +} + +export function parseUrlState(search: string): Dictionary { + const urlState: Dictionary = {}; + const parsedQueryString = parse(search, { sort: false }); + + try { + Object.keys(parsedQueryString).forEach((a) => { + if (isRisonSerializationRequired(a)) { + urlState[a] = decode(parsedQueryString[a] as string); + } else { + urlState[a] = parsedQueryString[a]; + } + }); + } catch (error) { + // eslint-disable-next-line no-console + console.error('Could not read url state', error); + } + + return urlState; +} + +// Compared to the original appState/globalState, +// this no longer makes use of fetch/save methods. +// - Reading from `location.search` is the successor of `fetch`. +// - `history.push()` is the successor of `save`. +// - The exposed state and set call make use of the above and make sure that +// different urlStates(e.g. `_a` / `_g`) don't overwrite each other. +// This uses a context to be able to maintain only one instance +// of the url state. It gets passed down with `UrlStateProvider` +// and can be used via `useUrlState`. +export const aiOpsUrlStateStore = createContext({ + searchString: '', + setUrlState: () => {}, +}); + +export const { Provider } = aiOpsUrlStateStore; + +export const useUrlState = (accessor: Accessor) => { + const { searchString, setUrlState: setUrlStateContext } = useContext(aiOpsUrlStateStore); + + const urlState = useMemo(() => { + const fullUrlState = parseUrlState(searchString); + if (typeof fullUrlState === 'object') { + return fullUrlState[accessor]; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [searchString]); + + const setUrlState = useCallback( + (attribute: string | Dictionary, value?: any, replaceState?: boolean) => { + setUrlStateContext(accessor, attribute, value, replaceState); + }, + [accessor, setUrlStateContext] + ); + return [urlState, setUrlState]; +}; + +export const AppStateKey = 'AIOPS_INDEX_VIEWER'; + +/** + * Hook for managing the URL state of the page. + */ +export const usePageUrlState = ( + pageKey: typeof AppStateKey, + defaultState?: PageUrlState +): [PageUrlState, (update: Partial, replaceState?: boolean) => void] => { + const [appState, setAppState] = useUrlState('_a'); + const pageState = appState?.[pageKey]; + + const resultPageState: PageUrlState = useMemo(() => { + return { + ...(defaultState ?? {}), + ...(pageState ?? {}), + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [pageState]); + + const onStateUpdate = useCallback( + (update: Partial, replaceState?: boolean) => { + setAppState( + pageKey, + { + ...resultPageState, + ...update, + }, + replaceState + ); + }, + [pageKey, resultPageState, setAppState] + ); + + return useMemo(() => { + return [resultPageState, onStateUpdate]; + }, [resultPageState, onStateUpdate]); +}; diff --git a/x-pack/plugins/aiops/public/hooks/use_data.ts b/x-pack/plugins/aiops/public/hooks/use_data.ts new file mode 100644 index 0000000000000..b3cadd2d44f79 --- /dev/null +++ b/x-pack/plugins/aiops/public/hooks/use_data.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect, useMemo, useState } from 'react'; // useCallback, useRef +import type { DataView } from '@kbn/data-views-plugin/public'; +import { merge } from 'rxjs'; +import { UI_SETTINGS } from '@kbn/data-plugin/common'; +import { useAiOpsKibana } from '../kibana_context'; +import { useTimefilter } from './use_time_filter'; +import { aiOpsRefresh$ } from '../application/services/timefilter_refresh_service'; +import { TimeBuckets } from '../../common/time_buckets'; +import { useDocumentCountStats } from './use_document_count_stats'; +import { Dictionary } from './url_state'; +import { DocumentStatsSearchStrategyParams } from '../get_document_stats'; + +export const useData = ( + currentDataView: DataView, + onUpdate: (params: Dictionary) => void +) => { + const { services } = useAiOpsKibana(); + const { uiSettings } = services; + const [lastRefresh, setLastRefresh] = useState(0); + + const _timeBuckets = useMemo(() => { + return new TimeBuckets({ + [UI_SETTINGS.HISTOGRAM_MAX_BARS]: uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS), + [UI_SETTINGS.HISTOGRAM_BAR_TARGET]: uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET), + dateFormat: uiSettings.get('dateFormat'), + 'dateFormat:scaled': uiSettings.get('dateFormat:scaled'), + }); + }, [uiSettings]); + + const timefilter = useTimefilter({ + timeRangeSelector: currentDataView?.timeFieldName !== undefined, + autoRefreshSelector: true, + }); + + const fieldStatsRequest: DocumentStatsSearchStrategyParams | undefined = useMemo( + () => { + // Obtain the interval to use for date histogram aggregations + // (such as the document count chart). Aim for 75 bars. + const buckets = _timeBuckets; + const tf = timefilter; + if (!buckets || !tf || !currentDataView) return; + const activeBounds = tf.getActiveBounds(); + let earliest: number | undefined; + let latest: number | undefined; + if (activeBounds !== undefined && currentDataView.timeFieldName !== undefined) { + earliest = activeBounds.min?.valueOf(); + latest = activeBounds.max?.valueOf(); + } + const bounds = tf.getActiveBounds(); + const BAR_TARGET = 75; + buckets.setInterval('auto'); + if (bounds) { + buckets.setBounds(bounds); + buckets.setBarTarget(BAR_TARGET); + } + const aggInterval = buckets.getInterval(); + + return { + earliest, + latest, + intervalMs: aggInterval?.asMilliseconds(), + index: currentDataView.title, + timeFieldName: currentDataView.timeFieldName, + runtimeFieldMap: currentDataView.getRuntimeMappings(), + }; + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [_timeBuckets, timefilter, currentDataView.id, lastRefresh] + ); + const { docStats } = useDocumentCountStats(fieldStatsRequest, lastRefresh); + + useEffect(() => { + const timeUpdateSubscription = merge( + timefilter.getTimeUpdate$(), + timefilter.getAutoRefreshFetch$(), + aiOpsRefresh$ + ).subscribe(() => { + if (onUpdate) { + onUpdate({ + time: timefilter.getTime(), + refreshInterval: timefilter.getRefreshInterval(), + }); + } + setLastRefresh(Date.now()); + }); + return () => { + timeUpdateSubscription.unsubscribe(); + }; + }); + + return { + docStats, + timefilter, + }; +}; diff --git a/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts new file mode 100644 index 0000000000000..e474d5ab9c474 --- /dev/null +++ b/x-pack/plugins/aiops/public/hooks/use_document_count_stats.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useEffect, useState, useMemo } from 'react'; +import { lastValueFrom } from 'rxjs'; +import { i18n } from '@kbn/i18n'; +import type { ToastsStart } from '@kbn/core/public'; +import { useAiOpsKibana } from '../kibana_context'; +import { extractErrorProperties } from '../../common/error_utils'; +import { + DocumentCountStats, + getDocumentCountStatsRequest, + processDocumentCountStats, + DocumentStatsSearchStrategyParams, +} from '../get_document_stats'; + +export interface DocumentStats { + totalCount: number; + documentCountStats?: DocumentCountStats; +} + +function displayError(toastNotifications: ToastsStart, index: string, err: any) { + if (err.statusCode === 500) { + toastNotifications.addError(err, { + title: i18n.translate('xpack.aiops.index.dataLoader.internalServerErrorMessage', { + defaultMessage: + 'Error loading data in index {index}. {message}. ' + + 'The request may have timed out. Try using a smaller sample size or narrowing the time range.', + values: { + index, + message: err.error ?? err.message, + }, + }), + }); + } else { + toastNotifications.addError(err, { + title: i18n.translate('xpack.aiops.index.errorLoadingDataMessage', { + defaultMessage: 'Error loading data in index {index}. {message}.', + values: { + index, + message: err.error ?? err.message, + }, + }), + }); + } +} + +export function useDocumentCountStats( + searchParams: TParams | undefined, + lastRefresh: number +): { + docStats: DocumentStats; +} { + const { + services: { + data, + notifications: { toasts }, + }, + } = useAiOpsKibana(); + + const [stats, setStats] = useState({ + totalCount: 0, + }); + + const fetchDocumentCountData = useCallback(async () => { + if (!searchParams) return; + + try { + const resp: any = await lastValueFrom( + data.search.search({ + params: getDocumentCountStatsRequest(searchParams).body, + }) + ); + const documentCountStats = processDocumentCountStats(resp?.rawResponse, searchParams); + const totalCount = documentCountStats?.totalCount ?? 0; + setStats({ + documentCountStats, + totalCount, + }); + } catch (error) { + displayError(toasts, searchParams!.index, extractErrorProperties(error)); + } + }, [data?.search, searchParams, toasts]); + + useEffect( + function getDocumentCountData() { + fetchDocumentCountData(); + }, + [fetchDocumentCountData] + ); + + return useMemo( + () => ({ + docStats: stats, + }), + [stats] + ); +} diff --git a/x-pack/plugins/aiops/public/hooks/use_storage.ts b/x-pack/plugins/aiops/public/hooks/use_storage.ts new file mode 100644 index 0000000000000..7459560d127bf --- /dev/null +++ b/x-pack/plugins/aiops/public/hooks/use_storage.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useState } from 'react'; +import { useAiOpsKibana } from '../kibana_context'; + +export const AIOPS_FROZEN_TIER_PREFERENCE = 'aiOps.frozenDataTierPreference'; + +export type AiOps = Partial<{ + [AIOPS_FROZEN_TIER_PREFERENCE]: 'exclude_frozen' | 'include_frozen'; +}> | null; + +export type AiOpsKey = keyof Exclude; + +/** + * Hook for accessing and changing a value in the storage. + * @param key - Storage key + * @param initValue + */ +export function useStorage(key: AiOpsKey, initValue?: T): [T, (value: T) => void] { + const { + services: { storage }, + } = useAiOpsKibana(); + + const [val, setVal] = useState(storage.get(key) ?? initValue); + + const setStorage = useCallback( + (value: T): void => { + try { + storage.set(key, value); + setVal(value); + } catch (e) { + throw new Error('Unable to update storage with provided value'); + } + }, + [key, storage] + ); + + return [val, setStorage]; +} diff --git a/x-pack/plugins/aiops/public/hooks/use_time_filter.ts b/x-pack/plugins/aiops/public/hooks/use_time_filter.ts new file mode 100644 index 0000000000000..d840a65deb34e --- /dev/null +++ b/x-pack/plugins/aiops/public/hooks/use_time_filter.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useEffect } from 'react'; +import { useAiOpsKibana } from '../kibana_context'; + +interface UseTimefilterOptions { + timeRangeSelector?: boolean; + autoRefreshSelector?: boolean; +} + +export const useTimefilter = ({ + timeRangeSelector, + autoRefreshSelector, +}: UseTimefilterOptions = {}) => { + const { services } = useAiOpsKibana(); + const { timefilter } = services.data.query.timefilter; + + useEffect(() => { + if (timeRangeSelector === true) { + timefilter.enableTimeRangeSelector(); + } else if (timeRangeSelector === false) { + timefilter.disableTimeRangeSelector(); + } + + if (autoRefreshSelector === true) { + timefilter.enableAutoRefreshSelector(); + } else if (autoRefreshSelector === false) { + timefilter.disableAutoRefreshSelector(); + } + }, [timeRangeSelector, autoRefreshSelector, timefilter]); + + return timefilter; +}; diff --git a/x-pack/plugins/aiops/public/kibana_context.ts b/x-pack/plugins/aiops/public/kibana_context.ts new file mode 100644 index 0000000000000..feb0c4ce16c62 --- /dev/null +++ b/x-pack/plugins/aiops/public/kibana_context.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreStart } from '@kbn/core/public'; +import { KibanaReactContextValue, useKibana } from '@kbn/kibana-react-plugin/public'; +import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; +import type { AiOpsStartDependencies } from './plugin'; + +export type StartServices = CoreStart & + AiOpsStartDependencies & { + storage: IStorageWrapper; + }; +export type AiOpsKibanaReactContextValue = KibanaReactContextValue; +export const useAiOpsKibana = () => useKibana(); diff --git a/x-pack/plugins/aiops/public/kibana_services.ts b/x-pack/plugins/aiops/public/kibana_services.ts new file mode 100644 index 0000000000000..e55f3acd81346 --- /dev/null +++ b/x-pack/plugins/aiops/public/kibana_services.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreStart } from '@kbn/core/public'; + +let coreStart: CoreStart; +export function setStartServices(core: CoreStart) { + coreStart = core; +} + +export const getCoreStart = () => coreStart; diff --git a/x-pack/plugins/aiops/public/plugin.ts b/x-pack/plugins/aiops/public/plugin.ts index ef65ab247c40f..8d774b1084b68 100755 --- a/x-pack/plugins/aiops/public/plugin.ts +++ b/x-pack/plugins/aiops/public/plugin.ts @@ -6,11 +6,23 @@ */ import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import { ChartsPluginStart } from '@kbn/charts-plugin/public'; +import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; import { AiopsPluginSetup, AiopsPluginStart } from './types'; +import { setStartServices } from './kibana_services'; + +export interface AiOpsStartDependencies { + data: DataPublicPluginStart; + charts: ChartsPluginStart; + fieldFormats: FieldFormatsStart; +} export class AiopsPlugin implements Plugin { public setup(core: CoreSetup) {} - public start(core: CoreStart) {} + public start(core: CoreStart) { + setStartServices(core); + } public stop() {} } diff --git a/x-pack/plugins/aiops/public/query_utils.ts b/x-pack/plugins/aiops/public/query_utils.ts new file mode 100644 index 0000000000000..e26b91f1fa6c6 --- /dev/null +++ b/x-pack/plugins/aiops/public/query_utils.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { Query } from '@kbn/es-query'; +import { cloneDeep } from 'lodash'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; + +/* + * Contains utility functions for building and processing queries. + */ + +// Builds the base filter criteria used in queries, +// adding criteria for the time range and an optional query. +export function buildBaseFilterCriteria( + timeFieldName?: string, + earliestMs?: number, + latestMs?: number, + query?: Query['query'] +): estypes.QueryDslQueryContainer[] { + const filterCriteria = []; + if (timeFieldName && earliestMs && latestMs) { + filterCriteria.push({ + range: { + [timeFieldName]: { + gte: earliestMs, + lte: latestMs, + format: 'epoch_millis', + }, + }, + }); + } + + if (query && typeof query === 'object') { + filterCriteria.push(query); + } + + return filterCriteria; +} + +export const addExcludeFrozenToQuery = (originalQuery: QueryDslQueryContainer | undefined) => { + const FROZEN_TIER_TERM = { + term: { + _tier: { + value: 'data_frozen', + }, + }, + }; + + if (!originalQuery) { + return { + bool: { + must_not: [FROZEN_TIER_TERM], + }, + }; + } + + const query = cloneDeep(originalQuery); + + delete query.match_all; + + if (isPopulatedObject(query.bool)) { + // Must_not can be both arrays or singular object + if (Array.isArray(query.bool.must_not)) { + query.bool.must_not.push(FROZEN_TIER_TERM); + } else { + // If there's already a must_not condition + if (isPopulatedObject(query.bool.must_not)) { + query.bool.must_not = [query.bool.must_not, FROZEN_TIER_TERM]; + } + if (query.bool.must_not === undefined) { + query.bool.must_not = [FROZEN_TIER_TERM]; + } + } + } else { + query.bool = { + must_not: [FROZEN_TIER_TERM], + }; + } + + return query; +}; diff --git a/x-pack/plugins/aiops/public/shared_lazy_components.tsx b/x-pack/plugins/aiops/public/shared_lazy_components.tsx index 852841a05c2cb..95dcdc50ae2ac 100644 --- a/x-pack/plugins/aiops/public/shared_lazy_components.tsx +++ b/x-pack/plugins/aiops/public/shared_lazy_components.tsx @@ -11,7 +11,9 @@ import { EuiErrorBoundary, EuiLoadingContent } from '@elastic/eui'; import type { ExplainLogRateSpikesProps } from './components/explain_log_rate_spikes'; -const ExplainLogRateSpikesLazy = React.lazy(() => import('./components/explain_log_rate_spikes')); +const ExplainLogRateSpikesWrapperLazy = React.lazy( + () => import('./components/explain_log_rate_spikes') +); const LazyWrapper: FC = ({ children }) => ( @@ -25,6 +27,6 @@ const LazyWrapper: FC = ({ children }) => ( */ export const ExplainLogRateSpikes: FC = (props) => ( - + ); diff --git a/x-pack/plugins/aiops/tsconfig.json b/x-pack/plugins/aiops/tsconfig.json index 2545c0e21ed03..fd2f5698c1591 100644 --- a/x-pack/plugins/aiops/tsconfig.json +++ b/x-pack/plugins/aiops/tsconfig.json @@ -24,5 +24,6 @@ { "path": "../../../src/plugins/navigation/tsconfig.json" }, { "path": "../../../src/plugins/unified_search/tsconfig.json" }, { "path": "../security/tsconfig.json" }, + { "path": "../../../src/plugins/charts/tsconfig.json" }, ] } From 1956eb67c611b1121faa1302a4c5691e5a0e1493 Mon Sep 17 00:00:00 2001 From: Ersin Erdal <92688503+ersin-erdal@users.noreply.github.com> Date: Tue, 12 Jul 2022 20:57:57 +0200 Subject: [PATCH 26/42] replace switchMap with concatMap in event log indexer (#136102) --- x-pack/plugins/event_log/server/es/cluster_client_adapter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts index a3ea88cfb526b..d097bb9ee1957 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts @@ -6,7 +6,7 @@ */ import { Subject } from 'rxjs'; -import { bufferTime, filter as rxFilter, switchMap } from 'rxjs/operators'; +import { bufferTime, filter as rxFilter, concatMap } from 'rxjs/operators'; import { reject, isUndefined, isNumber, pick } from 'lodash'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { Logger, ElasticsearchClient } from '@kbn/core/server'; @@ -87,7 +87,7 @@ export class ClusterClientAdapter docs.length > 0), - switchMap(async (docs) => await this.indexDocuments(docs)) + concatMap(async (docs) => await this.indexDocuments(docs)) ) .toPromise(); } From b286519a3d32f41a61ff660a4cec9501f12ad03b Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 12 Jul 2022 15:02:35 -0400 Subject: [PATCH 27/42] [Response Ops] Calculating overdue metrics using aggregation instead of fetch (#135824) * Switching to aggregation for overdue tasks with runtime mapping * Adding try/catch to gracefully handle search errors * Adding task manager library function for generating required query * Adding functional tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../register_cluster_collector.test.ts | 125 ++++++++++++++---- .../monitoring/register_cluster_collector.ts | 96 +++++++------- .../actions/server/monitoring/types.ts | 10 ++ .../register_cluster_collector.test.ts | 125 ++++++++++++++---- .../monitoring/register_cluster_collector.ts | 96 +++++++------- .../alerting/server/monitoring/types.ts | 10 ++ x-pack/plugins/task_manager/server/index.ts | 1 + x-pack/plugins/task_manager/server/mocks.ts | 1 + x-pack/plugins/task_manager/server/plugin.ts | 9 +- ..._task_overdue_percentiles_for_type.test.ts | 109 +++++++++++++++ ...egate_task_overdue_percentiles_for_type.ts | 71 ++++++++++ .../task_manager/server/task_store.test.ts | 99 +++++++++++++- .../plugins/task_manager/server/task_store.ts | 4 + .../spaces_only/tests/actions/index.ts | 1 + .../tests/actions/monitoring_collection.ts | 28 ++++ .../spaces_only/tests/alerting/index.ts | 2 +- ...ry_metrics.ts => monitoring_collection.ts} | 21 ++- 17 files changed, 648 insertions(+), 160 deletions(-) create mode 100644 x-pack/plugins/task_manager/server/queries/aggregate_task_overdue_percentiles_for_type.test.ts create mode 100644 x-pack/plugins/task_manager/server/queries/aggregate_task_overdue_percentiles_for_type.ts create mode 100644 x-pack/test/alerting_api_integration/spaces_only/tests/actions/monitoring_collection.ts rename x-pack/test/alerting_api_integration/spaces_only/tests/alerting/{in_memory_metrics.ts => monitoring_collection.ts} (83%) diff --git a/x-pack/plugins/actions/server/monitoring/register_cluster_collector.test.ts b/x-pack/plugins/actions/server/monitoring/register_cluster_collector.test.ts index 488e4b5d20e3a..7083012779ea6 100644 --- a/x-pack/plugins/actions/server/monitoring/register_cluster_collector.test.ts +++ b/x-pack/plugins/actions/server/monitoring/register_cluster_collector.test.ts @@ -18,7 +18,7 @@ jest.setSystemTime(new Date('2020-03-09').getTime()); describe('registerClusterCollector()', () => { const monitoringCollection = monitoringCollectionMock.createSetup(); const coreSetup = coreMock.createSetup() as unknown as CoreSetup; - const taskManagerFetch = jest.fn(); + const taskManagerAggregate = jest.fn(); beforeEach(() => { (coreSetup.getStartServices as jest.Mock).mockImplementation(async () => { @@ -26,7 +26,7 @@ describe('registerClusterCollector()', () => { undefined, { taskManager: { - fetch: taskManagerFetch, + aggregate: taskManagerAggregate, }, }, ]; @@ -44,22 +44,19 @@ describe('registerClusterCollector()', () => { expect(metricTypes.length).toBe(1); expect(metricTypes[0]).toBe('cluster_actions'); - const nowInMs = +new Date(); - const docs = [ - { - runAt: nowInMs - 1000, - }, - { - retryAt: nowInMs - 1000, - }, - ]; - taskManagerFetch.mockImplementation(async () => ({ docs })); + taskManagerAggregate.mockImplementation(async () => ({ + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 63, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { overdueByPercentiles: { values: { '50.0': 125369, '99.0': 229561.87 } } }, + })); const result = (await metrics.cluster_actions.fetch()) as ClusterActionsMetric; - expect(result.overdue.count).toBe(docs.length); - expect(result.overdue.delay.p50).toBe(1000); - expect(result.overdue.delay.p99).toBe(1000); - expect(taskManagerFetch).toHaveBeenCalledWith({ + expect(result.overdue.count).toEqual(63); + expect(result.overdue.delay.p50).toEqual(125369); + expect(result.overdue.delay.p99).toEqual(229561.87); + expect(taskManagerAggregate).toHaveBeenCalledWith({ query: { bool: { must: [ @@ -126,10 +123,88 @@ describe('registerClusterCollector()', () => { ], }, }, + runtime_mappings: { + overdueBy: { + type: 'long', + script: { + source: ` + def runAt = doc['task.runAt']; + if(!runAt.empty) { + emit(new Date().getTime() - runAt.value.getMillis()); + } else { + def retryAt = doc['task.retryAt']; + if(!retryAt.empty) { + emit(new Date().getTime() - retryAt.value.getMillis()); + } else { + emit(0); + } + } + `, + }, + }, + }, + aggs: { + overdueByPercentiles: { + percentiles: { + field: 'overdueBy', + percents: [50, 99], + }, + }, + }, + }); + }); + + it('should handle null results', async () => { + const metrics: Record> = {}; + monitoringCollection.registerMetric.mockImplementation((metric) => { + metrics[metric.type] = metric; + }); + registerClusterCollector({ monitoringCollection, core: coreSetup }); + + const metricTypes = Object.keys(metrics); + expect(metricTypes.length).toBe(1); + expect(metricTypes[0]).toBe('cluster_actions'); + + taskManagerAggregate.mockImplementation(async () => ({ + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: null, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: {}, + })); + + const result = (await metrics.cluster_actions.fetch()) as ClusterActionsMetric; + expect(result.overdue.count).toEqual(0); + expect(result.overdue.delay.p50).toEqual(0); + expect(result.overdue.delay.p99).toEqual(0); + }); + + it('should handle null percentile values', async () => { + const metrics: Record> = {}; + monitoringCollection.registerMetric.mockImplementation((metric) => { + metrics[metric.type] = metric; }); + registerClusterCollector({ monitoringCollection, core: coreSetup }); + + const metricTypes = Object.keys(metrics); + expect(metricTypes.length).toBe(1); + expect(metricTypes[0]).toBe('cluster_actions'); + + taskManagerAggregate.mockImplementation(async () => ({ + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: null, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { overdueByPercentiles: { values: { '50.0': null, '99.0': null } } }, + })); + + const result = (await metrics.cluster_actions.fetch()) as ClusterActionsMetric; + expect(result.overdue.count).toEqual(0); + expect(result.overdue.delay.p50).toEqual(0); + expect(result.overdue.delay.p99).toEqual(0); }); - it('should calculate accurate p50 and p99', async () => { + it('should gracefully handle search errors', async () => { const metrics: Record> = {}; monitoringCollection.registerMetric.mockImplementation((metric) => { metrics[metric.type] = metric; @@ -140,19 +215,11 @@ describe('registerClusterCollector()', () => { expect(metricTypes.length).toBe(1); expect(metricTypes[0]).toBe('cluster_actions'); - const nowInMs = +new Date(); - const docs = [ - { runAt: nowInMs - 1000 }, - { runAt: nowInMs - 2000 }, - { runAt: nowInMs - 3000 }, - { runAt: nowInMs - 4000 }, - { runAt: nowInMs - 40000 }, - ]; - taskManagerFetch.mockImplementation(async () => ({ docs })); + taskManagerAggregate.mockRejectedValue(new Error('Failure')); const result = (await metrics.cluster_actions.fetch()) as ClusterActionsMetric; - expect(result.overdue.count).toBe(docs.length); - expect(result.overdue.delay.p50).toBe(3000); - expect(result.overdue.delay.p99).toBe(40000); + expect(result.overdue.count).toEqual(0); + expect(result.overdue.delay.p50).toEqual(0); + expect(result.overdue.delay.p99).toEqual(0); }); }); diff --git a/x-pack/plugins/actions/server/monitoring/register_cluster_collector.ts b/x-pack/plugins/actions/server/monitoring/register_cluster_collector.ts index 895eb44bfceec..23c4953b40d6e 100644 --- a/x-pack/plugins/actions/server/monitoring/register_cluster_collector.ts +++ b/x-pack/plugins/actions/server/monitoring/register_cluster_collector.ts @@ -4,15 +4,15 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import stats from 'stats-lite'; +import type { + AggregationsKeyedPercentiles, + AggregationsPercentilesAggregateBase, +} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { MonitoringCollectionSetup } from '@kbn/monitoring-collection-plugin/server'; -import { - IdleTaskWithExpiredRunAt, - RunningOrClaimingTaskWithExpiredRetryAt, -} from '@kbn/task-manager-plugin/server'; +import { aggregateTaskOverduePercentilesForType } from '@kbn/task-manager-plugin/server'; import { CoreSetup } from '@kbn/core/server'; import { ActionsPluginsStart } from '../plugin'; -import { ClusterActionsMetric } from './types'; +import { ClusterActionsMetric, EMPTY_CLUSTER_ACTIONS_METRICS } from './types'; export function registerClusterCollector({ monitoringCollection, @@ -39,52 +39,56 @@ export function registerClusterCollector({ }, }, fetch: async () => { - const [_, pluginStart] = await core.getStartServices(); - const nowInMs = +new Date(); - const { docs: overdueTasks } = await pluginStart.taskManager.fetch({ - query: { - bool: { - must: [ - { - term: { - 'task.scope': { - value: 'actions', - }, - }, - }, - { - bool: { - should: [IdleTaskWithExpiredRunAt, RunningOrClaimingTaskWithExpiredRetryAt], - }, - }, - ], - }, - }, - }); + try { + const [_, pluginStart] = await core.getStartServices(); + const results = await pluginStart.taskManager.aggregate( + aggregateTaskOverduePercentilesForType('actions') + ); - const overdueTasksDelay = overdueTasks.map( - (overdueTask) => nowInMs - +new Date(overdueTask.runAt || overdueTask.retryAt) - ); + const totalOverdueTasks = + typeof results.hits.total === 'number' ? results.hits.total : results.hits.total?.value; + const aggregations = results.aggregations as { + overdueByPercentiles: AggregationsPercentilesAggregateBase; + }; + const overdueByValues: AggregationsKeyedPercentiles = + (aggregations?.overdueByPercentiles?.values as AggregationsKeyedPercentiles) ?? {}; - const metrics: ClusterActionsMetric = { - overdue: { - count: overdueTasks.length, - delay: { - p50: stats.percentile(overdueTasksDelay, 0.5), - p99: stats.percentile(overdueTasksDelay, 0.99), + /** + * Response format + * { + * "aggregations": { + * "overdueBy": { + * "values": { + * "50.0": 3027400 + * "99.0": 3035402 + * } + * } + * } + * } + */ + + const metrics: ClusterActionsMetric = { + overdue: { + count: totalOverdueTasks ?? 0, + delay: { + p50: (overdueByValues['50.0'] as number) ?? 0, + p99: (overdueByValues['99.0'] as number) ?? 0, + }, }, - }, - }; + }; - if (isNaN(metrics.overdue.delay.p50)) { - metrics.overdue.delay.p50 = 0; - } + if (isNaN(metrics.overdue.delay.p50)) { + metrics.overdue.delay.p50 = 0; + } - if (isNaN(metrics.overdue.delay.p99)) { - metrics.overdue.delay.p99 = 0; - } + if (isNaN(metrics.overdue.delay.p99)) { + metrics.overdue.delay.p99 = 0; + } - return metrics; + return metrics; + } catch (err) { + return EMPTY_CLUSTER_ACTIONS_METRICS; + } }, }); } diff --git a/x-pack/plugins/actions/server/monitoring/types.ts b/x-pack/plugins/actions/server/monitoring/types.ts index 6ce104118757e..83a43dd327d59 100644 --- a/x-pack/plugins/actions/server/monitoring/types.ts +++ b/x-pack/plugins/actions/server/monitoring/types.ts @@ -6,6 +6,16 @@ */ import { MetricResult } from '@kbn/monitoring-collection-plugin/server'; +export const EMPTY_CLUSTER_ACTIONS_METRICS: ClusterActionsMetric = { + overdue: { + count: 0, + delay: { + p50: 0, + p99: 0, + }, + }, +}; + export type ClusterActionsMetric = MetricResult<{ overdue: { count: number; diff --git a/x-pack/plugins/alerting/server/monitoring/register_cluster_collector.test.ts b/x-pack/plugins/alerting/server/monitoring/register_cluster_collector.test.ts index ac90fc283ca77..44edb05d1cfb3 100644 --- a/x-pack/plugins/alerting/server/monitoring/register_cluster_collector.test.ts +++ b/x-pack/plugins/alerting/server/monitoring/register_cluster_collector.test.ts @@ -18,7 +18,7 @@ jest.setSystemTime(new Date('2020-03-09').getTime()); describe('registerClusterCollector()', () => { const monitoringCollection = monitoringCollectionMock.createSetup(); const coreSetup = coreMock.createSetup() as unknown as CoreSetup; - const taskManagerFetch = jest.fn(); + const taskManagerAggregate = jest.fn(); beforeEach(() => { (coreSetup.getStartServices as jest.Mock).mockImplementation(async () => { @@ -26,7 +26,7 @@ describe('registerClusterCollector()', () => { undefined, { taskManager: { - fetch: taskManagerFetch, + aggregate: taskManagerAggregate, }, }, ]; @@ -44,22 +44,19 @@ describe('registerClusterCollector()', () => { expect(metricTypes.length).toBe(1); expect(metricTypes[0]).toBe('cluster_rules'); - const nowInMs = +new Date(); - const docs = [ - { - runAt: nowInMs - 1000, - }, - { - retryAt: nowInMs - 1000, - }, - ]; - taskManagerFetch.mockImplementation(async () => ({ docs })); + taskManagerAggregate.mockImplementation(async () => ({ + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 12, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { overdueByPercentiles: { values: { '50.0': 108525, '99.0': 322480 } } }, + })); const result = (await metrics.cluster_rules.fetch()) as ClusterRulesMetric; - expect(result.overdue.count).toBe(docs.length); - expect(result.overdue.delay.p50).toBe(1000); - expect(result.overdue.delay.p99).toBe(1000); - expect(taskManagerFetch).toHaveBeenCalledWith({ + expect(result.overdue.count).toEqual(12); + expect(result.overdue.delay.p50).toEqual(108525); + expect(result.overdue.delay.p99).toEqual(322480); + expect(taskManagerAggregate).toHaveBeenCalledWith({ query: { bool: { must: [ @@ -126,10 +123,88 @@ describe('registerClusterCollector()', () => { ], }, }, + runtime_mappings: { + overdueBy: { + type: 'long', + script: { + source: ` + def runAt = doc['task.runAt']; + if(!runAt.empty) { + emit(new Date().getTime() - runAt.value.getMillis()); + } else { + def retryAt = doc['task.retryAt']; + if(!retryAt.empty) { + emit(new Date().getTime() - retryAt.value.getMillis()); + } else { + emit(0); + } + } + `, + }, + }, + }, + aggs: { + overdueByPercentiles: { + percentiles: { + field: 'overdueBy', + percents: [50, 99], + }, + }, + }, + }); + }); + + it('should handle null results', async () => { + const metrics: Record> = {}; + monitoringCollection.registerMetric.mockImplementation((metric) => { + metrics[metric.type] = metric; + }); + registerClusterCollector({ monitoringCollection, core: coreSetup }); + + const metricTypes = Object.keys(metrics); + expect(metricTypes.length).toBe(1); + expect(metricTypes[0]).toBe('cluster_rules'); + + taskManagerAggregate.mockImplementation(async () => ({ + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: null, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: {}, + })); + + const result = (await metrics.cluster_rules.fetch()) as ClusterRulesMetric; + expect(result.overdue.count).toEqual(0); + expect(result.overdue.delay.p50).toEqual(0); + expect(result.overdue.delay.p99).toEqual(0); + }); + + it('should handle null percentile values', async () => { + const metrics: Record> = {}; + monitoringCollection.registerMetric.mockImplementation((metric) => { + metrics[metric.type] = metric; }); + registerClusterCollector({ monitoringCollection, core: coreSetup }); + + const metricTypes = Object.keys(metrics); + expect(metricTypes.length).toBe(1); + expect(metricTypes[0]).toBe('cluster_rules'); + + taskManagerAggregate.mockImplementation(async () => ({ + took: 1, + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: null, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { overdueByPercentiles: { values: { '50.0': null, '99.0': null } } }, + })); + + const result = (await metrics.cluster_rules.fetch()) as ClusterRulesMetric; + expect(result.overdue.count).toEqual(0); + expect(result.overdue.delay.p50).toEqual(0); + expect(result.overdue.delay.p99).toEqual(0); }); - it('should calculate accurate p50 and p99', async () => { + it('should gracefully handle search errors', async () => { const metrics: Record> = {}; monitoringCollection.registerMetric.mockImplementation((metric) => { metrics[metric.type] = metric; @@ -140,19 +215,11 @@ describe('registerClusterCollector()', () => { expect(metricTypes.length).toBe(1); expect(metricTypes[0]).toBe('cluster_rules'); - const nowInMs = +new Date(); - const docs = [ - { runAt: nowInMs - 1000 }, - { runAt: nowInMs - 2000 }, - { runAt: nowInMs - 3000 }, - { runAt: nowInMs - 4000 }, - { runAt: nowInMs - 40000 }, - ]; - taskManagerFetch.mockImplementation(async () => ({ docs })); + taskManagerAggregate.mockRejectedValue(new Error('Failure')); const result = (await metrics.cluster_rules.fetch()) as ClusterRulesMetric; - expect(result.overdue.count).toBe(docs.length); - expect(result.overdue.delay.p50).toBe(3000); - expect(result.overdue.delay.p99).toBe(40000); + expect(result.overdue.count).toEqual(0); + expect(result.overdue.delay.p50).toEqual(0); + expect(result.overdue.delay.p99).toEqual(0); }); }); diff --git a/x-pack/plugins/alerting/server/monitoring/register_cluster_collector.ts b/x-pack/plugins/alerting/server/monitoring/register_cluster_collector.ts index f1764d922069f..11832a5e5d00d 100644 --- a/x-pack/plugins/alerting/server/monitoring/register_cluster_collector.ts +++ b/x-pack/plugins/alerting/server/monitoring/register_cluster_collector.ts @@ -4,15 +4,15 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import stats from 'stats-lite'; +import type { + AggregationsKeyedPercentiles, + AggregationsPercentilesAggregateBase, +} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { MonitoringCollectionSetup } from '@kbn/monitoring-collection-plugin/server'; -import { - IdleTaskWithExpiredRunAt, - RunningOrClaimingTaskWithExpiredRetryAt, -} from '@kbn/task-manager-plugin/server'; +import { aggregateTaskOverduePercentilesForType } from '@kbn/task-manager-plugin/server'; import { CoreSetup } from '@kbn/core/server'; import { AlertingPluginsStart } from '../plugin'; -import { ClusterRulesMetric } from './types'; +import { ClusterRulesMetric, EMPTY_CLUSTER_RULES_METRICS } from './types'; export function registerClusterCollector({ monitoringCollection, @@ -39,52 +39,56 @@ export function registerClusterCollector({ }, }, fetch: async () => { - const [_, pluginStart] = await core.getStartServices(); - const now = +new Date(); - const { docs: overdueTasks } = await pluginStart.taskManager.fetch({ - query: { - bool: { - must: [ - { - term: { - 'task.scope': { - value: 'alerting', - }, - }, - }, - { - bool: { - should: [IdleTaskWithExpiredRunAt, RunningOrClaimingTaskWithExpiredRetryAt], - }, - }, - ], - }, - }, - }); + try { + const [_, pluginStart] = await core.getStartServices(); + const results = await pluginStart.taskManager.aggregate( + aggregateTaskOverduePercentilesForType('alerting') + ); - const overdueTasksDelay = overdueTasks.map( - (overdueTask) => now - +new Date(overdueTask.runAt || overdueTask.retryAt) - ); + const totalOverdueTasks = + typeof results.hits.total === 'number' ? results.hits.total : results.hits.total?.value; + const aggregations = results.aggregations as { + overdueByPercentiles: AggregationsPercentilesAggregateBase; + }; + const overdueByValues: AggregationsKeyedPercentiles = + (aggregations?.overdueByPercentiles?.values as AggregationsKeyedPercentiles) ?? {}; - const metrics: ClusterRulesMetric = { - overdue: { - count: overdueTasks.length, - delay: { - p50: stats.percentile(overdueTasksDelay, 0.5), - p99: stats.percentile(overdueTasksDelay, 0.99), + /** + * Response format + * { + * "aggregations": { + * "overdueBy": { + * "values": { + * "50.0": 3027400 + * "99.0": 3035402 + * } + * } + * } + * } + */ + + const metrics: ClusterRulesMetric = { + overdue: { + count: totalOverdueTasks ?? 0, + delay: { + p50: (overdueByValues['50.0'] as number) ?? 0, + p99: (overdueByValues['99.0'] as number) ?? 0, + }, }, - }, - }; + }; - if (isNaN(metrics.overdue.delay.p50)) { - metrics.overdue.delay.p50 = 0; - } + if (isNaN(metrics.overdue.delay.p50)) { + metrics.overdue.delay.p50 = 0; + } - if (isNaN(metrics.overdue.delay.p99)) { - metrics.overdue.delay.p99 = 0; - } + if (isNaN(metrics.overdue.delay.p99)) { + metrics.overdue.delay.p99 = 0; + } - return metrics; + return metrics; + } catch (err) { + return EMPTY_CLUSTER_RULES_METRICS; + } }, }); } diff --git a/x-pack/plugins/alerting/server/monitoring/types.ts b/x-pack/plugins/alerting/server/monitoring/types.ts index 4b159dd8349ae..ae469733a1f07 100644 --- a/x-pack/plugins/alerting/server/monitoring/types.ts +++ b/x-pack/plugins/alerting/server/monitoring/types.ts @@ -6,6 +6,16 @@ */ import { MetricResult } from '@kbn/monitoring-collection-plugin/server'; +export const EMPTY_CLUSTER_RULES_METRICS: ClusterRulesMetric = { + overdue: { + count: 0, + delay: { + p50: 0, + p99: 0, + }, + }, +}; + export type ClusterRulesMetric = MetricResult<{ overdue: { count: number; diff --git a/x-pack/plugins/task_manager/server/index.ts b/x-pack/plugins/task_manager/server/index.ts index 88a27d040b176..0b01d6a05b7e1 100644 --- a/x-pack/plugins/task_manager/server/index.ts +++ b/x-pack/plugins/task_manager/server/index.ts @@ -36,6 +36,7 @@ export { IdleTaskWithExpiredRunAt, RunningOrClaimingTaskWithExpiredRetryAt, } from './queries/mark_available_tasks_as_claimed'; +export { aggregateTaskOverduePercentilesForType } from './queries/aggregate_task_overdue_percentiles_for_type'; export type { TaskManagerPlugin as TaskManager, diff --git a/x-pack/plugins/task_manager/server/mocks.ts b/x-pack/plugins/task_manager/server/mocks.ts index a29ab9d014280..c94799c95bd5c 100644 --- a/x-pack/plugins/task_manager/server/mocks.ts +++ b/x-pack/plugins/task_manager/server/mocks.ts @@ -20,6 +20,7 @@ const createStartMock = () => { const mock: jest.Mocked = { fetch: jest.fn(), get: jest.fn(), + aggregate: jest.fn(), remove: jest.fn(), schedule: jest.fn(), runSoon: jest.fn(), diff --git a/x-pack/plugins/task_manager/server/plugin.ts b/x-pack/plugins/task_manager/server/plugin.ts index 357a1d85fffcf..530a539f8d7fb 100644 --- a/x-pack/plugins/task_manager/server/plugin.ts +++ b/x-pack/plugins/task_manager/server/plugin.ts @@ -7,6 +7,7 @@ import { combineLatest, Observable, Subject } from 'rxjs'; import { map, distinctUntilChanged } from 'rxjs/operators'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { UsageCollectionSetup, UsageCounter } from '@kbn/usage-collection-plugin/server'; import { PluginInitializerContext, @@ -23,13 +24,13 @@ import { createInitialMiddleware, addMiddlewareToChain, Middleware } from './lib import { removeIfExists } from './lib/remove_if_exists'; import { setupSavedObjects } from './saved_objects'; import { TaskDefinitionRegistry, TaskTypeDictionary, REMOVED_TYPES } from './task_type_dictionary'; -import { FetchResult, SearchOpts, TaskStore } from './task_store'; +import { AggregationOpts, FetchResult, SearchOpts, TaskStore } from './task_store'; import { createManagedConfiguration } from './lib/create_managed_configuration'; import { TaskScheduling } from './task_scheduling'; import { healthRoute } from './routes'; import { createMonitoringStats, MonitoringStats } from './monitoring'; -import { EphemeralTask } from './task'; import { EphemeralTaskLifecycle } from './ephemeral_task_lifecycle'; +import { EphemeralTask, ConcreteTaskInstance } from './task'; import { registerTaskManagerUsageCollector } from './usage'; import { TASK_MANAGER_INDEX } from './constants'; export interface TaskManagerSetupContract { @@ -49,7 +50,7 @@ export type TaskManagerStartContract = Pick< TaskScheduling, 'schedule' | 'runSoon' | 'ephemeralRunNow' | 'ensureScheduled' | 'bulkUpdateSchedules' > & - Pick & { + Pick & { removeIfExists: TaskStore['remove']; } & { supportsEphemeralTasks: () => boolean }; @@ -236,6 +237,8 @@ export class TaskManagerPlugin return { fetch: (opts: SearchOpts): Promise => taskStore.fetch(opts), + aggregate: (opts: AggregationOpts): Promise> => + taskStore.aggregate(opts), get: (id: string) => taskStore.get(id), remove: (id: string) => taskStore.remove(id), removeIfExists: (id: string) => removeIfExists(taskStore, id), diff --git a/x-pack/plugins/task_manager/server/queries/aggregate_task_overdue_percentiles_for_type.test.ts b/x-pack/plugins/task_manager/server/queries/aggregate_task_overdue_percentiles_for_type.test.ts new file mode 100644 index 0000000000000..a0f456e5dc48b --- /dev/null +++ b/x-pack/plugins/task_manager/server/queries/aggregate_task_overdue_percentiles_for_type.test.ts @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { aggregateTaskOverduePercentilesForType } from './aggregate_task_overdue_percentiles_for_type'; + +describe('aggregateTaskOverduePercentilesForType', () => { + test('correctly generates query, runtime_field and aggregation for determining overdue percentiles for a given type', () => { + expect(aggregateTaskOverduePercentilesForType('foo')).toEqual({ + query: { + bool: { + must: [ + { + term: { + 'task.scope': { + value: 'foo', + }, + }, + }, + { + bool: { + should: [ + { + bool: { + must: [ + { + term: { + 'task.status': 'idle', + }, + }, + { + range: { + 'task.runAt': { + lte: 'now', + }, + }, + }, + ], + }, + }, + { + bool: { + must: [ + { + bool: { + should: [ + { + term: { + 'task.status': 'running', + }, + }, + { + term: { + 'task.status': 'claiming', + }, + }, + ], + }, + }, + { + range: { + 'task.retryAt': { + lte: 'now', + }, + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + }, + runtime_mappings: { + overdueBy: { + type: 'long', + script: { + source: ` + def runAt = doc['task.runAt']; + if(!runAt.empty) { + emit(new Date().getTime() - runAt.value.getMillis()); + } else { + def retryAt = doc['task.retryAt']; + if(!retryAt.empty) { + emit(new Date().getTime() - retryAt.value.getMillis()); + } else { + emit(0); + } + } + `, + }, + }, + }, + aggs: { + overdueByPercentiles: { + percentiles: { + field: 'overdueBy', + percents: [50, 99], + }, + }, + }, + }); + }); +}); diff --git a/x-pack/plugins/task_manager/server/queries/aggregate_task_overdue_percentiles_for_type.ts b/x-pack/plugins/task_manager/server/queries/aggregate_task_overdue_percentiles_for_type.ts new file mode 100644 index 0000000000000..67b28ce600e3a --- /dev/null +++ b/x-pack/plugins/task_manager/server/queries/aggregate_task_overdue_percentiles_for_type.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + AggregationsAggregationContainer, + QueryDslQueryContainer, + MappingRuntimeFields, +} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { + IdleTaskWithExpiredRunAt, + RunningOrClaimingTaskWithExpiredRetryAt, +} from './mark_available_tasks_as_claimed'; + +export function aggregateTaskOverduePercentilesForType(type: string): { + aggs: Record; + query: QueryDslQueryContainer; + runtime_mappings: MappingRuntimeFields; +} { + return { + query: { + bool: { + must: [ + { + term: { + 'task.scope': { + value: type, + }, + }, + }, + { + bool: { + should: [IdleTaskWithExpiredRunAt, RunningOrClaimingTaskWithExpiredRetryAt], + }, + }, + ], + }, + }, + runtime_mappings: { + overdueBy: { + type: 'long', + script: { + source: ` + def runAt = doc['task.runAt']; + if(!runAt.empty) { + emit(new Date().getTime() - runAt.value.getMillis()); + } else { + def retryAt = doc['task.retryAt']; + if(!retryAt.empty) { + emit(new Date().getTime() - retryAt.value.getMillis()); + } else { + emit(0); + } + } + `, + }, + }, + }, + aggs: { + overdueByPercentiles: { + percentiles: { + field: 'overdueBy', + percents: [50, 99], + }, + }, + }, + }; +} diff --git a/x-pack/plugins/task_manager/server/task_store.test.ts b/x-pack/plugins/task_manager/server/task_store.test.ts index 46eb792eedcd9..106ab4759b59b 100644 --- a/x-pack/plugins/task_manager/server/task_store.test.ts +++ b/x-pack/plugins/task_manager/server/task_store.test.ts @@ -16,7 +16,7 @@ import { SerializedConcreteTaskInstance, } from './task'; import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; -import { TaskStore, SearchOpts } from './task_store'; +import { TaskStore, SearchOpts, AggregationOpts } from './task_store'; import { savedObjectsRepositoryMock } from '@kbn/core/server/mocks'; import { SavedObjectsSerializer, @@ -262,6 +262,103 @@ describe('TaskStore', () => { }); }); + describe('aggregate', () => { + let store: TaskStore; + let esClient: ReturnType['asInternalUser']; + + beforeAll(() => { + esClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + store = new TaskStore({ + index: 'tasky', + taskManagerId: '', + serializer, + esClient, + definitions: taskDefinitions, + savedObjectsRepository: savedObjectsClient, + }); + }); + + async function testAggregate( + opts: AggregationOpts, + hits: Array> = [] + ) { + esClient.search.mockResponse({ + hits: { hits, total: hits.length }, + aggregations: {}, + } as estypes.SearchResponse); + + const result = await store.aggregate(opts); + + expect(esClient.search).toHaveBeenCalledTimes(1); + + return { + result, + args: esClient.search.mock.calls[0][0], + }; + } + + test('empty call filters by type, sets size to 0, passes aggregation to esClient', async () => { + const { args } = await testAggregate({ + aggs: { testAgg: { terms: { field: 'task.taskType' } } }, + }); + expect(args).toMatchObject({ + index: 'tasky', + body: { + size: 0, + query: { bool: { filter: [{ term: { type: 'task' } }] } }, + aggs: { testAgg: { terms: { field: 'task.taskType' } } }, + }, + }); + }); + + test('allows custom queries', async () => { + const { args } = await testAggregate({ + aggs: { testAgg: { terms: { field: 'task.taskType' } } }, + query: { + term: { 'task.taskType': 'bar' }, + }, + }); + + expect(args).toMatchObject({ + body: { + size: 0, + query: { + bool: { + must: [ + { bool: { filter: [{ term: { type: 'task' } }] } }, + { term: { 'task.taskType': 'bar' } }, + ], + }, + }, + aggs: { testAgg: { terms: { field: 'task.taskType' } } }, + }, + }); + }); + + test('allows runtime mappings', async () => { + const { args } = await testAggregate({ + aggs: { testAgg: { terms: { field: 'task.taskType' } } }, + runtime_mappings: { testMapping: { type: 'long', script: { source: `` } } }, + }); + + expect(args).toMatchObject({ + body: { + size: 0, + query: { bool: { filter: [{ term: { type: 'task' } }] } }, + aggs: { testAgg: { terms: { field: 'task.taskType' } } }, + runtime_mappings: { testMapping: { type: 'long', script: { source: `` } } }, + }, + }); + }); + + test('throws error when esClient.search throws error', async () => { + esClient.search.mockRejectedValue(new Error('Failure')); + await expect(store.aggregate({ aggs: {} })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Failure"` + ); + }); + }); + describe('update', () => { let store: TaskStore; let esClient: ReturnType['asInternalUser']; diff --git a/x-pack/plugins/task_manager/server/task_store.ts b/x-pack/plugins/task_manager/server/task_store.ts index 1d0d6ce80e073..5a90fa42600cb 100644 --- a/x-pack/plugins/task_manager/server/task_store.ts +++ b/x-pack/plugins/task_manager/server/task_store.ts @@ -54,6 +54,7 @@ export interface SearchOpts { export interface AggregationOpts { aggs: Record; query?: estypes.QueryDslQueryContainer; + runtime_mappings?: estypes.MappingRuntimeFields; size?: number; } @@ -332,6 +333,8 @@ export class TaskStore { public async aggregate({ aggs, query, + // eslint-disable-next-line @typescript-eslint/naming-convention + runtime_mappings, size = 0, }: TSearchRequest): Promise> { const body = await this.esClient.search< @@ -344,6 +347,7 @@ export class TaskStore { body: ensureAggregationOnlyReturnsTaskObjects({ query, aggs, + runtime_mappings, size, }), }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts index 20498981ac2eb..5726023d9dc72 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts @@ -20,6 +20,7 @@ export default function actionsTests({ loadTestFile, getService }: FtrProviderCo loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./connector_types')); loadTestFile(require.resolve('./update')); + loadTestFile(require.resolve('./monitoring_collection')); loadTestFile(require.resolve('./execute')); loadTestFile(require.resolve('./enqueue')); loadTestFile(require.resolve('./builtin_action_types/email')); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/monitoring_collection.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/monitoring_collection.ts new file mode 100644 index 0000000000000..700191ecd1a66 --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/monitoring_collection.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +const CLUSTER_ACTIONS_MONITORING_COLLECTION_URL = `/api/monitoring_collection/cluster_actions`; + +// eslint-disable-next-line import/no-default-export +export default function actionsMonitoringCollectionTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('monitoring_collection', () => { + it('should calculate overdue task count and percentiles', async () => { + // We're not forcing any overdue tasks for this test, just testing that the + // route returns successfully and that the expected fields are there + const getResponse = await supertest.get(CLUSTER_ACTIONS_MONITORING_COLLECTION_URL); + expect(getResponse.status).to.eql(200); + expect(typeof getResponse.body.cluster_actions.overdue.count).to.eql('number'); + expect(typeof getResponse.body.cluster_actions.overdue.delay.p50).to.eql('number'); + expect(typeof getResponse.body.cluster_actions.overdue.delay.p99).to.eql('number'); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts index b853b5dbeb86d..795f61d689577 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts @@ -27,7 +27,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./rule_types')); loadTestFile(require.resolve('./event_log')); loadTestFile(require.resolve('./execution_status')); - loadTestFile(require.resolve('./in_memory_metrics')); + loadTestFile(require.resolve('./monitoring_collection')); loadTestFile(require.resolve('./monitoring')); loadTestFile(require.resolve('./mute_all')); loadTestFile(require.resolve('./mute_instance')); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/in_memory_metrics.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/monitoring_collection.ts similarity index 83% rename from x-pack/test/alerting_api_integration/spaces_only/tests/alerting/in_memory_metrics.ts rename to x-pack/test/alerting_api_integration/spaces_only/tests/alerting/monitoring_collection.ts index dd9cd6c4d7c6f..aae8090ed08b3 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/in_memory_metrics.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/monitoring_collection.ts @@ -19,13 +19,14 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; import { createEsDocuments } from './builtin_alert_types/lib/create_test_data'; const NODE_RULES_MONITORING_COLLECTION_URL = `/api/monitoring_collection/node_rules`; +const CLUSTER_RULES_MONITORING_COLLECTION_URL = `/api/monitoring_collection/cluster_rules`; const RULE_INTERVAL_SECONDS = 6; const RULE_INTERVALS_TO_WRITE = 5; const RULE_INTERVAL_MILLIS = RULE_INTERVAL_SECONDS * 1000; const ES_GROUPS_TO_WRITE = 3; // eslint-disable-next-line import/no-default-export -export default function inMemoryMetricsAlertTests({ getService }: FtrProviderContext) { +export default function alertingMonitoringCollectionTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const es = getService('es'); const log = getService('log'); @@ -33,7 +34,7 @@ export default function inMemoryMetricsAlertTests({ getService }: FtrProviderCon const esTestIndexTool = new ESTestIndexTool(es, retry); const waitForExecutionCount = createWaitForExecutionCount(supertest, Spaces.space1.id); - describe('inMemoryMetrics', () => { + describe('monitoring_collection', () => { let endDate: string; const objectRemover = new ObjectRemover(supertest); @@ -48,7 +49,7 @@ export default function inMemoryMetricsAlertTests({ getService }: FtrProviderCon after(async () => await objectRemover.removeAll()); - it('should count executions', async () => { + it('inMemoryMetrics should count executions', async () => { const createResponse = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) .set('kbn-xsrf', 'foo') @@ -67,7 +68,7 @@ export default function inMemoryMetricsAlertTests({ getService }: FtrProviderCon expect(getResponse.body.node_rules.executions).to.greaterThan(0); }); - it('should count failures', async () => { + it('inMemoryMetrics should count failures', async () => { const pattern = [false]; // Once we start failing, the rule type doesn't update state so the failures have to be at the end const createResponse = await supertest .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`) @@ -95,7 +96,7 @@ export default function inMemoryMetricsAlertTests({ getService }: FtrProviderCon expect(getResponse.body.node_rules.failures).to.greaterThan(0); }); - it('should count timeouts', async () => { + it('inMemoryMetrics should count timeouts', async () => { const body = await es.info(); if (!body.version.number.includes('SNAPSHOT')) { log.debug('Skipping because this build does not have the required shard_delay agg'); @@ -145,5 +146,15 @@ export default function inMemoryMetricsAlertTests({ getService }: FtrProviderCon expect(getResponse.status).to.eql(200); expect(getResponse.body.node_rules.timeouts).to.greaterThan(0); }); + + it('should calculate overdue task count and percentiles', async () => { + // We're not forcing any overdue tasks for this test, just testing that the + // route returns successfully and that the expected fields are there + const getResponse = await supertest.get(CLUSTER_RULES_MONITORING_COLLECTION_URL); + expect(getResponse.status).to.eql(200); + expect(typeof getResponse.body.cluster_rules.overdue.count).to.eql('number'); + expect(typeof getResponse.body.cluster_rules.overdue.delay.p50).to.eql('number'); + expect(typeof getResponse.body.cluster_rules.overdue.delay.p99).to.eql('number'); + }); }); } From b5b735625c2902f5f86e8134c9c485fde7d179f8 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 12 Jul 2022 14:55:50 -0500 Subject: [PATCH 28/42] [cft] Use spot instance (#135742) * [cft] Use spot instance, increase cpu * revert back to n2 * Update .buildkite/pipelines/pull_request/deploy_cloud.yml Co-authored-by: Tyler Smalley Co-authored-by: Tyler Smalley --- .buildkite/pipelines/pull_request/deploy_cloud.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.buildkite/pipelines/pull_request/deploy_cloud.yml b/.buildkite/pipelines/pull_request/deploy_cloud.yml index c6a15ec32e179..7185911953232 100644 --- a/.buildkite/pipelines/pull_request/deploy_cloud.yml +++ b/.buildkite/pipelines/pull_request/deploy_cloud.yml @@ -2,6 +2,10 @@ steps: - command: .buildkite/scripts/steps/cloud/build_and_deploy.sh label: 'Build and Deploy to Cloud' agents: - queue: n2-2 + queue: n2-2-spot depends_on: build timeout_in_minutes: 30 + retry: + automatic: + - exit_status: '-1' + limit: 3 From 5c8eaa3ef92cec77388db69bee123c87981e71a6 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Tue, 12 Jul 2022 13:08:11 -0700 Subject: [PATCH 29/42] [Security Solution] Migrate to fields API (#136163) * - * fixed tests * fixed linting rules * fixed mocks * removed docValueFields * - * fixed tests * - * fixed tests * fixed tests * - * changed the recursive approach * fixed tests * fixed tests * fixed tests data according to the new fields api results * - * fixed tests * - * - * fixed types * - * Fixed threat enrichment * Fixed unmapped alert details test * improved naming * Fixed rule detections tests, by parsing nested structure only for ECS objects * Fixed tests * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * Fixed type checks * Fixed merge issues * [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix' * Fixed snapshot Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../src/mock/mock_event_details.ts | 44 +- .../common/utils/field_formatters.test.ts | 122 +- .../common/utils/field_formatters.ts | 51 +- .../detection_alerts/alerts_details.spec.ts | 2 +- .../detection_alerts/alerts_details.spec.ts | 4 +- .../detection_alerts/cti_enrichments.spec.ts | 48 +- .../public/cases/pages/index.tsx | 5 +- .../__snapshots__/json_view.test.tsx.snap | 113 - .../cti_details/helpers.test.tsx | 148 +- .../event_details/cti_details/helpers.tsx | 39 +- .../common/components/events_viewer/index.tsx | 2 - .../components/last_event_time/index.test.tsx | 8 +- .../components/last_event_time/index.tsx | 6 +- .../events/last_event_time/index.test.ts | 4 - .../events/last_event_time/index.ts | 7 +- .../public/common/mock/mock_detail_item.ts | 103 - .../components/alerts_table/actions.tsx | 1 - .../rules/rule_preview/preview_histogram.tsx | 3 - .../public/hosts/pages/details/index.tsx | 3 +- .../public/hosts/pages/hosts.tsx | 9 +- .../public/network/pages/details/index.tsx | 3 +- .../public/network/pages/network.tsx | 1 - .../__snapshots__/index.test.tsx.snap | 3 - .../side_panel/event_details/flyout/index.tsx | 5 +- .../side_panel/event_details/index.test.tsx | 7 +- .../side_panel/event_details/index.tsx | 6 +- .../hooks/use_detail_panel.test.tsx | 2 - .../side_panel/hooks/use_detail_panel.tsx | 5 +- .../components/side_panel/index.test.tsx | 2 - .../timelines/components/side_panel/index.tsx | 5 +- .../timeline/eql_tab_content/index.tsx | 3 - .../timeline/notes_tab_content/index.tsx | 14 +- .../timeline/pinned_tab_content/index.tsx | 3 - .../timeline/query_tab_content/index.tsx | 3 - .../timelines/containers/details/index.tsx | 6 +- .../timelines/containers/index.test.tsx | 1 - .../public/timelines/containers/index.tsx | 7 +- .../public/users/pages/details/index.tsx | 4 +- .../public/users/pages/details/types.ts | 3 - .../public/users/pages/users.tsx | 6 +- .../cti/event_enrichment/query.test.ts | 10 +- .../factory/cti/event_enrichment/query.ts | 11 +- .../cti/event_enrichment/response.test.ts | 15 +- .../query.threat_intel_source.dsl.test.ts | 1 - x-pack/plugins/timelines/common/constants.ts | 1 + .../common/search_strategy/timeline/index.ts | 11 +- .../common/utils/field_formatters.test.ts | 518 +++-- .../common/utils/field_formatters.ts | 50 +- .../components/t_grid/integrated/index.tsx | 6 +- .../components/t_grid/standalone/index.tsx | 1 - .../timelines/public/container/index.tsx | 5 - .../timelines/public/mock/browser_fields.ts | 2 +- .../plugins/timelines/public/mock/t_grid.tsx | 3 +- x-pack/plugins/timelines/server/plugin.ts | 6 - .../plugins/timelines/server/routes/index.ts | 24 - .../search_strategy/index_fields/index.ts | 2 +- .../timeline/eql/__mocks__/index.ts | 1899 +++++++---------- .../timeline/eql/helpers.test.ts | 20 + .../search_strategy/timeline/eql/helpers.ts | 7 + .../events/all/query.events_all.dsl.test.ts | 90 + .../events/all/query.events_all.dsl.ts | 14 +- .../timeline/factory/events/details/index.ts | 23 +- .../details/query.events_details.dsl.test.ts | 32 +- .../details/query.events_details.dsl.ts | 23 +- .../factory/events/last_event_time/index.ts | 7 +- .../query.events_last_event_time.dsl.test.ts | 67 + .../query.events_last_event_time.dsl.ts | 38 +- .../factory/helpers/build_ecs_objects.test.ts | 10 - .../factory/helpers/build_ecs_objects.ts | 5 +- ...test.ts => build_object_recursive.test.ts} | 22 +- ...ield_path.ts => build_object_recursive.ts} | 15 +- .../timeline/factory/helpers/constants.ts | 4 +- .../helpers/format_timeline_data.test.ts | 76 +- .../factory/helpers/format_timeline_data.ts | 22 +- .../timeline/factory/helpers/get_timestamp.ts | 2 - .../security_solution/timeline_details.ts | 16 - .../apps/observability/pages/alerts/index.ts | 2 +- 77 files changed, 1743 insertions(+), 2128 deletions(-) delete mode 100644 x-pack/plugins/timelines/server/routes/index.ts create mode 100644 x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.test.ts create mode 100644 x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/query.events_last_event_time.dsl.test.ts rename x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/{build_object_for_field_path.test.ts => build_object_recursive.test.ts} (82%) rename x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/{build_object_for_field_path.ts => build_object_recursive.ts} (65%) diff --git a/packages/kbn-securitysolution-t-grid/src/mock/mock_event_details.ts b/packages/kbn-securitysolution-t-grid/src/mock/mock_event_details.ts index 93e3cd2a4ce02..8f756842e4b5b 100644 --- a/packages/kbn-securitysolution-t-grid/src/mock/mock_event_details.ts +++ b/packages/kbn-securitysolution-t-grid/src/mock/mock_event_details.ts @@ -292,37 +292,17 @@ export const eventDetailsFormattedFields = [ }, { category: 'threat', - field: 'threat.enrichments.lazer.great.field', - values: ['grrrrr', 'grrrrr_2'], - originalValue: ['grrrrr', 'grrrrr_2'], - isObjectArray: false, - }, - { - category: 'threat', - field: 'threat.enrichments.lazer.great.field.wowoe.fooooo', - values: ['grrrrr'], - originalValue: ['grrrrr'], - isObjectArray: false, - }, - { - category: 'threat', - field: 'threat.enrichments.lazer.great.field.astring', - values: ['cool'], - originalValue: ['cool'], - isObjectArray: false, - }, - { - category: 'threat', - field: 'threat.enrichments.lazer.great.field.aNumber', - values: ['1'], - originalValue: ['1'], - isObjectArray: false, - }, - { - category: 'threat', - field: 'threat.enrichments.lazer.great.field.neat', - values: ['true'], - originalValue: ['true'], - isObjectArray: false, + field: 'threat.enrichments.lazer', + values: [ + '{"great.field":["grrrrr"]}', + '{"great.field":["grrrrr_2"]}', + '{"great.field":[{"wowoe":[{"fooooo":["grrrrr"]}],"astring":"cool","aNumber":1,"neat":true}]}', + ], + originalValue: [ + '{"great.field":["grrrrr"]}', + '{"great.field":["grrrrr_2"]}', + '{"great.field":[{"wowoe":[{"fooooo":["grrrrr"]}],"astring":"cool","aNumber":1,"neat":true}]}', + ], + isObjectArray: true, }, ]; diff --git a/x-pack/plugins/security_solution/common/utils/field_formatters.test.ts b/x-pack/plugins/security_solution/common/utils/field_formatters.test.ts index 4b253a8838095..33d0c226fc44e 100644 --- a/x-pack/plugins/security_solution/common/utils/field_formatters.test.ts +++ b/x-pack/plugins/security_solution/common/utils/field_formatters.test.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { EventHit, EventSource } from '../search_strategy'; -import { getDataFromFieldsHits, getDataFromSourceHits, getDataSafety } from './field_formatters'; +import type { EventHit } from '../search_strategy'; +import { getDataFromFieldsHits, getDataSafety } from './field_formatters'; import { eventDetailsFormattedFields, eventHit } from '@kbn/securitysolution-t-grid'; describe('Events Details Helpers', () => { @@ -73,122 +73,20 @@ describe('Events Details Helpers', () => { const whackResultFields = [ { category: 'crazy', - field: 'crazy.pants.matched.field', - values: ['matched_field'], - originalValue: ['matched_field'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.first_seen', - values: ['2021-02-22T17:29:25.195Z'], - originalValue: ['2021-02-22T17:29:25.195Z'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.provider', - values: ['yourself'], - originalValue: ['yourself'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.type', - values: ['custom'], - originalValue: ['custom'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.matched.atomic', - values: ['matched_atomic'], - originalValue: ['matched_atomic'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.lazer.great.field', - values: ['grrrrr', 'grrrrr_2'], - originalValue: ['grrrrr', 'grrrrr_2'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.lazer.lazer.lazer.cool', - values: ['true', 'false'], - originalValue: ['true', 'false'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.lazer.lazer.lazer.lazer.lazer.lazer.lazer.whoa', - values: ['false'], - originalValue: ['false'], - isObjectArray: false, + field: 'crazy.pants', + values: [ + '{"matched.field":["matched_field"],"first_seen":["2021-02-22T17:29:25.195Z"],"provider":["yourself"],"type":["custom"],"matched.atomic":["matched_atomic"],"lazer":[{"great.field":["grrrrr"],"lazer":[{"lazer":[{"cool":true,"lazer":[{"lazer":[{"lazer":[{"lazer":[{"whoa":false}]}]}]}]}]},{"lazer":[{"cool":false}]}]},{"great.field":["grrrrr_2"]}]}', + ], + originalValue: [ + '{"matched.field":["matched_field"],"first_seen":["2021-02-22T17:29:25.195Z"],"provider":["yourself"],"type":["custom"],"matched.atomic":["matched_atomic"],"lazer":[{"great.field":["grrrrr"],"lazer":[{"lazer":[{"cool":true,"lazer":[{"lazer":[{"lazer":[{"lazer":[{"whoa":false}]}]}]}]}]},{"lazer":[{"cool":false}]}]},{"great.field":["grrrrr_2"]}]}', + ], + isObjectArray: true, }, ]; const result = getDataFromFieldsHits(whackFields); expect(result).toEqual(whackResultFields); }); }); - it('#getDataFromSourceHits', () => { - const _source: EventSource = { - '@timestamp': '2021-02-24T00:41:06.527Z', - 'kibana.alert.workflow_status': 'open', - 'signal.rule.name': 'Rawr', - 'threat.indicator': [ - { - provider: 'yourself', - type: 'custom', - first_seen: ['2021-02-22T17:29:25.195Z'], - matched: { atomic: 'atom', field: 'field', type: 'type' }, - }, - { - provider: 'other_you', - type: 'custom', - first_seen: '2021-02-22T17:29:25.195Z', - matched: { atomic: 'atom', field: 'field', type: 'type' }, - }, - ], - }; - expect(getDataFromSourceHits(_source)).toEqual([ - { - category: 'base', - field: '@timestamp', - values: ['2021-02-24T00:41:06.527Z'], - originalValue: ['2021-02-24T00:41:06.527Z'], - isObjectArray: false, - }, - { - category: 'kibana', - field: 'kibana.alert.workflow_status', - values: ['open'], - originalValue: ['open'], - isObjectArray: false, - }, - { - category: 'signal', - field: 'signal.rule.name', - values: ['Rawr'], - originalValue: ['Rawr'], - isObjectArray: false, - }, - { - category: 'threat', - field: 'threat.indicator', - values: [ - '{"provider":"yourself","type":"custom","first_seen":["2021-02-22T17:29:25.195Z"],"matched":{"atomic":"atom","field":"field","type":"type"}}', - '{"provider":"other_you","type":"custom","first_seen":"2021-02-22T17:29:25.195Z","matched":{"atomic":"atom","field":"field","type":"type"}}', - ], - originalValue: [ - '{"provider":"yourself","type":"custom","first_seen":["2021-02-22T17:29:25.195Z"],"matched":{"atomic":"atom","field":"field","type":"type"}}', - '{"provider":"other_you","type":"custom","first_seen":"2021-02-22T17:29:25.195Z","matched":{"atomic":"atom","field":"field","type":"type"}}', - ], - isObjectArray: true, - }, - ]); - }); it('#getDataSafety', async () => { const result = await getDataSafety(getDataFromFieldsHits, fields); expect(result).toEqual(resultFields); diff --git a/x-pack/plugins/security_solution/common/utils/field_formatters.ts b/x-pack/plugins/security_solution/common/utils/field_formatters.ts index 27188d53d1864..635a82004516d 100644 --- a/x-pack/plugins/security_solution/common/utils/field_formatters.ts +++ b/x-pack/plugins/security_solution/common/utils/field_formatters.ts @@ -5,9 +5,12 @@ * 2.0. */ -import { get, isEmpty, isNumber, isObject, isString } from 'lodash/fp'; +import { ecsFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/ecs_field_map'; +import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; +import { technicalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/technical_rule_field_map'; +import { isEmpty } from 'lodash/fp'; -import type { EventHit, EventSource, TimelineEventsDetailsItem } from '../search_strategy'; +import type { EventHit, TimelineEventsDetailsItem } from '../search_strategy'; import { toObjectArrayOfStrings, toStringArray } from './to_array'; export const baseCategoryFields = ['@timestamp', 'labels', 'message', 'tags']; @@ -38,40 +41,6 @@ export const formatGeoLocation = (item: unknown[]) => { export const isGeoField = (field: string) => field.includes('geo.location') || field.includes('geoip.location'); -export const getDataFromSourceHits = ( - sources: EventSource, - category?: string, - path?: string -): TimelineEventsDetailsItem[] => - Object.keys(sources).reduce((accumulator, source) => { - const item: EventSource = get(source, sources); - if (Array.isArray(item) || isString(item) || isNumber(item)) { - const field = path ? `${path}.${source}` : source; - const fieldCategory = getFieldCategory(field); - - const objArrStr = toObjectArrayOfStrings(item); - const strArr = objArrStr.map(({ str }) => str); - const isObjectArray = objArrStr.some((o) => o.isObjectArray); - - return [ - ...accumulator, - { - category: fieldCategory, - field, - values: strArr, - originalValue: strArr, - isObjectArray, - } as TimelineEventsDetailsItem, - ]; - } else if (isObject(item)) { - return [ - ...accumulator, - ...getDataFromSourceHits(item, category || source, path ? `${path}.${source}` : source), - ]; - } - return accumulator; - }, []); - export const getDataFromFieldsHits = ( fields: EventHit['fields'], prependField?: string, @@ -94,13 +63,19 @@ export const getDataFromFieldsHits = ( }, ]; } + const objArrStr = toObjectArrayOfStrings(item); const strArr = objArrStr.map(({ str }) => str); const isObjectArray = objArrStr.some((o) => o.isObjectArray); const dotField = prependField ? `${prependField}.${field}` : field; - // return simple field value (non-object, non-array) - if (!isObjectArray) { + // return simple field value (non-esc object, non-array) + if ( + !isObjectArray || + Object.keys({ ...ecsFieldMap, ...technicalRuleFieldMap, ...experimentalRuleFieldMap }).find( + (ecsField) => ecsField === field + ) === undefined + ) { return [ ...accumulator, { diff --git a/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts b/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts index cf6f13c0436a8..3211d9db2b343 100644 --- a/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts +++ b/x-pack/plugins/security_solution/cypress/ccs_integration/detection_alerts/alerts_details.spec.ts @@ -36,7 +36,7 @@ describe('Alert details with unmapped fields', () => { cy.get(JSON_TEXT).then((x) => { const parsed = JSON.parse(x.text()); - expect(parsed._source.unmapped).to.equal(expectedUnmappedValue); + expect(parsed.fields.unmapped[0]).to.equal(expectedUnmappedValue); }); }); }); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts index 63119610bdbf3..865be928e832e 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts @@ -50,7 +50,7 @@ describe('Alert details with unmapped fields', () => { cy.get(JSON_TEXT).then((x) => { const parsed = JSON.parse(x.text()); - expect(parsed._source.unmapped).to.equal(expectedUnmappedValue); + expect(parsed.fields.unmapped[0]).to.equal(expectedUnmappedValue); }); }); @@ -61,7 +61,7 @@ describe('Alert details with unmapped fields', () => { }; openTable(); - cy.get(ALERT_FLYOUT).find(pageSelector(5)).click({ force: true }); + cy.get(ALERT_FLYOUT).find(pageSelector(4)).click({ force: true }); cy.get(ALERT_FLYOUT) .find(TABLE_ROWS) .within(() => { diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts index be4aad2c12fe1..70898cc27e053 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/cti_enrichments.spec.ts @@ -67,32 +67,26 @@ describe('CTI Enrichment', () => { it('Displays persisted enrichments on the JSON view', () => { const expectedEnrichment = [ { - feed: { - name: 'AbuseCH malware', - }, - indicator: { - first_seen: '2021-03-10T08:02:14.000Z', - file: { - size: 80280, - pe: {}, - type: 'elf', - hash: { - sha256: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3', - tlsh: '6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE', - ssdeep: - '1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL', - md5: '9b6c3518a91d23ed77504b5416bfb5b3', - }, - }, - type: 'file', - }, - matched: { - atomic: 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3', - field: 'myhash.mysha256', - id: '84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f', - index: 'logs-ti_abusech.malware', - type: 'indicator_match_rule', - }, + 'indicator.file.hash.md5': ['9b6c3518a91d23ed77504b5416bfb5b3'], + 'matched.index': ['logs-ti_abusech.malware'], + 'indicator.file.type': ['elf'], + 'indicator.file.hash.tlsh': [ + '6D7312E017B517CC1371A8353BED205E9128223972AE35302E97528DF957703BAB2DBE', + ], + 'feed.name': ['AbuseCH malware'], + 'indicator.file.hash.ssdeep': [ + '1536:87vbq1lGAXSEYQjbChaAU2yU23M51DjZgSQAvcYkFtZTjzBht5:8D+CAXFYQChaAUk5ljnQssL', + ], + 'indicator.file.hash.sha256': [ + 'a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3', + ], + 'indicator.first_seen': ['2021-03-10T08:02:14.000Z'], + 'matched.field': ['myhash.mysha256'], + 'indicator.type': ['file'], + 'matched.type': ['indicator_match_rule'], + 'matched.id': ['84cf452c1e0375c3d4412cb550bd1783358468a3b3b777da4829d72c7d6fb74f'], + 'matched.atomic': ['a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3'], + 'indicator.file.size': [80280], }, ]; @@ -101,7 +95,7 @@ describe('CTI Enrichment', () => { cy.get(JSON_TEXT).then((x) => { const parsed = JSON.parse(x.text()); - expect(parsed._source.threat.enrichments).to.deep.equal(expectedEnrichment); + expect(parsed.fields['threat.enrichments']).to.deep.equal(expectedEnrichment); }); }); diff --git a/x-pack/plugins/security_solution/public/cases/pages/index.tsx b/x-pack/plugins/security_solution/public/cases/pages/index.tsx index 081f9a62ef96a..fc4a990c45082 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/index.tsx @@ -27,13 +27,10 @@ import { DetailsPanel } from '../../timelines/components/side_panel'; import { useFetchAlertData } from './use_fetch_alert_data'; const TimelineDetailsPanel = () => { - const { browserFields, docValueFields, runtimeMappings } = useSourcererDataView( - SourcererScopeName.detections - ); + const { browserFields, runtimeMappings } = useSourcererDataView(SourcererScopeName.detections); return ( { const data = [ { category: 'threat', - field: 'threat.enrichments', + field: 'threat.enrichments.indicator.first_seen', + isObjectArray: true, + originalValue: ['2021-03-21T19:40:19.000Z'], + values: ['2021-03-21T19:40:19.000Z'], + }, + { + category: 'threat', + field: 'threat.enrichments.indicator.provider', + isObjectArray: true, + originalValue: ['provider'], + values: ['provider'], + }, + { + category: 'threat', + field: 'threat.enrichments.indicator.reference', + isObjectArray: true, + originalValue: ['http://reference.url'], + values: ['http://reference.url'], + }, + { + category: 'threat', + field: 'threat.enrichments.indicator.ip', + isObjectArray: true, + originalValue: ['192.168.1.19'], + values: ['192.168.1.19'], + }, + { + category: 'threat', + field: 'threat.enrichments.indicator.type', + isObjectArray: true, + originalValue: ['ip'], + values: ['ip'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.atomic', + isObjectArray: true, + originalValue: ['192.168.1.19'], + values: ['192.168.1.19'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.field', + isObjectArray: true, + originalValue: ['host.ip'], + values: ['host.ip'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.id', + isObjectArray: true, + originalValue: ['0SIZMnoB_Blp1Ib9ZYHU'], + values: ['0SIZMnoB_Blp1Ib9ZYHU'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.index', + isObjectArray: true, + originalValue: ['filebeat-8.0.0-2021.05.28-000001'], + values: ['filebeat-8.0.0-2021.05.28-000001'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.type', isObjectArray: true, - originalValue: [ - `{"indicator":{"first_seen":"2021-03-21T19:40:19.000Z","provider":"provider","reference":"http://reference.url","ip":"192.168.1.19","type":"ip"},"matched":{"atomic":"192.168.1.19","field":"host.ip","id":"0SIZMnoB_Blp1Ib9ZYHU","index":"filebeat-8.0.0-2021.05.28-000001","type":"indicator_match_rule"}}`, - ], - values: [ - `{"indicator":{"first_seen":"2021-03-21T19:40:19.000Z","provider":"provider","reference":"http://reference.url","ip":"192.168.1.19","type":"ip"},"matched":{"atomic":"192.168.1.19","field":"host.ip","id":"0SIZMnoB_Blp1Ib9ZYHU","index":"filebeat-8.0.0-2021.05.28-000001","type":"indicator_match_rule"}}`, - ], + originalValue: ['indicator_match_rule'], + values: ['indicator_match_rule'], }, ]; @@ -140,16 +199,73 @@ describe('parseExistingEnrichments', () => { const data = [ { category: 'threat', - field: 'threat.enrichments', + field: 'threat.enrichments.indicator.first_seen', + isObjectArray: true, + originalValue: ['2021-03-21T19:40:19.000Z', '2021-03-21T19:40:19.000Z'], + values: ['2021-03-21T19:40:19.000Z', '2021-03-21T19:40:19.000Z'], + }, + { + category: 'threat', + field: 'threat.enrichments.indicator.provider', + isObjectArray: true, + originalValue: ['provider', 'other'], + values: ['provider', 'other'], + }, + { + category: 'threat', + field: 'threat.enrichments.indicator.reference', + isObjectArray: true, + originalValue: ['http://reference.url', 'http://reference.url'], + values: ['http://reference.url', 'http://reference.url'], + }, + { + category: 'threat', + field: 'threat.enrichments.indicator.ip', + isObjectArray: true, + originalValue: ['192.168.1.19', '192.168.1.19'], + values: ['192.168.1.19', '192.168.1.19'], + }, + { + category: 'threat', + field: 'threat.enrichments.indicator.type', + isObjectArray: true, + originalValue: ['ip', 'ip'], + values: ['ip', 'ip'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.atomic', + isObjectArray: true, + originalValue: ['192.168.1.19', '192.168.1.19'], + values: ['192.168.1.19', '192.168.1.19'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.field', + isObjectArray: true, + originalValue: ['host.ip', 'host.ip'], + values: ['host.ip', 'host.ip'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.id', + isObjectArray: true, + originalValue: ['0SIZMnoB_Blp1Ib9ZYHU', 'iiL9NHoB_Blp1Ib9yoJo'], + values: ['0SIZMnoB_Blp1Ib9ZYHU', 'iiL9NHoB_Blp1Ib9yoJo'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.index', + isObjectArray: true, + originalValue: ['filebeat-8.0.0-2021.05.28-000001', 'filebeat-8.0.0-2021.05.28-000001'], + values: ['filebeat-8.0.0-2021.05.28-000001', 'filebeat-8.0.0-2021.05.28-000001'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.type', isObjectArray: true, - originalValue: [ - `{"indicator":{"first_seen":"2021-03-21T19:40:19.000Z","provider":"provider","reference":"http://reference.url","ip":"192.168.1.19","type":"ip"},"matched":{"atomic":"192.168.1.19","field":"host.ip","id":"0SIZMnoB_Blp1Ib9ZYHU","index":"filebeat-8.0.0-2021.05.28-000001","type":"indicator_match_rule"}}`, - `{"indicator":{"first_seen":"2021-03-21T19:40:19.000Z","provider":"other","reference":"http://reference.url","ip":"192.168.1.19","type":"ip"},"matched":{"atomic":"192.168.1.19","field":"host.ip","id":"iiL9NHoB_Blp1Ib9yoJo","index":"filebeat-8.0.0-2021.05.28-000001","type":"indicator_match_rule"}}`, - ], - values: [ - `{"indicator":{"first_seen":"2021-03-21T19:40:19.000Z","provider":"provider","reference":"http://reference.url","ip":"192.168.1.19","type":"ip"},"matched":{"atomic":"192.168.1.19","field":"host.ip","id":"0SIZMnoB_Blp1Ib9ZYHU","index":"filebeat-8.0.0-2021.05.28-000001","type":"indicator_match_rule"}}`, - `{"indicator":{"first_seen":"2021-03-21T19:40:19.000Z","provider":"other","reference":"http://reference.url","ip":"192.168.1.19","type":"ip"},"matched":{"atomic":"192.168.1.19","field":"host.ip","id":"iiL9NHoB_Blp1Ib9yoJo","index":"filebeat-8.0.0-2021.05.28-000001","type":"indicator_match_rule"}}`, - ], + originalValue: ['indicator_match_rule', 'indicator_match_rule'], + values: ['indicator_match_rule', 'indicator_match_rule'], }, ]; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/helpers.tsx index 9e54506f424d7..eee4b731b2831 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/cti_details/helpers.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { groupBy } from 'lodash'; +import { groupBy, isArray } from 'lodash'; import { ENRICHMENT_DESTINATION_PATH } from '../../../../../common/constants'; import { ENRICHMENT_TYPES, @@ -24,7 +24,6 @@ import type { } from '../../../../../common/search_strategy/security_solution/cti'; import { isValidEventField } from '../../../../../common/search_strategy/security_solution/cti'; import { getFirstElement } from '../../../../../common/utils/data_retrieval'; -import { getDataFromSourceHits } from '../../../../../common/utils/field_formatters'; export const isInvestigationTimeEnrichment = (type: string | undefined) => type === ENRICHMENT_TYPES.InvestigationTime; @@ -32,21 +31,37 @@ export const isInvestigationTimeEnrichment = (type: string | undefined) => export const parseExistingEnrichments = ( data: TimelineEventsDetailsItem[] ): TimelineEventsDetailsItem[][] => { - const threatIndicatorField = data.find( - ({ field, originalValue }) => field === ENRICHMENT_DESTINATION_PATH && originalValue + const threatIndicatorFields = data.filter( + ({ field, originalValue }) => + field.startsWith(`${ENRICHMENT_DESTINATION_PATH}.`) && originalValue ); - if (!threatIndicatorField) { + if (threatIndicatorFields.length === 0) { return []; } - const { originalValue } = threatIndicatorField; - const enrichmentStrings = Array.isArray(originalValue) ? originalValue : [originalValue]; - - return enrichmentStrings.reduce( - (enrichments, enrichmentString) => { + return threatIndicatorFields.reduce( + (enrichments, enrichmentData) => { try { - const enrichment = getDataFromSourceHits(JSON.parse(enrichmentString)); - enrichments.push(enrichment); + if (isArray(enrichmentData.values)) { + for ( + let enrichmentIndex = 0; + enrichmentIndex < enrichmentData.values.length; + enrichmentIndex++ + ) { + if (!isArray(enrichments[enrichmentIndex])) { + enrichments[enrichmentIndex] = []; + } + const fieldParts = enrichmentData.field.split('.'); + enrichments[enrichmentIndex].push({ + ...enrichmentData, + isObjectArray: false, + field: enrichmentData.field.replace(`${ENRICHMENT_DESTINATION_PATH}.`, ''), + category: fieldParts.length > 3 ? fieldParts[2] : enrichmentData.category, + values: [enrichmentData.values[enrichmentIndex]], + originalValue: [enrichmentData.originalValue[enrichmentIndex]], + }); + } + } } catch (e) { // omit failed parse } diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx index 05718787d45ab..0556b3678d4db 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/index.tsx @@ -118,7 +118,6 @@ const StatefulEventsViewerComponent: React.FC = ({ const { browserFields, dataViewId, - docValueFields, indexPattern, runtimeMappings, selectedPatterns, @@ -225,7 +224,6 @@ const StatefulEventsViewerComponent: React.FC = ({ defaultCellActions, deletedEventIds, disabledCellActions: FIELDS_WITHOUT_CELL_ACTIONS, - docValueFields, end, entityType, fieldBrowserOptions, diff --git a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx index bee5f6d3f7e89..8e1970c36235f 100644 --- a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.test.tsx @@ -38,7 +38,7 @@ describe('Last Event Time Stat', () => { ]); const wrapper = mount( - + ); // Removed strict equality as the EuiLoader has been converted to Emotion and will no longer have the euiLoadingSpinner--medium class @@ -54,7 +54,7 @@ describe('Last Event Time Stat', () => { ]); const wrapper = mount( - + ); expect(wrapper.find(LastEventTime).html()).toBe( @@ -71,7 +71,7 @@ describe('Last Event Time Stat', () => { ]); const wrapper = mount( - + ); @@ -87,7 +87,7 @@ describe('Last Event Time Stat', () => { ]); const wrapper = mount( - + ); expect(wrapper.find(LastEventTime).html()).toContain(getEmptyValue()); diff --git a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.tsx b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.tsx index f5e24ddc072cf..f6349a3ddd2f4 100644 --- a/x-pack/plugins/security_solution/public/common/components/last_event_time/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/last_event_time/index.tsx @@ -9,13 +9,12 @@ import { EuiIcon, EuiLoadingSpinner, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { memo } from 'react'; -import type { DocValueFields, LastEventIndexKey } from '../../../../common/search_strategy'; +import type { LastEventIndexKey } from '../../../../common/search_strategy'; import { useTimelineLastEventTime } from '../../containers/events/last_event_time'; import { getEmptyTagValue } from '../empty_value'; import { FormattedRelativePreferenceDate } from '../formatted_date'; export interface LastEventTimeProps { - docValueFields: DocValueFields[]; hostName?: string; userName?: string; indexKey: LastEventIndexKey; @@ -24,9 +23,8 @@ export interface LastEventTimeProps { } export const LastEventTime = memo( - ({ docValueFields, hostName, userName, indexKey, ip, indexNames }) => { + ({ hostName, userName, indexKey, ip, indexNames }) => { const [loading, { lastSeen, errorMessage }] = useTimelineLastEventTime({ - docValueFields, indexKey, indexNames, details: { diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.test.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.test.ts index 3f81a24b619f3..6fb6a2bcedbd8 100644 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.test.ts +++ b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.test.ts @@ -67,7 +67,6 @@ describe('useTimelineLastEventTime', () => { useTimelineLastEventTime({ indexKey: LastEventIndexKey.hostDetails, details: {}, - docValueFields: [], indexNames: [], }) ); @@ -86,7 +85,6 @@ describe('useTimelineLastEventTime', () => { useTimelineLastEventTime({ indexKey: LastEventIndexKey.hostDetails, details: {}, - docValueFields: [], indexNames: [], }) ); @@ -95,7 +93,6 @@ describe('useTimelineLastEventTime', () => { expect(mockSearchStrategy.mock.calls[0][0]).toEqual({ defaultIndex: [], details: {}, - docValueFields: [], factoryQueryType: 'eventsLastEventTime', indexKey: 'hostDetails', }); @@ -111,7 +108,6 @@ describe('useTimelineLastEventTime', () => { useTimelineLastEventTime({ indexKey: LastEventIndexKey.hostDetails, details: {}, - docValueFields: [], indexNames: [], }) ); diff --git a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts index 6b5d064e9f4fa..b55c566084528 100644 --- a/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/events/last_event_time/index.ts @@ -21,7 +21,6 @@ import type { } from '../../../../../common/search_strategy/timeline'; import { TimelineEventsQueries } from '../../../../../common/search_strategy/timeline'; import * as i18n from './translations'; -import type { DocValueFields } from '../../../../../common/search_strategy'; import { useAppToasts } from '../../../hooks/use_app_toasts'; export interface UseTimelineLastEventTimeArgs { @@ -31,14 +30,12 @@ export interface UseTimelineLastEventTimeArgs { } interface UseTimelineLastEventTimeProps { - docValueFields: DocValueFields[]; indexKey: LastEventIndexKey; indexNames: string[]; details: LastTimeDetails; } export const useTimelineLastEventTime = ({ - docValueFields, indexKey, indexNames, details, @@ -51,7 +48,6 @@ export const useTimelineLastEventTime = ({ const [TimelineLastEventTimeRequest, setTimelineLastEventTimeRequest] = useState({ defaultIndex: indexNames, - docValueFields, factoryQueryType: TimelineEventsQueries.lastEventTime, indexKey, details, @@ -119,7 +115,6 @@ export const useTimelineLastEventTime = ({ const myRequest = { ...prevRequest, defaultIndex: indexNames, - docValueFields, indexKey, details, }; @@ -128,7 +123,7 @@ export const useTimelineLastEventTime = ({ } return prevRequest; }); - }, [indexNames, details, docValueFields, indexKey]); + }, [indexNames, details, indexKey]); useEffect(() => { timelineLastEventTimeSearch(TimelineLastEventTimeRequest); diff --git a/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts b/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts index 02554cfa83400..19bd391317449 100644 --- a/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts +++ b/x-pack/plugins/security_solution/public/common/mock/mock_detail_item.ts @@ -144,109 +144,6 @@ export const rawEventData = { _index: '.ds-logs-endpoint.events.network-default-2021.09.28-000001', _id: 'TUWyf3wBFCFU0qRJTauW', _score: 1, - _source: { - agent: { - id: '2ac9e9b3-f6d5-4ce6-915d-8f1f8f413624', - type: 'endpoint', - version: '8.0.0-SNAPSHOT', - }, - process: { - Ext: { - ancestry: [ - 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyMzY0LTEzMjc4NjA2NTAyLjA=', - 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTEtMTMyNzA3Njg2OTIuMA==', - ], - }, - name: 'filebeat', - pid: 22535, - entity_id: 'MmFjOWU5YjMtZjZkNS00Y2U2LTkxNWQtOGYxZjhmNDEzNjI0LTIyNTM1LTEzMjc4NjA2NTI4LjA=', - executable: - '/opt/Elastic/Agent/data/elastic-agent-058c40/install/filebeat-8.0.0-SNAPSHOT-linux-x86_64/filebeat', - }, - destination: { - address: '127.0.0.1', - port: 9200, - ip: '127.0.0.1', - }, - source: { - address: '127.0.0.1', - port: 54146, - ip: '127.0.0.1', - }, - message: 'Endpoint network event', - network: { - transport: 'tcp', - type: 'ipv4', - }, - '@timestamp': '2021-10-14T16:45:58.0310772Z', - ecs: { - version: '1.11.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.network', - }, - elastic: { - agent: { - id: '12345', - }, - }, - host: { - hostname: 'test-linux-1', - os: { - Ext: { - variant: 'Debian', - }, - kernel: '4.19.0-17-cloud-amd64 #1 SMP Debian 4.19.194-2 (2021-06-21)', - name: 'Linux', - family: 'debian', - type: 'linux', - version: '10', - platform: 'debian', - full: 'Debian 10', - }, - ip: ['127.0.0.1', '::1', '10.1.2.3', '2001:0DB8:AC10:FE01::'], - name: 'test-linux-1', - id: '76ea303129f249aa7382338e4263eac1', - mac: ['aa:bb:cc:dd:ee:ff'], - architecture: 'x86_64', - }, - event: { - agent_id_status: 'verified', - sequence: 44872, - ingested: '2021-10-14T16:46:04Z', - created: '2021-10-14T16:45:58.0310772Z', - kind: 'event', - module: 'endpoint', - action: 'connection_attempted', - id: 'MKPXftjGeHiQzUNj++++nn6R', - category: ['network'], - type: ['start'], - dataset: 'endpoint.events.network', - outcome: 'unknown', - }, - user: { - Ext: { - real: { - name: 'root', - id: 0, - }, - }, - name: 'root', - id: 0, - }, - group: { - Ext: { - real: { - name: 'root', - id: 0, - }, - }, - name: 'root', - id: 0, - }, - }, fields: { 'host.os.full.text': ['Debian 10'], 'event.category': ['network'], diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx index e45232ef56791..81925ec0eed56 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx @@ -542,7 +542,6 @@ export const sendAlertToTimelineAction = async ({ >( { defaultIndex: [], - docValueFields: [], indexName: ecsData._index ?? '', eventId: ecsData._id, factoryQueryType: TimelineEventsQueries.details, diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.tsx index 57ce7b1d305a7..36e765119e5e7 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/rule_preview/preview_histogram.tsx @@ -109,7 +109,6 @@ export const PreviewHistogram = ({ const { browserFields, - docValueFields, indexPattern, runtimeMappings, dataViewId: selectedDataViewId, @@ -205,7 +204,6 @@ export const PreviewHistogram = ({ dataProviders, deletedEventIds, disabledCellActions: FIELDS_WITHOUT_CELL_ACTIONS, - docValueFields, end: endDate, entityType: 'events', filters: [], @@ -235,7 +233,6 @@ export const PreviewHistogram = ({ = ({ detailName, hostDeta [dispatch] ); - const { docValueFields, indexPattern, indicesExist, selectedPatterns } = useSourcererDataView(); + const { indexPattern, indicesExist, selectedPatterns } = useSourcererDataView(); const [loading, { inspect, hostDetails: hostOverview, id, refetch }] = useHostDetails({ endDate: to, startDate: from, @@ -141,7 +141,6 @@ const HostDetailsComponent: React.FC = ({ detailName, hostDeta border subtitle={ { }, [dispatch] ); - const { docValueFields, indicesExist, indexPattern, selectedPatterns, loading } = - useSourcererDataView(); + const { indicesExist, indexPattern, selectedPatterns, loading } = useSourcererDataView(); const [filterQuery, kqlError] = useMemo( () => convertToBuildEsQuery({ @@ -197,11 +196,7 @@ const HostsComponent = () => { + } title={i18n.PAGE_TITLE} border diff --git a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx index 2828d7cedc6dc..809a4cfdb8ef5 100644 --- a/x-pack/plugins/security_solution/public/network/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/network/pages/details/index.tsx @@ -90,7 +90,7 @@ const NetworkDetailsComponent: React.FC = () => { dispatch(setNetworkDetailsTablesActivePageToZero()); }, [detailName, dispatch]); - const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); + const { indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); const ip = decodeIpv6(detailName); const [filterQuery, kqlError] = convertToBuildEsQuery({ config: getEsQueryConfig(uiSettings), @@ -141,7 +141,6 @@ const NetworkDetailsComponent: React.FC = () => { draggableArguments={headerDraggableArguments} subtitle={ ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap index dbc5ac5f25a83..cece572fdb839 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap @@ -3,7 +3,6 @@ exports[`Details Panel Component DetailsPanel: rendering it should not render the DetailsPanel if an expanded detail with a panelView, but not params have been set: { - const { browserFields, docValueFields, runtimeMappings } = useSourcererDataView( - SourcererScopeName.detections - ); + const { browserFields, runtimeMappings } = useSourcererDataView(SourcererScopeName.detections); const [alert, setAlert] = useState<{ id?: string; indexName?: string }>({ id: undefined, indexName: undefined, @@ -36,7 +34,6 @@ export const useToGetInternalFlyout = () => { const [loading, detailsData, rawEventData, ecsData, refetchFlyoutData] = useTimelineEventsDetails( { - docValueFields, entityType: EntityType.EVENTS, indexName: alert.indexName ?? '', eventId: alert.id ?? '', diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx index 996a0b6820466..e99072a759949 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.test.tsx @@ -18,11 +18,7 @@ import { useKibana, useGetUserCasesPermissions, } from '../../../../common/lib/kibana'; -import { - mockBrowserFields, - mockDocValueFields, - mockRuntimeMappings, -} from '../../../../common/containers/source/mock'; +import { mockBrowserFields, mockRuntimeMappings } from '../../../../common/containers/source/mock'; import { coreMock } from '@kbn/core/public/mocks'; import { mockCasesContext } from '@kbn/cases-plugin/public/mocks/mock_cases_context'; @@ -113,7 +109,6 @@ const defaultProps = { detailsData: mockAlertDetailsDataWithIsObject, tabType: TimelineTabs.query, browserFields: mockBrowserFields, - docValueFields: mockDocValueFields, runtimeMappings: mockRuntimeMappings, }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx index 4463d397b9564..8859b6f20ab33 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/index.tsx @@ -11,7 +11,7 @@ import React from 'react'; import deepEqual from 'fast-deep-equal'; import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { EntityType } from '@kbn/timelines-plugin/common'; -import type { BrowserFields, DocValueFields } from '../../../../common/containers/source'; +import type { BrowserFields } from '../../../../common/containers/source'; import { ExpandableEvent, ExpandableEventTitle } from './expandable_event'; import { useTimelineEventsDetails } from '../../../containers/details'; import type { TimelineTabs } from '../../../../../common/types/timeline'; @@ -24,7 +24,6 @@ import { useBasicDataFromDetailsData } from './helpers'; interface EventDetailsPanelProps { browserFields: BrowserFields; - docValueFields: DocValueFields[]; entityType?: EntityType; expandedEvent: { eventId: string; @@ -42,7 +41,6 @@ interface EventDetailsPanelProps { const EventDetailsPanelComponent: React.FC = ({ browserFields, - docValueFields, entityType = 'events', // Default to events so only alerts have to pass entityType in expandedEvent, handleOnEventClosed, @@ -55,7 +53,6 @@ const EventDetailsPanelComponent: React.FC = ({ }) => { const [loading, detailsData, rawEventData, ecsData, refetchFlyoutData] = useTimelineEventsDetails( { - docValueFields, entityType, indexName: expandedEvent.indexName ?? '', eventId: expandedEvent.eventId ?? '', @@ -182,7 +179,6 @@ export const EventDetailsPanel = React.memo( EventDetailsPanelComponent, (prevProps, nextProps) => deepEqual(prevProps.browserFields, nextProps.browserFields) && - deepEqual(prevProps.docValueFields, nextProps.docValueFields) && deepEqual(prevProps.expandedEvent, nextProps.expandedEvent) && prevProps.timelineId === nextProps.timelineId && prevProps.isDraggable === nextProps.isDraggable diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/hooks/use_detail_panel.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/hooks/use_detail_panel.test.tsx index ac7845dce7f9e..cdb13af5354c2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/hooks/use_detail_panel.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/hooks/use_detail_panel.test.tsx @@ -26,7 +26,6 @@ jest.mock('react-redux', () => { jest.mock('../../../../common/containers/sourcerer', () => { const mockSourcererReturn = { browserFields: {}, - docValueFields: [], loading: true, indexPattern: {}, selectedPatterns: [], @@ -140,7 +139,6 @@ describe('useDetailPanel', () => { expect(result.current.DetailsPanel).toMatchInlineSnapshot(` { - const { browserFields, docValueFields, selectedPatterns, runtimeMappings } = - useSourcererDataView(sourcererScope); + const { browserFields, selectedPatterns, runtimeMappings } = useSourcererDataView(sourcererScope); const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); const dispatch = useDispatch(); @@ -108,7 +107,6 @@ export const useDetailPanel = ({ shouldShowDetailsPanel ? ( { const mockProps = { browserFields: {}, - docValueFields: [], handleOnPanelClosed: jest.fn(), isFlyoutView: false, runtimeMappings: {}, @@ -151,7 +150,6 @@ describe('Details Panel Component', () => { expect(wrapper.find('DetailsPanel')).toMatchSnapshot(` void; isFlyoutView?: boolean; @@ -42,7 +41,6 @@ interface DetailsPanelProps { export const DetailsPanel = React.memo( ({ browserFields, - docValueFields, entityType, handleOnPanelClosed, isFlyoutView, @@ -84,7 +82,6 @@ export const DetailsPanel = React.memo( visiblePanel = ( = ({ const { browserFields, dataViewId, - docValueFields, loading: loadingSourcerer, runtimeMappings, selectedPatterns, @@ -211,7 +210,6 @@ export const EqlTabContentComponent: React.FC = ({ const [isQueryLoading, { events, inspect, totalCount, pageInfo, loadPage, updatedAt, refetch }] = useTimelineEvents({ dataViewId, - docValueFields, endDate: end, eqlOptions: restEqlOption, fields: getTimelineQueryFields(), @@ -355,7 +353,6 @@ export const EqlTabContentComponent: React.FC = ({ = ({ timelineId } noteIds, status: timelineStatus, } = useDeepEqualSelector((state) => getTimelineNotes(state, timelineId)); - const { browserFields, docValueFields, runtimeMappings } = useSourcererDataView( - SourcererScopeName.timeline - ); + const { browserFields, runtimeMappings } = useSourcererDataView(SourcererScopeName.timeline); const getNotesAsCommentsList = useMemo( () => appSelectors.selectNotesAsCommentsListSelector(), @@ -189,21 +187,13 @@ const NotesTabContentComponent: React.FC = ({ timelineId } expandedDetail[TimelineTabs.notes]?.panelView ? ( ) : null, - [ - browserFields, - docValueFields, - expandedDetail, - handleOnPanelClosed, - runtimeMappings, - timelineId, - ] + [browserFields, expandedDetail, handleOnPanelClosed, runtimeMappings, timelineId] ); const SidebarContent = useMemo( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx index 693d64b9377d1..fd98d906766c8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/pinned_tab_content/index.tsx @@ -119,7 +119,6 @@ export const PinnedTabContentComponent: React.FC = ({ }) => { const { browserFields, - docValueFields, dataViewId, loading: loadingSourcerer, runtimeMappings, @@ -186,7 +185,6 @@ export const PinnedTabContentComponent: React.FC = ({ const [isQueryLoading, { events, totalCount, pageInfo, loadPage, updatedAt, refetch }] = useTimelineEvents({ - docValueFields, endDate: '', id: `pinned-${timelineId}`, indexNames: selectedPatterns, @@ -276,7 +274,6 @@ export const PinnedTabContentComponent: React.FC = ({ = ({ const { browserFields, dataViewId, - docValueFields, loading: loadingSourcerer, indexPattern, runtimeMappings, @@ -286,7 +285,6 @@ export const QueryTabContentComponent: React.FC = ({ const [isQueryLoading, { events, inspect, totalCount, pageInfo, loadPage, updatedAt, refetch }] = useTimelineEvents({ dataViewId, - docValueFields, endDate: end, fields: getTimelineQueryFields(), filterQuery: combinedQueries?.filterQuery, @@ -445,7 +443,6 @@ export const QueryTabContentComponent: React.FC = ({ { const myRequest = { ...(prevRequest ?? {}), - docValueFields, entityType, indexName, eventId, @@ -137,7 +133,7 @@ export const useTimelineEventsDetails = ({ } return prevRequest; }); - }, [docValueFields, entityType, eventId, indexName, runtimeMappings]); + }, [entityType, eventId, indexName, runtimeMappings]); useEffect(() => { timelineDetailsSearch(timelineDetailsRequest); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.test.tsx index 96a5023d0c628..00197a8719a6d 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.test.tsx @@ -108,7 +108,6 @@ describe('useTimelineEvents', () => { const endDate: string = '3000-01-01T00:00:00.000Z'; const props: UseTimelineEventsProps = { dataViewId: 'data-view-id', - docValueFields: [], endDate: '', id: TimelineId.active, indexNames: ['filebeat-*'], 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 4fb4be47c4bf2..6024c4613f265 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -29,7 +29,6 @@ import type { TimelineEdges, TimelineItem, TimelineRequestSortField, - DocValueFields, } from '../../../common/search_strategy'; import { Direction, TimelineEventsQueries } from '../../../common/search_strategy'; import type { InspectResponse } from '../../types'; @@ -76,7 +75,6 @@ type TimelineResponse = T extends 'kuery' export interface UseTimelineEventsProps { dataViewId: string | null; - docValueFields?: DocValueFields[]; endDate: string; eqlOptions?: EqlOptionsSelected; fields: string[]; @@ -130,7 +128,6 @@ const deStructureEqlOptions = (eqlOptions?: EqlOptionsSelected) => ({ export const useTimelineEvents = ({ dataViewId, - docValueFields, endDate, eqlOptions = undefined, id = ID, @@ -366,10 +363,9 @@ export const useTimelineEvents = ({ const currentRequest = { defaultIndex: indexNames, - docValueFields: docValueFields ?? [], factoryQueryType: TimelineEventsQueries.all, fieldRequested: fields, - fields: [], + fields, filterQuery: createFilter(filterQuery), pagination: { activePage: newActivePage, @@ -401,7 +397,6 @@ export const useTimelineEvents = ({ dispatch, indexNames, activePage, - docValueFields, endDate, eqlOptions, filterQuery, diff --git a/x-pack/plugins/security_solution/public/users/pages/details/index.tsx b/x-pack/plugins/security_solution/public/users/pages/details/index.tsx index 4fca3261e5d00..e0fe59d9a1667 100644 --- a/x-pack/plugins/security_solution/public/users/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/details/index.tsx @@ -83,7 +83,7 @@ const UsersDetailsComponent: React.FC = ({ ); const getFilters = () => [...usersDetailsPageFilters, ...filters]; - const { docValueFields, indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); + const { indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); const [filterQuery, kqlError] = convertToBuildEsQuery({ config: getEsQueryConfig(kibana.services.uiSettings), @@ -132,7 +132,6 @@ const UsersDetailsComponent: React.FC = ({ border subtitle={ = ({ ; export type UsersDetailsTabsProps = UserBodyComponentDispatchProps & UsersQueryProps & { - docValueFields?: DocValueFields[]; indexNames: string[]; pageFilters?: Filter[]; filterQuery?: string; diff --git a/x-pack/plugins/security_solution/public/users/pages/users.tsx b/x-pack/plugins/security_solution/public/users/pages/users.tsx index 4aac53838661d..878324f3b55d1 100644 --- a/x-pack/plugins/security_solution/public/users/pages/users.tsx +++ b/x-pack/plugins/security_solution/public/users/pages/users.tsx @@ -186,11 +186,7 @@ const UsersComponent = () => { + } border title={i18n.PAGE_TITLE} diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/query.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/query.test.ts index 9dd3ceffdccc7..1863503bfcae3 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/query.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/query.test.ts @@ -58,11 +58,19 @@ describe('buildEventEnrichmentQuery', () => { const options = buildEventEnrichmentRequestOptionsMock(); const query = buildEventEnrichmentQuery(options); expect(query.body?.fields).toEqual([ - '*', + { field: '*', include_unmapped: true }, { field: '@timestamp', format: 'strict_date_optional_time', }, + { + field: 'code_signature.timestamp', + format: 'strict_date_optional_time', + }, + { + field: 'dll.code_signature.timestamp', + format: 'strict_date_optional_time', + }, ]); }); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/query.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/query.ts index e8d50cfc24ab5..cef953b5fb557 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/query.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/event_enrichment/query.ts @@ -33,12 +33,21 @@ export const buildEventEnrichmentQuery: SecuritySolutionFactory { const response = buildEventEnrichmentRawResponseMock(); const parsedResponse = await parseEventEnrichmentResponse(options, response); - const expectedInspect = expect.objectContaining({ + const expectedInspect = { allow_no_indices: true, body: { _source: false, fields: [ - '*', + { field: '*', include_unmapped: true }, { field: '@timestamp', format: 'strict_date_optional_time', }, + { + field: 'code_signature.timestamp', + format: 'strict_date_optional_time', + }, + { + field: 'dll.code_signature.timestamp', + format: 'strict_date_optional_time', + }, ], query: { bool: { @@ -62,10 +70,11 @@ describe('parseEventEnrichmentResponse', () => { ], }, }, + stored_fields: ['*'], }, ignore_unavailable: true, index: ['filebeat-*'], - }); + }; const parsedInspect = JSON.parse(parsedResponse.inspect.dsl[0]); expect(parsedInspect).toEqual(expectedInspect); }); diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.test.ts index a0df71e733871..faeb33b2369a1 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/cti/threat_intel_source/query.threat_intel_source.dsl.test.ts @@ -10,7 +10,6 @@ import { CtiQueries } from '../../../../../../common/search_strategy/security_so export const mockOptions = { defaultIndex: ['logs-ti_*', 'filebeat-8*'], - docValueFields: [], factoryQueryType: CtiQueries.dataSource, filterQuery: '', timerange: { diff --git a/x-pack/plugins/timelines/common/constants.ts b/x-pack/plugins/timelines/common/constants.ts index bc22c761c24e0..e41333eb03697 100644 --- a/x-pack/plugins/timelines/common/constants.ts +++ b/x-pack/plugins/timelines/common/constants.ts @@ -24,3 +24,4 @@ export const RAC_ALERTS_BULK_UPDATE_URL = '/internal/rac/alerts/bulk_update'; export const DETECTION_ENGINE_SIGNALS_STATUS_URL = '/api/detection_engine/signals/status'; export const DELETED_SECURITY_SOLUTION_DATA_VIEW = 'DELETED_SECURITY_SOLUTION_DATA_VIEW'; +export const ENRICHMENT_DESTINATION_PATH = 'threat.enrichments'; diff --git a/x-pack/plugins/timelines/common/search_strategy/timeline/index.ts b/x-pack/plugins/timelines/common/search_strategy/timeline/index.ts index 7e37d1d092edf..dfdc1ed3eabd4 100644 --- a/x-pack/plugins/timelines/common/search_strategy/timeline/index.ts +++ b/x-pack/plugins/timelines/common/search_strategy/timeline/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { IEsSearchRequest } from '@kbn/data-plugin/common'; import { ESQuery } from '../../typed_json'; import { @@ -19,13 +19,7 @@ import { TimelineKpiStrategyResponse, EntityType, } from './events'; -import { - DocValueFields, - PaginationInputPaginated, - TimerangeInput, - SortField, - Maybe, -} from '../common'; +import { PaginationInputPaginated, TimerangeInput, SortField, Maybe } from '../common'; import { DataProviderType, TimelineType, @@ -41,7 +35,6 @@ export interface TimelineRequestBasicOptions extends IEsSearchRequest { timerange: TimerangeInput; filterQuery: ESQuery | string | undefined; defaultIndex: string[]; - docValueFields?: DocValueFields[]; factoryQueryType?: TimelineFactoryQueryTypes; entityType?: EntityType; runtimeMappings: MappingRuntimeFields; diff --git a/x-pack/plugins/timelines/common/utils/field_formatters.test.ts b/x-pack/plugins/timelines/common/utils/field_formatters.test.ts index 8f417baf8378d..5cc339bc04e67 100644 --- a/x-pack/plugins/timelines/common/utils/field_formatters.test.ts +++ b/x-pack/plugins/timelines/common/utils/field_formatters.test.ts @@ -6,8 +6,8 @@ */ import { eventDetailsFormattedFields, eventHit } from '@kbn/securitysolution-t-grid'; -import { EventHit, EventSource } from '../search_strategy'; -import { getDataFromFieldsHits, getDataFromSourceHits, getDataSafety } from './field_formatters'; +import { EventHit } from '../search_strategy'; +import { getDataFromFieldsHits, getDataSafety } from './field_formatters'; describe('Events Details Helpers', () => { const fields: EventHit['fields'] = eventHit.fields; @@ -73,59 +73,14 @@ describe('Events Details Helpers', () => { const whackResultFields = [ { category: 'crazy', - field: 'crazy.pants.matched.field', - values: ['matched_field'], - originalValue: ['matched_field'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.first_seen', - values: ['2021-02-22T17:29:25.195Z'], - originalValue: ['2021-02-22T17:29:25.195Z'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.provider', - values: ['yourself'], - originalValue: ['yourself'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.type', - values: ['custom'], - originalValue: ['custom'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.matched.atomic', - values: ['matched_atomic'], - originalValue: ['matched_atomic'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.lazer.great.field', - values: ['grrrrr', 'grrrrr_2'], - originalValue: ['grrrrr', 'grrrrr_2'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.lazer.lazer.lazer.cool', - values: ['true', 'false'], - originalValue: ['true', 'false'], - isObjectArray: false, - }, - { - category: 'crazy', - field: 'crazy.pants.lazer.lazer.lazer.lazer.lazer.lazer.lazer.whoa', - values: ['false'], - originalValue: ['false'], - isObjectArray: false, + field: 'crazy.pants', + values: [ + '{"matched.field":["matched_field"],"first_seen":["2021-02-22T17:29:25.195Z"],"provider":["yourself"],"type":["custom"],"matched.atomic":["matched_atomic"],"lazer":[{"great.field":["grrrrr"],"lazer":[{"lazer":[{"cool":true,"lazer":[{"lazer":[{"lazer":[{"lazer":[{"whoa":false}]}]}]}]}]},{"lazer":[{"cool":false}]}]},{"great.field":["grrrrr_2"]}]}', + ], + originalValue: [ + '{"matched.field":["matched_field"],"first_seen":["2021-02-22T17:29:25.195Z"],"provider":["yourself"],"type":["custom"],"matched.atomic":["matched_atomic"],"lazer":[{"great.field":["grrrrr"],"lazer":[{"lazer":[{"cool":true,"lazer":[{"lazer":[{"lazer":[{"lazer":[{"whoa":false}]}]}]}]}]},{"lazer":[{"cool":false}]}]},{"great.field":["grrrrr_2"]}]}', + ], + isObjectArray: true, }, ]; const result = getDataFromFieldsHits(whackFields); @@ -165,138 +120,421 @@ describe('Events Details Helpers', () => { }, { category: 'kibana', - field: 'kibana.alert.rule.parameters.criteria.metric', + field: 'kibana.alert.rule.parameters.criteria', + isObjectArray: true, + originalValue: [ + '{"metric":"cpu","comparator":">","threshold":[3],"timeSize":1,"timeUnit":"m","customMetric":{"type":"custom","id":"alert-custom-metric","field":"","aggregation":"avg"}}', + ], + values: [ + '{"metric":"cpu","comparator":">","threshold":[3],"timeSize":1,"timeUnit":"m","customMetric":{"type":"custom","id":"alert-custom-metric","field":"","aggregation":"avg"}}', + ], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.sourceId', + isObjectArray: false, + originalValue: ['default'], + values: ['default'], + }, + ]; + + const result = getDataFromFieldsHits(ruleParameterFields); + expect(result).toEqual(ruleParametersResultFields); + }); + + it('get data from threat enrichments', () => { + const data = { + 'kibana.alert.rule.parameters': [ + { + severity_mapping: [], + references: [], + threat_language: 'kuery', + description: 'The threat indicator rule description.', + language: 'kuery', + threat_mapping: [ + { + entries: [ + { + field: 'myhash.mysha256', + type: 'mapping', + value: 'threat.indicator.file.hash.sha256', + }, + ], + }, + ], + type: 'threat_match', + threat_filters: [], + exceptions_list: [], + from: 'now-50000h', + timeline_id: '495ad7a7-316e-4544-8a0f-9c098daee76e', + severity: 'critical', + max_signals: 100, + risk_score: 20, + risk_score_mapping: [], + author: [], + threat_indicator_path: 'threat.indicator', + query: '*:*', + index: ['suspicious-*'], + version: 1, + threat_query: '*:*', + rule_id: 'rule_testing', + required_fields: [], + immutable: false, + related_integrations: [], + timeline_title: 'Generic Threat Match Timeline', + threat_index: ['filebeat-*'], + setup: '', + false_positives: [], + threat: [], + to: 'now', + }, + ], + 'signal.rule.version': ['1'], + 'kibana.alert.status': ['active'], + 'signal.ancestors.index': ['suspicious-source-event-001'], + 'signal.depth': [1], + 'signal.rule.immutable': ['false'], + 'kibana.alert.rule.rule_type_id': ['siem.indicatorRule'], + 'signal.rule.name': ['Threat Indicator Rule Test'], + 'signal.rule.rule_id': ['rule_testing'], + 'kibana.alert.rule.timeline_id': ['495ad7a7-316e-4544-8a0f-9c098daee76e'], + 'threat.enrichments': [ + { + 'matched.field': ['myhash.mysha256'], + 'matched.index': ['logs-ti_abusech.malware'], + 'matched.type': ['indicator_match_rule'], + 'feed.name': ['AbuseCH malware'], + 'matched.atomic': ['a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3'], + }, + ], + }; + + const ruleParametersResultFields = [ + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.severity_mapping', + isObjectArray: false, + originalValue: [], + values: [], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.references', + isObjectArray: false, + originalValue: [], + values: [], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.threat_language', + isObjectArray: false, + originalValue: ['kuery'], + values: ['kuery'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.description', + isObjectArray: false, + originalValue: ['The threat indicator rule description.'], + values: ['The threat indicator rule description.'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.language', + isObjectArray: false, + originalValue: ['kuery'], + values: ['kuery'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.threat_mapping', + isObjectArray: true, + originalValue: [ + '{"entries":[{"field":"myhash.mysha256","type":"mapping","value":"threat.indicator.file.hash.sha256"}]}', + ], + values: [ + '{"entries":[{"field":"myhash.mysha256","type":"mapping","value":"threat.indicator.file.hash.sha256"}]}', + ], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.type', + isObjectArray: false, + originalValue: ['threat_match'], + values: ['threat_match'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.threat_filters', + isObjectArray: false, + originalValue: [], + values: [], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.exceptions_list', + isObjectArray: false, + originalValue: [], + values: [], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.from', isObjectArray: false, - originalValue: ['cpu'], - values: ['cpu'], + originalValue: ['now-50000h'], + values: ['now-50000h'], }, { category: 'kibana', - field: 'kibana.alert.rule.parameters.criteria.comparator', - values: ['>'], - originalValue: ['>'], + field: 'kibana.alert.rule.parameters.timeline_id', isObjectArray: false, + originalValue: ['495ad7a7-316e-4544-8a0f-9c098daee76e'], + values: ['495ad7a7-316e-4544-8a0f-9c098daee76e'], }, { category: 'kibana', - field: 'kibana.alert.rule.parameters.criteria.threshold', + field: 'kibana.alert.rule.parameters.severity', isObjectArray: false, - originalValue: ['3'], - values: ['3'], + originalValue: ['critical'], + values: ['critical'], }, { category: 'kibana', - field: 'kibana.alert.rule.parameters.criteria.timeSize', + field: 'kibana.alert.rule.parameters.max_signals', + isObjectArray: false, + originalValue: ['100'], + values: ['100'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.risk_score', + isObjectArray: false, + originalValue: ['20'], + values: ['20'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.risk_score_mapping', + isObjectArray: false, + originalValue: [], + values: [], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.author', + isObjectArray: false, + originalValue: [], + values: [], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.threat_indicator_path', + isObjectArray: false, + originalValue: ['threat.indicator'], + values: ['threat.indicator'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.query', + isObjectArray: false, + originalValue: ['*:*'], + values: ['*:*'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.index', + isObjectArray: false, + originalValue: ['suspicious-*'], + values: ['suspicious-*'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.version', isObjectArray: false, originalValue: ['1'], values: ['1'], }, { category: 'kibana', - field: 'kibana.alert.rule.parameters.criteria.timeUnit', - values: ['m'], - originalValue: ['m'], + field: 'kibana.alert.rule.parameters.threat_query', + isObjectArray: false, + originalValue: ['*:*'], + values: ['*:*'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.rule_id', isObjectArray: false, + originalValue: ['rule_testing'], + values: ['rule_testing'], }, { category: 'kibana', - field: 'kibana.alert.rule.parameters.criteria.customMetric.type', + field: 'kibana.alert.rule.parameters.required_fields', isObjectArray: false, - originalValue: ['custom'], - values: ['custom'], + originalValue: [], + values: [], }, { category: 'kibana', - field: 'kibana.alert.rule.parameters.criteria.customMetric.id', + field: 'kibana.alert.rule.parameters.immutable', isObjectArray: false, - originalValue: ['alert-custom-metric'], - values: ['alert-custom-metric'], + originalValue: ['false'], + values: ['false'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.related_integrations', + isObjectArray: false, + originalValue: [], + values: [], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.timeline_title', + isObjectArray: false, + originalValue: ['Generic Threat Match Timeline'], + values: ['Generic Threat Match Timeline'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.threat_index', + isObjectArray: false, + originalValue: ['filebeat-*'], + values: ['filebeat-*'], }, { category: 'kibana', - field: 'kibana.alert.rule.parameters.criteria.customMetric.field', + field: 'kibana.alert.rule.parameters.setup', isObjectArray: false, originalValue: [''], values: [''], }, { category: 'kibana', - field: 'kibana.alert.rule.parameters.criteria.customMetric.aggregation', + field: 'kibana.alert.rule.parameters.false_positives', isObjectArray: false, - originalValue: ['avg'], - values: ['avg'], + originalValue: [], + values: [], }, { category: 'kibana', - field: 'kibana.alert.rule.parameters.sourceId', + field: 'kibana.alert.rule.parameters.threat', isObjectArray: false, - originalValue: ['default'], - values: ['default'], + originalValue: [], + values: [], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.parameters.to', + isObjectArray: false, + originalValue: ['now'], + values: ['now'], + }, + { + category: 'signal', + field: 'signal.rule.version', + isObjectArray: false, + originalValue: ['1'], + values: ['1'], + }, + { + category: 'kibana', + field: 'kibana.alert.status', + isObjectArray: false, + originalValue: ['active'], + values: ['active'], + }, + { + category: 'signal', + field: 'signal.ancestors.index', + isObjectArray: false, + originalValue: ['suspicious-source-event-001'], + values: ['suspicious-source-event-001'], + }, + { + category: 'signal', + field: 'signal.depth', + isObjectArray: false, + originalValue: ['1'], + values: ['1'], + }, + { + category: 'signal', + field: 'signal.rule.immutable', + isObjectArray: false, + originalValue: ['false'], + values: ['false'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.rule_type_id', + isObjectArray: false, + originalValue: ['siem.indicatorRule'], + values: ['siem.indicatorRule'], + }, + { + category: 'signal', + field: 'signal.rule.name', + isObjectArray: false, + originalValue: ['Threat Indicator Rule Test'], + values: ['Threat Indicator Rule Test'], + }, + { + category: 'signal', + field: 'signal.rule.rule_id', + isObjectArray: false, + originalValue: ['rule_testing'], + values: ['rule_testing'], + }, + { + category: 'kibana', + field: 'kibana.alert.rule.timeline_id', + isObjectArray: false, + originalValue: ['495ad7a7-316e-4544-8a0f-9c098daee76e'], + values: ['495ad7a7-316e-4544-8a0f-9c098daee76e'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.field', + isObjectArray: false, + originalValue: ['myhash.mysha256'], + values: ['myhash.mysha256'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.index', + isObjectArray: false, + originalValue: ['logs-ti_abusech.malware'], + values: ['logs-ti_abusech.malware'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.type', + isObjectArray: false, + originalValue: ['indicator_match_rule'], + values: ['indicator_match_rule'], + }, + { + category: 'threat', + field: 'threat.enrichments.feed.name', + isObjectArray: false, + originalValue: ['AbuseCH malware'], + values: ['AbuseCH malware'], + }, + { + category: 'threat', + field: 'threat.enrichments.matched.atomic', + isObjectArray: false, + originalValue: ['a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3'], + values: ['a04ac6d98ad989312783d4fe3456c53730b212c79a426fb215708b6c6daa3de3'], }, ]; - - const result = getDataFromFieldsHits(ruleParameterFields); + const result = getDataFromFieldsHits(data); expect(result).toEqual(ruleParametersResultFields); }); }); - it('#getDataFromSourceHits', () => { - const _source: EventSource = { - '@timestamp': '2021-02-24T00:41:06.527Z', - 'kibana.alert.workflow_status': 'open', - 'kibana.alert.rule.name': 'Rawr', - 'threat.indicator': [ - { - provider: 'yourself', - type: 'custom', - first_seen: ['2021-02-22T17:29:25.195Z'], - matched: { atomic: 'atom', field: 'field', type: 'type' }, - }, - { - provider: 'other_you', - type: 'custom', - first_seen: '2021-02-22T17:29:25.195Z', - matched: { atomic: 'atom', field: 'field', type: 'type' }, - }, - ], - }; - expect(getDataFromSourceHits(_source)).toEqual([ - { - category: 'base', - field: '@timestamp', - values: ['2021-02-24T00:41:06.527Z'], - originalValue: ['2021-02-24T00:41:06.527Z'], - isObjectArray: false, - }, - { - category: 'kibana', - field: 'kibana.alert.workflow_status', - values: ['open'], - originalValue: ['open'], - isObjectArray: false, - }, - { - category: 'kibana', - field: 'kibana.alert.rule.name', - values: ['Rawr'], - originalValue: ['Rawr'], - isObjectArray: false, - }, - { - category: 'threat', - field: 'threat.indicator', - values: [ - '{"provider":"yourself","type":"custom","first_seen":["2021-02-22T17:29:25.195Z"],"matched":{"atomic":"atom","field":"field","type":"type"}}', - '{"provider":"other_you","type":"custom","first_seen":"2021-02-22T17:29:25.195Z","matched":{"atomic":"atom","field":"field","type":"type"}}', - ], - originalValue: [ - '{"provider":"yourself","type":"custom","first_seen":["2021-02-22T17:29:25.195Z"],"matched":{"atomic":"atom","field":"field","type":"type"}}', - '{"provider":"other_you","type":"custom","first_seen":"2021-02-22T17:29:25.195Z","matched":{"atomic":"atom","field":"field","type":"type"}}', - ], - isObjectArray: true, - }, - ]); - }); it('#getDataSafety', async () => { const result = await getDataSafety(getDataFromFieldsHits, fields); expect(result).toEqual(resultFields); diff --git a/x-pack/plugins/timelines/common/utils/field_formatters.ts b/x-pack/plugins/timelines/common/utils/field_formatters.ts index 6730802d80e08..cb99b38860539 100644 --- a/x-pack/plugins/timelines/common/utils/field_formatters.ts +++ b/x-pack/plugins/timelines/common/utils/field_formatters.ts @@ -5,10 +5,13 @@ * 2.0. */ -import { get, isEmpty, isNumber, isObject, isString } from 'lodash/fp'; +import { isEmpty } from 'lodash/fp'; import { ALERT_RULE_PARAMETERS } from '@kbn/rule-data-utils'; -import { EventHit, EventSource, TimelineEventsDetailsItem } from '../search_strategy'; +import { ecsFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/ecs_field_map'; +import { technicalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/technical_rule_field_map'; +import { experimentalRuleFieldMap } from '@kbn/rule-registry-plugin/common/assets/field_maps/experimental_rule_field_map'; +import { EventHit, TimelineEventsDetailsItem } from '../search_strategy'; import { toObjectArrayOfStrings, toStringArray } from './to_array'; export const baseCategoryFields = ['@timestamp', 'labels', 'message', 'tags']; @@ -41,40 +44,6 @@ export const isGeoField = (field: string) => export const isRuleParametersFieldOrSubfield = (field: string, prependField?: string) => prependField?.includes(ALERT_RULE_PARAMETERS) || field === ALERT_RULE_PARAMETERS; -export const getDataFromSourceHits = ( - sources: EventSource, - category?: string, - path?: string -): TimelineEventsDetailsItem[] => - Object.keys(sources ?? {}).reduce((accumulator, source) => { - const item: EventSource = get(source, sources); - if (Array.isArray(item) || isString(item) || isNumber(item)) { - const field = path ? `${path}.${source}` : source; - const fieldCategory = getFieldCategory(field); - - const objArrStr = toObjectArrayOfStrings(item); - const strArr = objArrStr.map(({ str }) => str); - const isObjectArray = objArrStr.some((o) => o.isObjectArray); - - return [ - ...accumulator, - { - category: fieldCategory, - field, - values: strArr, - originalValue: strArr, - isObjectArray, - } as TimelineEventsDetailsItem, - ]; - } else if (isObject(item)) { - return [ - ...accumulator, - ...getDataFromSourceHits(item, category || source, path ? `${path}.${source}` : source), - ]; - } - return accumulator; - }, []); - export const getDataFromFieldsHits = ( fields: EventHit['fields'], prependField?: string, @@ -101,8 +70,13 @@ export const getDataFromFieldsHits = ( const isObjectArray = objArrStr.some((o) => o.isObjectArray); const dotField = prependField ? `${prependField}.${field}` : field; - // return simple field value (non-object, non-array) - if (!isObjectArray) { + // return simple field value (non-ecs object, non-array) + if ( + !isObjectArray || + Object.keys({ ...ecsFieldMap, ...technicalRuleFieldMap, ...experimentalRuleFieldMap }).find( + (ecsField) => ecsField === field + ) === undefined + ) { return [ ...accumulator, { diff --git a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx index 0798012821e1e..7183c164a24be 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/integrated/index.tsx @@ -12,14 +12,13 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import styled from 'styled-components'; import { useDispatch } from 'react-redux'; -import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { DataViewBase, Filter, Query } from '@kbn/es-query'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import type { CoreStart } from '@kbn/core/public'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { getEsQueryConfig } from '@kbn/data-plugin/common'; import { Direction, EntityType } from '../../../../common/search_strategy'; -import type { DocValueFields } from '../../../../common/search_strategy'; import type { BrowserFields } from '../../../../common/search_strategy/index_fields'; import { BulkActionsProp, @@ -105,7 +104,6 @@ export interface TGridIntegratedProps { defaultCellActions?: TGridCellAction[]; deletedEventIds: Readonly; disabledCellActions: string[]; - docValueFields: DocValueFields[]; end: string; entityType: EntityType; fieldBrowserOptions?: FieldBrowserOptions; @@ -151,7 +149,6 @@ const TGridIntegratedComponent: React.FC = ({ defaultCellActions, deletedEventIds, disabledCellActions, - docValueFields, end, entityType, fieldBrowserOptions, @@ -241,7 +238,6 @@ const TGridIntegratedComponent: React.FC = ({ alertConsumers: SECURITY_ALERTS_CONSUMERS, data, dataViewId, - docValueFields, endDate: end, entityType, fields, diff --git a/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx b/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx index 396e64faa166b..1eb327d95827a 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx +++ b/x-pack/plugins/timelines/public/components/t_grid/standalone/index.tsx @@ -218,7 +218,6 @@ const TGridStandaloneComponent: React.FC = ({ { consumers, events, updatedAt, loadPage, pageInfo, refetch, totalCount = 0, inspect }, ] = useTimelineEvents({ dataViewId, - docValueFields: [], entityType, excludeEcsData: true, fields, diff --git a/x-pack/plugins/timelines/public/container/index.tsx b/x-pack/plugins/timelines/public/container/index.tsx index 4ab6856c6df49..9e41932990ca9 100644 --- a/x-pack/plugins/timelines/public/container/index.tsx +++ b/x-pack/plugins/timelines/public/container/index.tsx @@ -28,7 +28,6 @@ import { EntityType, } from '../../common/search_strategy'; import type { - DocValueFields, Inspect, PaginationInputPaginated, TimelineStrategyResponseType, @@ -75,7 +74,6 @@ export interface UseTimelineEventsProps { alertConsumers?: AlertConsumers[]; data?: DataPublicPluginStart; dataViewId: string | null; - docValueFields?: DocValueFields[]; endDate: string; entityType: EntityType; excludeEcsData?: boolean; @@ -121,7 +119,6 @@ const NO_CONSUMERS: AlertConsumers[] = []; export const useTimelineEvents = ({ alertConsumers = NO_CONSUMERS, dataViewId, - docValueFields, endDate, entityType, excludeEcsData = false, @@ -298,7 +295,6 @@ export const useTimelineEvents = ({ const currentRequest = { alertConsumers, defaultIndex: indexNames, - docValueFields: docValueFields ?? [], excludeEcsData, factoryQueryType: TimelineEventsQueries.all, fieldRequested: fields, @@ -331,7 +327,6 @@ export const useTimelineEvents = ({ dispatch, indexNames, activePage, - docValueFields, endDate, excludeEcsData, filterQuery, diff --git a/x-pack/plugins/timelines/public/mock/browser_fields.ts b/x-pack/plugins/timelines/public/mock/browser_fields.ts index 948d1e1f68081..e38a435d1a4fb 100644 --- a/x-pack/plugins/timelines/public/mock/browser_fields.ts +++ b/x-pack/plugins/timelines/public/mock/browser_fields.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { DocValueFields } from '../../common/search_strategy'; import type { BrowserFields } from '../../common/search_strategy/index_fields'; diff --git a/x-pack/plugins/timelines/public/mock/t_grid.tsx b/x-pack/plugins/timelines/public/mock/t_grid.tsx index b4f0110136886..f0db0e3912773 100644 --- a/x-pack/plugins/timelines/public/mock/t_grid.tsx +++ b/x-pack/plugins/timelines/public/mock/t_grid.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { ALERT_START, ALERT_STATUS } from '@kbn/rule-data-utils'; import { TGridIntegratedProps } from '../components/t_grid/integrated'; -import { mockBrowserFields, mockDocValueFields, mockRuntimeMappings } from './browser_fields'; +import { mockBrowserFields, mockRuntimeMappings } from './browser_fields'; import { mockDataProviders } from './mock_data_providers'; import { mockTimelineData } from './mock_timeline_data'; import { ColumnHeaderOptions, TimelineId } from '../../common/types'; @@ -95,7 +95,6 @@ export const tGridIntegratedProps: TGridIntegratedProps = { dataViewId: 'data-view-id', deletedEventIds: [], disabledCellActions: [], - docValueFields: mockDocValueFields, end: '2021-08-19T00:30:00.000Z', entityType: 'alerts', filterStatus: 'open', diff --git a/x-pack/plugins/timelines/server/plugin.ts b/x-pack/plugins/timelines/server/plugin.ts index 3cc8356a90280..e7af669344d12 100644 --- a/x-pack/plugins/timelines/server/plugin.ts +++ b/x-pack/plugins/timelines/server/plugin.ts @@ -9,7 +9,6 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from ' import { SecurityPluginSetup } from '@kbn/security-plugin/server'; import { SetupPlugins, StartPlugins, TimelinesPluginUI, TimelinesPluginStart } from './types'; -import { defineRoutes } from './routes'; import { timelineSearchStrategyProvider } from './search_strategy/timeline'; import { timelineEqlSearchStrategyProvider } from './search_strategy/timeline/eql'; import { indexFieldsProvider } from './search_strategy/index_fields'; @@ -28,11 +27,6 @@ export class TimelinesPlugin this.logger.debug('timelines: Setup'); this.security = plugins.security; - const router = core.http.createRouter(); - - // Register server side APIs - defineRoutes(router); - const IndexFields = indexFieldsProvider(core.getStartServices); // Register search strategy core.getStartServices().then(([_, depsStart]) => { diff --git a/x-pack/plugins/timelines/server/routes/index.ts b/x-pack/plugins/timelines/server/routes/index.ts deleted file mode 100644 index c4cd611ca6719..0000000000000 --- a/x-pack/plugins/timelines/server/routes/index.ts +++ /dev/null @@ -1,24 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { IRouter } from '@kbn/core/server'; - -export function defineRoutes(router: IRouter) { - router.get( - { - path: '/api/timeline/example', - validate: false, - }, - async (context, request, response) => { - return response.ok({ - body: { - time: new Date().toISOString(), - }, - }); - } - ); -} diff --git a/x-pack/plugins/timelines/server/search_strategy/index_fields/index.ts b/x-pack/plugins/timelines/server/search_strategy/index_fields/index.ts index 6aa081f12266d..2a69b60b708b5 100644 --- a/x-pack/plugins/timelines/server/search_strategy/index_fields/index.ts +++ b/x-pack/plugins/timelines/server/search_strategy/index_fields/index.ts @@ -173,7 +173,7 @@ export const requestIndexFieldSearch = async ( _index: '', _id: '', _score: -1, - _source: null, + fields: {}, }, ], }, diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/eql/__mocks__/index.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/eql/__mocks__/index.ts index 9439f7a6423fb..1cfb1459f7fe9 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/eql/__mocks__/index.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/eql/__mocks__/index.ts @@ -27,218 +27,151 @@ export const sequenceResponse = { { _index: '.ds-logs-endpoint.events.security-default-2021.02.05-000005', _id: 'qhymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTIzODAtMTMyNTUwNzg2ODkuOTY1Nzg1NTAw', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU2OC0xMzI1NTA3ODY2Ny4zMjk3MDY2MDA=', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - }, - name: 'C:\\Program Files\\OpenSSH-Win64\\sshd.exe', - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', - executable: 'C:\\Program Files\\OpenSSH-Win64\\sshd.exe', - }, - message: 'Endpoint security event', - '@timestamp': '2021-02-08T21:50:28.3377092Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.security', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293866, - ingested: '2021-02-08T21:57:26.417559711Z', - created: '2021-02-08T21:50:28.3377092Z', - kind: 'event', - module: 'endpoint', - action: 'log_on', - id: 'LzzWB9jjGmCwGMvk++++FG/O', - category: ['authentication', 'session'], - type: ['start'], - dataset: 'endpoint.events.security', - outcome: 'success', - }, - user: { - domain: 'NT AUTHORITY', - name: 'SYSTEM', - }, + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3377092Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTIzODAtMTMyNTUwNzg2ODkuOTY1Nzg1NTAw', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU2OC0xMzI1NTA3ODY2Ny4zMjk3MDY2MDA=', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.name': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', + ], + 'process.executable': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293866], + 'event.ingested': ['2021-02-08T21:57:26.417559711Z'], + 'event.created': ['2021-02-08T21:50:28.3377092Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['log_on'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/O'], + 'event.category': ['authentication', 'session'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.security'], + 'event.outcome': ['success'], + 'user.domain': ['NT AUTHORITY'], + 'user.name': ['SYSTEM'], }, }, { _index: '.ds-logs-endpoint.events.security-default-2021.02.05-000005', _id: 'qxymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - }, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', - executable: 'C:\\Windows\\System32\\lsass.exe', - }, - message: 'Endpoint security event', - '@timestamp': '2021-02-08T21:50:28.3377142Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.security', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293867, - ingested: '2021-02-08T21:57:26.417596906Z', - created: '2021-02-08T21:50:28.3377142Z', - kind: 'event', - module: 'endpoint', - action: 'log_on', - id: 'LzzWB9jjGmCwGMvk++++FG/P', - category: ['authentication', 'session'], - type: ['start'], - dataset: 'endpoint.events.security', - outcome: 'success', - }, - user: { - domain: 'NT AUTHORITY', - name: 'SYSTEM', - }, + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3377142Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', + ], + 'process.executable': ['C:\\Windows\\System32\\lsass.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293867], + 'event.ingested': ['2021-02-08T21:57:26.417596906Z'], + 'event.created': ['2021-02-08T21:50:28.3377142Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['log_on'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/P'], + 'event.category': ['authentication', 'session'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.security'], + 'event.outcome': ['success'], + 'user.domain': ['NT AUTHORITY'], + 'user.name': ['SYSTEM'], }, }, { _index: '.ds-logs-endpoint.events.security-default-2021.02.05-000005', _id: 'rBymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - }, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', - executable: 'C:\\Windows\\System32\\lsass.exe', - }, - message: 'Endpoint security event', - '@timestamp': '2021-02-08T21:50:28.3381013Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.security', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293868, - ingested: '2021-02-08T21:57:26.417632166Z', - created: '2021-02-08T21:50:28.3381013Z', - kind: 'event', - module: 'endpoint', - id: 'LzzWB9jjGmCwGMvk++++FG/Q', - category: [], - type: [], - dataset: 'endpoint.events.security', - }, - user: { - domain: 'NT AUTHORITY', - name: 'SYSTEM', - }, + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3381013Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', + ], + 'process.executable': ['C:\\Windows\\System32\\lsass.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293868], + 'event.ingested': ['2021-02-08T21:57:26.417632166Z'], + 'event.created': ['2021-02-08T21:50:28.3381013Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['log_on'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/Q'], + 'event.category': ['authentication', 'session'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.security'], + 'event.outcome': ['success'], + 'user.domain': ['NT AUTHORITY'], + 'user.name': ['SYSTEM'], }, }, ], @@ -248,256 +181,187 @@ export const sequenceResponse = { events: [ { _index: '.ds-logs-endpoint.events.security-default-2021.02.05-000005', - _id: 'qxymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - }, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', - executable: 'C:\\Windows\\System32\\lsass.exe', - }, - message: 'Endpoint security event', - '@timestamp': '2021-02-08T21:50:28.3377142Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.security', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293867, - ingested: '2021-02-08T21:57:26.417596906Z', - created: '2021-02-08T21:50:28.3377142Z', - kind: 'event', - module: 'endpoint', - action: 'log_on', - id: 'LzzWB9jjGmCwGMvk++++FG/P', - category: ['authentication', 'session'], - type: ['start'], - dataset: 'endpoint.events.security', - outcome: 'success', - }, - user: { - domain: 'NT AUTHORITY', - name: 'SYSTEM', - }, + _id: 'qhymg3cBX5UUcOOYP3Ec', + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3377142Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', + ], + 'process.executable': ['C:\\Windows\\System32\\lsass.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293867], + 'event.ingested': ['2021-02-08T21:57:26.417596906Z'], + 'event.created': ['2021-02-08T21:50:28.3377142Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['log_on'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/P'], + 'event.category': ['authentication', 'session'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.security'], + 'event.outcome': ['success'], + 'user.domain': ['NT AUTHORITY'], + 'user.name': ['SYSTEM'], }, }, { _index: '.ds-logs-endpoint.events.security-default-2021.02.05-000005', _id: 'rBymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - }, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', - executable: 'C:\\Windows\\System32\\lsass.exe', - }, - message: 'Endpoint security event', - '@timestamp': '2021-02-08T21:50:28.3381013Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.security', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293868, - ingested: '2021-02-08T21:57:26.417632166Z', - created: '2021-02-08T21:50:28.3381013Z', - kind: 'event', - module: 'endpoint', - id: 'LzzWB9jjGmCwGMvk++++FG/Q', - category: [], - type: [], - dataset: 'endpoint.events.security', - }, - user: { - domain: 'NT AUTHORITY', - name: 'SYSTEM', - }, + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3381013Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', + ], + 'process.executable': ['C:\\Windows\\System32\\lsass.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293868], + 'event.ingested': ['2021-02-08T21:57:26.417632166Z'], + 'event.created': ['2021-02-08T21:50:28.3381013Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['log_on'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/Q'], + 'event.category': ['authentication', 'session'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.security'], + 'event.outcome': ['success'], + 'user.domain': ['NT AUTHORITY'], + 'user.name': ['SYSTEM'], }, }, { _index: '.ds-logs-endpoint.events.process-default-2021.02.02-000005', _id: 'pxymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTIzODAtMTMyNTUwNzg2ODkuOTY1Nzg1NTAw', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU2OC0xMzI1NTA3ODY2Ny4zMjk3MDY2MDA=', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - code_signature: [ - { - trusted: true, - subject_name: 'Microsoft Corporation', - exists: true, - status: 'trusted', - }, - ], - token: { - integrity_level_name: 'high', - elevation_level: 'default', - }, - }, - args: ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe', '-y'], - parent: { - args: ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe', '-R'], - name: 'sshd.exe', - pid: 5284, - args_count: 2, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', - command_line: '"C:\\Program Files\\OpenSSH-Win64\\sshd.exe" -R', - executable: 'C:\\Program Files\\OpenSSH-Win64\\sshd.exe', - }, - code_signature: { + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3446355Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTIzODAtMTMyNTUwNzg2ODkuOTY1Nzg1NTAw', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU2OC0xMzI1NTA3ODY2Ny4zMjk3MDY2MDA=', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.Ext.code_signature': [ + { trusted: true, subject_name: 'Microsoft Corporation', exists: true, status: 'trusted', }, - name: 'sshd.exe', - pid: 6368, - args_count: 2, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTYzNjgtMTMyNTcyOTQ2MjguMzQ0NjM1NTAw', - command_line: '"C:\\Program Files\\OpenSSH-Win64\\sshd.exe" -y', - executable: 'C:\\Program Files\\OpenSSH-Win64\\sshd.exe', - hash: { - sha1: '631244d731f406394c17c7dfd85203e317c74814', - sha256: 'e6a972f9db27de18be225095b3b3141b945be8aadc4014c8704ae5acafe3e8e0', - md5: '331ba0e529810ef718dd3efbd1242302', - }, - }, - message: 'Endpoint process event', - '@timestamp': '2021-02-08T21:50:28.3446355Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.process', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293863, - ingested: '2021-02-08T21:57:26.417387865Z', - created: '2021-02-08T21:50:28.3446355Z', - kind: 'event', - module: 'endpoint', - action: 'start', - id: 'LzzWB9jjGmCwGMvk++++FG/K', - category: ['process'], - type: ['start'], - dataset: 'endpoint.events.process', - }, - user: { - domain: '', - name: '', - }, + ], + 'process.Ext.token.integrity_level_name': ['high'], + 'process.Ext.token.elevation_level': ['default'], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTYzNjgtMTMyNTcyOTQ2MjguMzQ0NjM1NTAw', + ], + 'process.args': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe', '-y'], + 'process.parent.args': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe', '-R'], + 'process.parent.name': ['sshd.exe'], + 'process.parent.pid': [5284], + 'process.parent.args_count': [2], + 'process.parent.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', + ], + 'process.parent.command_line': [ + '"C:\\Program Files\\OpenSSH-Win64\\sshd.exe" -R', + ], + 'process.parent.executable': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'], + 'process.code_signature.trusted': [true], + 'process.code_signature.subject_name': ['Microsoft Corporation'], + 'process.code_signature.exists': [true], + 'process.code_signature.status': ['trusted'], + 'process.name': ['sshd.exe'], + 'process.pid': [6368], + 'process.args_count': [2], + 'process.command_line': ['"C:\\Program Files\\OpenSSH-Win64\\sshd.exe" -y'], + 'process.hash.sha1': ['631244d731f406394c17c7dfd85203e317c74814'], + 'process.hash.sha256': [ + 'e6a972f9db27de18be225095b3b3141b945be8aadc4014c8704ae5acafe3e8e0', + ], + 'process.hash.md5': ['331ba0e529810ef718dd3efbd1242302'], + 'process.executable': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293863], + 'event.ingested': ['2021-02-08T21:57:26.417387865Z'], + 'event.created': ['2021-02-08T21:50:28.3446355Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['log_on'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/K'], + 'event.category': ['authentication', 'session'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.security'], + 'event.outcome': ['success'], + 'user.domain': [''], + 'user.name': [''], }, }, ], @@ -508,274 +372,195 @@ export const sequenceResponse = { { _index: '.ds-logs-endpoint.events.security-default-2021.02.05-000005', _id: 'rBymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - }, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', - executable: 'C:\\Windows\\System32\\lsass.exe', - }, - message: 'Endpoint security event', - '@timestamp': '2021-02-08T21:50:28.3381013Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.security', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293868, - ingested: '2021-02-08T21:57:26.417632166Z', - created: '2021-02-08T21:50:28.3381013Z', - kind: 'event', - module: 'endpoint', - id: 'LzzWB9jjGmCwGMvk++++FG/Q', - category: [], - type: [], - dataset: 'endpoint.events.security', - }, - user: { - domain: 'NT AUTHORITY', - name: 'SYSTEM', - }, + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3381013Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', + ], + 'process.executable': ['C:\\Windows\\System32\\lsass.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293868], + 'event.ingested': ['2021-02-08T21:57:26.417632166Z'], + 'event.created': ['2021-02-08T21:50:28.3381013Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/Q'], + 'event.category': [], + 'event.type': [], + 'event.dataset': ['endpoint.events.security'], + 'user.domain': ['NT AUTHORITY'], + 'user.name': ['SYSTEM'], }, }, { _index: '.ds-logs-endpoint.events.process-default-2021.02.02-000005', _id: 'pxymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTIzODAtMTMyNTUwNzg2ODkuOTY1Nzg1NTAw', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU2OC0xMzI1NTA3ODY2Ny4zMjk3MDY2MDA=', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - code_signature: [ - { - trusted: true, - subject_name: 'Microsoft Corporation', - exists: true, - status: 'trusted', - }, - ], - token: { - integrity_level_name: 'high', - elevation_level: 'default', - }, - }, - args: ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe', '-y'], - parent: { - args: ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe', '-R'], - name: 'sshd.exe', - pid: 5284, - args_count: 2, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', - command_line: '"C:\\Program Files\\OpenSSH-Win64\\sshd.exe" -R', - executable: 'C:\\Program Files\\OpenSSH-Win64\\sshd.exe', - }, - code_signature: { + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3446355Z'], + message: ['Endpoint process event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTIzODAtMTMyNTUwNzg2ODkuOTY1Nzg1NTAw', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU2OC0xMzI1NTA3ODY2Ny4zMjk3MDY2MDA=', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.Ext.code_signature': [ + { trusted: true, subject_name: 'Microsoft Corporation', exists: true, status: 'trusted', }, - name: 'sshd.exe', - pid: 6368, - args_count: 2, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTYzNjgtMTMyNTcyOTQ2MjguMzQ0NjM1NTAw', - command_line: '"C:\\Program Files\\OpenSSH-Win64\\sshd.exe" -y', - executable: 'C:\\Program Files\\OpenSSH-Win64\\sshd.exe', - hash: { - sha1: '631244d731f406394c17c7dfd85203e317c74814', - sha256: 'e6a972f9db27de18be225095b3b3141b945be8aadc4014c8704ae5acafe3e8e0', - md5: '331ba0e529810ef718dd3efbd1242302', - }, - }, - message: 'Endpoint process event', - '@timestamp': '2021-02-08T21:50:28.3446355Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.process', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293863, - ingested: '2021-02-08T21:57:26.417387865Z', - created: '2021-02-08T21:50:28.3446355Z', - kind: 'event', - module: 'endpoint', - action: 'start', - id: 'LzzWB9jjGmCwGMvk++++FG/K', - category: ['process'], - type: ['start'], - dataset: 'endpoint.events.process', - }, - user: { - domain: '', - name: '', - }, + ], + 'process.Ext.token.integrity_level_name': ['high'], + 'process.Ext.token.elevation_level': ['default'], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTYzNjgtMTMyNTcyOTQ2MjguMzQ0NjM1NTAw', + ], + 'process.args': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe', '-y'], + 'process.parent.args': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe', '-R'], + 'process.parent.name': ['sshd.exe'], + 'process.parent.pid': [5284], + 'process.parent.args_count': [2], + 'process.parent.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', + ], + 'process.parent.command_line': [ + '"C:\\Program Files\\OpenSSH-Win64\\sshd.exe" -R', + ], + 'process.parent.executable': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'], + 'process.code_signature.trusted': [true], + 'process.code_signature.subject_name': ['Microsoft Corporation'], + 'process.code_signature.exists': [true], + 'process.code_signature.status': ['trusted'], + 'process.name': ['sshd.exe'], + 'process.pid': [6368], + 'process.args_count': [2], + 'process.command_line': ['"C:\\Program Files\\OpenSSH-Win64\\sshd.exe" -y'], + 'process.hash.sha1': ['631244d731f406394c17c7dfd85203e317c74814'], + 'process.hash.sha256': [ + 'e6a972f9db27de18be225095b3b3141b945be8aadc4014c8704ae5acafe3e8e0', + ], + 'process.hash.md5': ['331ba0e529810ef718dd3efbd1242302'], + 'process.executable': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.process'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293863], + 'event.ingested': ['2021-02-08T21:57:26.417387865Z'], + 'event.created': ['2021-02-08T21:50:28.3446355Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['start'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/K'], + 'event.category': ['process'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.process'], + 'user.domain': [''], + 'user.name': [''], }, }, { _index: '.ds-logs-endpoint.events.network-default-2021.02.02-000005', _id: 'qBymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU2OC0xMzI1NTA3ODY2Ny4zMjk3MDY2MDA=', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - }, - name: 'svchost.exe', - pid: 968, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTk2OC0xMzI1NTA3ODY3My4yNjQyNDcyMDA=', - executable: 'C:\\Windows\\System32\\svchost.exe', - }, - destination: { - address: '10.128.0.57', - port: 3389, - bytes: 1681, - ip: '10.128.0.57', - }, - source: { - address: '142.202.189.139', - port: 16151, - bytes: 1224, - ip: '142.202.189.139', - }, - message: 'Endpoint network event', - network: { - transport: 'tcp', - type: 'ipv4', - direction: 'incoming', - }, - '@timestamp': '2021-02-08T21:50:28.5553532Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.network', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293864, - ingested: '2021-02-08T21:57:26.417451347Z', - created: '2021-02-08T21:50:28.5553532Z', - kind: 'event', - module: 'endpoint', - action: 'disconnect_received', - id: 'LzzWB9jjGmCwGMvk++++FG/L', - category: ['network'], - type: ['end'], - dataset: 'endpoint.events.network', - }, - user: { - domain: 'NT AUTHORITY', - name: 'NETWORK SERVICE', - }, + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.5553532Z'], + message: ['Endpoint network event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU2OC0xMzI1NTA3ODY2Ny4zMjk3MDY2MDA=', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.name': ['svchost.exe'], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTk2OC0xMzI1NTA3ODY3My4yNjQyNDcyMDA=', + ], + 'process.executable': ['C:\\Windows\\System32\\svchost.exe'], + 'ecs.version': ['1.5.0'], + 'destination.address': ['10.128.0.57'], + 'destination.port': [3389], + 'destination.bytes': [1681], + 'destination.ip': ['10.128.0.57'], + 'source.address': ['142.202.189.139'], + 'source.port': [16151], + 'source.bytes': [1224], + 'source.ip': ['142.202.189.139'], + 'network.transport': ['tcp'], + 'network.type': ['ipv4'], + 'network.direction': ['incoming'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293864], + 'event.ingested': ['2021-02-08T21:57:26.417451347Z'], + 'event.created': ['2021-02-08T21:50:28.5553532Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['disconnect_received'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/L'], + 'event.category': ['network'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.network'], + 'user.domain': ['NT AUTHORITY'], + 'user.name': ['SYSTEM'], }, }, ], @@ -806,472 +591,334 @@ export const eventsResponse = { { _index: '.ds-logs-endpoint.events.security-default-2021.02.05-000005', _id: 'qhymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTIzODAtMTMyNTUwNzg2ODkuOTY1Nzg1NTAw', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU2OC0xMzI1NTA3ODY2Ny4zMjk3MDY2MDA=', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - }, - name: 'C:\\Program Files\\OpenSSH-Win64\\sshd.exe', - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', - executable: 'C:\\Program Files\\OpenSSH-Win64\\sshd.exe', - }, - message: 'Endpoint security event', - '@timestamp': '2021-02-08T21:50:28.3377092Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.security', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293866, - ingested: '2021-02-08T21:57:26.417559711Z', - created: '2021-02-08T21:50:28.3377092Z', - kind: 'event', - module: 'endpoint', - action: 'log_on', - id: 'LzzWB9jjGmCwGMvk++++FG/O', - category: ['authentication', 'session'], - type: ['start'], - dataset: 'endpoint.events.security', - outcome: 'success', - }, - user: { - domain: 'NT AUTHORITY', - name: 'SYSTEM', - }, + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3377092Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTIzODAtMTMyNTUwNzg2ODkuOTY1Nzg1NTAw', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU2OC0xMzI1NTA3ODY2Ny4zMjk3MDY2MDA=', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.name': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', + ], + 'process.executable': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293866], + 'event.ingested': ['2021-02-08T21:57:26.417559711Z'], + 'event.created': ['2021-02-08T21:50:28.3377092Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['log_on'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/O'], + 'event.category': ['authentication', 'session'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.security'], + 'event.outcome': ['success'], + 'user.domain': ['NT AUTHORITY'], + 'user.name': ['SYSTEM'], }, }, { _index: '.ds-logs-endpoint.events.security-default-2021.02.05-000005', _id: 'qxymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - }, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', - executable: 'C:\\Windows\\System32\\lsass.exe', - }, - message: 'Endpoint security event', - '@timestamp': '2021-02-08T21:50:28.3377142Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.security', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293867, - ingested: '2021-02-08T21:57:26.417596906Z', - created: '2021-02-08T21:50:28.3377142Z', - kind: 'event', - module: 'endpoint', - action: 'log_on', - id: 'LzzWB9jjGmCwGMvk++++FG/P', - category: ['authentication', 'session'], - type: ['start'], - dataset: 'endpoint.events.security', - outcome: 'success', - }, - user: { - domain: 'NT AUTHORITY', - name: 'SYSTEM', - }, + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3377142Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', + ], + 'process.executable': ['C:\\Windows\\System32\\lsass.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293867], + 'event.ingested': ['2021-02-08T21:57:26.417596906Z'], + 'event.created': ['2021-02-08T21:50:28.3377142Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['log_on'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/P'], + 'event.category': ['authentication', 'session'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.security'], + 'event.outcome': ['success'], + 'user.domain': ['NT AUTHORITY'], + 'user.name': ['SYSTEM'], }, }, { _index: '.ds-logs-endpoint.events.security-default-2021.02.05-000005', _id: 'rBymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - }, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', - executable: 'C:\\Windows\\System32\\lsass.exe', - }, - message: 'Endpoint security event', - '@timestamp': '2021-02-08T21:50:28.3381013Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.security', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293868, - ingested: '2021-02-08T21:57:26.417632166Z', - created: '2021-02-08T21:50:28.3381013Z', - kind: 'event', - module: 'endpoint', - id: 'LzzWB9jjGmCwGMvk++++FG/Q', - category: [], - type: [], - dataset: 'endpoint.events.security', - }, - user: { - domain: 'NT AUTHORITY', - name: 'SYSTEM', - }, + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3381013Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', + ], + 'process.executable': ['C:\\Windows\\System32\\lsass.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293868], + 'event.ingested': ['2021-02-08T21:57:26.417632166Z'], + 'event.created': ['2021-02-08T21:50:28.3381013Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['log_on'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/Q'], + 'event.category': ['authentication', 'session'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.security'], + 'event.outcome': ['success'], + 'user.domain': ['NT AUTHORITY'], + 'user.name': ['SYSTEM'], }, }, { _index: '.ds-logs-endpoint.events.security-default-2021.02.05-000005', _id: 'qxymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - }, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', - executable: 'C:\\Windows\\System32\\lsass.exe', - }, - message: 'Endpoint security event', - '@timestamp': '2021-02-08T21:50:28.3377142Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.security', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293867, - ingested: '2021-02-08T21:57:26.417596906Z', - created: '2021-02-08T21:50:28.3377142Z', - kind: 'event', - module: 'endpoint', - action: 'log_on', - id: 'LzzWB9jjGmCwGMvk++++FG/P', - category: ['authentication', 'session'], - type: ['start'], - dataset: 'endpoint.events.security', - outcome: 'success', - }, - user: { - domain: 'NT AUTHORITY', - name: 'SYSTEM', - }, + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3377142Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', + ], + 'process.executable': ['C:\\Windows\\System32\\lsass.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293867], + 'event.ingested': ['2021-02-08T21:57:26.417596906Z'], + 'event.created': ['2021-02-08T21:50:28.3377142Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['log_on'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/P'], + 'event.category': ['authentication', 'session'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.security'], + 'event.outcome': ['success'], + 'user.domain': ['NT AUTHORITY'], + 'user.name': ['SYSTEM'], }, }, { _index: '.ds-logs-endpoint.events.security-default-2021.02.05-000005', _id: 'rBymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - }, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', - executable: 'C:\\Windows\\System32\\lsass.exe', - }, - message: 'Endpoint security event', - '@timestamp': '2021-02-08T21:50:28.3381013Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.security', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293868, - ingested: '2021-02-08T21:57:26.417632166Z', - created: '2021-02-08T21:50:28.3381013Z', - kind: 'event', - module: 'endpoint', - id: 'LzzWB9jjGmCwGMvk++++FG/Q', - category: [], - type: [], - dataset: 'endpoint.events.security', - }, - user: { - domain: 'NT AUTHORITY', - name: 'SYSTEM', - }, + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3381013Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU4MC0xMzI1NTA3ODY2Ny45MTg5Njc1MDA=', + ], + 'process.executable': ['C:\\Windows\\System32\\lsass.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293868], + 'event.ingested': ['2021-02-08T21:57:26.417632166Z'], + 'event.created': ['2021-02-08T21:50:28.3381013Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['log_on'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/Q'], + 'event.category': ['authentication', 'session'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.security'], + 'event.outcome': ['success'], + 'user.domain': ['NT AUTHORITY'], + 'user.name': ['SYSTEM'], }, }, { _index: '.ds-logs-endpoint.events.process-default-2021.02.02-000005', _id: 'pxymg3cBX5UUcOOYP3Ec', - _source: { - agent: { - id: '1d15cf9e-3dc7-5b97-f586-743f7c2518b2', - type: 'endpoint', - version: '7.10.0', - }, - process: { - Ext: { - ancestry: [ - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTIzODAtMTMyNTUwNzg2ODkuOTY1Nzg1NTAw', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU2OC0xMzI1NTA3ODY2Ny4zMjk3MDY2MDA=', - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', - ], - code_signature: [ - { - trusted: true, - subject_name: 'Microsoft Corporation', - exists: true, - status: 'trusted', - }, - ], - token: { - integrity_level_name: 'high', - elevation_level: 'default', - }, - }, - args: ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe', '-y'], - parent: { - args: ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe', '-R'], - name: 'sshd.exe', - pid: 5284, - args_count: 2, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', - command_line: '"C:\\Program Files\\OpenSSH-Win64\\sshd.exe" -R', - executable: 'C:\\Program Files\\OpenSSH-Win64\\sshd.exe', - }, - code_signature: { + fields: { + 'agent.id': ['1d15cf9e-3dc7-5b97-f586-743f7c2518b2'], + 'agent.type': ['endpoint'], + 'agent.version': ['7.10.0'], + '@timestamp': ['2021-02-08T21:50:28.3446355Z'], + message: ['Endpoint security event'], + 'process.Ext.ancestry': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTIzODAtMTMyNTUwNzg2ODkuOTY1Nzg1NTAw', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTU2OC0xMzI1NTA3ODY2Ny4zMjk3MDY2MDA=', + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTQ2OC0xMzI1NTA3ODY2NS42Mzg5MzY1MDA=', + ], + 'process.Ext.code_signature': [ + { trusted: true, subject_name: 'Microsoft Corporation', exists: true, status: 'trusted', }, - name: 'sshd.exe', - pid: 6368, - args_count: 2, - entity_id: - 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTYzNjgtMTMyNTcyOTQ2MjguMzQ0NjM1NTAw', - command_line: '"C:\\Program Files\\OpenSSH-Win64\\sshd.exe" -y', - executable: 'C:\\Program Files\\OpenSSH-Win64\\sshd.exe', - hash: { - sha1: '631244d731f406394c17c7dfd85203e317c74814', - sha256: 'e6a972f9db27de18be225095b3b3141b945be8aadc4014c8704ae5acafe3e8e0', - md5: '331ba0e529810ef718dd3efbd1242302', - }, - }, - message: 'Endpoint process event', - '@timestamp': '2021-02-08T21:50:28.3446355Z', - ecs: { - version: '1.5.0', - }, - data_stream: { - namespace: 'default', - type: 'logs', - dataset: 'endpoint.events.process', - }, - elastic: { - agent: { - id: 'f5dec71e-438c-424e-ac9b-0281f10412b9', - }, - }, - host: { - hostname: 'win2019-endpoint-mr-pedro', - os: { - Ext: { - variant: 'Windows Server 2019 Datacenter', - }, - kernel: '1809 (10.0.17763.1697)', - name: 'Windows', - family: 'windows', - version: '1809 (10.0.17763.1697)', - platform: 'windows', - full: 'Windows Server 2019 Datacenter 1809 (10.0.17763.1697)', - }, - ip: ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], - name: 'win2019-endpoint-mr-pedro', - id: 'd8ad572e-d224-4044-a57d-f5a84c0dfe5d', - mac: ['42:01:0a:80:00:39'], - architecture: 'x86_64', - }, - event: { - sequence: 3293863, - ingested: '2021-02-08T21:57:26.417387865Z', - created: '2021-02-08T21:50:28.3446355Z', - kind: 'event', - module: 'endpoint', - action: 'start', - id: 'LzzWB9jjGmCwGMvk++++FG/K', - category: ['process'], - type: ['start'], - dataset: 'endpoint.events.process', - }, - user: { - domain: '', - name: '', - }, + ], + 'process.Ext.token.integrity_level_name': ['high'], + 'process.Ext.token.elevation_level': ['default'], + 'process.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTYzNjgtMTMyNTcyOTQ2MjguMzQ0NjM1NTAw', + ], + 'process.args': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe', '-y'], + 'process.parent.args': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe', '-R'], + 'process.parent.name': ['sshd.exe'], + 'process.parent.pid': [5284], + 'process.parent.args_count': [2], + 'process.parent.entity_id': [ + 'MWQxNWNmOWUtM2RjNy01Yjk3LWY1ODYtNzQzZjdjMjUxOGIyLTUyODQtMTMyNTcyOTQ2MjMuOTk2NTkxMDAw', + ], + 'process.parent.command_line': ['"C:\\Program Files\\OpenSSH-Win64\\sshd.exe" -R'], + 'process.parent.executable': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'], + 'process.code_signature.trusted': [true], + 'process.code_signature.subject_name': ['Microsoft Corporation'], + 'process.code_signature.exists': [true], + 'process.code_signature.status': ['trusted'], + 'process.name': ['sshd.exe'], + 'process.pid': [6368], + 'process.args_count': [2], + 'process.command_line': ['"C:\\Program Files\\OpenSSH-Win64\\sshd.exe" -y'], + 'process.hash.sha1': ['631244d731f406394c17c7dfd85203e317c74814'], + 'process.hash.sha256': [ + 'e6a972f9db27de18be225095b3b3141b945be8aadc4014c8704ae5acafe3e8e0', + ], + 'process.hash.md5': ['331ba0e529810ef718dd3efbd1242302'], + 'process.executable': ['C:\\Program Files\\OpenSSH-Win64\\sshd.exe'], + 'ecs.version': ['1.5.0'], + 'data_stream.namespace': ['default'], + 'data_stream.type': ['logs'], + 'data_stream.dataset': ['endpoint.events.security'], + 'elastic.agent.id': ['f5dec71e-438c-424e-ac9b-0281f10412b9'], + 'host.hostname': ['win2019-endpoint-mr-pedro'], + 'host.os.Ext.variant': ['Windows Server 2019 Datacenter'], + 'host.os.name': ['Windows'], + 'host.os.kernel': ['1809 (10.0.17763.1697)'], + 'host.os.family': ['windows'], + 'host.os.version': ['1809 (10.0.17763.1697)'], + 'host.os.platform': ['windows'], + 'host.os.full': ['Windows Server 2019 Datacenter 1809 (10.0.17763.1697)'], + 'host.ip': ['10.128.0.57', 'fe80::9ced:8f1c:880b:3e1f', '127.0.0.1', '::1'], + 'host.name': ['win2019-endpoint-mr-pedro'], + 'host.id': ['d8ad572e-d224-4044-a57d-f5a84c0dfe5d'], + 'host.mac': ['42:01:0a:80:00:39'], + 'host.architecture': ['x86_64'], + 'event.sequence': [3293863], + 'event.ingested': ['2021-02-08T21:57:26.417387865Z'], + 'event.created': ['2021-02-08T21:50:28.3446355Z'], + 'event.kind': ['event'], + 'event.module': ['endpoint'], + 'event.action': ['log_on'], + 'event.id': ['LzzWB9jjGmCwGMvk++++FG/K'], + 'event.category': ['authentication', 'session'], + 'event.type': ['start'], + 'event.dataset': ['endpoint.events.security'], + 'event.outcome': ['success'], + 'user.domain': [''], + 'user.name': [''], }, }, ], diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/eql/helpers.test.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/eql/helpers.test.ts index 10a4fae0a036d..76aba9b9c62cc 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/eql/helpers.test.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/eql/helpers.test.ts @@ -52,6 +52,16 @@ describe('Search Strategy EQL helper', () => { "allow_no_indices": true, "body": Object { "event_category_field": "event.category", + "fields": Array [ + Object { + "field": "*", + "include_unmapped": true, + }, + Object { + "field": "@timestamp", + "format": "strict_date_optional_time", + }, + ], "filter": Object { "bool": Object { "filter": Array [ @@ -106,6 +116,16 @@ describe('Search Strategy EQL helper', () => { "allow_no_indices": true, "body": Object { "event_category_field": "event.super.category", + "fields": Array [ + Object { + "field": "*", + "include_unmapped": true, + }, + Object { + "field": "@timestamp", + "format": "strict_date_optional_time", + }, + ], "filter": Object { "bool": Object { "filter": Array [ diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/eql/helpers.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/eql/helpers.ts index c30a11ef76f1e..5424568c44a70 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/eql/helpers.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/eql/helpers.ts @@ -58,6 +58,13 @@ export const buildEqlDsl = (options: TimelineEqlRequestOptions): Record { + it('should return ip details query if index key is ipDetails', () => { + const defaultIndex = ['.siem-signals-default']; + const query = buildTimelineEventsAllQuery({ + fields: [], + defaultIndex, + filterQuery: '', + language: 'eql', + pagination: { + activePage: 0, + querySize: 100, + }, + runtimeMappings: {}, + sort: [ + { + direction: Direction.asc, + field: '@timestamp', + type: 'datetime', + esTypes: ['date'], + }, + ], + timerange: { + from: '', + interval: '5m', + to: '', + }, + }); + expect(query).toMatchInlineSnapshot(` + Object { + "allow_no_indices": true, + "body": Object { + "_source": false, + "aggregations": Object { + "producers": Object { + "terms": Object { + "exclude": Array [ + "alerts", + ], + "field": "kibana.alert.rule.producer", + }, + }, + }, + "fields": Array [ + "signal.*", + "kibana.alert.*", + Object { + "field": "@timestamp", + "format": "strict_date_optional_time", + }, + ], + "from": 0, + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "match_all": Object {}, + }, + ], + }, + }, + "runtime_mappings": Object {}, + "size": 100, + "sort": Array [ + Object { + "@timestamp": Object { + "order": "asc", + "unmapped_type": "date", + }, + }, + ], + "track_total_hits": true, + }, + "ignore_unavailable": true, + "index": Array [ + ".siem-signals-default", + ], + } + `); + }); +}); diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts index 53009e797e82f..9f6902c65f639 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/all/query.events_all.dsl.ts @@ -20,7 +20,6 @@ import { getPreferredEsType } from './helpers'; export const buildTimelineEventsAllQuery = ({ authFilter, defaultIndex, - docValueFields, fields, filterQuery, pagination: { activePage, querySize }, @@ -69,7 +68,6 @@ export const buildTimelineEventsAllQuery = ({ index: defaultIndex, ignore_unavailable: true, body: { - ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), aggregations: { producers: { terms: { field: ALERT_RULE_PRODUCER, exclude: ['alerts'] }, @@ -85,8 +83,16 @@ export const buildTimelineEventsAllQuery = ({ size: querySize, track_total_hits: true, sort: getSortField(sort), - fields, - _source: ['signal.*', 'kibana.alert.*'], + fields: [ + 'signal.*', + 'kibana.alert.*', + ...fields, + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + ], + _source: false, }, }; diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/index.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/index.ts index e0fe70af27c25..72645b4f5eeae 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/index.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { cloneDeep, merge, unionBy } from 'lodash/fp'; +import { merge } from 'lodash/fp'; import type { IEsSearchResponse } from '@kbn/data-plugin/common'; import { @@ -14,25 +14,22 @@ import { TimelineEventsDetailsStrategyResponse, TimelineEventsDetailsRequestOptions, TimelineEventsDetailsItem, - EventSource, } from '../../../../../../common/search_strategy'; import { inspectStringifyObject } from '../../../../../utils/build_query'; import { TimelineFactory } from '../../types'; import { buildTimelineDetailsQuery } from './query.events_details.dsl'; import { getDataFromFieldsHits, - getDataFromSourceHits, getDataSafety, } from '../../../../../../common/utils/field_formatters'; import { buildEcsObjects } from '../../helpers/build_ecs_objects'; export const timelineEventsDetails: TimelineFactory = { buildDsl: ({ authFilter, ...options }: TimelineEventsDetailsRequestOptions) => { - const { indexName, eventId, docValueFields = [], runtimeMappings = {} } = options; + const { indexName, eventId, runtimeMappings = {} } = options; return buildTimelineDetailsQuery({ indexName, id: eventId, - docValueFields, runtimeMappings, authFilter, }); @@ -41,12 +38,13 @@ export const timelineEventsDetails: TimelineFactory ): Promise => { - const { indexName, eventId, docValueFields = [], runtimeMappings = {} } = options; - const { _source, fields, ...hitsData } = cloneDeep(response.rawResponse.hits.hits[0] ?? {}); + const { indexName, eventId, runtimeMappings = {} } = options; + const { fields, ...hitsData } = response.rawResponse.hits.hits[0] ?? {}; + const inspect = { dsl: [ inspectStringifyObject( - buildTimelineDetailsQuery({ indexName, id: eventId, docValueFields, runtimeMappings }) + buildTimelineDetailsQuery({ indexName, id: eventId, runtimeMappings }) ), ], }; @@ -57,23 +55,18 @@ export const timelineEventsDetails: TimelineFactory( - getDataFromSourceHits, - // @ts-expect-error @elastic/elasticsearch _source is optional - _source - ); + const fieldsData = await getDataSafety( getDataFromFieldsHits, merge(fields, hitsData) ); - const data = unionBy('field', fieldsData, sourceData); const rawEventData = response.rawResponse.hits.hits[0]; const ecs = buildEcsObjects(rawEventData as EventHit); return { ...response, - data, + data: fieldsData, ecs, inspect, rawEventData, diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.test.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.test.ts index f34b54f3029a5..81dd5580b5c24 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.test.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.test.ts @@ -11,17 +11,10 @@ describe('buildTimelineDetailsQuery', () => { it('returns the expected query', () => { const indexName = '.siem-signals-default'; const eventId = 'f0a936d50b5b3a5a193d415459c14587fe633f7e519df7b5dc151d56142680e3'; - const docValueFields = [ - { field: '@timestamp' }, - { field: 'agent.ephemeral_id' }, - { field: 'agent.id' }, - { field: 'agent.name' }, - ]; const query = buildTimelineDetailsQuery({ indexName, id: eventId, - docValueFields, runtimeMappings: {}, }); @@ -29,25 +22,23 @@ describe('buildTimelineDetailsQuery', () => { Object { "allow_no_indices": true, "body": Object { - "_source": true, - "docvalue_fields": Array [ - Object { - "field": "@timestamp", - }, + "_source": false, + "fields": Array [ Object { - "field": "agent.ephemeral_id", + "field": "*", + "include_unmapped": true, }, Object { - "field": "agent.id", + "field": "@timestamp", + "format": "strict_date_optional_time", }, Object { - "field": "agent.name", + "field": "code_signature.timestamp", + "format": "strict_date_optional_time", }, - ], - "fields": Array [ Object { - "field": "*", - "include_unmapped": true, + "field": "dll.code_signature.timestamp", + "format": "strict_date_optional_time", }, ], "query": Object { @@ -58,6 +49,9 @@ describe('buildTimelineDetailsQuery', () => { }, }, "runtime_mappings": Object {}, + "stored_fields": Array [ + "*", + ], }, "ignore_unavailable": true, "index": ".siem-signals-default", diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.ts index 5baa471e5c526..33ec6d02d6a1a 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/details/query.events_details.dsl.ts @@ -7,17 +7,14 @@ import { JsonObject } from '@kbn/utility-types'; import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { DocValueFields } from '../../../../../../common/search_strategy'; export const buildTimelineDetailsQuery = ({ authFilter, - docValueFields, id, indexName, runtimeMappings, }: { authFilter?: JsonObject; - docValueFields: DocValueFields[]; id: string; indexName: string; runtimeMappings: MappingRuntimeFields; @@ -45,12 +42,26 @@ export const buildTimelineDetailsQuery = ({ index: indexName, ignore_unavailable: true, body: { - docvalue_fields: docValueFields, query, - fields: [{ field: '*', include_unmapped: true }], + fields: [ + { field: '*', include_unmapped: true }, + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + { + field: 'code_signature.timestamp', + format: 'strict_date_optional_time', + }, + { + field: 'dll.code_signature.timestamp', + format: 'strict_date_optional_time', + }, + ], // Remove and instead pass index_pattern.id once issue resolved: https://github.com/elastic/kibana/issues/111762 runtime_mappings: runtimeMappings, - _source: true, + stored_fields: ['*'], + _source: false, }, size: 1, }; diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/index.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/index.ts index b83441900a41a..a6425737d6de0 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/index.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/index.ts @@ -28,15 +28,14 @@ export const timelineEventsLastEventTime: TimelineFactory { + it('should return ip details query if index key is ipDetails', () => { + const defaultIndex = ['.siem-signals-default']; + const query = buildLastEventTimeQuery({ + indexKey: LastEventIndexKey.ipDetails, + details: { ip: '12345567' }, + defaultIndex, + }); + expect(query).toMatchInlineSnapshot(` + Object { + "allow_no_indices": true, + "body": Object { + "_source": false, + "fields": Array [ + Object { + "field": "@timestamp", + "format": "strict_date_optional_time", + }, + ], + "query": Object { + "bool": Object { + "filter": Object { + "bool": Object { + "should": Array [ + Object { + "term": Object { + "source.ip": "12345567", + }, + }, + Object { + "term": Object { + "destination.ip": "12345567", + }, + }, + ], + }, + }, + }, + }, + "size": 1, + "sort": Array [ + Object { + "@timestamp": Object { + "order": "desc", + }, + }, + ], + }, + "ignore_unavailable": true, + "index": Array [ + ".siem-signals-default", + ], + "track_total_hits": false, + } + `); + }); +}); diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/query.events_last_event_time.dsl.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/query.events_last_event_time.dsl.ts index 4f50ef11ff4f8..24bd1aa6b9971 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/query.events_last_event_time.dsl.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/events/last_event_time/query.events_last_event_time.dsl.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { isEmpty } from 'lodash/fp'; import type { ISearchRequestParams } from '@kbn/data-plugin/common'; import { TimelineEventsLastEventTimeRequestOptions, @@ -22,7 +21,6 @@ export const buildLastEventTimeQuery = ({ indexKey, details, defaultIndex, - docValueFields, }: TimelineEventsLastEventTimeRequestOptions) => { const indicesToQuery: EventIndices = { hosts: defaultIndex, @@ -44,9 +42,14 @@ export const buildLastEventTimeQuery = ({ ignore_unavailable: true, track_total_hits: false, body: { - ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), query: { bool: { filter: { bool: { should: getIpDetailsFilter(details.ip) } } } }, - _source: ['@timestamp'], + _source: false, + fields: [ + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + ], size: 1, sort: [ { @@ -67,9 +70,14 @@ export const buildLastEventTimeQuery = ({ ignore_unavailable: true, track_total_hits: false, body: { - ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), query: { bool: { filter: getHostDetailsFilter(details.hostName) } }, - _source: ['@timestamp'], + _source: false, + fields: [ + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + ], size: 1, sort: [ { @@ -90,9 +98,14 @@ export const buildLastEventTimeQuery = ({ ignore_unavailable: true, track_total_hits: false, body: { - ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), query: { bool: { filter: getUserDetailsFilter(details.userName) } }, - _source: ['@timestamp'], + _source: false, + fields: [ + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + ], size: 1, sort: [ { @@ -114,9 +127,14 @@ export const buildLastEventTimeQuery = ({ ignore_unavailable: true, track_total_hits: false, body: { - ...(!isEmpty(docValueFields) ? { docvalue_fields: docValueFields } : {}), query: { match_all: {} }, - _source: ['@timestamp'], + _source: false, + fields: [ + { + field: '@timestamp', + format: 'strict_date_optional_time', + }, + ], size: 1, sort: [ { diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_ecs_objects.test.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_ecs_objects.test.ts index 0545b243aabe4..0ad98c3771014 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_ecs_objects.test.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_ecs_objects.test.ts @@ -15,16 +15,6 @@ describe('buildEcsObjects', () => { _index: '.test-index', _id: 'test-id', _score: 0, - _source: { - '@timestamp': 123456, - host: { - architecture: 'windows98', - hostname: 'test-name', - id: 'some-id', - ip: [], - name: 'test-name', - }, - }, fields: { '@timestamp': [123456], 'host.architecture': ['windows98'], diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_ecs_objects.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_ecs_objects.ts index 866a52ca41e12..621fa656cb406 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_ecs_objects.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_ecs_objects.ts @@ -10,7 +10,7 @@ import { EventHit } from '../../../../../common/search_strategy'; import { ECS_METADATA_FIELDS, TIMELINE_EVENTS_FIELDS } from './constants'; import { Ecs } from '../../../../../common/ecs'; import { getTimestamp } from './get_timestamp'; -import { buildObjectForFieldPath } from './build_object_for_field_path'; +import { buildObjectRecursive } from './build_object_recursive'; import { getNestedParentPath } from './get_nested_parent_path'; export const buildEcsObjects = (hit: EventHit): Ecs => { @@ -20,11 +20,10 @@ export const buildEcsObjects = (hit: EventHit): Ecs => { const nestedParentPath = getNestedParentPath(field, hit.fields); if ( nestedParentPath != null || - has(field, hit._source) || has(field, hit.fields) || ECS_METADATA_FIELDS.includes(field) ) { - return merge(acc, buildObjectForFieldPath(field, hit)); + return merge(acc, buildObjectRecursive(field, hit.fields)); } return acc; }, diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_object_for_field_path.test.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_object_recursive.test.ts similarity index 82% rename from x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_object_for_field_path.test.ts rename to x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_object_recursive.test.ts index f7e4c1bfbaaf6..3d05a42f527b4 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_object_for_field_path.test.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_object_recursive.test.ts @@ -7,11 +7,11 @@ import { eventHit } from '@kbn/securitysolution-t-grid'; import { EventHit } from '../../../../../common/search_strategy'; -import { buildObjectForFieldPath } from './build_object_for_field_path'; +import { buildObjectRecursive } from './build_object_recursive'; -describe('buildObjectForFieldPath', () => { +describe('buildObjectRecursive', () => { it('builds an object from a single non-nested field', () => { - expect(buildObjectForFieldPath('@timestamp', eventHit)).toEqual({ + expect(buildObjectRecursive('@timestamp', eventHit.fields)).toEqual({ '@timestamp': ['2020-11-17T14:48:08.922Z'], }); }); @@ -19,7 +19,7 @@ describe('buildObjectForFieldPath', () => { it('builds an object with no fields response', () => { const { fields, ...fieldLessHit } = eventHit; // @ts-expect-error fieldLessHit is intentionally missing fields - expect(buildObjectForFieldPath('@timestamp', fieldLessHit)).toEqual({ + expect(buildObjectRecursive('@timestamp', fieldLessHit)).toEqual({ '@timestamp': [], }); }); @@ -33,7 +33,7 @@ describe('buildObjectForFieldPath', () => { }, }; - expect(buildObjectForFieldPath('foo.barBaz', hit)).toEqual({ + expect(buildObjectRecursive('foo.barBaz', hit.fields)).toEqual({ foo: { barBaz: ['foo'] }, }); }); @@ -45,7 +45,7 @@ describe('buildObjectForFieldPath', () => { foo: [{ bar: ['baz'] }], }, }; - expect(buildObjectForFieldPath('foo.bar', hit)).toEqual({ + expect(buildObjectRecursive('foo.bar', hit.fields)).toEqual({ foo: [{ bar: ['baz'] }], }); }); @@ -61,7 +61,7 @@ describe('buildObjectForFieldPath', () => { ], }, }; - expect(buildObjectForFieldPath('foo.bar.baz', nestedHit)).toEqual({ + expect(buildObjectRecursive('foo.bar.baz', nestedHit.fields)).toEqual({ foo: { bar: [ { @@ -73,7 +73,7 @@ describe('buildObjectForFieldPath', () => { }); it('builds intermediate objects at multiple levels', () => { - expect(buildObjectForFieldPath('threat.enrichments.matched.atomic', eventHit)).toEqual({ + expect(buildObjectRecursive('threat.enrichments.matched.atomic', eventHit.fields)).toEqual({ threat: { enrichments: [ { @@ -92,7 +92,7 @@ describe('buildObjectForFieldPath', () => { }); it('preserves multiple values for a single leaf', () => { - expect(buildObjectForFieldPath('threat.enrichments.matched.field', eventHit)).toEqual({ + expect(buildObjectRecursive('threat.enrichments.matched.field', eventHit.fields)).toEqual({ threat: { enrichments: [ { @@ -136,7 +136,7 @@ describe('buildObjectForFieldPath', () => { }); it('includes objects without the field', () => { - expect(buildObjectForFieldPath('nested_1.foo.nested_2.bar.leaf', nestedHit)).toEqual({ + expect(buildObjectRecursive('nested_1.foo.nested_2.bar.leaf', nestedHit.fields)).toEqual({ nested_1: { foo: [ { @@ -155,7 +155,7 @@ describe('buildObjectForFieldPath', () => { }); it('groups multiple leaf values', () => { - expect(buildObjectForFieldPath('nested_1.foo.nested_2.bar.leaf_2', nestedHit)).toEqual({ + expect(buildObjectRecursive('nested_1.foo.nested_2.bar.leaf_2', nestedHit.fields)).toEqual({ nested_1: { foo: [ { diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_object_for_field_path.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_object_recursive.ts similarity index 65% rename from x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_object_for_field_path.ts rename to x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_object_recursive.ts index b27b7029887db..c02e8eb2f4487 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_object_for_field_path.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/build_object_recursive.ts @@ -6,13 +6,13 @@ */ import { set } from '@elastic/safer-lodash-set'; -import { get, has } from 'lodash/fp'; +import { get } from 'lodash/fp'; import { Ecs } from '../../../../../common/ecs'; -import { EventHit, Fields } from '../../../../../common/search_strategy'; +import { Fields } from '../../../../../common/search_strategy'; import { toStringArray } from '../../../../../common/utils/to_array'; import { getNestedParentPath } from './get_nested_parent_path'; -const buildObjectRecursive = (fieldPath: string, fields: Fields): Partial => { +export const buildObjectRecursive = (fieldPath: string, fields: Fields): Partial => { const nestedParentPath = getNestedParentPath(fieldPath, fields); if (!nestedParentPath) { return set({}, fieldPath, toStringArray(get(fieldPath, fields))); @@ -26,12 +26,3 @@ const buildObjectRecursive = (fieldPath: string, fields: Fields): Partial = subFields.map((subField) => buildObjectRecursive(subPath, subField)) ); }; - -export const buildObjectForFieldPath = (fieldPath: string, hit: EventHit): Partial => { - if (has(fieldPath, hit._source)) { - const value = get(fieldPath, hit._source); - return set({}, fieldPath, toStringArray(value)); - } - - return buildObjectRecursive(fieldPath, hit.fields); -}; diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/constants.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/constants.ts index 068b52b8cd821..fe07732e4731d 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/constants.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/constants.ts @@ -6,9 +6,7 @@ */ import { ALERT_RULE_CONSUMER, ALERT_RISK_SCORE, ALERT_SEVERITY } from '@kbn/rule-data-utils'; - -// TODO: share with security_solution/common/cti/constants.ts -export const ENRICHMENT_DESTINATION_PATH = 'threat.enrichments'; +import { ENRICHMENT_DESTINATION_PATH } from '../../../../../common/constants'; export const MATCHED_ATOMIC = 'matched.atomic'; export const MATCHED_FIELD = 'matched.field'; diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/format_timeline_data.test.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/format_timeline_data.test.ts index 4c850665a903b..746b71198ee66 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/format_timeline_data.test.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/format_timeline_data.test.ts @@ -132,70 +132,7 @@ describe('formatTimelineData', () => { _index: '.siem-signals-patrykkopycinski-default-000007', _id: 'a77040f198355793c35bf22b900902371309be615381f0a2ec92c208b6132562', _score: 0, - _source: { - kibana: { - alert: { - threshold_result: { - count: 10000, - value: '2a990c11-f61b-4c8e-b210-da2574e9f9db', - }, - depth: 1, - _meta: { - version: 14, - }, - severity: 'low', - risk_score: 21, - rule: { - note: null, - throttle: null, - references: [], - description: 'asdasd', - created_at: '2021-01-09T11:25:45.046Z', - building_block_type: null, - type: 'threshold', - rule_name_override: null, - enabled: true, - exceptions_list: [], - updated_at: '2021-01-09T13:36:39.204Z', - timestamp_override: null, - from: 'now-360s', - uuid: '696c24e0-526d-11eb-836c-e1620268b945', - timeline_id: null, - max_signals: 100, - author: [], - created_by: 'patryk_test_user', - version: 1, - tags: [], - rule_id: '2a990c11-f61b-4c8e-b210-da2574e9f9db', - license: '', - immutable: false, - timeline_title: null, - meta: { - from: '1m', - kibana_siem_app_url: 'http://localhost:5601/app/security', - }, - name: 'Threshold test', - updated_by: 'patryk_test_user', - interval: '5m', - false_positives: [], - to: 'now', - threat: [], - actions: [], - }, - original_time: '2021-01-09T13:39:32.595Z', - ancestors: [ - { - depth: 0, - index: - 'apm-*-transaction*,traces-apm*,auditbeat-*,endgame-*,filebeat-*,logs-*,packetbeat-*,winlogbeat-*', - id: '0268af90-d8da-576a-9747-2a191519416a', - type: 'event', - }, - ], - workflow_status: 'open', - }, - }, - }, + _source: {}, fields: { 'kibana.alert.rule.from': ['now-360s'], '@timestamp': ['2021-01-09T13:41:40.517Z'], @@ -209,6 +146,17 @@ describe('formatTimelineData', () => { 'kibana.alert.rule.version': ['1'], 'kibana.alert.rule.name': ['Threshold test'], 'kibana.alert.rule.to': ['now'], + 'kibana.alert.rule.building_block_type': [], + 'kibana.alert.rule.note': [], + 'kibana.alert.rule.timeline_id': [], + 'kibana.alert.rule.timeline_title': [], + 'kibana.alert.rule.exceptions_list': [], + 'kibana.alert.threshold_result': [ + { + count: 10000, + value: '2a990c11-f61b-4c8e-b210-da2574e9f9db', + }, + ], }, sort: ['1610199700517'], }; diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/format_timeline_data.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/format_timeline_data.ts index 1650dab073a4d..e2ac37c12b669 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/format_timeline_data.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/format_timeline_data.ts @@ -11,7 +11,7 @@ import { toStringArray } from '../../../../../common/utils/to_array'; import { getDataFromFieldsHits, getDataSafety } from '../../../../../common/utils/field_formatters'; import { getTimestamp } from './get_timestamp'; import { getNestedParentPath } from './get_nested_parent_path'; -import { buildObjectForFieldPath } from './build_object_for_field_path'; +import { buildObjectRecursive } from './build_object_recursive'; import { ECS_METADATA_FIELDS } from './constants'; export const formatTimelineData = async ( @@ -59,20 +59,15 @@ const getValuesFromFields = async ( } let fieldToEval; - if (has(fieldName, hit._source)) { + + if (nestedParentFieldName == null) { fieldToEval = { - [fieldName]: get(fieldName, hit._source), + [fieldName]: hit.fields[fieldName], }; } else { - if (nestedParentFieldName == null) { - fieldToEval = { - [fieldName]: hit.fields[fieldName], - }; - } else { - fieldToEval = { - [nestedParentFieldName]: hit.fields[nestedParentFieldName], - }; - } + fieldToEval = { + [nestedParentFieldName]: hit.fields[nestedParentFieldName], + }; } const formattedData = await getDataSafety(getDataFromFieldsHits, fieldToEval); return formattedData.reduce( @@ -94,7 +89,6 @@ const mergeTimelineFieldsWithHit = async ( const nestedParentPath = getNestedParentPath(fieldName, hit.fields); if ( nestedParentPath != null || - has(fieldName, hit._source) || has(fieldName, hit.fields) || ECS_METADATA_FIELDS.includes(fieldName) ) { @@ -110,7 +104,7 @@ const mergeTimelineFieldsWithHit = async ( ecs: ecsFields.includes(fieldName) ? { ...get('node.ecs', flattenedFields), - ...buildObjectForFieldPath(fieldName, hit), + ...buildObjectRecursive(fieldName, hit.fields), } : get('node.ecs', flattenedFields), }, diff --git a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/get_timestamp.ts b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/get_timestamp.ts index c278b7efd64d1..bccb5b377edb5 100644 --- a/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/get_timestamp.ts +++ b/x-pack/plugins/timelines/server/search_strategy/timeline/factory/helpers/get_timestamp.ts @@ -10,8 +10,6 @@ import { EventHit } from '../../../../../common/search_strategy'; export const getTimestamp = (hit: EventHit): string => { if (hit.fields && hit.fields['@timestamp']) { return `${hit.fields['@timestamp'][0] ?? ''}`; - } else if (hit._source && hit._source['@timestamp']) { - return hit._source['@timestamp']; } return ''; }; diff --git a/x-pack/test/api_integration/apis/security_solution/timeline_details.ts b/x-pack/test/api_integration/apis/security_solution/timeline_details.ts index 830cb9d84b71b..6358d9dc3104c 100644 --- a/x-pack/test/api_integration/apis/security_solution/timeline_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/timeline_details.ts @@ -387,20 +387,6 @@ const EXPECTED_DATA = [ originalValue: ['{"lon":-122.3341,"lat":47.6103}'], isObjectArray: true, }, - { - category: 'source', - field: 'source.geo.location.lat', - values: ['47.6103'], - originalValue: ['47.6103'], - isObjectArray: false, - }, - { - category: 'source', - field: 'source.geo.location.lon', - values: ['-122.3341'], - originalValue: ['-122.3341'], - isObjectArray: false, - }, { category: 'source', field: 'source.geo.region_iso_code', @@ -689,7 +675,6 @@ export default function ({ getService }: FtrProviderContext) { supertest, options: { factoryQueryType: TimelineEventsQueries.details, - docValueFields: [], indexName: INDEX_NAME, inspect: false, eventId: ID, @@ -705,7 +690,6 @@ export default function ({ getService }: FtrProviderContext) { supertest, options: { factoryQueryType: TimelineEventsQueries.kpi, - docValueFields: [], indexName: INDEX_NAME, inspect: false, eventId: ID, diff --git a/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts b/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts index df4efd8261659..cdb0ea37a6417 100644 --- a/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts +++ b/x-pack/test/observability_functional/apps/observability/pages/alerts/index.ts @@ -164,7 +164,7 @@ export default ({ getService }: FtrProviderContext) => { 'Oct 19, 2021 @ 15:20:38.749', '20 minutes', '5', - '30.727896995708154', + '30.73', 'Failed transaction rate threshold', ]; From 8aa01c3a8188954d7ff3ea0a724868605abacb28 Mon Sep 17 00:00:00 2001 From: Rickyanto Ang Date: Tue, 12 Jul 2022 13:58:59 -0700 Subject: [PATCH 30/42] [8.4][Kubernetes Security]Count Widget (#135969) * added base count widget * merged with latest main + clean up + checks fail fixes * check type fix * check type fix again * [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' * PR Comments + deleted KubernetesWidget * added new test file for helper functions, addresing PR comments * added test case for tooltips * addressing PR comments * check types error fix * addressing pr comments * addressing PR comments * updated groupBy value to correct value * fixed check types erro Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../kubernetes_security/common/constants.ts | 13 + .../common/translations.ts | 29 ++ .../components/count_widget/helpers.test.ts | 52 +++ .../public/components/count_widget/helpers.ts | 99 ++++++ .../public/components/count_widget/hooks.ts | 43 +++ .../components/count_widget/index.test.tsx | 146 ++++++++ .../public/components/count_widget/index.tsx | 85 +++++ .../public/components/count_widget/styles.ts | 66 ++++ .../kubernetes_security_routes/index.test.tsx | 10 +- .../kubernetes_security_routes/index.tsx | 327 ++++++++++-------- .../kubernetes_security_routes/styles.ts | 20 +- .../components/kubernetes_widget/index.tsx | 56 --- .../components/percent_widget/index.tsx | 4 +- .../kubernetes_security/public/types.ts | 5 + 14 files changed, 745 insertions(+), 210 deletions(-) create mode 100644 x-pack/plugins/kubernetes_security/public/components/count_widget/helpers.test.ts create mode 100644 x-pack/plugins/kubernetes_security/public/components/count_widget/helpers.ts create mode 100644 x-pack/plugins/kubernetes_security/public/components/count_widget/hooks.ts create mode 100644 x-pack/plugins/kubernetes_security/public/components/count_widget/index.test.tsx create mode 100644 x-pack/plugins/kubernetes_security/public/components/count_widget/index.tsx create mode 100644 x-pack/plugins/kubernetes_security/public/components/count_widget/styles.ts delete mode 100644 x-pack/plugins/kubernetes_security/public/components/kubernetes_widget/index.tsx diff --git a/x-pack/plugins/kubernetes_security/common/constants.ts b/x-pack/plugins/kubernetes_security/common/constants.ts index d94e997dd2bf3..625946b99a123 100644 --- a/x-pack/plugins/kubernetes_security/common/constants.ts +++ b/x-pack/plugins/kubernetes_security/common/constants.ts @@ -19,6 +19,7 @@ export const AGGREGATE_MAX_BUCKETS = 2000; // react-query caching keys export const QUERY_KEY_PERCENT_WIDGET = 'kubernetesSecurityPercentWidget'; +export const QUERY_KEY_COUNT_WIDGET = 'kubernetesSecurityCountWidget'; export const DEFAULT_QUERY = '{"bool":{"must":[],"filter":[],"should":[],"must_not":[]}}'; @@ -26,3 +27,15 @@ export const DEFAULT_QUERY = '{"bool":{"must":[],"filter":[],"should":[],"must_n export const ENTRY_LEADER_INTERACTIVE = 'process.entry_leader.interactive'; export const ENTRY_LEADER_USER_ID = 'process.entry_leader.user.id'; export const ENTRY_LEADER_ENTITY_ID = 'process.entry_leader.entity_id'; + +export const ORCHESTRATOR_CLUSTER_ID = 'orchestrator.cluster.name'; +export const ORCHESTRATOR_NAMESPACE = 'orchestrator.namespace'; +export const ORCHESTRATOR_RESOURCE_ID = 'orchestrator.resource.name'; +export const CONTAINER_IMAGE_NAME = 'container.image.name'; +export const CLOUD_INSTANCE_NAME = 'cloud.instance.name'; + +export const COUNT_WIDGET_KEY_CLUSTERS = 'CountClustersWidget'; +export const COUNT_WIDGET_KEY_NAMESPACE = 'CountNamespaceWidgets'; +export const COUNT_WIDGET_KEY_NODES = 'CountNodesWidgets'; +export const COUNT_WIDGET_KEY_PODS = 'CountPodsWidgets'; +export const COUNT_WIDGET_KEY_CONTAINER_IMAGES = 'CountContainerImagesWidgets'; diff --git a/x-pack/plugins/kubernetes_security/common/translations.ts b/x-pack/plugins/kubernetes_security/common/translations.ts index 6d0a88e5dc904..d13bf8be60265 100644 --- a/x-pack/plugins/kubernetes_security/common/translations.ts +++ b/x-pack/plugins/kubernetes_security/common/translations.ts @@ -29,3 +29,32 @@ export const WIDGET_TOGGLE_SHOW = i18n.translate('xpack.kubernetesSecurity.widge export const WIDGET_TOGGLE_HIDE = i18n.translate('xpack.kubernetesSecurity.widgetsToggle.hide', { defaultMessage: 'Hide widgets', }); + +export const COUNT_WIDGET_CLUSTERS = i18n.translate( + 'xpack.kubernetesSecurity.countWidget.clusters', + { + defaultMessage: 'Clusters', + } +); + +export const COUNT_WIDGET_NAMESPACE = i18n.translate( + 'xpack.kubernetesSecurity.countWidget.namespace', + { + defaultMessage: 'Namespace', + } +); + +export const COUNT_WIDGET_NODES = i18n.translate('xpack.kubernetesSecurity.countWidget.nodes', { + defaultMessage: 'Nodes', +}); + +export const COUNT_WIDGET_PODS = i18n.translate('xpack.kubernetesSecurity.countWidget.pods', { + defaultMessage: 'Pods', +}); + +export const COUNT_WIDGET_CONTAINER_IMAGES = i18n.translate( + 'xpack.kubernetesSecurity.countWidget.containerImages', + { + defaultMessage: 'Container Images', + } +); diff --git a/x-pack/plugins/kubernetes_security/public/components/count_widget/helpers.test.ts b/x-pack/plugins/kubernetes_security/public/components/count_widget/helpers.test.ts new file mode 100644 index 0000000000000..f3741e38d7947 --- /dev/null +++ b/x-pack/plugins/kubernetes_security/public/components/count_widget/helpers.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { addResourceTypeToFilterQuery, numberFormatter } from './helpers'; + +const TEST_DATA_ARRAY: number[] = [ + 32, + 2200, + 999232, + 1310000, + 999999999, + 999999999999, + 1230000000000, + Infinity, + NaN, + -1, + -Infinity, + 1e15 - 1, +]; + +const TEST_QUERY = `{"bool":{"must":[],"filter":[],"should":[],"must_not":[]}}`; + +const RESULT_QUERY_NODE = + '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match_phrase":{"orchestrator.resource.type":"node"}}]}}],"should":[],"must_not":[]}}'; +const RESULT_QUERY_POD = + '{"bool":{"must":[],"filter":[{"bool":{"should":[{"match_phrase":{"orchestrator.resource.type":"pod"}}]}}],"should":[],"must_not":[]}}'; + +describe('Testing Helper functions', () => { + it('Testing numberFormatter helper function', () => { + expect(numberFormatter(TEST_DATA_ARRAY[0])).toBe('32'); + expect(numberFormatter(TEST_DATA_ARRAY[1])).toBe('2K'); + expect(numberFormatter(TEST_DATA_ARRAY[2])).toBe('999K'); + expect(numberFormatter(TEST_DATA_ARRAY[3])).toBe('1.3M'); + expect(numberFormatter(TEST_DATA_ARRAY[4])).toBe('999M'); + expect(numberFormatter(TEST_DATA_ARRAY[5])).toBe('999B'); + expect(numberFormatter(TEST_DATA_ARRAY[6])).toBe('1.2T'); + expect(numberFormatter(TEST_DATA_ARRAY[7])).toBe('NaN'); + expect(numberFormatter(TEST_DATA_ARRAY[8])).toBe('NaN'); + expect(numberFormatter(TEST_DATA_ARRAY[9])).toBe('NaN'); + expect(numberFormatter(TEST_DATA_ARRAY[10])).toBe('NaN'); + expect(numberFormatter(TEST_DATA_ARRAY[11])).toBe('999T'); + }); + + it('Testing addResourceTypeToFilterQuery helper function', () => { + expect(addResourceTypeToFilterQuery(TEST_QUERY, 'node')).toBe(RESULT_QUERY_NODE); + expect(addResourceTypeToFilterQuery(TEST_QUERY, 'pod')).toBe(RESULT_QUERY_POD); + }); +}); diff --git a/x-pack/plugins/kubernetes_security/public/components/count_widget/helpers.ts b/x-pack/plugins/kubernetes_security/public/components/count_widget/helpers.ts new file mode 100644 index 0000000000000..7f57bd038aa75 --- /dev/null +++ b/x-pack/plugins/kubernetes_security/public/components/count_widget/helpers.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DEFAULT_QUERY } from '../../../common/constants'; +import { QueryDslQueryContainerBool } from '../../types'; + +export const addResourceTypeToFilterQuery = ( + filterQuery: string | undefined, + resourceType: 'node' | 'pod' +) => { + let validFilterQuery = DEFAULT_QUERY; + + try { + const parsedFilterQuery: QueryDslQueryContainerBool = JSON.parse(filterQuery || '{}'); + if (!(parsedFilterQuery?.bool?.filter && Array.isArray(parsedFilterQuery.bool.filter))) { + throw new Error('Invalid filter query'); + } + parsedFilterQuery.bool.filter.push({ + bool: { + should: [ + { + match_phrase: { + 'orchestrator.resource.type': resourceType, + }, + }, + ], + }, + }); + validFilterQuery = JSON.stringify(parsedFilterQuery); + } catch { + // no-op since validFilterQuery is initialized to be DEFAULT_QUERY + } + + return validFilterQuery; +}; + +export const numberFormatter = (num: number) => { + if (Number.isFinite(num) && num >= 0) { + if (num >= 1e15 - 1) { + const newNum = Math.floor(num / 1e12) * 1e12; + return new Intl.NumberFormat('en-GB', { + // @ts-ignore + notation: 'compact', + compactDisplay: 'short', + }).format(newNum); + } + // Trillion + if (num >= 1e12 - 1) { + const newNum = Math.floor(num / 1e9) * 1e9; + return new Intl.NumberFormat('en-GB', { + // @ts-ignore + notation: 'compact', + compactDisplay: 'short', + }).format(newNum); + } + // Billion + if (num >= 1e9 - 1) { + const newNum = Math.floor(num / 1e6) * 1e6; + return new Intl.NumberFormat('en-GB', { + // @ts-ignore + notation: 'compact', + compactDisplay: 'short', + }).format(newNum); + } + // Hundreds Thousands + if (num >= 1e6 - 1) { + const newNum = Math.floor(num / 1000) * 1000; + return new Intl.NumberFormat('en-GB', { + // @ts-ignore + notation: 'compact', + compactDisplay: 'short', + }).format(newNum); + } + // Thousands + if (num >= 1e3 - 1) { + const newNum = Math.floor(num / 1000) * 1000; + return new Intl.NumberFormat('en-GB', { + // @ts-ignore + notation: 'compact', + compactDisplay: 'short', + }).format(newNum); + } + + if (num < 1e3) { + return num.toString(); + } + + return new Intl.NumberFormat('en-GB', { + // @ts-ignore + notation: 'compact', + compactDisplay: 'short', + }).format(num); + } + return 'NaN'; +}; diff --git a/x-pack/plugins/kubernetes_security/public/components/count_widget/hooks.ts b/x-pack/plugins/kubernetes_security/public/components/count_widget/hooks.ts new file mode 100644 index 0000000000000..d194e74ae8c62 --- /dev/null +++ b/x-pack/plugins/kubernetes_security/public/components/count_widget/hooks.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useInfiniteQuery } from 'react-query'; +import { CoreStart } from '@kbn/core/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { QUERY_KEY_COUNT_WIDGET, COUNT_ROUTE } from '../../../common/constants'; + +export const useFetchCountWidgetData = ( + widgetKey: string, + filterQuery: string, + groupedBy: string, + index?: string +) => { + const { http } = useKibana().services; + const cachingKeys = [QUERY_KEY_COUNT_WIDGET, widgetKey, filterQuery, groupedBy, index]; + + const query = useInfiniteQuery( + cachingKeys, + async () => { + const res = await http.get(COUNT_ROUTE, { + query: { + query: filterQuery, + field: groupedBy, + index, + }, + }); + + return res; + }, + { + refetchOnWindowFocus: false, + refetchOnMount: false, + refetchOnReconnect: false, + } + ); + + return query; +}; diff --git a/x-pack/plugins/kubernetes_security/public/components/count_widget/index.test.tsx b/x-pack/plugins/kubernetes_security/public/components/count_widget/index.test.tsx new file mode 100644 index 0000000000000..72d087f47d364 --- /dev/null +++ b/x-pack/plugins/kubernetes_security/public/components/count_widget/index.test.tsx @@ -0,0 +1,146 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { AppContextTestRender, createAppRootMockRenderer } from '../../test'; +import { GlobalFilter } from '../../types'; +import { CountWidget, LOADING_TEST_ID, TOOLTIP_TEST_ID } from '.'; +import { useFetchCountWidgetData } from './hooks'; +import { fireEvent, waitFor } from '@testing-library/dom'; + +const TITLE = 'Count Widget Title'; +const GLOBAL_FILTER: GlobalFilter = { + endDate: '2022-06-15T14:15:25.777Z', + filterQuery: '{"bool":{"must":[],"filter":[],"should":[],"must_not":[]}}', + startDate: '2022-05-15T14:15:25.777Z', +}; + +const MOCK_DATA_NORMAL = { + pages: [12], +}; + +const MOCK_DATA_MILLION = { + pages: [1210000], +}; + +const MOCK_DATA_THOUSAND = { + pages: [5236], +}; + +const MOCK_DATA_CLOSE_TO_MILLION = { + pages: [999999], +}; + +jest.mock('../../hooks/use_filter', () => ({ + useSetFilter: () => ({ + getFilterForValueButton: jest.fn(), + getFilterOutValueButton: jest.fn(), + filterManager: {}, + }), +})); + +jest.mock('./hooks'); +const mockUseFetchData = useFetchCountWidgetData as jest.Mock; + +describe('CountWidget component', () => { + let renderResult: ReturnType; + const mockedContext = createAppRootMockRenderer(); + const render: () => ReturnType = () => + (renderResult = mockedContext.render( + + )); + + describe('When PercentWidget is mounted', () => { + describe('with small amount of data (less than 1000)', () => { + beforeEach(() => { + mockUseFetchData.mockImplementation(() => ({ + data: MOCK_DATA_NORMAL, + isLoading: false, + })); + }); + + it('should show title and count numbers correctly', async () => { + render(); + + expect(renderResult.getByText(TITLE)).toBeVisible(); + expect(renderResult.getByText('12')).toBeVisible(); + }); + }); + + describe('with moderate amount of data (more than 1000 less than 1 million)', () => { + beforeEach(() => { + mockUseFetchData.mockImplementation(() => ({ + data: MOCK_DATA_THOUSAND, + isLoading: false, + })); + }); + + it('should show title and count numbers (formatted thousands with comma)correctly', async () => { + render(); + + expect(renderResult.getByText(TITLE)).toBeVisible(); + expect(renderResult.getByText('5K')).toBeVisible(); + }); + }); + + describe('with huge amount of data (more than 1 million)', () => { + it('should show title and count numbers (formatted remove the zeroes and add M) correctly', async () => { + mockUseFetchData.mockImplementation(() => ({ + data: MOCK_DATA_MILLION, + isLoading: false, + })); + render(); + + expect(renderResult.getByText(TITLE)).toBeVisible(); + expect(renderResult.getByText('1.2M')).toBeVisible(); + }); + }); + + describe('with huge amount of data (Very close to 1 million)', () => { + it('should show title and count numbers (formatted remove the zeroes and add K) correctly', async () => { + mockUseFetchData.mockImplementation(() => ({ + data: MOCK_DATA_CLOSE_TO_MILLION, + isLoading: false, + })); + render(); + + expect(renderResult.getByText(TITLE)).toBeVisible(); + expect(renderResult.getByText('999K')).toBeVisible(); + }); + }); + + describe('When data is loading', () => { + it('should show the loading icon', async () => { + mockUseFetchData.mockImplementation(() => ({ + data: MOCK_DATA_MILLION, + isLoading: true, + })); + render(); + + expect(renderResult.getAllByTestId(LOADING_TEST_ID)).toHaveLength(1); + }); + }); + + describe('Testing Tooltips', () => { + it('Tooltips show the real count value (not formatted)', async () => { + mockUseFetchData.mockImplementation(() => ({ + data: MOCK_DATA_THOUSAND, + isLoading: false, + })); + render(); + fireEvent.mouseOver(renderResult.getByText('Info')); + await waitFor(() => renderResult.getByTestId(TOOLTIP_TEST_ID)); + expect(renderResult.queryByText(MOCK_DATA_THOUSAND.pages[0])).toBeTruthy(); + }); + }); + }); +}); diff --git a/x-pack/plugins/kubernetes_security/public/components/count_widget/index.tsx b/x-pack/plugins/kubernetes_security/public/components/count_widget/index.tsx new file mode 100644 index 0000000000000..5e5f5eacc374f --- /dev/null +++ b/x-pack/plugins/kubernetes_security/public/components/count_widget/index.tsx @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useMemo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiLoadingSpinner, EuiIconTip } from '@elastic/eui'; +import { useStyles } from './styles'; +import type { IndexPattern, GlobalFilter } from '../../types'; +import { addTimerangeToQuery } from '../../utils/add_timerange_to_query'; +import { useFetchCountWidgetData } from './hooks'; +import { addResourceTypeToFilterQuery, numberFormatter } from './helpers'; +import { COUNT_WIDGET_KEY_PODS } from '../../../common/constants'; + +export const LOADING_TEST_ID = 'kubernetesSecurity:countWidgetLoading'; +export const TOOLTIP_TEST_ID = 'kubernetesSecurity:countWidgetTooltip'; + +export interface CountWidgetDeps { + title: string; + widgetKey: string; + indexPattern?: IndexPattern; + globalFilter: GlobalFilter; + groupedBy: string; +} + +export const CountWidget = ({ + title, + widgetKey, + indexPattern, + globalFilter, + groupedBy, +}: CountWidgetDeps) => { + const styles = useStyles(); + + const filterQueryWithTimeRange = useMemo(() => { + let globalFilterModified = globalFilter.filterQuery; + + if (widgetKey === COUNT_WIDGET_KEY_PODS) { + globalFilterModified = addResourceTypeToFilterQuery(globalFilter.filterQuery, 'pod'); + } + return addTimerangeToQuery(globalFilterModified, globalFilter.startDate, globalFilter.endDate); + }, [globalFilter.filterQuery, globalFilter.startDate, globalFilter.endDate, widgetKey]); + + const { data, isLoading } = useFetchCountWidgetData( + widgetKey, + filterQueryWithTimeRange, + groupedBy, + indexPattern?.title + ); + + const countValue = useMemo((): number => { + return data ? data?.pages[0] : 0; + }, [data]); + + const formattedNumber = useMemo((): string => { + return numberFormatter(countValue); + }, [countValue]); + + return ( +
    +
    + {title} + +
    + + + + {isLoading ? ( + + ) : ( + formattedNumber + )} + + + +
    + ); +}; diff --git a/x-pack/plugins/kubernetes_security/public/components/count_widget/styles.ts b/x-pack/plugins/kubernetes_security/public/components/count_widget/styles.ts new file mode 100644 index 0000000000000..abd89925b5cf3 --- /dev/null +++ b/x-pack/plugins/kubernetes_security/public/components/count_widget/styles.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; +import { CSSObject } from '@emotion/react'; +import { useEuiTheme } from '../../hooks'; + +export const useStyles = () => { + const { euiTheme } = useEuiTheme(); + + const cached = useMemo(() => { + const { size, font, border } = euiTheme; + + const container: CSSObject = { + padding: size.base, + border: border.thin, + borderRadius: border.radius.medium, + overflow: 'auto', + position: 'relative', + }; + + const title: CSSObject = { + marginBottom: size.m, + fontSize: size.m, + fontWeight: font.weight.bold, + }; + + const dataInfo: CSSObject = { + marginBottom: size.xs, + display: 'flex', + alignItems: 'center', + height: '18px', + fontSize: size.l, + fontWeight: font.weight.bold, + }; + + const dataValue: CSSObject = { + fontWeight: font.weight.semiBold, + marginLeft: 'auto', + }; + + const filters: CSSObject = { + marginLeft: size.s, + }; + + const loadingSpinner: CSSObject = { + alignItems: 'center', + margin: `${size.xs} auto ${size.xl} auto`, + }; + + return { + container, + title, + dataInfo, + dataValue, + filters, + loadingSpinner, + }; + }, [euiTheme]); + + return cached; +}; diff --git a/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/index.test.tsx b/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/index.test.tsx index aa1e3ff802136..5399975091420 100644 --- a/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/index.test.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/index.test.tsx @@ -13,10 +13,6 @@ import { MemoryRouter } from 'react-router-dom'; import { KubernetesSecurityRoutes } from '.'; import { createAppRootMockRenderer } from '../../test'; -jest.mock('../kubernetes_widget', () => ({ - KubernetesWidget: () =>
    {'Mock kubernetes widget'}
    , -})); - jest.mock('../percent_widget', () => ({ PercentWidget: () =>
    {'Mock percent widget'}
    , })); @@ -25,6 +21,10 @@ jest.mock('../../hooks/use_last_updated', () => ({ useLastUpdated: () =>
    {'Mock updated now'}
    , })); +jest.mock('../count_widget', () => ({ + CountWidget: () =>
    {'Mock count widget'}
    , +})); + const renderWithRouter = ( initialEntries: MemoryRouterProps['initialEntries'] = ['/kubernetes'] ) => { @@ -68,7 +68,7 @@ const renderWithRouter = ( describe('Kubernetes security routes', () => { it('navigates to the kubernetes page', () => { renderWithRouter(); - expect(screen.getAllByText('Mock kubernetes widget')).toHaveLength(3); + expect(screen.getAllByText('Mock count widget')).toHaveLength(5); expect(screen.getAllByText('Mock percent widget')).toHaveLength(2); expect(screen.getAllByText('Mock updated now')).toHaveLength(1); }); diff --git a/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/index.tsx b/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/index.tsx index 1c826b9dcf1fe..182873f954d8d 100644 --- a/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/index.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/index.tsx @@ -8,16 +8,7 @@ import React, { useCallback } from 'react'; import { Route, Switch } from 'react-router-dom'; import useLocalStorage from 'react-use/lib/useLocalStorage'; -import { - EuiBadge, - EuiFlexGroup, - EuiFlexItem, - EuiIconTip, - EuiSpacer, - EuiText, - EuiTextColor, - EuiTitle, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiIconTip, EuiText, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { euiThemeVars } from '@kbn/ui-theme'; @@ -28,15 +19,31 @@ import { ENTRY_LEADER_INTERACTIVE, ENTRY_LEADER_USER_ID, ENTRY_LEADER_ENTITY_ID, + ORCHESTRATOR_CLUSTER_ID, + ORCHESTRATOR_NAMESPACE, + ORCHESTRATOR_RESOURCE_ID, + CONTAINER_IMAGE_NAME, + CLOUD_INSTANCE_NAME, + COUNT_WIDGET_KEY_CLUSTERS, + COUNT_WIDGET_KEY_NAMESPACE, + COUNT_WIDGET_KEY_NODES, + COUNT_WIDGET_KEY_CONTAINER_IMAGES, } from '../../../common/constants'; -import { KubernetesWidget } from '../kubernetes_widget'; import { PercentWidget } from '../percent_widget'; +import { CountWidget } from '../count_widget'; import { KubernetesSecurityDeps } from '../../types'; import { AggregateResult } from '../../../common/types/aggregate'; import { useLastUpdated } from '../../hooks'; import { useStyles } from './styles'; import { TreeViewContainer } from '../tree_view_container'; import { WidgetsToggle } from '../widgets_toggle'; +import { + COUNT_WIDGET_CLUSTERS, + COUNT_WIDGET_NAMESPACE, + COUNT_WIDGET_NODES, + COUNT_WIDGET_PODS, + COUNT_WIDGET_CONTAINER_IMAGES, +} from '../../../common/translations'; const KubernetesSecurityRoutesComponent = ({ filter, @@ -99,144 +106,172 @@ const KubernetesSecurityRoutesComponent = ({ {!shouldHideWidgets && ( <> - - - - -
    {'93 alerts '}
    View alerts -
    -
    -
    - - - - - - - - 1000 - {' live'} - - 42 - {' disabled'} - - - -
    - - + - - - + + + + - - - } - /> - - } - widgetKey="sessionsPercentage" - indexPattern={indexPattern} - globalFilter={globalFilter} - dataValueMap={{ - true: { - name: i18n.translate('xpack.kubernetesSecurity.sessionChart.interactive', { - defaultMessage: 'Interactive', - }), - fieldName: ENTRY_LEADER_INTERACTIVE, - color: euiThemeVars.euiColorVis0, - }, - false: { - name: i18n.translate('xpack.kubernetesSecurity.sessionChart.nonInteractive', { - defaultMessage: 'Non-interactive', - }), - fieldName: ENTRY_LEADER_INTERACTIVE, - color: euiThemeVars.euiColorVis1, - shouldHideFilter: true, - }, - }} - groupedBy={ENTRY_LEADER_INTERACTIVE} - countBy={ENTRY_LEADER_ENTITY_ID} - onReduce={onReduceInteractiveAggs} - /> - - - - - + + + + + + + + - - - } - /> - - } - widgetKey="rootLoginPercentage" - indexPattern={indexPattern} - globalFilter={globalFilter} - dataValueMap={{ - '0': { - name: i18n.translate('xpack.kubernetesSecurity.entryUserChart.root', { - defaultMessage: 'Root', - }), - fieldName: ENTRY_LEADER_USER_ID, - color: euiThemeVars.euiColorVis2, - }, - nonRoot: { - name: i18n.translate('xpack.kubernetesSecurity.entryUserChart.nonRoot', { - defaultMessage: 'Non-root', - }), - fieldName: ENTRY_LEADER_USER_ID, - color: euiThemeVars.euiColorVis3, - shouldHideFilter: true, - }, - }} - groupedBy={ENTRY_LEADER_USER_ID} - countBy={ENTRY_LEADER_ENTITY_ID} - onReduce={onReduceRootAggs} - /> + + + + + + + + + + + + + + + } + /> + + } + widgetKey="sessionsPercentage" + indexPattern={indexPattern} + globalFilter={globalFilter} + dataValueMap={{ + true: { + name: i18n.translate( + 'xpack.kubernetesSecurity.sessionChart.interactive', + { + defaultMessage: 'Interactive', + } + ), + fieldName: ENTRY_LEADER_INTERACTIVE, + color: euiThemeVars.euiColorVis0, + }, + false: { + name: i18n.translate( + 'xpack.kubernetesSecurity.sessionChart.nonInteractive', + { + defaultMessage: 'Non-interactive', + } + ), + fieldName: ENTRY_LEADER_INTERACTIVE, + color: euiThemeVars.euiColorVis1, + shouldHideFilter: true, + }, + }} + groupedBy={ENTRY_LEADER_INTERACTIVE} + countBy={ENTRY_LEADER_ENTITY_ID} + onReduce={onReduceInteractiveAggs} + /> + + + + + + + + } + /> + + } + widgetKey="rootLoginPercentage" + indexPattern={indexPattern} + globalFilter={globalFilter} + dataValueMap={{ + '0': { + name: i18n.translate('xpack.kubernetesSecurity.entryUserChart.root', { + defaultMessage: 'Root', + }), + fieldName: ENTRY_LEADER_USER_ID, + color: euiThemeVars.euiColorVis2, + }, + nonRoot: { + name: i18n.translate( + 'xpack.kubernetesSecurity.entryUserChart.nonRoot', + { + defaultMessage: 'Non-root', + } + ), + fieldName: ENTRY_LEADER_USER_ID, + color: euiThemeVars.euiColorVis3, + shouldHideFilter: true, + }, + }} + groupedBy={ENTRY_LEADER_USER_ID} + countBy={ENTRY_LEADER_ENTITY_ID} + onReduce={onReduceRootAggs} + /> + + + + + + + + +
    PlaceHolder for Container Name Widget
    +
    +
    diff --git a/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/styles.ts b/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/styles.ts index 3bab1ded50f55..889f99715393b 100644 --- a/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/styles.ts +++ b/x-pack/plugins/kubernetes_security/public/components/kubernetes_security_routes/styles.ts @@ -13,7 +13,7 @@ export const useStyles = () => { const { euiTheme } = useEuiTheme(); const cached = useMemo(() => { - const { size, font } = euiTheme; + const { size, font, border } = euiTheme; const titleSection: CSSObject = { marginBottom: size.l, @@ -58,6 +58,22 @@ export const useStyles = () => { fontWeight: font.weight.bold, }; + const countWidgets: CSSObject = { + margin: size.l, + }; + + const widgetHolder: CSSObject = { + position: 'relative', + width: '332px', + height: '235px', + padding: size.base, + border: border.thin, + borderRadius: border.radius.medium, + fontWeight: font.weight.bold, + fontSize: size.m, + lineHeight: size.base, + }; + return { titleSection, titleActions, @@ -66,6 +82,8 @@ export const useStyles = () => { treeViewContainer, percentageWidgets, percentageChartTitle, + countWidgets, + widgetHolder, }; }, [euiTheme]); diff --git a/x-pack/plugins/kubernetes_security/public/components/kubernetes_widget/index.tsx b/x-pack/plugins/kubernetes_security/public/components/kubernetes_widget/index.tsx deleted file mode 100644 index 904a444bb4d68..0000000000000 --- a/x-pack/plugins/kubernetes_security/public/components/kubernetes_widget/index.tsx +++ /dev/null @@ -1,56 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { ReactNode } from 'react'; -import { EuiIcon } from '@elastic/eui'; -import { CSSObject } from '@emotion/react'; - -const widget = (isAlert?: boolean): CSSObject => ({ - position: 'relative', - height: '180px', - padding: '16px', - border: `1px solid ${isAlert ? '#BD271E' : '#D3DAE6'}`, - borderRadius: '6px', - fontWeight: 700, - fontSize: '12px', - lineHeight: '16px', -}); - -const widgetData: CSSObject = { - display: 'flex', - alignItems: 'center', - marginTop: '16px', - fontSize: '27px', - lineHeight: '32px', -}; - -interface KubernetesWidgetDeps { - title: string; - icon: string; - iconColor: string; - data: number; - isAlert?: boolean; - children?: ReactNode; -} - -export const KubernetesWidget = ({ - title, - icon, - iconColor, - data, - isAlert, - children, -}: KubernetesWidgetDeps) => ( -
    -
    {title}
    -
    - - {data} -
    - {children} -
    -); diff --git a/x-pack/plugins/kubernetes_security/public/components/percent_widget/index.tsx b/x-pack/plugins/kubernetes_security/public/components/percent_widget/index.tsx index a5eac7bca92b3..0c3cb3da9c5a8 100644 --- a/x-pack/plugins/kubernetes_security/public/components/percent_widget/index.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/percent_widget/index.tsx @@ -14,8 +14,8 @@ import { addTimerangeToQuery } from '../../utils/add_timerange_to_query'; import { AggregateResult } from '../../../common/types/aggregate'; import { useFetchPercentWidgetData } from './hooks'; -export const LOADING_TEST_ID = 'kubernetesSecurity:percent-widget-loading'; -export const PERCENT_DATA_TEST_ID = 'kubernetesSecurity:percentage-widget-data'; +export const LOADING_TEST_ID = 'kubernetesSecurity:percentWidgetLoading'; +export const PERCENT_DATA_TEST_ID = 'kubernetesSecurity:percentWidgetData'; export interface PercenWidgetDataValueMap { name: string; diff --git a/x-pack/plugins/kubernetes_security/public/types.ts b/x-pack/plugins/kubernetes_security/public/types.ts index bf25e00eec2b2..5a41e72d89d89 100644 --- a/x-pack/plugins/kubernetes_security/public/types.ts +++ b/x-pack/plugins/kubernetes_security/public/types.ts @@ -10,6 +10,7 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { FieldSpec } from '@kbn/data-plugin/common'; import type { TimelinesUIStart } from '@kbn/timelines-plugin/public'; import type { SessionViewStart } from '@kbn/session-view-plugin/public'; +import { BoolQuery } from '@kbn/es-query'; export interface StartPlugins { data: DataPublicPluginStart; @@ -40,3 +41,7 @@ export interface KubernetesSecurityDeps { export interface KubernetesSecurityStart { getKubernetesPage: (kubernetesSecurityDeps: KubernetesSecurityDeps) => JSX.Element; } + +export type QueryDslQueryContainerBool = { + bool: BoolQuery; +}; From 6e012042fa0fbd0ae52a9f12348114c705e496af Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 12 Jul 2022 22:06:30 +0100 Subject: [PATCH 31/42] skip flaky suite (#136165 136166) --- .../host_isolation_exceptions/view/components/form.test.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.test.tsx index 90cb1b6adac81..9287931ddb11e 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/form.test.tsx @@ -92,7 +92,9 @@ describe('When on the host isolation exceptions entry form', () => { }); }); - describe('and creating a new exception', () => { + // FLAKY: https://github.com/elastic/kibana/issues/136165 + // FLAKY: https://github.com/elastic/kibana/issues/136166 + describe.skip('and creating a new exception', () => { beforeEach(async () => { await render(); }); From dde4fd5fc99ad09d2e039a744323b7d556126d5b Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 12 Jul 2022 22:15:24 +0100 Subject: [PATCH 32/42] skip flaky suite (#135196) --- x-pack/test/accessibility/apps/stack_monitoring.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/accessibility/apps/stack_monitoring.ts b/x-pack/test/accessibility/apps/stack_monitoring.ts index 4505f05eb4a8f..f5896cfb8be36 100644 --- a/x-pack/test/accessibility/apps/stack_monitoring.ts +++ b/x-pack/test/accessibility/apps/stack_monitoring.ts @@ -48,7 +48,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.click('alerts-modal-remind-later-button'); }); - it('a11y tests for Kibana Overview', async function () { + // FLAKY: https://github.com/elastic/kibana/issues/135196 + it.skip('a11y tests for Kibana Overview', async function () { await clusterOverview.clickKibanaOverview(); await kibanaOverview.isOnOverview(); await a11y.testAppSnapshot(); From 4f6fad21aec2f55f812bf207e9eca162446cf90c Mon Sep 17 00:00:00 2001 From: Luke Elmers Date: Tue, 12 Jul 2022 15:34:09 -0600 Subject: [PATCH 33/42] [http][elasticsearch] Updates header filters for request/response logs. (#136044) --- .../client/get_ecs_response_log.test.ts | 22 +++++++++++++++++++ .../client/get_ecs_response_log.ts | 4 +++- .../http/logging/get_response_log.test.ts | 22 +++++++++++++++++++ .../server/http/logging/get_response_log.ts | 4 +++- 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/core/server/elasticsearch/client/get_ecs_response_log.test.ts b/src/core/server/elasticsearch/client/get_ecs_response_log.test.ts index 547aae3224756..ec00badbcdf28 100644 --- a/src/core/server/elasticsearch/client/get_ecs_response_log.test.ts +++ b/src/core/server/elasticsearch/client/get_ecs_response_log.test.ts @@ -68,6 +68,28 @@ describe('getEcsResponseLog', () => { `); }); + test('redacts x-elastic-app-auth headers by default', () => { + const event = createResponseEvent({ + requestParams: { headers: { 'x-elastic-app-auth': 'hello', 'user-agent': 'world' } }, + response: { headers: { 'content-length': '123', 'x-elastic-app-auth': 'abc' } }, + }); + const log = getEcsResponseLog(event); + // @ts-expect-error ECS custom field + expect(log.http.request.headers).toMatchInlineSnapshot(` + Object { + "user-agent": "world", + "x-elastic-app-auth": "[REDACTED]", + } + `); + // @ts-expect-error ECS custom field + expect(log.http.response.headers).toMatchInlineSnapshot(` + Object { + "content-length": "123", + "x-elastic-app-auth": "[REDACTED]", + } + `); + }); + test('does not mutate original headers', () => { const reqHeaders = { a: 'foo', b: ['hello', 'world'] }; const resHeaders = { c: 'bar' }; diff --git a/src/core/server/elasticsearch/client/get_ecs_response_log.ts b/src/core/server/elasticsearch/client/get_ecs_response_log.ts index 1a75967cf66d7..b977f03a9a062 100644 --- a/src/core/server/elasticsearch/client/get_ecs_response_log.ts +++ b/src/core/server/elasticsearch/client/get_ecs_response_log.ts @@ -9,7 +9,9 @@ import { type IncomingHttpHeaders } from 'http'; import { type DiagnosticResult } from '@elastic/elasticsearch'; import { type LogMeta } from '@kbn/logging'; -const FORBIDDEN_HEADERS = ['authorization', 'cookie', 'set-cookie']; +// If you are updating these, consider whether they should also be updated in the +// http service `getResponseLog` +const FORBIDDEN_HEADERS = ['authorization', 'cookie', 'set-cookie', 'x-elastic-app-auth']; const REDACTED_HEADER_TEXT = '[REDACTED]'; // We are excluding sensitive headers by default, until we have a log filtering mechanism. diff --git a/src/core/server/http/logging/get_response_log.test.ts b/src/core/server/http/logging/get_response_log.test.ts index 386bf2a263934..a8f607c5c6865 100644 --- a/src/core/server/http/logging/get_response_log.test.ts +++ b/src/core/server/http/logging/get_response_log.test.ts @@ -189,6 +189,28 @@ describe('getEcsResponseLog', () => { `); }); + test('redacts x-elastic-app-auth headers by default', () => { + const req = createMockHapiRequest({ + headers: { 'x-elastic-app-auth': 'hello', 'user-agent': 'world' }, + response: { headers: { 'content-length': '123', 'x-elastic-app-auth': 'abc' } }, + }); + const result = getEcsResponseLog(req, logger); + // @ts-expect-error ECS custom field + expect(result.meta.http.request.headers).toMatchInlineSnapshot(` + Object { + "user-agent": "world", + "x-elastic-app-auth": "[REDACTED]", + } + `); + // @ts-expect-error ECS custom field + expect(result.meta.http.response.headers).toMatchInlineSnapshot(` + Object { + "content-length": "123", + "x-elastic-app-auth": "[REDACTED]", + } + `); + }); + test('does not mutate original headers', () => { const reqHeaders = { a: 'foo', b: ['hello', 'world'] }; const resHeaders = { headers: { c: 'bar' } }; diff --git a/src/core/server/http/logging/get_response_log.ts b/src/core/server/http/logging/get_response_log.ts index 711c4bc1e58c9..f356bf077095a 100644 --- a/src/core/server/http/logging/get_response_log.ts +++ b/src/core/server/http/logging/get_response_log.ts @@ -14,7 +14,9 @@ import type { LogMeta, Logger } from '@kbn/logging'; import type { KibanaRequestState } from '@kbn/core-http-server'; import { getResponsePayloadBytes } from './get_payload_size'; -const FORBIDDEN_HEADERS = ['authorization', 'cookie', 'set-cookie']; +// If you are updating these, consider whether they should also be updated in the +// elasticsearch service `getEcsResponseLog` +const FORBIDDEN_HEADERS = ['authorization', 'cookie', 'set-cookie', 'x-elastic-app-auth']; const REDACTED_HEADER_TEXT = '[REDACTED]'; type HapiHeaders = Record; From 1469d60490b772ed0c0caf757f9063ebd2bf2fb6 Mon Sep 17 00:00:00 2001 From: "Joey F. Poon" Date: Tue, 12 Jul 2022 16:40:18 -0500 Subject: [PATCH 34/42] [Security Solution] bubble up macos system extension errors (#136038) --- packages/kbn-doc-links/src/get_doc_links.ts | 1 + packages/kbn-doc-links/src/types.ts | 1 + .../policy_response/policy_response.tsx | 9 +- .../policy_response_action_item.tsx | 2 +- .../policy_response_friendly_names.ts | 98 +++++++++++---- .../policy_response_wrapper.test.tsx | 116 ++++++++++++++---- .../policy_response_wrapper.tsx | 12 +- 7 files changed, 182 insertions(+), 57 deletions(-) diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 87bc77e61bae5..ee2fb5143e92b 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -341,6 +341,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { blocklist: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/blocklist.html`, policyResponseTroubleshooting: { full_disk_access: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/deploy-elastic-endpoint.html#enable-fda-endpoint`, + macos_system_ext: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/deploy-elastic-endpoint.html#system-extension-endpoint`, }, }, query: { diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 3a0e57b43c169..8a7a0e788cbc3 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -247,6 +247,7 @@ export interface DocLinks { readonly blocklist: string; readonly policyResponseTroubleshooting: { full_disk_access: string; + macos_system_ext: string; }; }; readonly query: { diff --git a/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response.tsx b/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response.tsx index 4e5ecebd2c0db..f930ba66747ae 100644 --- a/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response.tsx +++ b/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response.tsx @@ -8,7 +8,6 @@ import React, { memo, useCallback } from 'react'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { DocLinksStart } from '@kbn/core/public'; import { EuiHealth, EuiText, EuiTreeView, EuiNotificationBadge } from '@elastic/eui'; import { useKibana } from '../../../common/lib/kibana'; import type { @@ -47,6 +46,7 @@ const StyledEuiTreeView = styled(EuiTreeView)` `; interface PolicyResponseProps { + hostOs: string; policyResponseConfig: Immutable; policyResponseActions: Immutable; policyResponseAttentionCount: Map; @@ -57,6 +57,7 @@ interface PolicyResponseProps { */ export const PolicyResponse = memo( ({ + hostOs, policyResponseConfig, policyResponseActions, policyResponseAttentionCount, @@ -93,9 +94,8 @@ export const PolicyResponse = memo( const policyResponseActionFormatter = new PolicyResponseActionFormatter( action || {}, - docLinks.links.securitySolution.policyResponseTroubleshooting[ - action.name as keyof DocLinksStart['links']['securitySolution']['policyResponseTroubleshooting'] - ] + docLinks.links.securitySolution.policyResponseTroubleshooting, + hostOs ); return { label: ( @@ -144,6 +144,7 @@ export const PolicyResponse = memo( docLinks.links.securitySolution.policyResponseTroubleshooting, getEntryIcon, policyResponseActions, + hostOs, ] ); diff --git a/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_action_item.tsx b/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_action_item.tsx index 35007e338b78c..f9609141a4705 100644 --- a/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_action_item.tsx +++ b/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_action_item.tsx @@ -38,7 +38,7 @@ export const PolicyResponseActionItem = memo( {policyResponseActionFormatter.linkText && policyResponseActionFormatter.linkUrl && ( {policyResponseActionFormatter.linkText} diff --git a/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_friendly_names.ts b/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_friendly_names.ts index 4c82964853868..deeb50136b3f0 100644 --- a/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_friendly_names.ts +++ b/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_friendly_names.ts @@ -6,8 +6,9 @@ */ import { i18n } from '@kbn/i18n'; +import type { DocLinks } from '@kbn/doc-links'; +import { HostPolicyResponseActionStatus } from '../../../../common/endpoint/types'; import type { - HostPolicyResponseActionStatus, HostPolicyResponseAppliedAction, ImmutableObject, } from '../../../../common/endpoint/types'; @@ -320,6 +321,12 @@ const policyResponseTitles = Object.freeze( defaultMessage: 'Full Disk Access', }), ], + [ + 'macos_system_ext', + i18n.translate('xpack.securitySolution.endpoint.details.policyResponse.macos_system_ext', { + defaultMessage: 'Permissions required', + }), + ], ]) ); @@ -328,25 +335,25 @@ type PolicyResponseStatus = `${HostPolicyResponseActionStatus}`; const policyResponseStatuses = Object.freeze( new Map([ [ - 'success', + HostPolicyResponseActionStatus.success, i18n.translate('xpack.securitySolution.endpoint.details.policyResponse.success', { defaultMessage: 'Success', }), ], [ - 'warning', + HostPolicyResponseActionStatus.warning, i18n.translate('xpack.securitySolution.endpoint.details.policyResponse.warning', { defaultMessage: 'Warning', }), ], [ - 'failure', + HostPolicyResponseActionStatus.failure, i18n.translate('xpack.securitySolution.endpoint.details.policyResponse.failed', { defaultMessage: 'Failed', }), ], [ - 'unsupported', + HostPolicyResponseActionStatus.unsupported, i18n.translate('xpack.securitySolution.endpoint.details.policyResponse.unsupported', { defaultMessage: 'Unsupported', }), @@ -361,7 +368,17 @@ const descriptions = Object.freeze( i18n.translate( 'xpack.securitySolution.endpoint.details.policyResponse.description.full_disk_access', { - defaultMessage: 'You must enable full disk access for Elastic Endpoint on your machine. ', + defaultMessage: 'You must enable full disk access for Elastic Endpoint on your machine.', + } + ), + ], + [ + 'macos_system_ext', + i18n.translate( + 'xpack.securitySolution.endpoint.details.policyResponse.description.macos_system_ext', + { + defaultMessage: + 'You must enable the Mac system extension for Elastic Endpoint on your machine.', } ), ], @@ -375,17 +392,33 @@ const linkTexts = Object.freeze( i18n.translate( 'xpack.securitySolution.endpoint.details.policyResponse.link.text.full_disk_access', { - defaultMessage: 'Learn more.', + defaultMessage: ' Learn more.', + } + ), + ], + [ + 'macos_system_ext', + i18n.translate( + 'xpack.securitySolution.endpoint.details.policyResponse.link.text.macos_system_ext', + { + defaultMessage: ' Learn more.', } ), ], ]) ); -/** - * An array with errors we want to bubble up in policy response - */ -const GENERIC_ACTION_ERRORS: readonly string[] = Object.freeze(['full_disk_access']); +function isMacosFullDiskAccessError(os: string, policyAction: HostPolicyResponseAppliedAction) { + return os === 'macos' && policyAction.name === 'full_disk_access'; +} + +function isMacosSystemExtensionError(os: string, policyAction: HostPolicyResponseAppliedAction) { + return ( + os === 'macos' && + policyAction.name === 'connect_kernel' && + policyAction.status === HostPolicyResponseActionStatus.failure + ); +} export class PolicyResponseActionFormatter { public key: string; @@ -396,28 +429,49 @@ export class PolicyResponseActionFormatter { public errorDescription?: string; public status?: string; public linkText?: string; - public linkUrl?: string; constructor( - policyResponseAppliedAction: ImmutableObject, - link?: string + private policyResponseAppliedAction: ImmutableObject, + private docLinks: DocLinks['securitySolution']['policyResponseTroubleshooting'], + private os: string = '' ) { this.key = policyResponseAppliedAction.name; this.title = - policyResponseTitles.get(this.key) ?? + policyResponseTitles.get(this.errorKey || this.key) ?? this.key.replace(/_/g, ' ').replace(/\b(\w)/g, (m) => m.toUpperCase()); this.hasError = - policyResponseAppliedAction.status === 'failure' || - policyResponseAppliedAction.status === 'warning'; + policyResponseAppliedAction.status === HostPolicyResponseActionStatus.failure || + policyResponseAppliedAction.status === HostPolicyResponseActionStatus.warning; this.description = descriptions.get(this.key) || policyResponseAppliedAction.message; - this.errorDescription = descriptions.get(this.key) || policyResponseAppliedAction.message; + this.errorDescription = + descriptions.get(this.errorKey || this.key) || this.policyResponseAppliedAction.message; this.errorTitle = this.errorDescription ? this.title : policyResponseAppliedAction.name; this.status = policyResponseStatuses.get(policyResponseAppliedAction.status); - this.linkText = linkTexts.get(this.key); - this.linkUrl = link; + this.linkText = linkTexts.get(this.errorKey || this.key); } - public isGeneric(): boolean { - return GENERIC_ACTION_ERRORS.includes(this.key); + public get linkUrl(): string { + return this.docLinks[this.errorKey]; + } + + public get isGeneric(): boolean { + if (isMacosFullDiskAccessError(this.os, this.policyResponseAppliedAction)) { + return true; + } + + if (isMacosSystemExtensionError(this.os, this.policyResponseAppliedAction)) { + return true; + } + + return false; + } + + private get errorKey(): keyof DocLinks['securitySolution']['policyResponseTroubleshooting'] { + if (isMacosSystemExtensionError(this.os, this.policyResponseAppliedAction)) { + return 'macos_system_ext'; + } + + return this.policyResponseAppliedAction + .name as keyof DocLinks['securitySolution']['policyResponseTroubleshooting']; } } diff --git a/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_wrapper.test.tsx b/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_wrapper.test.tsx index 127216bf81ef1..dbd604d4c2c54 100644 --- a/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_wrapper.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_wrapper.test.tsx @@ -18,48 +18,76 @@ import type { HostPolicyResponseAppliedAction, } from '../../../../common/endpoint/types'; import { EndpointDocGenerator } from '../../../../common/endpoint/generate_data'; +import { useGetEndpointDetails } from '../../hooks'; jest.mock('../../hooks/endpoint/use_get_endpoint_policy_response'); +jest.mock('../../hooks/endpoint/use_get_endpoint_details'); describe('when on the policy response', () => { const docGenerator = new EndpointDocGenerator(); const createPolicyResponse = ( - overallStatus: HostPolicyResponseActionStatus = HostPolicyResponseActionStatus.success + overallStatus: HostPolicyResponseActionStatus = HostPolicyResponseActionStatus.success, + extraActions: HostPolicyResponseAppliedAction[] = [ + { + name: 'download_model', + message: 'Failed to apply a portion of the configuration (kernel)', + status: overallStatus, + }, + ] ): HostPolicyResponse => { const policyResponse = docGenerator.generatePolicyResponse(); const malwareResponseConfigurations = policyResponse.Endpoint.policy.applied.response.configurations.malware; policyResponse.Endpoint.policy.applied.status = overallStatus; malwareResponseConfigurations.status = overallStatus; - let downloadModelAction = policyResponse.Endpoint.policy.applied.actions.find( - (action) => action.name === 'download_model' - ); - if (!downloadModelAction) { - downloadModelAction = { - name: 'download_model', - message: 'Failed to apply a portion of the configuration (kernel)', - status: overallStatus, - }; - policyResponse.Endpoint.policy.applied.actions.push(downloadModelAction); - } else { - // Else, make sure the status of the generated action matches what was passed in - downloadModelAction.status = overallStatus; - } + for (const extraAction of extraActions) { + let foundExtraAction = policyResponse.Endpoint.policy.applied.actions.find( + (action) => action.name === extraAction.name + ); - if ( - overallStatus === HostPolicyResponseActionStatus.failure || - overallStatus === HostPolicyResponseActionStatus.warning - ) { - downloadModelAction.message = 'no action taken'; - } + if (!foundExtraAction) { + foundExtraAction = extraAction; + policyResponse.Endpoint.policy.applied.actions.push(foundExtraAction); + } else { + // Else, make sure the status of the generated action matches what was passed in + foundExtraAction.status = overallStatus; + } - // Make sure that at least one configuration has the above action, else - // we get into an out-of-sync condition - if (malwareResponseConfigurations.concerned_actions.indexOf(downloadModelAction.name) === -1) { - malwareResponseConfigurations.concerned_actions.push(downloadModelAction.name); + if ( + overallStatus === HostPolicyResponseActionStatus.failure || + overallStatus === HostPolicyResponseActionStatus.warning + ) { + foundExtraAction.message = 'no action taken'; + } + + // Make sure that at least one configuration has the above action, else + // we get into an out-of-sync condition + if (malwareResponseConfigurations.concerned_actions.indexOf(foundExtraAction.name) === -1) { + malwareResponseConfigurations.concerned_actions.push(foundExtraAction.name); + } } + // if extra actions exist more than once, remove dupes to maintain exact counts + Object.entries(policyResponse.Endpoint.policy.applied.response.configurations).forEach( + ([responseConfigurationKey, responseConfiguration]) => { + if (responseConfigurationKey === 'malware') { + return; + } + + extraActions.forEach((extraAction) => { + const extraActionIndex = responseConfiguration.concerned_actions.indexOf( + extraAction.name + ); + if (extraActionIndex === -1) { + return; + } + + responseConfiguration.concerned_actions.splice(extraActionIndex, 1); + }); + } + ); + // Add an unknown Action Name - to ensure we handle the format of it on the UI const unknownAction: HostPolicyResponseAppliedAction = { status: HostPolicyResponseActionStatus.success, @@ -75,6 +103,7 @@ describe('when on the policy response', () => { let commonPolicyResponse: HostPolicyResponse; const useGetEndpointPolicyResponseMock = useGetEndpointPolicyResponse as jest.Mock; + const useGetEndpointDetailsMock = useGetEndpointDetails as jest.Mock; let render: ( props?: Partial ) => ReturnType; @@ -87,6 +116,14 @@ describe('when on the policy response', () => { isFetching: false, isError: false, }); + useGetEndpointDetailsMock.mockReturnValue({ + data: { + metadata: { host: { os: { name: 'macOS' } } }, + }, + isLoading: false, + isFetching: false, + isError: false, + }); }; beforeEach(() => { @@ -263,5 +300,34 @@ describe('when on the policy response', () => { const calloutLinks = component.queryAllByTestId('endpointPolicyResponseErrorCallOutLink'); expect(calloutLinks.length).toEqual(2); }); + + it('should display correct description and link for macos_system_ext failure', async () => { + policyResponse = createPolicyResponse(HostPolicyResponseActionStatus.failure, [ + { + name: 'connect_kernel', + message: '', + status: HostPolicyResponseActionStatus.failure, + }, + ]); + runMock(policyResponse); + + const component = await renderOpenedTree(); + + const macosSystemExtTitle = 'Permissions required'; + const calloutTitles = component + .queryAllByTestId('endpointPolicyResponseErrorCallOut') + .filter((calloutTitle) => calloutTitle.innerHTML.includes(macosSystemExtTitle)); + expect(calloutTitles.length).toEqual(2); + + const macosSystemExtMessage = + 'You must enable the Mac system extension for Elastic Endpoint on your machine.'; + const calloutMessages = component + .queryAllByTestId('endpointPolicyResponseErrorCallOut') + .filter((calloutMessage) => calloutMessage.innerHTML.includes(macosSystemExtMessage)); + expect(calloutMessages.length).toEqual(2); + + const calloutLinks = component.queryAllByTestId('endpointPolicyResponseErrorCallOutLink'); + expect(calloutLinks.length).toEqual(2); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_wrapper.tsx b/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_wrapper.tsx index fc8fd56184f78..d80f7403e8e72 100644 --- a/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/management/components/policy_response/policy_response_wrapper.tsx @@ -7,7 +7,6 @@ import React, { memo, useEffect, useState, useMemo } from 'react'; import { EuiEmptyPrompt, EuiLoadingSpinner, EuiSpacer, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import type { DocLinksStart } from '@kbn/core/public'; import { useKibana } from '../../../common/lib/kibana'; import type { HostPolicyResponse } from '../../../../common/endpoint/types'; import { PreferenceFormattedDateFromPrimitive } from '../../../common/components/formatted_date'; @@ -16,6 +15,7 @@ import { PolicyResponse } from './policy_response'; import { getFailedOrWarningActionCountFromPolicyResponse } from '../../pages/endpoint_hosts/store/utils'; import { PolicyResponseActionItem } from './policy_response_action_item'; import { PolicyResponseActionFormatter } from './policy_response_friendly_names'; +import { useGetEndpointDetails } from '../../hooks'; export interface PolicyResponseWrapperProps { endpointId: string; @@ -26,6 +26,7 @@ export interface PolicyResponseWrapperProps { export const PolicyResponseWrapper = memo( ({ endpointId, showRevisionMessage = true, onShowNeedsAttentionBadge }) => { const { data, isLoading, isFetching, isError } = useGetEndpointPolicyResponse(endpointId); + const { data: endpointDetails } = useGetEndpointDetails(endpointId); const { docLinks } = useKibana().services; @@ -73,12 +74,11 @@ export const PolicyResponseWrapper = memo( (acc, currentAction) => { const policyResponseActionFormatter = new PolicyResponseActionFormatter( currentAction, - docLinks.links.securitySolution.policyResponseTroubleshooting[ - currentAction.name as keyof DocLinksStart['links']['securitySolution']['policyResponseTroubleshooting'] - ] + docLinks.links.securitySolution.policyResponseTroubleshooting, + endpointDetails?.metadata.host.os.name.toLowerCase() ); - if (policyResponseActionFormatter.isGeneric() && policyResponseActionFormatter.hasError) { + if (policyResponseActionFormatter.isGeneric && policyResponseActionFormatter.hasError) { acc.push(policyResponseActionFormatter); } @@ -90,6 +90,7 @@ export const PolicyResponseWrapper = memo( docLinks.links.securitySolution.policyResponseTroubleshooting, policyResponseActions, policyResponseConfig, + endpointDetails?.metadata.host.os.name, ]); return ( @@ -127,6 +128,7 @@ export const PolicyResponseWrapper = memo( {policyResponseConfig !== undefined && policyResponseActions !== undefined && ( <> Date: Tue, 12 Jul 2022 18:50:15 -0400 Subject: [PATCH 35/42] skip failing test suite (#136242) --- x-pack/test/accessibility/apps/stack_monitoring.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/accessibility/apps/stack_monitoring.ts b/x-pack/test/accessibility/apps/stack_monitoring.ts index f5896cfb8be36..9fbb6ed94e321 100644 --- a/x-pack/test/accessibility/apps/stack_monitoring.ts +++ b/x-pack/test/accessibility/apps/stack_monitoring.ts @@ -18,7 +18,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const kibanaOverview = getService('monitoringKibanaOverview'); const clusterOverview = getService('monitoringClusterOverview'); - describe('Kibana Stack Monitoring a11y tests', () => { + // Failing: See https://github.com/elastic/kibana/issues/136242 + describe.skip('Kibana Stack Monitoring a11y tests', () => { before(async () => { await PageObjects.common.navigateToApp('monitoring'); await a11y.testAppSnapshot(); From 0301d201ee4b6d33dcd4c21794d8a98ac1f02c2f Mon Sep 17 00:00:00 2001 From: Andrew Goldstein Date: Tue, 12 Jul 2022 22:09:17 -0600 Subject: [PATCH 36/42] [Security Solution] Alerts Treemap and Multi-Dimensional Alert Grouping (#126896) ## [Security Solution] Alerts Treemap and Multi-Dimensional Alert Grouping This PR introduces the new _Treemap_ and _Multi-Dimensional Alert Grouping_ to the Alerts page. The initial commit was developed as an _ON week_ proof of concept (POC). It has since been updated to incorporate product and UX feedback. ### Alerts treemap The new _Alerts_ page treemap is shown in the screenshot below: ![treemap](https://user-images.githubusercontent.com/4459398/178233664-c45be7ca-b03e-40b9-b423-aeeaa47461c0.png) _Above: The new `treemap` in the Alerts page_ - Alerts are colored by risk score - Clicking on a cell instantly filters the _Alerts_ page - Treemap legend items may be added to filters and Timeline investigations - The new treemap supports multi-dimensional grouping and filtering - Alerts are grouped by `kibana.alert.rule.name` and `host.name` by default ### Multi-Dimensional Alert Grouping The table on the Alerts page, which previously supported grouping by a single field, has also been enhanced to support multi-dimensional grouping, per the screenshot below: ![alerts-table-multi-dimensional-grouping](https://user-images.githubusercontent.com/4459398/178240710-ecf66799-35a8-4874-8882-5ccfcccc86fe.png) _Above: The table in the Alerts page, enhanced to support multi-dimensional grouping_ ## Filtering the Alerts page by risk score Every rule, including prebuilt Elastic rules and custom rules created by users, must specify a risk score at rule creation time, per the screenshot below: ![rule_risk_score_configuration](https://user-images.githubusercontent.com/4459398/156712042-19b71f53-f337-4aed-bebf-ce10ea2b9f63.png) _Above: Every rule has a risk score specified when it's created_ The colors of the alerts displayed in the treemap are determined by the rule's risk score. This makes it easy to quickly filter the entire alerts page by clicking on the riskiest alerts. Clicking on a cell in the treemap adds two filters, one for each group by field, per the screenshot below: ![two-filters](https://user-images.githubusercontent.com/4459398/178252768-7c66dc5e-8a3c-41d8-95e2-eeca20133127.png) _Above: Two filters, (one for each group by field), are added to the page when a cell is clicked_ The Alerts page updates instantly when filters are added or removed. In the screenshot below, the 2nd filter was removed to filter the page to all `mimikatz process started` alerts: ![second-filter-removed](https://user-images.githubusercontent.com/4459398/178253726-66905d60-99da-4d76-9ea1-744cb53abd6f.png) _Above: Removing the 2nd filter, a specific `host.name`, revealed all the hosts in the `mimikatz process started` alerts_ ### Switching views Users may switch between the following views: - Table - Trend (default) - Treemap per the screenshot below: ![view-selection](https://user-images.githubusercontent.com/4459398/178412669-f437cfe4-f819-45b5-8359-987a2a3c6645.png) _Above: View selection_ The default _Trend_ view is shown in the screenshot below: ![trend-view](https://user-images.githubusercontent.com/4459398/178242769-58d6c800-69db-4e14-87e4-9232f3e35427.png) _Above: The (default) Trend view_ - The Trend chart's legend has been enhanced to display counts, per the design detailed in issue - The Trend view only supports visualizing a single dimension. Hovering over the disabled `Group by top` select in the Trend view displays the tooltip shown in the screenshot below: ![tooltip](https://user-images.githubusercontent.com/4459398/178243356-9bfe7f54-5b31-4f61-a795-0cfa0a70285b.png) _Above: The Trend view only supports visualizing a single dimension_ ### Collapsing the panel The panel may optionally be collapsed to save space, per the screenshot below: ![collapsed](https://user-images.githubusercontent.com/4459398/178244282-525d4c9f-a23b-4ec4-8f72-7e813e771687.png) _Above: The panel (optionally) collapsed_ ### User preferences are persisted to local storage Previously, the _Group by_ selections on the Alerts page were always forgotten when users navigated away from the Alerts page. As a result, users had to re-select their preferred Group by fields every time they visited the page. We now store all of the following preferences in local storage: - View selection (Table, Trend, Treemap) - Group by selections - Panel collapse state The preferences above are now restored the next time users return to the Alerts page. ### Group by field selection is synchronized between views Group by field selection is synchronized between views. For example, if a user changes the Group by fields in the Treemap view and then switches to the Table view, the same Group by fields will be displayed in the Table view. ### Resetting Group by fields to their defaults Users may reset the Group by fields to their defaults (`kibana.alert.rule.name` and `host.name`) for any visualization via the menu shown in the screenshot below: ![reset-group-by-fields](https://user-images.githubusercontent.com/4459398/178246997-70d89763-40d4-4c93-b0b6-b439fc3e22cd.png) _Above: Resetting Group by fields to defaults via the menu_ --- .../changing_alert_status.spec.ts | 10 + .../cypress/screens/alerts.ts | 6 + .../security_solution/cypress/tasks/alerts.ts | 14 +- .../cypress/tasks/rule_details.ts | 4 +- .../components/alerts_treemap/index.test.tsx | 68 ++++ .../components/alerts_treemap/index.tsx | 203 ++++++++++ .../lib/chart_palette/index.test.ts | 108 ++++++ .../alerts_treemap/lib/chart_palette/index.ts | 66 ++++ .../lib/flatten/flatten_bucket.test.ts | 54 +++ .../lib/flatten/flatten_bucket.ts | 23 ++ .../lib/flatten/get_flattened_buckets.test.ts | 148 ++++++++ .../lib/flatten/get_flattened_buckets.ts | 20 + .../lib/flatten/mocks/mock_buckets.ts | 96 +++++ .../flatten/mocks/mock_flattened_buckets.ts | 137 +++++++ .../alerts_treemap/lib/helpers.test.ts | 235 ++++++++++++ .../components/alerts_treemap/lib/helpers.ts | 69 ++++ .../alerts_treemap/lib/labels/index.test.ts | 33 ++ .../alerts_treemap/lib/labels/index.ts | 16 + .../alerts_treemap/lib/layers/index.test.ts | 202 ++++++++++ .../alerts_treemap/lib/layers/index.ts | 101 +++++ .../legend/get_flattened_legend_items.test.ts | 153 ++++++++ .../lib/legend/get_flattened_legend_items.ts | 60 +++ .../alerts_treemap/lib/legend/index.test.ts | 275 ++++++++++++++ .../alerts_treemap/lib/legend/index.ts | 120 ++++++ .../lib/mocks/mock_alert_search_response.ts | 140 +++++++ .../alerts_treemap/no_data/index.test.tsx | 21 ++ .../alerts_treemap/no_data/index.tsx | 30 ++ .../alerts_treemap/query/index.test.ts | 122 ++++++ .../components/alerts_treemap/query/index.ts | 110 ++++++ .../components/alerts_treemap/translations.ts | 38 ++ .../common/components/alerts_treemap/types.ts | 31 ++ .../alerts_treemap_panel/index.test.tsx | 245 ++++++++++++ .../components/alerts_treemap_panel/index.tsx | 204 ++++++++++ .../authentications_host_table.test.tsx.snap | 2 + .../authentications_user_table.test.tsx.snap | 2 + .../configurations/default/index.test.tsx | 78 ++++ .../configurations/default/index.tsx | 69 ++++ .../configurations/default/translations.ts | 22 ++ .../chart_settings_popover/index.test.tsx | 47 +++ .../chart_settings_popover/index.tsx | 64 ++++ .../chart_settings_popover/translations.ts | 15 + .../charts/draggable_legend.test.tsx | 24 +- .../components/charts/draggable_legend.tsx | 8 +- .../charts/draggable_legend_item.test.tsx | 30 ++ .../charts/draggable_legend_item.tsx | 54 ++- .../components/field_selection/index.test.tsx | 77 ++++ .../components/field_selection/index.tsx | 73 ++++ .../__snapshots__/index.test.tsx.snap | 6 +- .../components/header_section/index.test.tsx | 111 +++++- .../components/header_section/index.tsx | 37 +- .../common/components/inspect/index.test.tsx | 27 ++ .../common/components/inspect/index.tsx | 6 +- .../components/local_storage/helpers.test.ts | 37 ++ .../components/local_storage/helpers.ts | 22 ++ .../components/local_storage/index.test.tsx | 182 +++++++++ .../common/components/local_storage/index.tsx | 57 +++ .../matrix_histogram/index.test.tsx | 10 +- .../alerts_count_panel/alerts_count.test.tsx | 210 ++++++++--- .../alerts_count_panel/alerts_count.tsx | 137 ++++--- .../alerts_count_panel/columns.test.tsx | 77 ++++ .../alerts_count_panel/columns.tsx | 108 ++++++ .../alerts_count_panel/helpers.test.tsx | 132 +++++++ .../alerts_count_panel/helpers.tsx | 39 +- .../alerts_count_panel/index.test.tsx | 77 +++- .../alerts_kpis/alerts_count_panel/index.tsx | 85 ++++- .../mocks/mock_response_empty_field0.ts | 35 ++ .../mocks/mock_response_multi_group.ts | 61 +++ .../mocks/mock_response_single_group.ts | 146 ++++++++ .../alerts_count_panel/translations.ts | 8 +- .../alerts_kpis/alerts_count_panel/types.ts | 8 +- .../alerts_histogram.test.tsx | 71 +++- .../alerts_histogram.tsx | 8 +- .../alerts_histogram_panel/index.test.tsx | 340 ++++++++++++++++- .../alerts_histogram_panel/index.tsx | 83 ++++- .../alerts_histogram_panel/mock_data.ts | 351 ++++++++++++++++++ .../alerts_histogram_panel/translations.ts | 7 + .../alerts_histogram_panel/types.ts | 1 + .../alerts_kpis/common/components.test.tsx | 205 ++++++++++ .../alerts_kpis/common/components.tsx | 52 ++- .../components/alerts_kpis/common/config.ts | 1 + .../alerts_kpis/common/translations.ts | 14 + .../components/rules/step_about_rule/data.tsx | 30 +- .../detection_engine/alerts/types.ts | 1 + .../alerts_local_storage/constants.ts | 36 ++ .../alerts_local_storage/index.test.tsx | 36 ++ .../alerts_local_storage/index.tsx | 118 ++++++ .../alerts_local_storage/types.ts | 25 ++ .../chart_context_menu/index.test.tsx | 87 +++++ .../chart_panels/chart_context_menu/index.tsx | 54 +++ .../chart_panels/chart_select/helpers.test.ts | 90 +++++ .../chart_panels/chart_select/helpers.ts | 78 ++++ .../chart_panels/chart_select/index.test.tsx | 43 +++ .../chart_panels/chart_select/index.tsx | 73 ++++ .../chart_panels/chart_select/translations.ts | 30 ++ .../chart_panels/index.test.tsx | 233 ++++++++++++ .../detection_engine/chart_panels/index.tsx | 204 ++++++++++ .../detection_engine.test.tsx | 18 + .../detection_engine/detection_engine.tsx | 79 ++-- 98 files changed, 7449 insertions(+), 264 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/index.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/chart_palette/index.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/chart_palette/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/flatten_bucket.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/flatten_bucket.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/get_flattened_buckets.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/get_flattened_buckets.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/mocks/mock_buckets.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/mocks/mock_flattened_buckets.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/helpers.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/helpers.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/labels/index.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/labels/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/layers/index.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/layers/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/get_flattened_legend_items.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/get_flattened_legend_items.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/index.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/mocks/mock_alert_search_response.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/no_data/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/no_data/index.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/query/index.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/query/index.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/translations.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap/types.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/chart_settings_popover/configurations/default/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/chart_settings_popover/configurations/default/index.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/chart_settings_popover/configurations/default/translations.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/chart_settings_popover/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/chart_settings_popover/index.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/chart_settings_popover/translations.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/field_selection/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/field_selection/index.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/local_storage/helpers.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/local_storage/helpers.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/local_storage/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/local_storage/index.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_empty_field0.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_multi_group.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_single_group.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/alerts_kpis/common/components.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/constants.ts create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/index.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/types.ts create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_context_menu/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_context_menu/index.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.ts create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/translations.ts create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/changing_alert_status.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/changing_alert_status.spec.ts index 45910fede0686..9500e19be545f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/changing_alert_status.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/changing_alert_status.spec.ts @@ -20,12 +20,14 @@ import { waitForAlerts, markAcknowledgedFirstAlert, goToAcknowledgedAlerts, + clearGroupByTopInput, closeAlerts, closeFirstAlert, goToClosedAlerts, goToOpenedAlerts, openAlerts, openFirstAlert, + selectCountTable, } from '../../tasks/alerts'; import { createCustomRuleEnabled } from '../../tasks/api_calls/rules'; import { cleanKibana, deleteAlertsAndRules } from '../../tasks/common'; @@ -53,6 +55,8 @@ describe('Changing alert status', () => { cy.get(SELECTED_ALERTS).should('have.text', `Selected 3 alerts`); closeAlerts(); waitForAlerts(); + selectCountTable(); + clearGroupByTopInput(); }); it('Open one alert when more than one closed alerts are selected', () => { @@ -110,6 +114,8 @@ describe('Changing alert status', () => { createCustomRuleEnabled(getNewRule()); visit(ALERTS_URL); waitForAlertsToPopulate(); + selectCountTable(); + clearGroupByTopInput(); }); it('Mark one alert as acknowledged when more than one open alerts are selected', () => { cy.get(ALERTS_COUNT) @@ -148,6 +154,8 @@ describe('Changing alert status', () => { createCustomRuleEnabled(getNewRule(), '1', '100m', 100); visit(ALERTS_URL); waitForAlertsToPopulate(); + selectCountTable(); + clearGroupByTopInput(); }); it('Closes and opens alerts', () => { const numberOfAlertsToBeClosed = 3; @@ -298,6 +306,8 @@ describe('Changing alert status', () => { createCustomRuleEnabled(getNewRule()); visit(ALERTS_URL); waitForAlertsToPopulate(); + selectCountTable(); + clearGroupByTopInput(); }); it('Mark one alert as acknowledged when more than one open alerts are selected', () => { cy.get(ALERTS_COUNT) diff --git a/x-pack/plugins/security_solution/cypress/screens/alerts.ts b/x-pack/plugins/security_solution/cypress/screens/alerts.ts index b37cc82e7108b..913ef6bd724b4 100644 --- a/x-pack/plugins/security_solution/cypress/screens/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/screens/alerts.ts @@ -33,6 +33,8 @@ export const ALERTS_COUNT = export const ALERTS_TREND_SIGNAL_RULE_NAME_PANEL = '[data-test-subj="render-content-kibana.alert.rule.name"]'; +export const CHART_SELECT = '[data-test-subj="chartSelect"]'; + export const CLOSE_ALERT_BTN = '[data-test-subj="close-alert-status"]'; export const CLOSE_SELECTED_ALERTS_BTN = '[data-test-subj="close-alert-status"]'; @@ -45,6 +47,8 @@ export const EMPTY_ALERT_TABLE = '[data-test-subj="tGridEmptyState"]'; export const EXPAND_ALERT_BTN = '[data-test-subj="expand-event"]'; +export const GROUP_BY_TOP_INPUT = '[data-test-subj="groupByTop"] [data-test-subj="comboBoxInput"]'; + export const HOST_NAME = '[data-test-subj^=formatted-field][data-test-subj$=host\\.name]'; export const ACKNOWLEDGED_ALERTS_FILTER_BTN = '[data-test-subj="acknowledgedAlerts"]'; @@ -74,6 +78,8 @@ export const RULE_NAME = '[data-test-subj^=formatted-field][data-test-subj$=rule export const SELECTED_ALERTS = '[data-test-subj="selectedShowBulkActionsButton"]'; +export const SELECT_TABLE = '[data-test-subj="table"]'; + export const SEND_ALERT_TO_TIMELINE_BTN = '[data-test-subj="send-alert-to-timeline-button"]'; export const SEVERITY = '[data-test-subj^=formatted-field][data-test-subj$=severity]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts index ea86f1ba96559..22e57d8b0c7db 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/alerts.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/alerts.ts @@ -9,10 +9,12 @@ import { ADD_EXCEPTION_BTN, ALERT_RISK_SCORE_HEADER, ALERT_CHECKBOX, + CHART_SELECT, CLOSE_ALERT_BTN, CLOSE_SELECTED_ALERTS_BTN, CLOSED_ALERTS_FILTER_BTN, EXPAND_ALERT_BTN, + GROUP_BY_TOP_INPUT, ACKNOWLEDGED_ALERTS_FILTER_BTN, LOADING_ALERTS_PANEL, MANAGE_ALERT_DETECTION_RULES_BTN, @@ -20,6 +22,7 @@ import { OPEN_ALERT_BTN, OPENED_ALERTS_FILTER_BTN, SEND_ALERT_TO_TIMELINE_BTN, + SELECT_TABLE, TAKE_ACTION_POPOVER_BTN, TIMELINE_CONTEXT_MENU_BTN, } from '../screens/alerts'; @@ -65,7 +68,6 @@ export const closeAlerts = () => { export const expandFirstAlertActions = () => { cy.get(TIMELINE_CONTEXT_MENU_BTN).should('be.visible'); - cy.get(TIMELINE_CONTEXT_MENU_BTN).find('svg').should('have.attr', 'data-is-loaded'); cy.get(TIMELINE_CONTEXT_MENU_BTN).first().click({ force: true }); }; @@ -125,6 +127,16 @@ export const openAlerts = () => { cy.get(OPEN_ALERT_BTN).click(); }; +export const selectCountTable = () => { + cy.get(CHART_SELECT).click({ force: true }); + cy.get(SELECT_TABLE).click(); +}; + +export const clearGroupByTopInput = () => { + cy.get(GROUP_BY_TOP_INPUT).focus(); + cy.get(GROUP_BY_TOP_INPUT).type('{backspace}'); +}; + export const goToAcknowledgedAlerts = () => { cy.get(ACKNOWLEDGED_ALERTS_FILTER_BTN).click(); cy.get(REFRESH_BUTTON).should('not.have.attr', 'aria-label', 'Needs updating'); diff --git a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts index 7f46061d4b03c..15ef032ca4878 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/rule_details.ts @@ -99,9 +99,9 @@ export const goToExceptionsTab = () => { }; export const editException = () => { - cy.get(EXCEPTION_ITEM_ACTIONS_BUTTON).click(); + cy.get(EXCEPTION_ITEM_ACTIONS_BUTTON).click({ force: true }); - cy.get(EDIT_EXCEPTION_BTN).click(); + cy.get(EDIT_EXCEPTION_BTN).click({ force: true }); }; export const removeException = () => { diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/index.test.tsx new file mode 100644 index 0000000000000..1337d9234c3a1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/index.test.tsx @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { TestProviders } from '../../mock'; +import { + mockAlertSearchResponse, + mockNoDataAlertSearchResponse, +} from './lib/mocks/mock_alert_search_response'; +import * as i18n from './translations'; +import type { Props } from '.'; +import { AlertsTreemap } from '.'; + +const defaultProps: Props = { + data: mockAlertSearchResponse, + maxBuckets: 1000, + minChartHeight: 370, + stackByField0: 'kibana.alert.rule.name', + stackByField1: 'host.name', +}; + +describe('AlertsTreemap', () => { + describe('when the response has data', () => { + beforeEach(() => { + render( + + + + ); + }); + + test('it renders the treemap', () => { + expect(screen.getByTestId('treemap').querySelector('.echChart')).toBeInTheDocument(); + }); + + test('it renders the legend', () => { + expect(screen.getByTestId('draggable-legend')).toBeInTheDocument(); + }); + }); + + describe('when the response does NOT have data', () => { + beforeEach(() => { + render( + + + + ); + }); + + test('it does NOT render the treemap', () => { + expect(screen.queryByTestId('treemap')).not.toBeInTheDocument(); + }); + + test('it does NOT render the legend', () => { + expect(screen.queryByTestId('draggable-legend')).not.toBeInTheDocument(); + }); + + test('it renders the "no data" message', () => { + expect(screen.getByText(i18n.NO_DATA_LABEL)).toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/index.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/index.tsx new file mode 100644 index 0000000000000..07777f3bdfdcc --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/index.tsx @@ -0,0 +1,203 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Datum, ElementClickListener, PartialTheme } from '@elastic/charts'; +import { Chart, Partition, PartitionLayout, Settings } from '@elastic/charts'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { isEmpty } from 'lodash/fp'; +import React, { useCallback, useMemo } from 'react'; +import styled from 'styled-components'; + +import { useTheme } from '../charts/common'; +import { DraggableLegend } from '../charts/draggable_legend'; +import type { LegendItem } from '../charts/draggable_legend_item'; +import type { AlertSearchResponse } from '../../../detections/containers/detection_engine/alerts/types'; +import { getRiskScorePalette, RISK_SCORE_STEPS } from './lib/chart_palette'; +import { getFlattenedBuckets } from './lib/flatten/get_flattened_buckets'; +import { getFlattenedLegendItems } from './lib/legend/get_flattened_legend_items'; +import { + getGroupByFieldsOnClick, + getMaxRiskSubAggregations, + getUpToMaxBuckets, + hasOptionalStackByField, +} from './lib/helpers'; +import { getLayersMultiDimensional, getLayersOneDimension } from './lib/layers'; +import { getFirstGroupLegendItems } from './lib/legend'; +import { NoData } from './no_data'; +import type { AlertsTreeMapAggregation, FlattenedBucket, RawBucket } from './types'; + +export const DEFAULT_MIN_CHART_HEIGHT = 370; // px +const DEFAULT_LEGEND_WIDTH = 300; // px + +export interface Props { + addFilter?: ({ field, value }: { field: string; value: string | number }) => void; + data: AlertSearchResponse; + maxBuckets: number; + minChartHeight?: number; + stackByField0: string; + stackByField1: string | undefined; +} + +const LegendContainer = styled.div` + margin-left: ${({ theme }) => theme.eui.euiSizeS}; +`; + +const ChartFlexItem = styled(EuiFlexItem)<{ $minChartHeight: number }>` + min-height: ${({ $minChartHeight }) => `${$minChartHeight}px`}; +`; + +const AlertsTreemapComponent: React.FC = ({ + addFilter, + data, + maxBuckets, + minChartHeight = DEFAULT_MIN_CHART_HEIGHT, + stackByField0, + stackByField1, +}: Props) => { + const theme = useTheme(); + const fillColor = useMemo(() => theme.background.color, [theme.background.color]); + + const treemapTheme: PartialTheme[] = useMemo( + () => [ + { + partition: { + fillLabel: { valueFont: { fontWeight: 700 } }, + idealFontSizeJump: 1.15, + maxFontSize: 16, + minFontSize: 8, + sectorLineStroke: fillColor, // draws the light or dark "lines" between partitions + sectorLineWidth: 1.5, + }, + }, + ], + [fillColor] + ); + + const buckets: RawBucket[] = useMemo( + () => + getUpToMaxBuckets({ + buckets: data.aggregations?.stackByField0?.buckets, + maxItems: maxBuckets, + }), + [data.aggregations?.stackByField0?.buckets, maxBuckets] + ); + + const maxRiskSubAggregations = useMemo(() => getMaxRiskSubAggregations(buckets), [buckets]); + + const flattenedBuckets: FlattenedBucket[] = useMemo( + () => + getFlattenedBuckets({ + buckets, + maxRiskSubAggregations, + stackByField0, + }), + [buckets, maxRiskSubAggregations, stackByField0] + ); + + const colorPalette = useMemo(() => getRiskScorePalette(RISK_SCORE_STEPS), []); + + const legendItems: LegendItem[] = useMemo( + () => + flattenedBuckets.length === 0 + ? getFirstGroupLegendItems({ + buckets, + colorPalette, + maxRiskSubAggregations, + stackByField0, + }) + : getFlattenedLegendItems({ + buckets, + colorPalette, + flattenedBuckets, + maxRiskSubAggregations, + stackByField0, + stackByField1, + }), + [buckets, colorPalette, flattenedBuckets, maxRiskSubAggregations, stackByField0, stackByField1] + ); + + const onElementClick: ElementClickListener = useCallback( + (event) => { + const { groupByField0, groupByField1 } = getGroupByFieldsOnClick(event); + + if (addFilter != null && !isEmpty(groupByField0.trim())) { + addFilter({ field: stackByField0, value: groupByField0 }); + } + + if (addFilter != null && !isEmpty(stackByField1?.trim()) && !isEmpty(groupByField1.trim())) { + addFilter({ field: `${stackByField1}`, value: groupByField1 }); + } + }, + [addFilter, stackByField0, stackByField1] + ); + + const layers = useMemo( + () => + hasOptionalStackByField(stackByField1) + ? getLayersMultiDimensional({ + colorPalette, + layer0FillColor: fillColor, + maxRiskSubAggregations, + }) + : getLayersOneDimension({ colorPalette, maxRiskSubAggregations }), + [colorPalette, fillColor, maxRiskSubAggregations, stackByField1] + ); + + const valueAccessor = useMemo( + () => + hasOptionalStackByField(stackByField1) + ? (d: Datum) => d.stackByField1DocCount + : (d: Datum) => d.doc_count, + [stackByField1] + ); + + const normalizedData: FlattenedBucket[] = hasOptionalStackByField(stackByField1) + ? flattenedBuckets + : buckets; + + if (buckets.length === 0) { + return ; + } + + return ( +
    + + + + + + + + + + + {legendItems.length > 0 && ( + + )} + + + +
    + ); +}; + +export const AlertsTreemap = React.memo(AlertsTreemapComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/chart_palette/index.test.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/chart_palette/index.test.ts new file mode 100644 index 0000000000000..8cc9e45d43b6e --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/chart_palette/index.test.ts @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { euiPaletteWarm } from '@elastic/eui'; +import { + RISK_COLOR_LOW, + RISK_COLOR_MEDIUM, + RISK_COLOR_HIGH, + RISK_COLOR_CRITICAL, + RISK_SCORE_MEDIUM, + RISK_SCORE_HIGH, + RISK_SCORE_CRITICAL, +} from '../../../../../detections/components/rules/step_about_rule/data'; +import { getFillColor, getRiskScorePalette, RISK_SCORE_STEPS } from '.'; + +describe('getFillColor', () => { + describe('when using the Risk Score palette', () => { + const colorPalette = getRiskScorePalette(RISK_SCORE_STEPS); + + it('returns the expected fill color', () => { + expect(getFillColor({ riskScore: 50, colorPalette })).toEqual('#d6bf57'); + }); + + it('returns the expected fill color when risk score is zero', () => { + expect(getFillColor({ riskScore: 0, colorPalette })).toEqual('#54b399'); + }); + + it('returns the expected fill color when risk score is less than zero', () => { + expect(getFillColor({ riskScore: -1, colorPalette })).toEqual('#54b399'); + }); + + it('returns the expected fill color when risk score is 100', () => { + expect(getFillColor({ riskScore: 100, colorPalette })).toEqual('#e7664c'); + }); + + it('returns the expected fill color when risk score is greater than 100', () => { + expect(getFillColor({ riskScore: 101, colorPalette })).toEqual('#e7664c'); + }); + + it('returns the expected fill color when risk score is greater than RISK_SCORE_CRITICAL', () => { + expect(getFillColor({ riskScore: RISK_SCORE_CRITICAL + 1, colorPalette })).toEqual( + RISK_COLOR_CRITICAL + ); + }); + + it('returns the expected fill color when risk score is equal to RISK_SCORE_CRITICAL', () => { + expect(getFillColor({ riskScore: RISK_SCORE_CRITICAL, colorPalette })).toEqual( + RISK_COLOR_CRITICAL + ); + }); + + it('returns the expected fill color when risk score is greater than RISK_SCORE_HIGH', () => { + expect(getFillColor({ riskScore: RISK_SCORE_HIGH + 1, colorPalette })).toEqual( + RISK_COLOR_HIGH + ); + }); + + it('returns the expected fill color when risk score is equal to RISK_SCORE_HIGH', () => { + expect(getFillColor({ riskScore: RISK_SCORE_HIGH, colorPalette })).toEqual(RISK_COLOR_HIGH); + }); + + it('returns the expected fill color when risk score is greater than RISK_SCORE_MEDIUM', () => { + expect(getFillColor({ riskScore: RISK_SCORE_MEDIUM + 1, colorPalette })).toEqual( + RISK_COLOR_MEDIUM + ); + }); + + it('returns the expected fill color when risk score is equal to RISK_SCORE_MEDIUM', () => { + expect(getFillColor({ riskScore: RISK_SCORE_MEDIUM, colorPalette })).toEqual( + RISK_COLOR_MEDIUM + ); + }); + + it('returns the expected fill color when risk score is less than RISK_SCORE_MEDIUM', () => { + expect(getFillColor({ riskScore: RISK_SCORE_MEDIUM - 1, colorPalette })).toEqual( + RISK_COLOR_LOW + ); + }); + }); + + describe('when using an EUI palette', () => { + const colorPalette = euiPaletteWarm(RISK_SCORE_STEPS); + + it('returns the expected fill color', () => { + expect(getFillColor({ riskScore: 50, colorPalette })).toEqual('#efb685'); + }); + + it('returns the expected fill color when risk score is zero', () => { + expect(getFillColor({ riskScore: 0, colorPalette })).toEqual('#fbfada'); + }); + + it('returns the expected fill color when risk score is less than zero', () => { + expect(getFillColor({ riskScore: -1, colorPalette })).toEqual('#fbfada'); + }); + + it('returns the expected fill color when risk score is 100', () => { + expect(getFillColor({ riskScore: 100, colorPalette })).toEqual('#e7664c'); + }); + + it('returns the expected fill color when risk score is greater than 100', () => { + expect(getFillColor({ riskScore: 101, colorPalette })).toEqual('#e7664c'); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/chart_palette/index.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/chart_palette/index.ts new file mode 100644 index 0000000000000..56c474522172a --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/chart_palette/index.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { clamp } from 'lodash/fp'; + +import { + RISK_COLOR_LOW, + RISK_COLOR_MEDIUM, + RISK_COLOR_HIGH, + RISK_COLOR_CRITICAL, + RISK_SCORE_MEDIUM, + RISK_SCORE_HIGH, + RISK_SCORE_CRITICAL, +} from '../../../../../detections/components/rules/step_about_rule/data'; + +/** + * The detection engine creates risk scores in the range 1 - 100. + * These steps also include a score of "zero", to enable lookups + * via an array index. + */ +export const RISK_SCORE_STEPS = 101; + +/** + * Returns a color palette that maps a risk score to the risk score colors + * defined by the Security Solution. + * + * The pallet defines values for a risk score between 0 and 100 (inclusive), + * but in practice, the detection engine only generates scores between 1-100. + * + * This pallet has the same type as `EuiPalette`, which is not exported by + * EUI at the time of this writing. + */ +export const getRiskScorePalette = (steps: number): string[] => + Array(steps) + .fill(0) + .map((_, i) => { + if (i >= RISK_SCORE_CRITICAL) { + return RISK_COLOR_CRITICAL; + } else if (i >= RISK_SCORE_HIGH) { + return RISK_COLOR_HIGH; + } else if (i >= RISK_SCORE_MEDIUM) { + return RISK_COLOR_MEDIUM; + } else { + return RISK_COLOR_LOW; + } + }); + +/** Returns a fill color based on the index of the risk score in the color palette */ +export const getFillColor = ({ + riskScore, + colorPalette, +}: { + riskScore: number; + colorPalette: string[]; +}): string => { + const MIN_RISK_SCORE = 0; + const MAX_RISK_SCORE = Math.min(100, colorPalette.length); + + const clampedScore = clamp(MIN_RISK_SCORE, MAX_RISK_SCORE, riskScore); + + return colorPalette[clampedScore]; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/flatten_bucket.test.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/flatten_bucket.test.ts new file mode 100644 index 0000000000000..141d73c923bb8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/flatten_bucket.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { flattenBucket } from './flatten_bucket'; +import { + bucketsWithStackByField1, + bucketsWithoutStackByField1, + maxRiskSubAggregations, +} from './mocks/mock_buckets'; + +describe('flattenBucket', () => { + it(`returns the expected flattened buckets when stackByField1 has buckets`, () => { + expect(flattenBucket({ bucket: bucketsWithStackByField1[0], maxRiskSubAggregations })).toEqual([ + { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1DocCount: 12, + stackByField1Key: 'Host-k8iyfzraq9', + }, + { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1DocCount: 10, + stackByField1Key: 'Host-ao1a4wu7vn', + }, + { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1DocCount: 7, + stackByField1Key: 'Host-3fbljiq8rj', + }, + { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1DocCount: 5, + stackByField1Key: 'Host-r4y6xi92ob', + }, + ]); + }); + + it(`returns an empty array when there's nothing to flatten, because stackByField1 is undefined`, () => { + expect( + flattenBucket({ bucket: bucketsWithoutStackByField1[0], maxRiskSubAggregations }) + ).toEqual([]); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/flatten_bucket.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/flatten_bucket.ts new file mode 100644 index 0000000000000..aa7966e7f79cd --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/flatten_bucket.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RawBucket, FlattenedBucket } from '../../types'; + +export const flattenBucket = ({ + bucket, + maxRiskSubAggregations, +}: { + bucket: RawBucket; + maxRiskSubAggregations: Record; +}): FlattenedBucket[] => + bucket.stackByField1?.buckets?.map((x) => ({ + doc_count: bucket.doc_count, + key: bucket.key, + maxRiskSubAggregation: bucket.maxRiskSubAggregation, + stackByField1Key: x.key, + stackByField1DocCount: x.doc_count, + })) ?? []; diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/get_flattened_buckets.test.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/get_flattened_buckets.test.ts new file mode 100644 index 0000000000000..f3292c0c8b2d3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/get_flattened_buckets.test.ts @@ -0,0 +1,148 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getFlattenedBuckets } from './get_flattened_buckets'; +import { + bucketsWithStackByField1, + bucketsWithoutStackByField1, + maxRiskSubAggregations, +} from './mocks/mock_buckets'; + +describe('getFlattenedBuckets', () => { + it(`returns the expected flattened buckets when stackByField1 has buckets`, () => { + expect( + getFlattenedBuckets({ + buckets: bucketsWithStackByField1, + maxRiskSubAggregations, + stackByField0: 'kibana.alert.rule.name', + }) + ).toEqual([ + { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1Key: 'Host-k8iyfzraq9', + stackByField1DocCount: 12, + }, + { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1Key: 'Host-ao1a4wu7vn', + stackByField1DocCount: 10, + }, + { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1Key: 'Host-3fbljiq8rj', + stackByField1DocCount: 7, + }, + { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1Key: 'Host-r4y6xi92ob', + stackByField1DocCount: 5, + }, + { + doc_count: 28, + key: 'EQL process sequence', + maxRiskSubAggregation: { value: 73 }, + stackByField1Key: 'Host-k8iyfzraq9', + stackByField1DocCount: 10, + }, + { + doc_count: 28, + key: 'EQL process sequence', + maxRiskSubAggregation: { value: 73 }, + stackByField1Key: 'Host-ao1a4wu7vn', + stackByField1DocCount: 7, + }, + { + doc_count: 28, + key: 'EQL process sequence', + maxRiskSubAggregation: { value: 73 }, + stackByField1Key: 'Host-3fbljiq8rj', + stackByField1DocCount: 5, + }, + { + doc_count: 28, + key: 'EQL process sequence', + maxRiskSubAggregation: { value: 73 }, + stackByField1Key: 'Host-r4y6xi92ob', + stackByField1DocCount: 3, + }, + { + doc_count: 19, + key: 'Endpoint Security', + maxRiskSubAggregation: { value: 47 }, + stackByField1Key: 'Host-ao1a4wu7vn', + stackByField1DocCount: 11, + }, + { + doc_count: 19, + key: 'Endpoint Security', + maxRiskSubAggregation: { value: 47 }, + stackByField1Key: 'Host-3fbljiq8rj', + stackByField1DocCount: 6, + }, + { + doc_count: 19, + key: 'Endpoint Security', + maxRiskSubAggregation: { value: 47 }, + stackByField1Key: 'Host-k8iyfzraq9', + stackByField1DocCount: 1, + }, + { + doc_count: 19, + key: 'Endpoint Security', + maxRiskSubAggregation: { value: 47 }, + stackByField1Key: 'Host-r4y6xi92ob', + stackByField1DocCount: 1, + }, + { + doc_count: 5, + key: 'mimikatz process started', + maxRiskSubAggregation: { value: 99 }, + stackByField1Key: 'Host-k8iyfzraq9', + stackByField1DocCount: 3, + }, + { + doc_count: 5, + key: 'mimikatz process started', + maxRiskSubAggregation: { value: 99 }, + stackByField1Key: 'Host-3fbljiq8rj', + stackByField1DocCount: 1, + }, + { + doc_count: 5, + key: 'mimikatz process started', + maxRiskSubAggregation: { value: 99 }, + stackByField1Key: 'Host-r4y6xi92ob', + stackByField1DocCount: 1, + }, + { + doc_count: 1, + key: 'Threshold rule', + maxRiskSubAggregation: { value: 99 }, + stackByField1Key: 'Host-r4y6xi92ob', + stackByField1DocCount: 1, + }, + ]); + }); + + it(`returns an empty array when there's nothing to flatten, because stackByField1 is undefined`, () => { + expect( + getFlattenedBuckets({ + buckets: bucketsWithoutStackByField1, + maxRiskSubAggregations, + stackByField0: 'kibana.alert.rule.name', + }) + ).toEqual([]); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/get_flattened_buckets.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/get_flattened_buckets.ts new file mode 100644 index 0000000000000..a09a9c505a5ec --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/get_flattened_buckets.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { flattenBucket } from './flatten_bucket'; +import type { RawBucket, FlattenedBucket } from '../../types'; + +export const getFlattenedBuckets = ({ + buckets, + maxRiskSubAggregations, + stackByField0, +}: { + buckets: RawBucket[]; + maxRiskSubAggregations: Record; + stackByField0: string; +}): FlattenedBucket[] => + buckets.flatMap((bucket) => flattenBucket({ bucket, maxRiskSubAggregations })); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/mocks/mock_buckets.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/mocks/mock_buckets.ts new file mode 100644 index 0000000000000..59f1cb6de0997 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/mocks/mock_buckets.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RawBucket } from '../../../types'; + +export const bucketsWithStackByField1: RawBucket[] = [ + { + key: 'matches everything', + doc_count: 34, + maxRiskSubAggregation: { value: 21 }, + stackByField1: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'Host-k8iyfzraq9', doc_count: 12 }, + { key: 'Host-ao1a4wu7vn', doc_count: 10 }, + { key: 'Host-3fbljiq8rj', doc_count: 7 }, + { key: 'Host-r4y6xi92ob', doc_count: 5 }, + ], + }, + }, + { + key: 'EQL process sequence', + doc_count: 28, + maxRiskSubAggregation: { value: 73 }, + stackByField1: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'Host-k8iyfzraq9', doc_count: 10 }, + { key: 'Host-ao1a4wu7vn', doc_count: 7 }, + { key: 'Host-3fbljiq8rj', doc_count: 5 }, + { key: 'Host-r4y6xi92ob', doc_count: 3 }, + ], + }, + }, + { + key: 'Endpoint Security', + doc_count: 19, + maxRiskSubAggregation: { value: 47 }, + stackByField1: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'Host-ao1a4wu7vn', doc_count: 11 }, + { key: 'Host-3fbljiq8rj', doc_count: 6 }, + { key: 'Host-k8iyfzraq9', doc_count: 1 }, + { key: 'Host-r4y6xi92ob', doc_count: 1 }, + ], + }, + }, + { + key: 'mimikatz process started', + doc_count: 5, + maxRiskSubAggregation: { value: 99 }, + stackByField1: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'Host-k8iyfzraq9', doc_count: 3 }, + { key: 'Host-3fbljiq8rj', doc_count: 1 }, + { key: 'Host-r4y6xi92ob', doc_count: 1 }, + ], + }, + }, + { + key: 'Threshold rule', + doc_count: 1, + maxRiskSubAggregation: { value: 99 }, + stackByField1: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [{ key: 'Host-r4y6xi92ob', doc_count: 1 }], + }, + }, +]; + +export const bucketsWithoutStackByField1 = [ + { key: 'matches everything', doc_count: 34, maxRiskSubAggregation: { value: 21 } }, + { key: 'EQL process sequence', doc_count: 28, maxRiskSubAggregation: { value: 73 } }, + { key: 'Endpoint Security', doc_count: 19, maxRiskSubAggregation: { value: 47 } }, + { key: 'mimikatz process started', doc_count: 5, maxRiskSubAggregation: { value: 99 } }, + { key: 'Threshold rule', doc_count: 1, maxRiskSubAggregation: { value: 99 } }, +]; + +export const maxRiskSubAggregations = { + 'matches everything': 21, + 'EQL process sequence': 73, + 'Endpoint Security': 47, + 'mimikatz process started': 99, + 'Threshold rule': 99, +}; diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/mocks/mock_flattened_buckets.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/mocks/mock_flattened_buckets.ts new file mode 100644 index 0000000000000..0c465d6af116f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/flatten/mocks/mock_flattened_buckets.ts @@ -0,0 +1,137 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { FlattenedBucket } from '../../../types'; + +export const flattenedBuckets: FlattenedBucket[] = [ + { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1Key: 'Host-k8iyfzraq9', + stackByField1DocCount: 12, + }, + { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1Key: 'Host-ao1a4wu7vn', + stackByField1DocCount: 10, + }, + { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1Key: 'Host-3fbljiq8rj', + stackByField1DocCount: 7, + }, + { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1Key: 'Host-r4y6xi92ob', + stackByField1DocCount: 5, + }, + { + doc_count: 28, + key: 'EQL process sequence', + maxRiskSubAggregation: { value: 73 }, + stackByField1Key: 'Host-k8iyfzraq9', + stackByField1DocCount: 10, + }, + { + doc_count: 28, + key: 'EQL process sequence', + maxRiskSubAggregation: { value: 73 }, + stackByField1Key: 'Host-ao1a4wu7vn', + stackByField1DocCount: 7, + }, + { + doc_count: 28, + key: 'EQL process sequence', + maxRiskSubAggregation: { value: 73 }, + stackByField1Key: 'Host-3fbljiq8rj', + stackByField1DocCount: 5, + }, + { + doc_count: 28, + key: 'EQL process sequence', + maxRiskSubAggregation: { value: 73 }, + stackByField1Key: 'Host-r4y6xi92ob', + stackByField1DocCount: 3, + }, + { + doc_count: 19, + key: 'Endpoint Security', + maxRiskSubAggregation: { value: 47 }, + stackByField1Key: 'Host-ao1a4wu7vn', + stackByField1DocCount: 11, + }, + { + doc_count: 19, + key: 'Endpoint Security', + maxRiskSubAggregation: { value: 47 }, + stackByField1Key: 'Host-3fbljiq8rj', + stackByField1DocCount: 6, + }, + { + doc_count: 19, + key: 'Endpoint Security', + maxRiskSubAggregation: { value: 47 }, + stackByField1Key: 'Host-k8iyfzraq9', + stackByField1DocCount: 1, + }, + { + doc_count: 19, + key: 'Endpoint Security', + maxRiskSubAggregation: { value: 47 }, + stackByField1Key: 'Host-r4y6xi92ob', + stackByField1DocCount: 1, + }, + { + doc_count: 5, + key: 'mimikatz process started', + maxRiskSubAggregation: { value: 99 }, + stackByField1Key: 'Host-k8iyfzraq9', + stackByField1DocCount: 3, + }, + { + doc_count: 5, + key: 'mimikatz process started', + maxRiskSubAggregation: { value: 99 }, + stackByField1Key: 'Host-3fbljiq8rj', + stackByField1DocCount: 1, + }, + { + doc_count: 5, + key: 'mimikatz process started', + maxRiskSubAggregation: { value: 99 }, + stackByField1Key: 'Host-r4y6xi92ob', + stackByField1DocCount: 1, + }, + { + doc_count: 2, + key: 'Has Slack Interval Action', + maxRiskSubAggregation: { value: 21 }, + stackByField1Key: 'Host-k8iyfzraq9', + stackByField1DocCount: 1, + }, + { + doc_count: 2, + key: 'Has Slack Interval Action', + maxRiskSubAggregation: { value: 21 }, + stackByField1Key: 'Host-ryqxt6jjy2', + stackByField1DocCount: 1, + }, + { + doc_count: 1, + key: 'Threshold rule', + maxRiskSubAggregation: { value: 99 }, + stackByField1Key: 'Host-r4y6xi92ob', + stackByField1DocCount: 1, + }, +]; diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/helpers.test.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/helpers.test.ts new file mode 100644 index 0000000000000..9155c396390ca --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/helpers.test.ts @@ -0,0 +1,235 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { PartitionElementEvent } from '@elastic/charts'; +import { omit } from 'lodash/fp'; + +import { bucketsWithStackByField1, maxRiskSubAggregations } from './flatten/mocks/mock_buckets'; +import { + getGroupByFieldsOnClick, + getMaxRiskSubAggregations, + getUpToMaxBuckets, + hasOptionalStackByField, +} from './helpers'; + +describe('helpers', () => { + describe('getUpToMaxBuckets', () => { + it('returns the expected buckets when maxItems is smaller than the collection of buckets', () => { + expect(getUpToMaxBuckets({ buckets: bucketsWithStackByField1, maxItems: 2 })).toEqual([ + { + key: 'matches everything', + doc_count: 34, + maxRiskSubAggregation: { value: 21 }, + stackByField1: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'Host-k8iyfzraq9', doc_count: 12 }, + { key: 'Host-ao1a4wu7vn', doc_count: 10 }, + { key: 'Host-3fbljiq8rj', doc_count: 7 }, + { key: 'Host-r4y6xi92ob', doc_count: 5 }, + ], + }, + }, + { + key: 'EQL process sequence', + doc_count: 28, + maxRiskSubAggregation: { value: 73 }, + stackByField1: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { key: 'Host-k8iyfzraq9', doc_count: 10 }, + { key: 'Host-ao1a4wu7vn', doc_count: 7 }, + { key: 'Host-3fbljiq8rj', doc_count: 5 }, + { key: 'Host-r4y6xi92ob', doc_count: 3 }, + ], + }, + }, + ]); + }); + + it('returns the expected buckets when maxItems is the same length as the collection of buckets', () => { + expect( + getUpToMaxBuckets({ + buckets: bucketsWithStackByField1, + maxItems: bucketsWithStackByField1.length, + }) + ).toEqual(bucketsWithStackByField1); + }); + + it('returns the expected buckets when maxItems is greater than length as the collection of buckets', () => { + expect( + getUpToMaxBuckets({ + buckets: bucketsWithStackByField1, + maxItems: bucketsWithStackByField1.length + 50, + }) + ).toEqual(bucketsWithStackByField1); + }); + + it('returns an empty array when maxItems is zero', () => { + expect( + getUpToMaxBuckets({ + buckets: bucketsWithStackByField1, + maxItems: 0, + }) + ).toEqual([]); + }); + + it('returns an empty array when buckets is undefined', () => { + expect( + getUpToMaxBuckets({ + buckets: undefined, + maxItems: 50, + }) + ).toEqual([]); + }); + }); + + describe('getMaxRiskSubAggregations', () => { + it('returns the expected sub aggregations when all the buckets have a `maxRiskSubAggregation`', () => { + expect(getMaxRiskSubAggregations(bucketsWithStackByField1)).toEqual(maxRiskSubAggregations); + }); + + it('returns the expected sub aggregations when only some the buckets have a `maxRiskSubAggregation`', () => { + const hasMissingMaxRiskSubAggregation = bucketsWithStackByField1.map((x) => + x.key === 'EQL process sequence' ? omit('maxRiskSubAggregation', x) : x + ); + + expect(getMaxRiskSubAggregations(hasMissingMaxRiskSubAggregation)).toEqual({ + 'matches everything': 21, + 'EQL process sequence': undefined, + 'Endpoint Security': 47, + 'mimikatz process started': 99, + 'Threshold rule': 99, + }); + }); + }); + + describe('getGroupByFieldsOnClick', () => { + it('returns the expected group by fields when the event has two fields', () => { + const event: PartitionElementEvent[] = [ + [ + [ + { + smAccessorValue: '', + groupByRollup: 'mimikatz process started', + value: 5, + depth: 1, + sortIndex: 3, + path: [ + { index: 0, value: '__null_small_multiples_key__' }, + { index: 0, value: '__root_key__' }, + { index: 3, value: 'mimikatz process started' }, + ], + }, + { + smAccessorValue: '', + groupByRollup: 'Host-k8iyfzraq9', + value: 3, + depth: 2, + sortIndex: 0, + path: [ + { index: 0, value: '__null_small_multiples_key__' }, + { index: 0, value: '__root_key__' }, + { index: 3, value: 'mimikatz process started' }, + { index: 0, value: 'Host-k8iyfzraq9' }, + ], + }, + ], + { specId: 'spec_1', key: 'spec{spec_1}' }, + ], + ]; + + expect(getGroupByFieldsOnClick(event)).toEqual({ + groupByField0: 'mimikatz process started', + groupByField1: 'Host-k8iyfzraq9', + }); + }); + + it('returns the expected group by fields when the event has one field', () => { + const event: PartitionElementEvent[] = [ + [ + [ + { + smAccessorValue: '', + groupByRollup: 'matches everything', + value: 34, + depth: 1, + sortIndex: 0, + path: [ + { index: 0, value: '__null_small_multiples_key__' }, + { index: 0, value: '__root_key__' }, + { index: 0, value: 'matches everything' }, + ], + }, + ], + { specId: 'spec_1', key: 'spec{spec_1}' }, + ], + ]; + + expect(getGroupByFieldsOnClick(event)).toEqual({ + groupByField0: 'matches everything', + groupByField1: '', + }); + }); + + it('returns the expected group by fields groupByRollup is null', () => { + const event: PartitionElementEvent[] = [ + [ + [ + { + smAccessorValue: '', + groupByRollup: null, + value: 5, + depth: 1, + sortIndex: 3, + path: [ + { index: 0, value: '__null_small_multiples_key__' }, + { index: 0, value: '__root_key__' }, + { index: 3, value: 'mimikatz process started' }, + ], + }, + { + smAccessorValue: '', + groupByRollup: 'Host-k8iyfzraq9', + value: 3, + depth: 2, + sortIndex: 0, + path: [ + { index: 0, value: '__null_small_multiples_key__' }, + { index: 0, value: '__root_key__' }, + { index: 3, value: 'mimikatz process started' }, + { index: 0, value: 'Host-k8iyfzraq9' }, + ], + }, + ], + { specId: 'spec_1', key: 'spec{spec_1}' }, + ], + ]; + + expect(getGroupByFieldsOnClick(event)).toEqual({ + groupByField0: '', + groupByField1: 'Host-k8iyfzraq9', + }); + }); + }); + + describe('hasOptionalStackByField', () => { + it('returns true for a valid field', () => { + expect(hasOptionalStackByField('host.name')).toBe(true); + }); + + it('returns false when stackByField1 is undefined', () => { + expect(hasOptionalStackByField(undefined)).toBe(false); + }); + + it('returns false when stackByField1 is just whitespace', () => { + expect(hasOptionalStackByField(' ')).toBe(false); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/helpers.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/helpers.ts new file mode 100644 index 0000000000000..f71c49f08b05f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/helpers.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + FlameElementEvent, + HeatmapElementEvent, + MetricElementEvent, + PartitionElementEvent, + WordCloudElementEvent, + XYChartElementEvent, +} from '@elastic/charts'; + +import type { RawBucket } from '../types'; + +export const getUpToMaxBuckets = ({ + buckets, + maxItems, +}: { + buckets: RawBucket[] | undefined; + maxItems: number; +}): RawBucket[] => buckets?.slice(0, maxItems) ?? []; + +export const getMaxRiskSubAggregations = ( + buckets: RawBucket[] +): Record => + buckets.reduce>( + (acc, x) => ({ ...acc, [x.key]: x.maxRiskSubAggregation?.value ?? undefined }), + {} + ); + +interface GetGroupByFieldsResult { + groupByField0: string; + groupByField1: string; +} + +export const getGroupByFieldsOnClick = ( + elements: Array< + | FlameElementEvent + | HeatmapElementEvent + | MetricElementEvent + | PartitionElementEvent + | WordCloudElementEvent + | XYChartElementEvent + > +): GetGroupByFieldsResult => { + const flattened = elements.flat(2); + + const groupByField0 = + flattened.length > 0 && 'groupByRollup' in flattened[0] && flattened[0].groupByRollup != null + ? `${flattened[0].groupByRollup}` + : ''; + + const groupByField1 = + flattened.length > 1 && 'groupByRollup' in flattened[1] && flattened[1].groupByRollup != null + ? `${flattened[1].groupByRollup}` + : ''; + + return { + groupByField0, + groupByField1, + }; +}; + +export const hasOptionalStackByField = (stackByField1: string | undefined): boolean => + stackByField1 != null && stackByField1.trim() !== ''; diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/labels/index.test.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/labels/index.test.ts new file mode 100644 index 0000000000000..33a4b62db5896 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/labels/index.test.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getLabel } from '.'; + +describe('labels', () => { + describe('getLabel', () => { + it('returns the expected label when risk score is a number', () => { + const baseLabel = 'mimikatz process started'; + const riskScore = 99; + + expect(getLabel({ baseLabel, riskScore })).toBe('mimikatz process started (Risk 99)'); + }); + + it('returns the expected label when risk score is null', () => { + const baseLabel = 'mimikatz process started'; + const riskScore = null; + + expect(getLabel({ baseLabel, riskScore })).toBe(baseLabel); + }); + + it('returns the expected label when risk score is undefined', () => { + const baseLabel = 'mimikatz process started'; + const riskScore = undefined; + + expect(getLabel({ baseLabel, riskScore })).toBe(baseLabel); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/labels/index.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/labels/index.ts new file mode 100644 index 0000000000000..dc1be5a766162 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/labels/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as i18n from '../../translations'; + +export const getLabel = ({ + baseLabel, + riskScore, +}: { + baseLabel: string; + riskScore: number | null | undefined; +}): string => (riskScore != null ? `${baseLabel} ${i18n.RISK_LABEL(riskScore)}` : baseLabel); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/layers/index.test.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/layers/index.test.ts new file mode 100644 index 0000000000000..216de08851399 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/layers/index.test.ts @@ -0,0 +1,202 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getRiskScorePalette, RISK_SCORE_STEPS } from '../chart_palette'; +import { maxRiskSubAggregations } from '../flatten/mocks/mock_buckets'; +import type { DataName, FillColorDatum, Path } from '.'; +import { getGroupFromPath, getLayersOneDimension, getLayersMultiDimensional } from '.'; + +describe('layers', () => { + const colorPalette = getRiskScorePalette(RISK_SCORE_STEPS); + + describe('getGroupFromPath', () => { + it('returns the expected group from the path', () => { + expect( + getGroupFromPath({ + path: [ + { index: 0, value: '__null_small_multiples_key__' }, + { index: 0, value: '__root_key__' }, + { index: 0, value: 'matches everything' }, + { index: 0, value: 'Host-k8iyfzraq9' }, + ], + }) + ).toEqual('matches everything'); + }); + + it('returns undefined when path is undefined', () => { + const datumWithUndefinedPath: FillColorDatum = {}; + + expect(getGroupFromPath(datumWithUndefinedPath)).toBeUndefined(); + }); + + it('returns undefined when path is an empty array', () => { + expect( + getGroupFromPath({ + path: [], + }) + ).toBeUndefined(); + }); + + it('returns undefined when path is an array with only one value', () => { + expect( + getGroupFromPath({ + path: [{ index: 0, value: '__null_small_multiples_key__' }], + }) + ).toBeUndefined(); + }); + }); + + describe('getLayersOneDimension', () => { + it('returns the expected number of layers', () => { + expect(getLayersOneDimension({ colorPalette, maxRiskSubAggregations }).length).toEqual(1); + }); + + it('returns the expected fillLabel valueFormatter function', () => { + expect( + getLayersOneDimension({ colorPalette, maxRiskSubAggregations })[0].fillLabel.valueFormatter( + 123 + ) + ).toEqual('123'); + }); + + it('returns the expected groupByRollup function', () => { + expect( + getLayersOneDimension({ colorPalette, maxRiskSubAggregations })[0].groupByRollup({ + key: 'keystone', + }) + ).toEqual('keystone'); + }); + + it('returns the expected nodeLabel function', () => { + expect( + getLayersOneDimension({ colorPalette, maxRiskSubAggregations })[0].nodeLabel( + 'matches everything' + ) + ).toEqual('matches everything (Risk 21)'); + }); + + it('returns the expected shape fillColor function', () => { + const dataName: DataName = { dataName: 'mimikatz process started' }; + expect( + getLayersOneDimension({ colorPalette, maxRiskSubAggregations })[0].shape.fillColor(dataName) + ).toEqual('#e7664c'); + }); + + it('return the default fill color when dataName is not found in the maxRiskSubAggregations', () => { + const dataName: DataName = { dataName: 'this does not exist' }; + expect( + getLayersOneDimension({ colorPalette, maxRiskSubAggregations })[0].shape.fillColor(dataName) + ).toEqual('#54b399'); + }); + }); + + describe('getLayersMultiDimensional', () => { + const layer0FillColor = 'transparent'; + it('returns the expected number of layers', () => { + expect( + getLayersMultiDimensional({ colorPalette, layer0FillColor, maxRiskSubAggregations }).length + ).toEqual(2); + }); + + it('returns the expected fillLabel valueFormatter function', () => { + getLayersMultiDimensional({ colorPalette, layer0FillColor, maxRiskSubAggregations }).forEach( + (x) => expect(x.fillLabel.valueFormatter(123)).toEqual('123') + ); + }); + + it('returns the expected groupByRollup function for layer 0', () => { + expect( + getLayersMultiDimensional({ + colorPalette, + layer0FillColor, + maxRiskSubAggregations, + })[0].groupByRollup({ + key: 'keystone', + }) + ).toEqual('keystone'); + }); + + it('returns the expected groupByRollup function for layer 1, which has a different implementation', () => { + expect( + getLayersMultiDimensional({ + colorPalette, + layer0FillColor, + maxRiskSubAggregations, + })[1].groupByRollup({ + stackByField1Key: 'host.name', + }) + ).toEqual('host.name'); + }); + + it('returns the expected nodeLabel function for layer 0', () => { + expect( + getLayersMultiDimensional({ + colorPalette, + layer0FillColor, + maxRiskSubAggregations, + })[0].nodeLabel('matches everything') + ).toEqual('matches everything (Risk 21)'); + }); + + it('returns the expected nodeLabel function for layer 1, which has a different implementation', () => { + expect( + getLayersMultiDimensional({ + colorPalette, + layer0FillColor, + maxRiskSubAggregations, + })[1].nodeLabel('Host-k8iyfzraq9') + ).toEqual('Host-k8iyfzraq9'); + }); + + it('returns the expected shape fillColor for layer 0', () => { + expect( + getLayersMultiDimensional({ colorPalette, layer0FillColor, maxRiskSubAggregations })[0] + .shape.fillColor + ).toEqual(layer0FillColor); + }); + + it('returns the expected shape fill color function for layer 1, which has a different implementation', () => { + const fillColorFn = getLayersMultiDimensional({ + colorPalette, + layer0FillColor, + maxRiskSubAggregations, + })[1].shape.fillColor as ({ dataName, path }: { dataName: string; path: Path[] }) => string; + + expect( + fillColorFn({ + dataName: 'Host-k8iyfzraq9', + path: [ + { index: 0, value: '__null_small_multiples_key__' }, + { index: 0, value: '__root_key__' }, + { index: 0, value: 'mimikatz process started' }, + { index: 0, value: 'Host-k8iyfzraq9' }, + ], + }) + ).toEqual('#e7664c'); + }); + + it('returns the default fillColor for layer 1 when the group from path is not found', () => { + const fillColorFn = getLayersMultiDimensional({ + colorPalette, + layer0FillColor, + maxRiskSubAggregations, + })[1].shape.fillColor as ({ dataName, path }: { dataName: string; path: Path[] }) => string; + + expect( + fillColorFn({ + dataName: 'nope', + path: [ + { index: 0, value: '__null_small_multiples_key__' }, + { index: 0, value: '__root_key__' }, + { index: 0, value: 'matches everything' }, + { index: 0, value: 'nope' }, + ], + }) + ).toEqual('#54b399'); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/layers/index.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/layers/index.ts new file mode 100644 index 0000000000000..09a4d95bdcb0f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/layers/index.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Datum } from '@elastic/charts'; + +import { getFillColor } from '../chart_palette'; +import { getLabel } from '../labels'; + +export interface DataName { + dataName: string; +} + +export interface Path { + index: number; + value: string; +} + +export interface FillColorDatum { + path?: Path[]; +} + +// common functions used by getLayersOneDimension and getLayersMultiDimensional: +const valueFormatter = (d: number) => `${d}`; +const groupByRollup = (d: Datum) => d.key; + +/** + * Extracts the first group name from the data representing the second group + */ +export const getGroupFromPath = (datum: FillColorDatum): string | undefined => { + const OFFSET_FROM_END = 2; // The offset from the end of the path array containing the group + + const pathLength = datum.path?.length ?? 0; + const groupIndex = pathLength - OFFSET_FROM_END; + + return Array.isArray(datum.path) && groupIndex > 0 ? datum.path[groupIndex].value : undefined; +}; + +export const getLayersOneDimension = ({ + colorPalette, + maxRiskSubAggregations, +}: { + colorPalette: string[]; + maxRiskSubAggregations: Record; +}) => [ + { + fillLabel: { + valueFormatter, + }, + groupByRollup, + nodeLabel: (d: Datum) => getLabel({ baseLabel: d, riskScore: maxRiskSubAggregations[d] }), + shape: { + fillColor: (d: DataName) => + getFillColor({ + riskScore: maxRiskSubAggregations[d.dataName] ?? 0, + colorPalette, + }), + }, + }, +]; + +export const getLayersMultiDimensional = ({ + colorPalette, + layer0FillColor, + maxRiskSubAggregations, +}: { + colorPalette: string[]; + layer0FillColor: string; + maxRiskSubAggregations: Record; +}) => [ + { + fillLabel: { + valueFormatter, + }, + groupByRollup, + nodeLabel: (d: Datum) => getLabel({ baseLabel: d, riskScore: maxRiskSubAggregations[d] }), + shape: { + fillColor: layer0FillColor, + }, + }, + { + fillLabel: { + valueFormatter, + }, + groupByRollup: (d: Datum) => d.stackByField1Key, // different implementation than layer 0 + nodeLabel: (d: Datum) => `${d}`, + shape: { + fillColor: (d: FillColorDatum) => { + const groupFromPath = getGroupFromPath(d) ?? ''; + + return getFillColor({ + riskScore: maxRiskSubAggregations[groupFromPath] ?? 0, + colorPalette, + }); + }, + }, + }, +]; diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/get_flattened_legend_items.test.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/get_flattened_legend_items.test.ts new file mode 100644 index 0000000000000..325e2bab84d6f --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/get_flattened_legend_items.test.ts @@ -0,0 +1,153 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { omit } from 'lodash/fp'; + +import type { LegendItem } from '../../../charts/draggable_legend_item'; +import { getRiskScorePalette, RISK_SCORE_STEPS } from '../chart_palette'; +import { getFlattenedLegendItems } from './get_flattened_legend_items'; +import { bucketsWithStackByField1, maxRiskSubAggregations } from '../flatten/mocks/mock_buckets'; +import { flattenedBuckets } from '../flatten/mocks/mock_flattened_buckets'; + +describe('getFlattenedLegendItems', () => { + it('returns the expected legend items', () => { + const expected: Array> = [ + { + count: 34, + field: 'kibana.alert.rule.name', + value: 'matches everything', + }, + { + color: '#54b399', + count: 12, + field: 'host.name', + value: 'Host-k8iyfzraq9', + }, + { + color: '#54b399', + count: 10, + field: 'host.name', + value: 'Host-ao1a4wu7vn', + }, + { + color: '#54b399', + count: 7, + field: 'host.name', + value: 'Host-3fbljiq8rj', + }, + { + color: '#54b399', + count: 5, + field: 'host.name', + value: 'Host-r4y6xi92ob', + }, + { + count: 28, + field: 'kibana.alert.rule.name', + value: 'EQL process sequence', + }, + { + color: '#da8b45', + count: 10, + field: 'host.name', + value: 'Host-k8iyfzraq9', + }, + { + color: '#da8b45', + count: 7, + field: 'host.name', + value: 'Host-ao1a4wu7vn', + }, + { + color: '#da8b45', + count: 5, + field: 'host.name', + value: 'Host-3fbljiq8rj', + }, + { + color: '#da8b45', + count: 3, + field: 'host.name', + value: 'Host-r4y6xi92ob', + }, + { + count: 19, + field: 'kibana.alert.rule.name', + value: 'Endpoint Security', + }, + { + color: '#d6bf57', + count: 11, + field: 'host.name', + value: 'Host-ao1a4wu7vn', + }, + { + color: '#d6bf57', + count: 6, + field: 'host.name', + value: 'Host-3fbljiq8rj', + }, + { + color: '#d6bf57', + count: 1, + field: 'host.name', + value: 'Host-k8iyfzraq9', + }, + { + color: '#d6bf57', + count: 1, + field: 'host.name', + value: 'Host-r4y6xi92ob', + }, + { + count: 5, + field: 'kibana.alert.rule.name', + value: 'mimikatz process started', + }, + { + color: '#e7664c', + count: 3, + field: 'host.name', + value: 'Host-k8iyfzraq9', + }, + { + color: '#e7664c', + count: 1, + field: 'host.name', + value: 'Host-3fbljiq8rj', + }, + { + color: '#e7664c', + count: 1, + field: 'host.name', + value: 'Host-r4y6xi92ob', + }, + { + count: 1, + field: 'kibana.alert.rule.name', + value: 'Threshold rule', + }, + { + color: '#e7664c', + count: 1, + field: 'host.name', + value: 'Host-r4y6xi92ob', + }, + ]; + + const legendItems = getFlattenedLegendItems({ + buckets: bucketsWithStackByField1, + colorPalette: getRiskScorePalette(RISK_SCORE_STEPS), + flattenedBuckets, + maxRiskSubAggregations, + stackByField0: 'kibana.alert.rule.name', + stackByField1: 'host.name', + }); + + expect(legendItems.map((x) => omit(['render', 'dataProviderId'], x))).toEqual(expected); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/get_flattened_legend_items.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/get_flattened_legend_items.ts new file mode 100644 index 0000000000000..a904d6ef90bd0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/get_flattened_legend_items.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { LegendItem } from '../../../charts/draggable_legend_item'; +import { getLegendMap, getLegendItemFromFlattenedBucket } from '.'; +import type { FlattenedBucket, RawBucket } from '../../types'; + +export const getFlattenedLegendItems = ({ + buckets, + colorPalette, + flattenedBuckets, + maxRiskSubAggregations, + stackByField0, + stackByField1, +}: { + buckets: RawBucket[]; + colorPalette: string[]; + flattenedBuckets: FlattenedBucket[]; + maxRiskSubAggregations: Record; + stackByField0: string; + stackByField1: string | undefined; +}): LegendItem[] => { + // create a map of bucket.key -> LegendItem[] from the raw buckets: + const legendMap: Record = getLegendMap({ + buckets, + colorPalette, + maxRiskSubAggregations, + stackByField0, + }); + + // append each flattened bucket to the appropriate parent in the legendMap: + const combinedLegendItems: Record = flattenedBuckets.reduce< + Record + >( + (acc, flattenedBucket) => ({ + ...acc, + [flattenedBucket.key]: [ + ...(acc[flattenedBucket.key] ?? []), + getLegendItemFromFlattenedBucket({ + colorPalette, + flattenedBucket, + maxRiskSubAggregations, + stackByField0, + stackByField1, + }), + ], + }), + legendMap + ); + + // reduce all the legend items to a single array in the same order as the raw buckets: + return buckets.reduce( + (acc, bucket) => [...acc, ...combinedLegendItems[bucket.key]], + [] + ); +}; diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/index.test.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/index.test.ts new file mode 100644 index 0000000000000..514b2743504d4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/index.test.ts @@ -0,0 +1,275 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { omit } from 'lodash/fp'; + +import type { LegendItem } from '../../../charts/draggable_legend_item'; +import { getRiskScorePalette, RISK_SCORE_STEPS } from '../chart_palette'; +import { bucketsWithStackByField1, maxRiskSubAggregations } from '../flatten/mocks/mock_buckets'; +import { + getFirstGroupLegendItems, + getLegendItemFromRawBucket, + getLegendItemFromFlattenedBucket, + getLegendMap, +} from '.'; +import type { FlattenedBucket } from '../../types'; + +describe('legend', () => { + const colorPalette = getRiskScorePalette(RISK_SCORE_STEPS); + + describe('getLegendItemFromRawBucket', () => { + it('returns an undefined color when showColor is false', () => { + expect( + getLegendItemFromRawBucket({ + bucket: bucketsWithStackByField1[0], + colorPalette, + maxRiskSubAggregations, + showColor: false, + stackByField0: 'kibana.alert.rule.name', + }).color + ).toBeUndefined(); + }); + + it('returns the expected color when showColor is true', () => { + expect( + getLegendItemFromRawBucket({ + bucket: bucketsWithStackByField1[0], + colorPalette, + maxRiskSubAggregations, + showColor: true, + stackByField0: 'kibana.alert.rule.name', + }).color + ).toEqual('#54b399'); + }); + + it('returns the expected count', () => { + expect( + getLegendItemFromRawBucket({ + bucket: bucketsWithStackByField1[0], + colorPalette, + maxRiskSubAggregations, + showColor: true, + stackByField0: 'kibana.alert.rule.name', + }).count + ).toEqual(34); + }); + + it('returns the expected dataProviderId', () => { + expect( + getLegendItemFromRawBucket({ + bucket: bucketsWithStackByField1[0], + colorPalette, + maxRiskSubAggregations, + showColor: true, + stackByField0: 'kibana.alert.rule.name', + }).dataProviderId + ).toContain('draggable-legend-item-treemap-kibana_alert_rule_name-matches everything-'); + }); + + it('returns the expected field', () => { + expect( + getLegendItemFromRawBucket({ + bucket: bucketsWithStackByField1[0], + colorPalette, + maxRiskSubAggregations, + showColor: true, + stackByField0: 'kibana.alert.rule.name', + }).field + ).toEqual('kibana.alert.rule.name'); + }); + + it('returns the expected value', () => { + expect( + getLegendItemFromRawBucket({ + bucket: bucketsWithStackByField1[0], + colorPalette, + maxRiskSubAggregations, + showColor: true, + stackByField0: 'kibana.alert.rule.name', + }).value + ).toEqual('matches everything'); + }); + }); + + describe('getLegendItemFromFlattenedBucket', () => { + const flattenedBucket: FlattenedBucket = { + doc_count: 34, + key: 'matches everything', + maxRiskSubAggregation: { value: 21 }, + stackByField1DocCount: 12, + stackByField1Key: 'Host-k8iyfzraq9', + }; + + it('returns the expected legend item', () => { + expect( + omit( + ['render', 'dataProviderId'], + getLegendItemFromFlattenedBucket({ + colorPalette, + flattenedBucket, + maxRiskSubAggregations, + stackByField0: 'kibana.alert.rule.name', + stackByField1: 'host.name', + }) + ) + ).toEqual({ + color: '#54b399', + count: 12, + field: 'host.name', + value: 'Host-k8iyfzraq9', + }); + }); + + it('returns the expected render function', () => { + const legendItem = getLegendItemFromFlattenedBucket({ + colorPalette, + flattenedBucket, + maxRiskSubAggregations, + stackByField0: 'kibana.alert.rule.name', + stackByField1: 'host.name', + }); + + expect(legendItem.render != null && legendItem.render()).toEqual('Host-k8iyfzraq9'); + }); + + it('returns the expected dataProviderId', () => { + const legendItem = getLegendItemFromFlattenedBucket({ + colorPalette, + flattenedBucket, + maxRiskSubAggregations, + stackByField0: 'kibana.alert.rule.name', + stackByField1: 'host.name', + }); + + expect(legendItem.dataProviderId).toContain( + 'draggable-legend-item-treemap-matches everything-Host-k8iyfzraq9-' + ); + }); + }); + + describe('getFirstGroupLegendItems', () => { + it('returns the expected legend item', () => { + expect( + getFirstGroupLegendItems({ + buckets: bucketsWithStackByField1, + colorPalette, + maxRiskSubAggregations, + stackByField0: 'kibana.alert.rule.name', + }).map((x) => omit(['render', 'dataProviderId'], x)) + ).toEqual([ + { + color: '#54b399', + count: 34, + field: 'kibana.alert.rule.name', + value: 'matches everything', + }, + { + color: '#da8b45', + count: 28, + field: 'kibana.alert.rule.name', + value: 'EQL process sequence', + }, + { + color: '#d6bf57', + count: 19, + field: 'kibana.alert.rule.name', + value: 'Endpoint Security', + }, + { + color: '#e7664c', + count: 5, + field: 'kibana.alert.rule.name', + value: 'mimikatz process started', + }, + { + color: '#e7664c', + count: 1, + field: 'kibana.alert.rule.name', + value: 'Threshold rule', + }, + ]); + }); + + it('returns the expected render function', () => { + expect( + getFirstGroupLegendItems({ + buckets: bucketsWithStackByField1, + colorPalette, + maxRiskSubAggregations, + stackByField0: 'kibana.alert.rule.name', + }).map((x) => (x.render != null ? x.render() : null)) + ).toEqual([ + 'matches everything (Risk 21)', + 'EQL process sequence (Risk 73)', + 'Endpoint Security (Risk 47)', + 'mimikatz process started (Risk 99)', + 'Threshold rule (Risk 99)', + ]); + }); + }); + + describe('getLegendMap', () => { + it('returns the expected legend item', () => { + const expected: Record< + string, + Array> + > = { + 'matches everything': [ + { + color: undefined, + count: 34, + field: 'kibana.alert.rule.name', + value: 'matches everything', + }, + ], + 'EQL process sequence': [ + { + color: undefined, + count: 28, + field: 'kibana.alert.rule.name', + value: 'EQL process sequence', + }, + ], + 'Endpoint Security': [ + { + color: undefined, + count: 19, + field: 'kibana.alert.rule.name', + value: 'Endpoint Security', + }, + ], + 'mimikatz process started': [ + { + color: undefined, + count: 5, + field: 'kibana.alert.rule.name', + value: 'mimikatz process started', + }, + ], + 'Threshold rule': [ + { + color: undefined, + count: 1, + field: 'kibana.alert.rule.name', + value: 'Threshold rule', + }, + ], + }; + + const legendMap = getLegendMap({ + buckets: bucketsWithStackByField1, + colorPalette, + maxRiskSubAggregations, + stackByField0: 'kibana.alert.rule.name', + }); + + Object.keys(expected).forEach((key) => { + expect(omit(['render', 'dataProviderId'], legendMap[key][0])).toEqual(expected[key][0]); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/index.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/index.ts new file mode 100644 index 0000000000000..77865b7d55013 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/legend/index.ts @@ -0,0 +1,120 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import uuid from 'uuid'; + +import type { LegendItem } from '../../../charts/draggable_legend_item'; +import { getFillColor } from '../chart_palette'; +import { escapeDataProviderId } from '../../../drag_and_drop/helpers'; +import { getLabel } from '../labels'; +import type { FlattenedBucket, RawBucket } from '../../types'; + +export const getLegendItemFromRawBucket = ({ + bucket, + colorPalette, + maxRiskSubAggregations, + showColor, + stackByField0, +}: { + bucket: RawBucket; + colorPalette: string[]; + maxRiskSubAggregations: Record; + showColor: boolean; + stackByField0: string; +}): LegendItem => ({ + color: showColor + ? getFillColor({ + riskScore: maxRiskSubAggregations[bucket.key] ?? 0, + colorPalette, + }) + : undefined, + count: bucket.doc_count, + dataProviderId: escapeDataProviderId( + `draggable-legend-item-treemap-${stackByField0}-${bucket.key}-${uuid.v4()}` + ), + render: () => + getLabel({ + baseLabel: bucket.key, + riskScore: bucket.maxRiskSubAggregation?.value, + }), + field: stackByField0, + value: bucket.key, +}); + +export const getLegendItemFromFlattenedBucket = ({ + colorPalette, + flattenedBucket: { key, stackByField1Key, stackByField1DocCount }, + maxRiskSubAggregations, + stackByField0, + stackByField1, +}: { + colorPalette: string[]; + flattenedBucket: FlattenedBucket; + maxRiskSubAggregations: Record; + stackByField0: string; + stackByField1: string | undefined; +}): LegendItem => ({ + color: getFillColor({ + riskScore: maxRiskSubAggregations[key] ?? 0, + colorPalette, + }), + count: stackByField1DocCount, + dataProviderId: escapeDataProviderId( + `draggable-legend-item-treemap-${key}-${stackByField1Key}-${uuid.v4()}` + ), + render: () => `${stackByField1Key}`, + field: `${stackByField1}`, + value: `${stackByField1Key}`, +}); + +export const getFirstGroupLegendItems = ({ + buckets, + colorPalette, + maxRiskSubAggregations, + stackByField0, +}: { + buckets: RawBucket[]; + colorPalette: string[]; + maxRiskSubAggregations: Record; + stackByField0: string; +}): LegendItem[] => + buckets.map((bucket) => + getLegendItemFromRawBucket({ + bucket, + colorPalette, + maxRiskSubAggregations, + showColor: true, + stackByField0, + }) + ); + +export const getLegendMap = ({ + buckets, + colorPalette, + maxRiskSubAggregations, + stackByField0, +}: { + buckets: RawBucket[]; + colorPalette: string[]; + maxRiskSubAggregations: Record; + stackByField0: string; +}): Record => + buckets.reduce>( + (acc, bucket) => ({ + ...acc, + [bucket.key]: [ + getLegendItemFromRawBucket({ + bucket, + colorPalette, + maxRiskSubAggregations, + showColor: false, // don't show colors for stackByField0 + stackByField0, + }), + ], + }), + {} + ); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/mocks/mock_alert_search_response.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/mocks/mock_alert_search_response.ts new file mode 100644 index 0000000000000..b28b55a396ad1 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/lib/mocks/mock_alert_search_response.ts @@ -0,0 +1,140 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AlertSearchResponse } from '../../../../../detections/containers/detection_engine/alerts/types'; +import type { AlertsTreeMapAggregation } from '../../types'; + +export const mockAlertSearchResponse: AlertSearchResponse = { + took: 1, + timeout: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 75, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + stackByField0: { + buckets: [ + { + key: 'Endpoint Security', + doc_count: 50, + maxRiskSubAggregation: { + value: 47, + }, + stackByField1: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'Host-p3afpacfut', + doc_count: 30, + }, + { + key: 'Host-wgrua1nhzb', + doc_count: 20, + }, + ], + }, + }, + { + key: 'matches everything', + doc_count: 23, + maxRiskSubAggregation: { + value: 21, + }, + stackByField1: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'Host-p3afpacfut', + doc_count: 15, + }, + { + key: 'Host-wgrua1nhzb', + doc_count: 7, + }, + { + key: 'Host-bnrf4ss7ez', + doc_count: 1, + }, + ], + }, + }, + { + key: 'Threshold rule', + doc_count: 1, + maxRiskSubAggregation: { + value: 99, + }, + stackByField1: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'Host-p3afpacfut', + doc_count: 1, + }, + ], + }, + }, + { + key: 'mimikatz process started', + doc_count: 1, + maxRiskSubAggregation: { + value: 99, + }, + stackByField1: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'Host-wgrua1nhzb', + doc_count: 1, + }, + ], + }, + }, + ], + }, + }, +}; + +export const mockNoDataAlertSearchResponse = { + took: 1, + timeout: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 80, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + stackByField0: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], // <-- empty buckets + }, + }, +}; diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/no_data/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/no_data/index.test.tsx new file mode 100644 index 0000000000000..0cf39beae7b2d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/no_data/index.test.tsx @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import * as i18n from '../translations'; + +import { NoData } from '.'; + +describe('NoData', () => { + test('renders the expected "no data" message', () => { + render(); + + expect(screen.getByText(i18n.NO_DATA_LABEL)).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/no_data/index.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/no_data/index.tsx new file mode 100644 index 0000000000000..2dba94d3c12fa --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/no_data/index.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import React from 'react'; +import styled from 'styled-components'; + +import * as i18n from '../translations'; + +const NoDataLabel = styled(EuiText)` + text-align: center; +`; + +const NoDataComponent: React.FC = () => ( + + + + {i18n.NO_DATA_LABEL} + + + +); + +NoDataComponent.displayName = 'NoDataComponent'; + +export const NoData = React.memo(NoDataComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/query/index.test.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/query/index.test.ts new file mode 100644 index 0000000000000..9ef576cf931d6 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/query/index.test.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { DEFAULT_STACK_BY_FIELD1_SIZE, getAlertsRiskQuery, getOptionalSubAggregation } from '.'; + +describe('query', () => { + describe('getOptionalSubAggregation', () => { + it('returns the expected sub aggregation', () => { + expect( + getOptionalSubAggregation({ + stackByField1: 'host.name', + stackByField1Size: DEFAULT_STACK_BY_FIELD1_SIZE, + }) + ).toEqual({ + stackByField1: { + terms: { + field: 'host.name', + order: { + _count: 'desc', + }, + size: 1000, + }, + }, + }); + }); + }); + + describe('getAlertsRiskQuery', () => { + it('returns the expected query', () => { + expect( + getAlertsRiskQuery({ + additionalFilters: [ + { + bool: { + must: [], + filter: [{ term: { 'kibana.alert.workflow_status': 'open' } }], + should: [], + must_not: [{ exists: { field: 'kibana.alert.building_block_type' } }], + }, + }, + ], + from: '2021-03-10T07:00:00.000Z', + runtimeMappings: {}, + stackByField0: 'kibana.alert.rule.name', + stackByField0Size: 1000, + stackByField1: 'host.name', + stackByField1Size: 1000, + riskSubAggregationField: 'signal.rule.risk_score', + to: '2022-03-11T06:13:10.002Z', + }) + ).toEqual({ + aggs: { + stackByField0: { + aggs: { + maxRiskSubAggregation: { + max: { + field: 'signal.rule.risk_score', + }, + }, + stackByField1: { + terms: { + field: 'host.name', + order: { + _count: 'desc', + }, + size: 1000, + }, + }, + }, + terms: { + field: 'kibana.alert.rule.name', + order: { + _count: 'desc', + }, + size: 1000, + }, + }, + }, + query: { + bool: { + filter: [ + { + bool: { + filter: [ + { + term: { + 'kibana.alert.workflow_status': 'open', + }, + }, + ], + must: [], + must_not: [ + { + exists: { + field: 'kibana.alert.building_block_type', + }, + }, + ], + should: [], + }, + }, + { + range: { + '@timestamp': { + gte: '2021-03-10T07:00:00.000Z', + lte: '2022-03-11T06:13:10.002Z', + }, + }, + }, + ], + }, + }, + runtime_mappings: {}, + size: 0, + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/query/index.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/query/index.ts new file mode 100644 index 0000000000000..cd73b9a5af434 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/query/index.ts @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isEmpty } from 'lodash/fp'; +import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +/** The maximum number of items to render */ +export const DEFAULT_STACK_BY_FIELD0_SIZE = 1000; +export const DEFAULT_STACK_BY_FIELD1_SIZE = 1000; + +interface OptionalSubAggregation { + stackByField1: { + terms: { + field: string; + order: { + _count: 'desc'; + }; + size: number; + }; + }; +} + +export const getOptionalSubAggregation = ({ + stackByField1, + stackByField1Size, +}: { + stackByField1: string | undefined; + stackByField1Size: number; +}): OptionalSubAggregation | {} => + stackByField1 != null && !isEmpty(stackByField1.trim()) + ? { + stackByField1: { + terms: { + field: stackByField1, + order: { + _count: 'desc', + }, + size: stackByField1Size, + }, + }, + } + : {}; + +export const getAlertsRiskQuery = ({ + additionalFilters = [], + from, + runtimeMappings, + stackByField0, + stackByField0Size = DEFAULT_STACK_BY_FIELD0_SIZE, + stackByField1, + stackByField1Size = DEFAULT_STACK_BY_FIELD1_SIZE, + riskSubAggregationField, + to, +}: { + additionalFilters: Array<{ + bool: { filter: unknown[]; should: unknown[]; must_not: unknown[]; must: unknown[] }; + }>; + from: string; + runtimeMappings?: MappingRuntimeFields; + stackByField0: string; + stackByField0Size?: number; + stackByField1: string | undefined; + stackByField1Size?: number; + riskSubAggregationField: string; + to: string; +}) => ({ + size: 0, + aggs: { + stackByField0: { + terms: { + field: stackByField0, + order: { + _count: 'desc', + }, + size: stackByField0Size, + }, + aggs: { + ...getOptionalSubAggregation({ + stackByField1, + stackByField1Size, + }), + maxRiskSubAggregation: { + max: { + field: riskSubAggregationField, + }, + }, + }, + }, + }, + query: { + bool: { + filter: [ + ...additionalFilters, + { + range: { + '@timestamp': { + gte: from, + lte: to, + }, + }, + }, + ], + }, + }, + runtime_mappings: runtimeMappings, +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/translations.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/translations.ts new file mode 100644 index 0000000000000..c5566e62506a8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/translations.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const NO_DATA_LABEL = i18n.translate( + 'xpack.securitySolution.components.alertsTreemap.noDataLabel', + { + defaultMessage: 'No data to display', + } +); + +export const RISK_LABEL = (riskScore: number) => + i18n.translate('xpack.securitySolution.components.alertsTreemap.riskLabel', { + values: { + riskScore, + }, + defaultMessage: '(Risk {riskScore})', + }); + +export const SUBTITLE = (maxItems: number) => + i18n.translate('xpack.securitySolution.components.alertsTreemap.subtitle', { + values: { + maxItems, + }, + defaultMessage: 'Showing the top {maxItems} most frequently occurring alerts', + }); + +export const SHOW_ALL = i18n.translate( + 'xpack.securitySolution.components.alertsTreemap.showAllButton', + { + defaultMessage: 'Show all alerts', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap/types.ts b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/types.ts new file mode 100644 index 0000000000000..b0316952487d3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap/types.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { GenericBuckets } from '../../../../common/search_strategy/common'; + +export type RawBucket = GenericBuckets & { + maxRiskSubAggregation?: { + value?: number | null; // Elasticsearch returns `null` when a sub-aggregation cannot be computed + }; + stackByField1?: { + buckets?: GenericBuckets[]; + doc_count_error_upper_bound?: number; + sum_other_doc_count?: number; + }; +}; + +/** Defines the shape of the aggregation returned by Elasticsearch to visualize the treemap */ +export interface AlertsTreeMapAggregation { + stackByField0?: { + buckets?: RawBucket[]; + }; +} + +export type FlattenedBucket = Pick & { + stackByField1Key?: string; + stackByField1DocCount?: number; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.test.tsx new file mode 100644 index 0000000000000..95f992d46aec7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.test.tsx @@ -0,0 +1,245 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen, waitFor } from '@testing-library/react'; +import React from 'react'; +import { useLocation } from 'react-router-dom'; + +import { SecurityPageName } from '../../../../common/constants'; +import { + DEFAULT_STACK_BY_FIELD, + DEFAULT_STACK_BY_FIELD1, +} from '../../../detections/components/alerts_kpis/common/config'; +import { useQueryAlerts } from '../../../detections/containers/detection_engine/alerts/use_query'; +import { ChartContextMenu } from '../../../detections/pages/detection_engine/chart_panels/chart_context_menu'; +import { ChartSelect } from '../../../detections/pages/detection_engine/chart_panels/chart_select'; +import { TestProviders } from '../../mock/test_providers'; +import type { Props } from '.'; +import { AlertsTreemapPanel } from '.'; +import { mockAlertSearchResponse } from '../alerts_treemap/lib/mocks/mock_alert_search_response'; + +jest.mock('react-router-dom', () => { + const actual = jest.requireActual('react-router-dom'); + return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; +}); + +jest.mock('../../lib/kibana', () => { + const originalModule = jest.requireActual('../../lib/kibana'); + return { + ...originalModule, + useUiSetting$: () => ['0,0.[000]'], + }; +}); + +jest.mock('../../../detections/containers/detection_engine/alerts/use_query', () => ({ + useQueryAlerts: jest.fn(), +})); + +const defaultProps: Props = { + addFilter: jest.fn(), + alignHeader: 'flexStart', + chartOptionsContextMenu: (queryId: string) => ( + + ), + + isPanelExpanded: true, + filters: [ + { + meta: { + alias: null, + negate: true, + disabled: false, + type: 'exists', + key: 'kibana.alert.building_block_type', + value: 'exists', + }, + query: { + exists: { + field: 'kibana.alert.building_block_type', + }, + }, + }, + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'kibana.alert.workflow_status', + params: { + query: 'open', + }, + }, + query: { + term: { + 'kibana.alert.workflow_status': 'open', + }, + }, + }, + ], + query: { + query: '', + language: 'kuery', + }, + riskSubAggregationField: 'signal.rule.risk_score', + runtimeMappings: { + test_via_alerts_table: { + type: 'keyword', + script: { + source: 'emit("Hello World!");', + }, + }, + }, + setIsPanelExpanded: jest.fn(), + setStackByField0: jest.fn(), + setStackByField1: jest.fn(), + signalIndexName: '.alerts-security.alerts-default', + stackByField0: 'kibana.alert.rule.name', + stackByField1: 'host.name', + title: , +}; + +describe('AlertsTreemapPanel', () => { + beforeEach(() => { + jest.resetAllMocks(); + + (useLocation as jest.Mock).mockReturnValue([ + { pageName: SecurityPageName.alerts, detailName: undefined }, + ]); + + (useQueryAlerts as jest.Mock).mockReturnValue({ + loading: false, + data: mockAlertSearchResponse, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, + }); + }); + + it('renders the panel', async () => { + render( + + + + ); + + await waitFor(() => expect(screen.getByTestId('treemapPanel')).toBeInTheDocument()); + }); + + it('renders the panel with a hidden overflow-x', async () => { + render( + + + + ); + + await waitFor(() => + expect(screen.getByTestId('treemapPanel')).toHaveStyleRule('overflow-x', 'hidden') + ); + }); + + it('renders the panel with an auto overflow-y to allow vertical scrolling when necessary', async () => { + render( + + + + ); + + await waitFor(() => + expect(screen.getByTestId('treemapPanel')).toHaveStyleRule('overflow-y', 'auto') + ); + }); + + it('renders the chart selector as a custom header title', async () => { + render( + + + + ); + + await waitFor(() => expect(screen.getByTestId('chartSelect')).toBeInTheDocument()); + }); + + it('renders field selection when `isPanelExpanded` is true', async () => { + render( + + + + ); + + await waitFor(() => expect(screen.getByTestId('fieldSelection')).toBeInTheDocument()); + }); + + it('does NOT render field selection when `isPanelExpanded` is false', async () => { + render( + + + + ); + + await waitFor(() => expect(screen.queryByTestId('fieldSelection')).not.toBeInTheDocument()); + }); + + it('renders the progress bar when data is loading', async () => { + (useQueryAlerts as jest.Mock).mockReturnValue({ + loading: true, + data: mockAlertSearchResponse, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, + }); + + render( + + + + ); + + await waitFor(() => expect(screen.getByTestId('progress')).toBeInTheDocument()); + }); + + it('does NOT render the progress bar when data has loaded', async () => { + render( + + + + ); + + await waitFor(() => expect(screen.queryByTestId('progress')).not.toBeInTheDocument()); + }); + + it('renders the treemap when data is available and `isPanelExpanded` is true', async () => { + jest.mock('../../../detections/containers/detection_engine/alerts/use_query', () => { + return { + useQueryAlerts: () => ({ + loading: true, + data: mockAlertSearchResponse, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, + }), + }; + }); + + render( + + + + ); + + await waitFor(() => expect(screen.getByTestId('treemap')).toBeInTheDocument()); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.tsx b/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.tsx new file mode 100644 index 0000000000000..93df193537486 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/alerts_treemap_panel/index.tsx @@ -0,0 +1,204 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; +import { EuiProgress } from '@elastic/eui'; +import type { Filter, Query } from '@kbn/es-query'; +import { buildEsQuery } from '@kbn/es-query'; +import React, { useEffect, useMemo } from 'react'; +import uuid from 'uuid'; + +import { useGlobalTime } from '../../containers/use_global_time'; +import { AlertsTreemap, DEFAULT_MIN_CHART_HEIGHT } from '../alerts_treemap'; +import { KpiPanel } from '../../../detections/components/alerts_kpis/common/components'; +import { useInspectButton } from '../../../detections/components/alerts_kpis/common/hooks'; +import type { AlertSearchResponse } from '../../../detections/containers/detection_engine/alerts/types'; +import { useQueryAlerts } from '../../../detections/containers/detection_engine/alerts/use_query'; +import { FieldSelection } from '../field_selection'; +import { HeaderSection } from '../header_section'; +import { InspectButtonContainer } from '../inspect'; +import { DEFAULT_STACK_BY_FIELD0_SIZE, getAlertsRiskQuery } from '../alerts_treemap/query'; +import type { AlertsTreeMapAggregation } from '../alerts_treemap/types'; + +const DEFAULT_HEIGHT = DEFAULT_MIN_CHART_HEIGHT + 134; // px + +const COLLAPSED_HEIGHT = 64; // px + +const ALERTS_TREEMAP_ID = 'alerts-treemap'; + +export interface Props { + addFilter?: ({ field, value }: { field: string; value: string | number }) => void; + alignHeader?: 'center' | 'baseline' | 'stretch' | 'flexStart' | 'flexEnd'; + chartOptionsContextMenu?: (queryId: string) => React.ReactNode; + isPanelExpanded: boolean; + filters?: Filter[]; + height?: number; + query?: Query; + riskSubAggregationField: string; + runtimeMappings?: MappingRuntimeFields; + setIsPanelExpanded: (value: boolean) => void; + setStackByField0: (stackBy: string) => void; + setStackByField1: (stackBy: string | undefined) => void; + signalIndexName: string | null; + stackByField0: string; + stackByField1: string | undefined; + stackByWidth?: number; + title: React.ReactNode; +} + +export const getBucketsCount = ( + data: AlertSearchResponse | null +): number => data?.aggregations?.stackByField0?.buckets?.length ?? 0; + +const AlertsTreemapPanelComponent: React.FC = ({ + addFilter, + alignHeader, + chartOptionsContextMenu, + isPanelExpanded, + filters, + height = DEFAULT_HEIGHT, + query, + riskSubAggregationField, + runtimeMappings, + setIsPanelExpanded, + setStackByField0, + setStackByField1, + signalIndexName, + stackByField0, + stackByField1, + stackByWidth, + title, +}: Props) => { + const { to, from, deleteQuery, setQuery } = useGlobalTime(); + + // create a unique, but stable (across re-renders) query id + const uniqueQueryId = useMemo(() => `${ALERTS_TREEMAP_ID}-${uuid.v4()}`, []); + + const additionalFilters = useMemo(() => { + try { + return [ + buildEsQuery( + undefined, + query != null ? [query] : [], + filters?.filter((f) => f.meta.disabled === false) ?? [] + ), + ]; + } catch (e) { + return []; + } + }, [query, filters]); + + const { + data: alertsData, + loading: isLoadingAlerts, + refetch, + request, + response, + setQuery: setAlertsQuery, + } = useQueryAlerts<{}, AlertsTreeMapAggregation>({ + query: getAlertsRiskQuery({ + additionalFilters, + from, + riskSubAggregationField, + runtimeMappings, + stackByField0, + stackByField1, + to, + }), + skip: !isPanelExpanded, + indexName: signalIndexName, + }); + + useEffect(() => { + setAlertsQuery( + getAlertsRiskQuery({ + additionalFilters, + from, + riskSubAggregationField, + runtimeMappings, + stackByField0, + stackByField1, + to, + }) + ); + }, [ + additionalFilters, + from, + riskSubAggregationField, + runtimeMappings, + setAlertsQuery, + stackByField0, + stackByField1, + to, + ]); + + useInspectButton({ + deleteQuery, + loading: isLoadingAlerts, + response, + setQuery, + refetch, + request, + uniqueQueryId, + }); + + return ( + + + + {isPanelExpanded && ( + + )} + + + {isLoadingAlerts ? ( + + ) : ( + <> + {alertsData != null && isPanelExpanded && ( + + )} + + )} + + + ); +}; + +AlertsTreemapPanelComponent.displayName = 'AlertsTreemapPanelComponent'; + +export const AlertsTreemapPanel = React.memo(AlertsTreemapPanelComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap index d934c117625d7..3445bc360b521 100644 --- a/x-pack/plugins/security_solution/public/common/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap @@ -66,12 +66,14 @@ exports[`Authentication Host Table Component rendering it renders the host authe >
    ({ + useInspect: () => ({ handleClick: mockHandleClick }), +})); + +describe('useChartSettingsPopoverConfiguration', () => { + const onResetStackByFields = jest.fn(); + const queryId = 'abcd'; + + const state: State = mockGlobalState; + const { storage } = createSecuritySolutionStorageMock(); + const store = createStore(state, SUB_PLUGINS_REDUCER, kibanaObservable, storage); + const wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + + beforeEach(() => jest.resetAllMocks()); + + test('it returns the expected defaultInitialPanelId', () => { + const { result } = renderHook( + () => useChartSettingsPopoverConfiguration({ onResetStackByFields, queryId }), + { wrapper } + ); + + expect(result.current.defaultInitialPanelId).toEqual('default-initial-panel'); + }); + + test('it invokes handleClick when the Inspect menu item is clicked', () => { + const { result } = renderHook( + () => useChartSettingsPopoverConfiguration({ onResetStackByFields, queryId }), + { wrapper } + ); + + ( + result.current.defaultMenuItems[0].items?.find((x) => x.name === i18n.INSPECT) + ?.onClick as () => void + )(); + + expect(mockHandleClick).toBeCalled(); + }); + + test('it invokes onResetStackByFields when the Reset menu item is clicked', () => { + const { result } = renderHook( + () => useChartSettingsPopoverConfiguration({ onResetStackByFields, queryId }), + { wrapper } + ); + + ( + result.current.defaultMenuItems[0].items?.find((x) => x.name === i18n.RESET_GROUP_BY_FIELDS) + ?.onClick as () => void + )(); + + expect(onResetStackByFields).toBeCalled(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/configurations/default/index.tsx b/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/configurations/default/index.tsx new file mode 100644 index 0000000000000..58b9ab25e008a --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/configurations/default/index.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { EuiContextMenuPanelDescriptor } from '@elastic/eui'; +import type { Dispatch, SetStateAction } from 'react'; +import { useMemo, useState } from 'react'; + +import { useInspect } from '../../../inspect/use_inspect'; + +import * as i18n from './translations'; + +const defaultInitialPanelId = 'default-initial-panel'; + +interface Props { + onResetStackByFields: () => void; + queryId: string; +} + +export const useChartSettingsPopoverConfiguration = ({ + onResetStackByFields, + queryId, +}: Props): { + defaultInitialPanelId: string; + defaultMenuItems: EuiContextMenuPanelDescriptor[]; + isPopoverOpen: boolean; + setIsPopoverOpen: Dispatch>; +} => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const { handleClick } = useInspect({ + queryId, + }); + + const defaultMenuItems: EuiContextMenuPanelDescriptor[] = useMemo( + () => [ + { + id: defaultInitialPanelId, + items: [ + { + icon: 'inspect', + name: i18n.INSPECT, + onClick: () => { + setIsPopoverOpen(false); + handleClick(); + }, + }, + { + name: i18n.RESET_GROUP_BY_FIELDS, + onClick: () => { + setIsPopoverOpen(false); + onResetStackByFields(); + }, + }, + ], + }, + ], + [handleClick, onResetStackByFields] + ); + + return { + defaultInitialPanelId, + defaultMenuItems, + isPopoverOpen, + setIsPopoverOpen, + }; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/configurations/default/translations.ts b/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/configurations/default/translations.ts new file mode 100644 index 0000000000000..61a0e6d0904b4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/configurations/default/translations.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const INSPECT = i18n.translate( + 'xpack.securitySolution.components.chartSettingsPopover.contextMenuItems.inspectTitle', + { + defaultMessage: 'Inspect', + } +); + +export const RESET_GROUP_BY_FIELDS = i18n.translate( + 'xpack.securitySolution.components.chartSettingsPopover.contextMenuItems.resetGroupByFieldsMenuItem', + { + defaultMessage: 'Reset group by fields', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/index.test.tsx new file mode 100644 index 0000000000000..c6517a789859a --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/index.test.tsx @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { CHART_SETTINGS_POPOVER_ARIA_LABEL } from './translations'; +import { ChartSettingsPopover } from '.'; + +describe('ChartSettingsPopover', () => { + const setIsPopoverOpen = jest.fn(); + const initialPanelId = 'default-initial-panel'; + + const panels = [ + { + id: initialPanelId, + items: [ + { + icon: 'inspect', + name: 'Inspect', + }, + { + name: 'Reset group by fields', + }, + ], + }, + ]; + + it('renders the chart settings popover', () => { + render( + + ); + + expect( + screen.getByRole('button', { name: CHART_SETTINGS_POPOVER_ARIA_LABEL }) + ).toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/index.tsx b/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/index.tsx new file mode 100644 index 0000000000000..bfa6d0dff4a7c --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/index.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { EuiContextMenuPanelDescriptor } from '@elastic/eui'; +import { EuiButtonIcon, EuiContextMenu, EuiPopover } from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; + +import { BUTTON_CLASS } from '../inspect'; +import * as i18n from './translations'; + +interface Props { + initialPanelId: string; + isPopoverOpen: boolean; + panels: EuiContextMenuPanelDescriptor[]; + setIsPopoverOpen: React.Dispatch>; +} + +const ChartSettingsPopoverComponent: React.FC = ({ + initialPanelId, + isPopoverOpen, + panels, + setIsPopoverOpen, +}: Props) => { + const onButtonClick = useCallback( + () => setIsPopoverOpen((isOpen) => !isOpen), + [setIsPopoverOpen] + ); + + const closePopover = useCallback(() => setIsPopoverOpen(false), [setIsPopoverOpen]); + + const button = useMemo( + () => ( + + ), + [onButtonClick] + ); + + return ( + + + + ); +}; + +ChartSettingsPopoverComponent.displayName = 'ChartSettingsPopoverComponent'; + +export const ChartSettingsPopover = React.memo(ChartSettingsPopoverComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/translations.ts b/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/translations.ts new file mode 100644 index 0000000000000..4c9d7751e43fa --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/chart_settings_popover/translations.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const CHART_SETTINGS_POPOVER_ARIA_LABEL = i18n.translate( + 'xpack.securitySolution.components.chartSettingsPopover.ariaLabel', + { + defaultMessage: 'Chart settings', + } +); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx index 6b7fa6fb5354b..dbfe911c3d6cf 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.test.tsx @@ -13,7 +13,7 @@ import '../../mock/match_media'; import '../../mock/react_beautiful_dnd'; import { TestProviders } from '../../mock'; -import { MIN_LEGEND_HEIGHT, DraggableLegend } from './draggable_legend'; +import { DEFAULT_WIDTH, MIN_LEGEND_HEIGHT, DraggableLegend } from './draggable_legend'; import type { LegendItem } from './draggable_legend_item'; jest.mock('../../lib/kibana'); @@ -76,6 +76,28 @@ describe('DraggableLegend', () => { ); }); + it(`renders a container with the default 'min-width'`, () => { + expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule( + 'min-width', + `${DEFAULT_WIDTH}px` + ); + }); + + it(`renders a container with the specified 'min-width'`, () => { + const width = 1234; + + wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule( + 'min-width', + `${width}px` + ); + }); + it('scrolls when necessary', () => { expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule( 'overflow', diff --git a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.tsx b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.tsx index 6c511ba874128..00be24188d5dc 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend.tsx @@ -14,8 +14,9 @@ import type { LegendItem } from './draggable_legend_item'; import { DraggableLegendItem } from './draggable_legend_item'; export const MIN_LEGEND_HEIGHT = 175; +export const DEFAULT_WIDTH = 165; // px -const DraggableLegendContainer = styled.div<{ height: number }>` +const DraggableLegendContainer = styled.div<{ height: number; $minWidth: number }>` height: ${({ height }) => `${height}px`}; overflow: auto; scrollbar-width: thin; @@ -23,6 +24,7 @@ const DraggableLegendContainer = styled.div<{ height: number }>` @media only screen and (min-width: ${({ theme }) => theme.eui.euiBreakpoints.m}) { width: 165px; } + min-width: ${({ $minWidth }) => `${$minWidth}px`}; &::-webkit-scrollbar { height: ${({ theme }) => theme.eui.euiScrollBar}; @@ -44,7 +46,8 @@ const DraggableLegendContainer = styled.div<{ height: number }>` const DraggableLegendComponent: React.FC<{ height: number; legendItems: LegendItem[]; -}> = ({ height, legendItems }) => { + minWidth?: number; +}> = ({ height, legendItems, minWidth = DEFAULT_WIDTH }) => { if (legendItems.length === 0) { return null; } @@ -53,6 +56,7 @@ const DraggableLegendComponent: React.FC<{ diff --git a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx index 5072df82c96c6..aa1d1e57760f1 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.test.tsx @@ -56,6 +56,36 @@ describe('DraggableLegendItem', () => { ).toEqual(legendItem.value); }); + it('renders a custom legend item via the `render` prop when provided', () => { + const render = (fieldValuePair?: { field: string; value: string | number }) => ( +
    {`${fieldValuePair?.field} - ${fieldValuePair?.value}`}
    + ); + + const customLegendItem = { ...legendItem, render }; + + wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="custom"]`).first().text()).toEqual( + `${legendItem.field} - ${legendItem.value}` + ); + }); + + it('renders an item count via the `count` prop when provided', () => { + const customLegendItem = { ...legendItem, count: 1234 }; + + wrapper = mount( + + + + ); + + expect(wrapper.find(`[data-test-subj="legendItemCount"]`).first().exists()).toBe(true); + }); + it('always hides the Top N action for legend items', () => { expect( wrapper.find(`[data-test-subj="legend-item-${legendItem.dataProviderId}"]`).prop('hideTopN') diff --git a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx index b61dc4331d5c1..16f1cc8c926b5 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/draggable_legend_item.tsx @@ -6,18 +6,28 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiHealth, EuiText } from '@elastic/eui'; +import numeral from '@elastic/numeral'; import React from 'react'; +import styled from 'styled-components'; +import { DEFAULT_NUMBER_FORMAT } from '../../../../common/constants'; import { DefaultDraggable } from '../draggables'; +import { useUiSetting$ } from '../../lib/kibana'; import { EMPTY_VALUE_LABEL } from './translation'; import { hasValueToDisplay } from '../../utils/validators'; +const CountFlexItem = styled(EuiFlexItem)` + ${({ theme }) => `margin-right: ${theme.eui.euiSizeS};`} +`; + export interface LegendItem { color?: string; dataProviderId: string; + render?: (fieldValuePair?: { field: string; value: string | number }) => React.ReactNode; field: string; timelineId?: string; value: string | number; + count?: number; } /** @@ -36,7 +46,8 @@ ValueWrapper.displayName = 'ValueWrapper'; const DraggableLegendItemComponent: React.FC<{ legendItem: LegendItem; }> = ({ legendItem }) => { - const { color, dataProviderId, field, timelineId, value } = legendItem; + const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); + const { color, count, dataProviderId, field, timelineId, value } = legendItem; return ( @@ -47,18 +58,37 @@ const DraggableLegendItemComponent: React.FC<{ )} - - + - - + + + {legendItem.render == null ? ( + + ) : ( + legendItem.render({ field, value }) + )} + + + + {count != null && ( + + {numeral(count).format(defaultNumberFormat)} + + )} +
    diff --git a/x-pack/plugins/security_solution/public/common/components/field_selection/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/field_selection/index.test.tsx new file mode 100644 index 0000000000000..91cb4f7b581be --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/field_selection/index.test.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { TestProviders } from '../../mock'; +import type { Props } from '.'; +import { FieldSelection } from '.'; +import { + GROUP_BY_LABEL, + GROUP_BY_TOP_LABEL, +} from '../../../detections/components/alerts_kpis/common/translations'; + +jest.mock('react-router-dom', () => { + const actual = jest.requireActual('react-router-dom'); + return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; +}); + +const defaultProps: Props = { + setStackByField0: jest.fn(), + setStackByField1: jest.fn(), + stackByField0: 'kibana.alert.rule.name', + stackByField1: 'host.name', + uniqueQueryId: 'alerts-treemap-7cc69a83-1cd0-4d6e-89fa-f9010e9073db', +}; + +describe('FieldSelection', () => { + test('it renders the (first) "Group by" selection', () => { + render( + + + + ); + + expect(screen.getByRole('combobox', { name: GROUP_BY_LABEL })).toBeInTheDocument(); + }); + + test('it renders the (second) "Group by top" selection', () => { + render( + + + + ); + + expect(screen.getByRole('combobox', { name: GROUP_BY_TOP_LABEL })).toBeInTheDocument(); + }); + + test('it renders the chart options context menu using the provided `uniqueQueryId`', () => { + const propsWithContextMenu = { + ...defaultProps, + chartOptionsContextMenu: (queryId: string) =>
    {queryId}
    , + }; + + render( + + + + ); + + expect(screen.getByText(defaultProps.uniqueQueryId)).toBeInTheDocument(); + }); + + test('it does NOT render the chart options context menu when `chartOptionsContextMenu` is undefined', () => { + render( + + + + ); + + expect(screen.queryByText(defaultProps.uniqueQueryId)).not.toBeInTheDocument(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/field_selection/index.tsx b/x-pack/plugins/security_solution/public/common/components/field_selection/index.tsx new file mode 100644 index 0000000000000..275e02bf21ce8 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/field_selection/index.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import React from 'react'; +import styled from 'styled-components'; + +import { StackByComboBox } from '../../../detections/components/alerts_kpis/common/components'; +import { + GROUP_BY_LABEL, + GROUP_BY_TOP_LABEL, +} from '../../../detections/components/alerts_kpis/common/translations'; + +const ChartOptionsFlexItem = styled(EuiFlexItem)` + margin-left: ${({ theme }) => theme.eui.euiSizeS}; +`; + +export interface Props { + chartOptionsContextMenu?: (queryId: string) => React.ReactNode; + setStackByField0: (stackBy: string) => void; + setStackByField1: (stackBy: string | undefined) => void; + stackByField0: string; + stackByField1: string | undefined; + stackByWidth?: number; + uniqueQueryId: string; +} + +const FieldSelectionComponent: React.FC = ({ + chartOptionsContextMenu, + setStackByField0, + setStackByField1, + stackByField0, + stackByField1, + stackByWidth, + uniqueQueryId, +}: Props) => ( + + + + + + + + {chartOptionsContextMenu != null && ( + + {chartOptionsContextMenu(uniqueQueryId)} + + )} + + +); + +FieldSelectionComponent.displayName = 'FieldSelectionComponent'; + +export const FieldSelection = React.memo(FieldSelectionComponent); diff --git a/x-pack/plugins/security_solution/public/common/components/header_section/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/header_section/__snapshots__/index.test.tsx.snap index 058f38c944347..00732ec7b82e8 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_section/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/header_section/__snapshots__/index.test.tsx.snap @@ -7,13 +7,17 @@ exports[`HeaderSection it renders 1`] = ` data-test-subj="header-section" > - + diff --git a/x-pack/plugins/security_solution/public/common/components/header_section/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/header_section/index.test.tsx index d026e3c15ea35..9e89acc20b3f1 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_section/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_section/index.test.tsx @@ -10,7 +10,7 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; import { TestProviders } from '../../mock'; -import { HeaderSection } from '.'; +import { getHeaderAlignment, HeaderSection } from '.'; describe('HeaderSection', () => { test('it renders', () => { @@ -205,6 +205,90 @@ describe('HeaderSection', () => { expect(wrapper.find('[data-test-subj="query-toggle-header"]').first().exists()).toBe(true); }); + test('it does NOT align items to flex start in the outer flex group when stackHeader is true', () => { + const wrapper = mount( + + +

    {'Test children'}

    +
    +
    + ); + + expect( + wrapper.find('[data-test-subj="headerSectionOuterFlexGroup"]').first().getDOMNode() + ).not.toHaveClass('euiFlexGroup--alignItemsFlexStart'); + }); + + test(`it uses the 'column' direction in the outer flex group by default`, () => { + const wrapper = mount( + + +

    {'Test children'}

    +
    +
    + ); + + expect( + wrapper.find('[data-test-subj="headerSectionOuterFlexGroup"]').first().getDOMNode() + ).toHaveClass('euiFlexGroup--directionColumn'); + }); + + test('it uses the `outerDirection` prop to specify the direction of the outer flex group when it is provided', () => { + const wrapper = mount( + + +

    {'Test children'}

    +
    +
    + ); + + expect( + wrapper.find('[data-test-subj="headerSectionOuterFlexGroup"]').first().getDOMNode() + ).toHaveClass('euiFlexGroup--directionRow'); + }); + + test('it defaults to center alignment in the inner flex group', () => { + const wrapper = mount( + + +

    {'Test children'}

    +
    +
    + ); + + expect( + wrapper.find('[data-test-subj="headerSectionInnerFlexGroup"]').first().getDOMNode() + ).toHaveClass('euiFlexGroup--alignItemsCenter'); + }); + + test('it aligns items using the value of the `alignHeader` prop in the inner flex group when specified', () => { + const wrapper = mount( + + +

    {'Test children'}

    +
    +
    + ); + + expect( + wrapper.find('[data-test-subj="headerSectionInnerFlexGroup"]').first().getDOMNode() + ).toHaveClass('euiFlexGroup--alignItemsFlexEnd'); + }); + + test('it does NOT default to center alignment in the inner flex group when the `stackHeader` prop is true', () => { + const wrapper = mount( + + +

    {'Test children'}

    +
    +
    + ); + + expect( + wrapper.find('[data-test-subj="headerSectionInnerFlexGroup"]').first().getDOMNode() + ).not.toHaveClass('euiFlexGroup--alignItemsCenter'); + }); + test('it does render everything but title when toggleStatus = true', () => { const wrapper = mount( @@ -292,4 +376,29 @@ describe('HeaderSection', () => { wrapper.find('[data-test-subj="query-toggle-header"]').first().simulate('click'); expect(mockToggle).toBeCalledWith(false); }); + + describe('getHeaderAlignment', () => { + test(`it always returns the value of alignHeader when it's provided`, () => { + const alignHeader = 'flexStart'; + const stackHeader = true; + + expect(getHeaderAlignment({ alignHeader, stackHeader })).toEqual(alignHeader); + }); + + test(`it returns undefined when stackHeader is true`, () => { + const stackHeader = true; + + expect(getHeaderAlignment({ stackHeader })).toBeUndefined(); + }); + + test(`it returns 'center' when stackHeader is false`, () => { + const stackHeader = false; + + expect(getHeaderAlignment({ stackHeader })).toEqual('center'); + }); + + test(`it returns 'center' by default`, () => { + expect(getHeaderAlignment({})).toEqual('center'); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/header_section/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_section/index.tsx index cadd0ec41b429..e8fe65e52d60c 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_section/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_section/index.tsx @@ -51,7 +51,9 @@ const Header = styled.header` Header.displayName = 'Header'; export interface HeaderSectionProps extends HeaderProps { + alignHeader?: 'center' | 'baseline' | 'stretch' | 'flexStart' | 'flexEnd'; children?: React.ReactNode; + outerDirection?: 'row' | 'rowReverse' | 'column' | 'columnReverse' | undefined; growLeftSplit?: boolean; headerFilters?: string | React.ReactNode; height?: number; @@ -70,9 +72,27 @@ export interface HeaderSectionProps extends HeaderProps { tooltip?: string; } +export const getHeaderAlignment = ({ + alignHeader, + stackHeader, +}: { + alignHeader?: 'center' | 'baseline' | 'stretch' | 'flexStart' | 'flexEnd'; + stackHeader?: boolean; +}) => { + if (alignHeader != null) { + return alignHeader; + } else if (stackHeader) { + return undefined; + } else { + return 'center'; + } +}; + const HeaderSectionComponent: React.FC = ({ + alignHeader, border, children, + outerDirection = 'column', growLeftSplit = true, headerFilters, height, @@ -108,10 +128,16 @@ const HeaderSectionComponent: React.FC = ({ className={classNames} $hideSubtitle={hideSubtitle} > - - + + @@ -158,13 +184,14 @@ const HeaderSectionComponent: React.FC = ({ - {id && showInspectButton && toggleStatus && ( + {id && toggleStatus && ( )} diff --git a/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx index 7e92f8e9d3931..8cc5951d5701a 100644 --- a/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/inspect/index.test.tsx @@ -70,6 +70,22 @@ describe('Inspect Button', () => { ); }); + test('it does NOT render the Empty Button when showInspectButton is false', () => { + const wrapper = mount( + + + + ); + expect(wrapper.find('button[data-test-subj="inspect-empty-button"]').first().exists()).toBe( + false + ); + }); + test('Eui Icon Button', () => { const wrapper = mount( @@ -92,6 +108,17 @@ describe('Inspect Button', () => { ); }); + test('it does NOT render the Icon Button when showInspectButton is false', () => { + const wrapper = mount( + + + + ); + expect(wrapper.find('button[data-test-subj="inspect-icon-button"]').first().exists()).toBe( + false + ); + }); + test('Eui Empty Button disabled', () => { const wrapper = mount( diff --git a/x-pack/plugins/security_solution/public/common/components/inspect/index.tsx b/x-pack/plugins/security_solution/public/common/components/inspect/index.tsx index fb827fd222731..0c9fc02478f92 100644 --- a/x-pack/plugins/security_solution/public/common/components/inspect/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/inspect/index.tsx @@ -40,6 +40,7 @@ interface InspectButtonProps { multiple?: boolean; onCloseInspect?: () => void; queryId: string; + showInspectButton?: boolean; title: string | React.ReactElement | React.ReactNode; } @@ -51,6 +52,7 @@ const InspectButtonComponent: React.FC = ({ multiple = false, // If multiple = true we ignore the inspectIndex and pass all requests and responses to the inspect modal onCloseInspect, queryId = '', + showInspectButton = true, title = '', }) => { const { @@ -74,7 +76,7 @@ const InspectButtonComponent: React.FC = ({ return ( <> - {inputId === 'timeline' && !compact && ( + {inputId === 'timeline' && !compact && showInspectButton && ( = ({ {i18n.INSPECT} )} - {(inputId === 'global' || compact) && ( + {(inputId === 'global' || compact) && showInspectButton && ( { + describe('getSettingKey', () => { + it('returns the expected key', () => { + expect( + getSettingKey({ + category: TREEMAP_CATEGORY, + page: ALERTS_PAGE, + setting: STACK_BY_SETTING_NAME, + }) + ).toEqual(`${ALERTS_PAGE}.${TREEMAP_CATEGORY}.${STACK_BY_SETTING_NAME}`); + }); + }); + + describe('isDefaultWhenEmptyString', () => { + it('returns true when value is empty', () => { + expect(isDefaultWhenEmptyString('')).toBe(true); + }); + + it('returns false when value is non-empty', () => { + expect(isDefaultWhenEmptyString('foozle')).toBe(false); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/local_storage/helpers.ts b/x-pack/plugins/security_solution/public/common/components/local_storage/helpers.ts new file mode 100644 index 0000000000000..2297fe2652286 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/local_storage/helpers.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isEmpty } from 'lodash/fp'; + +/** Returns a settings key, typically used with local storage */ +export const getSettingKey = ({ + category, + page, + setting, +}: { + category: string; + page: string; + setting: string; +}): string => `${page}.${category}.${setting}`; + +export const isDefaultWhenEmptyString = (value: T): boolean => + typeof value !== 'string' || isEmpty(value.trim()); diff --git a/x-pack/plugins/security_solution/public/common/components/local_storage/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/local_storage/index.test.tsx new file mode 100644 index 0000000000000..d7dbfdeb5d026 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/local_storage/index.test.tsx @@ -0,0 +1,182 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { act, renderHook } from '@testing-library/react-hooks'; + +import { APP_ID } from '../../../../common/constants'; +import { DEFAULT_STACK_BY_FIELD } from '../../../detections/components/alerts_kpis/common/config'; +import { + ALERTS_PAGE, + EXPAND_SETTING_NAME, + STACK_BY_SETTING_NAME, + TREEMAP_CATEGORY, +} from '../../../detections/pages/detection_engine/chart_panels/alerts_local_storage/constants'; +import { getSettingKey, isDefaultWhenEmptyString } from './helpers'; +import { useKibana as mockUseKibana } from '../../lib/kibana/__mocks__'; +import { useLocalStorage } from '.'; + +const mockedUseKibana = { + ...mockUseKibana(), + services: { + ...mockUseKibana().services, + storage: { + ...mockUseKibana().services.storage, + get: jest.fn(), + set: jest.fn(), + }, + }, +}; + +jest.mock('../../lib/kibana', () => { + return { + useKibana: () => mockedUseKibana, + }; +}); + +describe('useLocalStorage', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('it returns the expected value from local storage', async () => { + const mockLocalStorageValue = 'baz'; + mockedUseKibana.services.storage.get.mockReturnValue(mockLocalStorageValue); + + const { result } = renderHook(() => + useLocalStorage({ + defaultValue: DEFAULT_STACK_BY_FIELD, + key: getSettingKey({ + category: TREEMAP_CATEGORY, + page: ALERTS_PAGE, + setting: STACK_BY_SETTING_NAME, + }), + plugin: APP_ID, + }) + ); + + const [riskVisualizationStackBy] = result.current; + + expect(riskVisualizationStackBy).toEqual(mockLocalStorageValue); + }); + + test('it returns the expected default when the value in local storage is undefined', async () => { + const { result } = renderHook(() => + useLocalStorage({ + defaultValue: true, + key: getSettingKey({ + category: TREEMAP_CATEGORY, + page: ALERTS_PAGE, + setting: EXPAND_SETTING_NAME, + }), + plugin: APP_ID, + }) + ); + + const [riskVisualizationStackBy] = result.current; + + expect(riskVisualizationStackBy).toEqual(true); + }); + + test('it returns the expected default when the type of the value in local storage does not match the default', async () => { + const mockLocalStorageValue = 'abcd'; // <-- this type does not match the type of the expected default value + mockedUseKibana.services.storage.get.mockReturnValue(mockLocalStorageValue); + + const { result } = renderHook(() => + useLocalStorage({ + defaultValue: 1234, + key: getSettingKey({ + category: TREEMAP_CATEGORY, + page: ALERTS_PAGE, + setting: EXPAND_SETTING_NAME, + }), + plugin: APP_ID, + }) + ); + + const [riskVisualizationStackBy] = result.current; + + expect(riskVisualizationStackBy).toEqual(1234); + }); + + describe('setValue', () => { + test('it updates local storage', async () => { + const newValue = 'bazfact'; + + const { result } = renderHook(() => + useLocalStorage({ + defaultValue: DEFAULT_STACK_BY_FIELD, + key: getSettingKey({ + category: TREEMAP_CATEGORY, + page: ALERTS_PAGE, + setting: STACK_BY_SETTING_NAME, + }), + plugin: APP_ID, + isInvalidDefault: isDefaultWhenEmptyString, + }) + ); + + const [_, setValue] = result.current; + + act(() => setValue(newValue)); + + expect(mockedUseKibana.services.storage.set).toBeCalledWith( + `${APP_ID}.${getSettingKey({ + category: TREEMAP_CATEGORY, + page: ALERTS_PAGE, + setting: STACK_BY_SETTING_NAME, + })}`, + newValue + ); + }); + }); + + describe('isInvalidDefault', () => { + test('it returns the expected default value when the value in local storage matches the value returned by isInvalidDefault', async () => { + const mockLocalStorageValue = ''; // <-- this matches the value specified by isInvalidDefault + mockedUseKibana.services.storage.get.mockReturnValue(mockLocalStorageValue); + + const { result } = renderHook(() => + useLocalStorage({ + defaultValue: DEFAULT_STACK_BY_FIELD, + key: getSettingKey({ + category: TREEMAP_CATEGORY, + page: ALERTS_PAGE, + setting: STACK_BY_SETTING_NAME, + }), + plugin: APP_ID, + isInvalidDefault: isDefaultWhenEmptyString, + }) + ); + + const [riskVisualizationStackBy] = result.current; + + expect(riskVisualizationStackBy).toEqual(DEFAULT_STACK_BY_FIELD); + }); + + test('it returns the value from local storage when it does NOT match the value returned by isInvalidDefault', async () => { + const mockLocalStorageValue = 'totally valid'; // <-- this matches the value specified by isInvalidDefault + mockedUseKibana.services.storage.get.mockReturnValue(mockLocalStorageValue); + + const { result } = renderHook(() => + useLocalStorage({ + defaultValue: DEFAULT_STACK_BY_FIELD, + key: getSettingKey({ + category: TREEMAP_CATEGORY, + page: ALERTS_PAGE, + setting: STACK_BY_SETTING_NAME, + }), + plugin: APP_ID, + isInvalidDefault: isDefaultWhenEmptyString, + }) + ); + + const [riskVisualizationStackBy] = result.current; + + expect(riskVisualizationStackBy).toEqual(mockLocalStorageValue); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/local_storage/index.tsx b/x-pack/plugins/security_solution/public/common/components/local_storage/index.tsx new file mode 100644 index 0000000000000..8422b4dc96f28 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/local_storage/index.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useEffect, useState } from 'react'; + +import { APP_ID } from '../../../../common/constants'; + +import { useKibana } from '../../lib/kibana'; +interface Props { + defaultValue: T; + isInvalidDefault?: (value: T) => boolean; + key: string; + plugin?: string; +} + +/** Reads and writes settings from local storage */ +export const useLocalStorage = ({ + defaultValue, + key, + plugin = APP_ID, + isInvalidDefault, +}: Props): [T, (value: T) => void] => { + const { storage } = useKibana().services; + const [initialized, setInitialized] = useState(false); + const [_value, _setValue] = useState(defaultValue); + + const readValueFromLocalStorage = useCallback(() => { + const value = storage.get(`${plugin}.${key}`); + + const valueAndDefaultTypesAreDifferent = typeof value !== typeof defaultValue; + const valueIsInvalid = isInvalidDefault != null && isInvalidDefault(value); + + _setValue(valueAndDefaultTypesAreDifferent || valueIsInvalid ? defaultValue : value); + }, [defaultValue, isInvalidDefault, key, plugin, storage]); + + const setValue = useCallback( + (value: T) => { + storage.set(`${plugin}.${key}`, value); + + _setValue(value); + }, + [key, plugin, storage] + ); + + useEffect(() => { + if (!initialized) { + readValueFromLocalStorage(); + setInitialized(true); + } + }, [initialized, readValueFromLocalStorage]); + + return [_value, setValue]; +}; diff --git a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx index 14593d10f1f89..68ef266b07b25 100644 --- a/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/matrix_histogram/index.test.tsx @@ -36,10 +36,6 @@ jest.mock('../visualization_actions', () => ({ )), })); -jest.mock('../inspect', () => ({ - InspectButton: jest.fn(() =>
    ), -})); - jest.mock('./utils', () => ({ getBarchartConfigs: jest.fn(), getCustomChartData: jest.fn().mockReturnValue(true), @@ -196,7 +192,7 @@ describe('Matrix Histogram Component', () => { wrapper = mount(, { wrappingComponent: TestProviders, }); - expect(wrapper.find('[data-test-subj="mock-inspect"]').exists()).toBe(false); + expect(wrapper.find('[data-test-subj="inspect-icon-button"]').exists()).toBe(false); }); test("it doesn't render Inspect button by default on Network page", () => { @@ -215,7 +211,7 @@ describe('Matrix Histogram Component', () => { wrapper = mount(, { wrappingComponent: TestProviders, }); - expect(wrapper.find('[data-test-subj="mock-inspect"]').exists()).toBe(false); + expect(wrapper.find('[data-test-subj="inspect-icon-button"]').exists()).toBe(false); }); test('it render Inspect button by default on other pages', () => { @@ -234,7 +230,7 @@ describe('Matrix Histogram Component', () => { wrapper = mount(, { wrappingComponent: TestProviders, }); - expect(wrapper.find('[data-test-subj="mock-inspect"]').exists()).toBe(true); + expect(wrapper.find('[data-test-subj="inspect-icon-button"]').exists()).toBe(true); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.test.tsx index 3fae45e1e0086..56aad747a856d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.test.tsx @@ -14,6 +14,15 @@ import { TestProviders } from '../../../../common/mock'; import { DragDropContextWrapper } from '../../../../common/components/drag_and_drop/drag_drop_context_wrapper'; import { mockBrowserFields } from '../../../../common/containers/source/mock'; import type { AlertsCountAggregation } from './types'; +import { emptyStackByField0Response } from './mocks/mock_response_empty_field0'; +import { + buckets as oneGroupByResponseBuckets, + mockMultiGroupResponse, +} from './mocks/mock_response_multi_group'; +import { + buckets as twoGroupByResponseBuckets, + singleGroupResponse, +} from './mocks/mock_response_single_group'; jest.mock('../../../../common/lib/kibana'); const mockDispatch = jest.fn(); @@ -37,60 +46,173 @@ describe('AlertsCount', () => { } loading={false} - selectedStackByOption={'test_selected_field'} + stackByField0={'test_selected_field'} + stackByField1={undefined} /> ); expect(wrapper.find('[data-test-subj="alertsCountTable"]').exists()).toBeTruthy(); }); - it('renders the given alert item', () => { - const alertFiedlKey = 'test_stack_by_test_key'; - const alertFiedlCount = 999; - const alertData = { - took: 0, - timeout: false, - hits: { - hits: [], - sequences: [], - events: [], - total: { - relation: 'eq', - value: 0, - }, - }, - _shards: { - failed: 0, - skipped: 0, - successful: 1, - total: 1, - }, - aggregations: { - alertsByGroupingCount: { - buckets: [ - { - key: alertFiedlKey, - doc_count: alertFiedlCount, - }, - ], - }, - alertsByGrouping: { buckets: [] }, - }, - } as AlertSearchResponse; - + it('renders the expected table body message when stackByField0 is an empty string', () => { const wrapper = mount( - - - + ); - expect(wrapper.text()).toContain(alertFiedlKey); - expect(wrapper.text()).toContain(alertFiedlCount); + expect(wrapper.find('[data-test-subj="alertsCountTable"] tbody').text()).toEqual( + 'No items found' + ); + }); + + describe('one group by field', () => { + oneGroupByResponseBuckets.forEach((bucket, i) => { + it(`renders the expected stackByField0 column text for bucket '${bucket.key}'`, () => { + const wrapper = mount( + + + + + + ); + + expect( + wrapper + .find(`[data-test-subj="stackByField0Key"] > div.euiTableCellContent`) + .hostNodes() + .at(i) + .text() + ).toEqual(bucket.key); + }); + }); + + oneGroupByResponseBuckets.forEach((bucket, i) => { + it(`renders the expected doc_count column value for bucket '${bucket.key}'`, () => { + const wrapper = mount( + + + + + + ); + + expect( + wrapper + .find(`[data-test-subj="doc_count"] > div.euiTableCellContent`) + .hostNodes() + .at(i) + .text() + ).toEqual(`${bucket.doc_count}`); + }); + }); + }); + + describe('two group by fields: stackByField0 column', () => { + let resultRow = 0; + + twoGroupByResponseBuckets.forEach((bucket) => { + bucket.stackByField1.buckets.forEach((b) => { + it(`renders the expected stackByField0 column text for stackByField0: '${bucket.key}', stackByField1 '${b.key}'`, () => { + const wrapper = mount( + + + + + + ); + + expect( + wrapper + .find(`[data-test-subj="stackByField0Key"] > div.euiTableCellContent`) + .hostNodes() + .at(resultRow++) + .text() + ).toEqual(bucket.key); + }); + }); + }); + }); + + describe('two group by fields: stackByField1 column', () => { + let resultRow = 0; + + twoGroupByResponseBuckets.forEach((bucket) => { + bucket.stackByField1.buckets.forEach((b, i) => { + it(`renders the expected stackByField1 column text for stackByField0: '${bucket.key}', stackByField1 '${b.key}'`, () => { + const wrapper = mount( + + + + + + ); + + expect( + wrapper + .find(`[data-test-subj="stackByField1Key"] > div.euiTableCellContent`) + .hostNodes() + .at(resultRow++) + .text() + ).toEqual(b.key); + }); + }); + }); + }); + + describe('two group by fields: stackByField1DocCount column', () => { + let resultRow = 0; + + twoGroupByResponseBuckets.forEach((bucket) => { + bucket.stackByField1.buckets.forEach((b, i) => { + it(`renders the expected doc_count column value for stackByField0: '${bucket.key}', stackByField1 '${b.key}'`, () => { + const wrapper = mount( + + + + + + ); + + expect( + wrapper + .find(`[data-test-subj="stackByField1DocCount"] > div.euiTableCellContent`) + .hostNodes() + .at(resultRow++) + .text() + ).toEqual(`${b.doc_count}`); + }); + }); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx index 730ae0405c722..4f538b64b31ee 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/alerts_count.tsx @@ -5,85 +5,100 @@ * 2.0. */ -import type { EuiBasicTableColumn } from '@elastic/eui'; -import { EuiProgress, EuiInMemoryTable } from '@elastic/eui'; -import React, { memo, useMemo } from 'react'; +import { EuiInMemoryTable } from '@elastic/eui'; +import { isEmpty } from 'lodash/fp'; +import React, { useMemo } from 'react'; import styled from 'styled-components'; -import numeral from '@elastic/numeral'; + import { useUiSetting$ } from '../../../../common/lib/kibana'; import { DEFAULT_NUMBER_FORMAT } from '../../../../../common/constants'; -import * as i18n from './translations'; -import { DefaultDraggable } from '../../../../common/components/draggables'; -import type { GenericBuckets } from '../../../../../common/search_strategy'; import type { AlertSearchResponse } from '../../../containers/detection_engine/alerts/types'; import type { AlertsCountAggregation } from './types'; +import { + getMaxRiskSubAggregations, + getUpToMaxBuckets, +} from '../../../../common/components/alerts_treemap/lib/helpers'; +import { getFlattenedBuckets } from '../../../../common/components/alerts_treemap/lib/flatten/get_flattened_buckets'; +import type { + FlattenedBucket, + RawBucket, +} from '../../../../common/components/alerts_treemap/types'; +import { + getMultiGroupAlertsCountTableColumns, + getSingleGroupByAlertsCountTableColumns, +} from './columns'; +import { DEFAULT_STACK_BY_FIELD0_SIZE } from './helpers'; interface AlertsCountProps { loading: boolean; - data: AlertSearchResponse | null; - selectedStackByOption: string; + data: AlertSearchResponse; + stackByField0: string; + stackByField1: string | undefined; } const Wrapper = styled.div` margin-top: -${({ theme }) => theme.eui.euiSizeS}; `; -const getAlertsCountTableColumns = ( - selectedStackByOption: string, - defaultNumberFormat: string -): Array> => { - return [ - { - field: 'key', - name: selectedStackByOption, - truncateText: false, - render: function DraggableStackOptionField(value: string) { - return ( - - ); - }, - }, - { - field: 'doc_count', - name: i18n.COUNT_TABLE_COLUMN_TITLE, - sortable: true, - textOnly: true, - dataType: 'number', - render: (item: string) => numeral(item).format(defaultNumberFormat), - }, - ]; -}; - -export const AlertsCount = memo(({ loading, selectedStackByOption, data }) => { +export const AlertsCountComponent: React.FC = ({ + data, + loading, + stackByField0, + stackByField1, +}) => { const [defaultNumberFormat] = useUiSetting$(DEFAULT_NUMBER_FORMAT); - const listItems: GenericBuckets[] = data?.aggregations?.alertsByGroupingCount?.buckets ?? []; + const tableColumns = useMemo( - () => getAlertsCountTableColumns(selectedStackByOption, defaultNumberFormat), - [selectedStackByOption, defaultNumberFormat] + () => + isEmpty(stackByField1?.trim()) + ? getSingleGroupByAlertsCountTableColumns({ + defaultNumberFormat, + stackByField0, + }) + : getMultiGroupAlertsCountTableColumns({ + defaultNumberFormat, + stackByField0, + stackByField1, + }), + [defaultNumberFormat, stackByField0, stackByField1] ); - return ( - <> - {loading && } + const buckets: RawBucket[] = useMemo( + () => + getUpToMaxBuckets({ + buckets: data.aggregations?.stackByField0?.buckets, + maxItems: DEFAULT_STACK_BY_FIELD0_SIZE, + }), + [data.aggregations?.stackByField0?.buckets] + ); - - - - + const maxRiskSubAggregations = useMemo(() => getMaxRiskSubAggregations(buckets), [buckets]); + + const items: FlattenedBucket[] = useMemo( + () => + isEmpty(stackByField1?.trim()) + ? buckets + : getFlattenedBuckets({ + buckets, + maxRiskSubAggregations, + stackByField0, + }), + [buckets, maxRiskSubAggregations, stackByField0, stackByField1] + ); + + return ( + + + ); -}); +}; + +AlertsCountComponent.displayName = 'AlertsCountComponent'; -AlertsCount.displayName = 'AlertsCount'; +export const AlertsCount = React.memo(AlertsCountComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.test.tsx new file mode 100644 index 0000000000000..c5600fe7eda94 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.test.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { omit } from 'lodash/fp'; +import { + getMultiGroupAlertsCountTableColumns, + getSingleGroupByAlertsCountTableColumns, +} from './columns'; + +describe('columns', () => { + const defaultNumberFormat = '0,0.[000]'; + const stackByField0 = 'kibana.alert.rule.name'; + + describe('getMultiGroupAlertsCountTableColumns', () => { + const stackByField1 = 'host.name'; + + test('it returns the expected columns', () => { + expect( + getMultiGroupAlertsCountTableColumns({ + defaultNumberFormat, + stackByField0, + stackByField1, + }).map((x) => omit('render', x)) + ).toEqual([ + { + 'data-test-subj': 'stackByField0Key', + field: 'key', + name: 'Top 1000 values of kibana.alert.rule.name', + truncateText: false, + }, + { + 'data-test-subj': 'stackByField1Key', + field: 'stackByField1Key', + name: 'Top 1000 values of host.name', + truncateText: false, + }, + { + 'data-test-subj': 'stackByField1DocCount', + dataType: 'number', + field: 'stackByField1DocCount', + name: 'Count of records', + sortable: true, + textOnly: true, + }, + ]); + }); + }); + + describe('getSingleGroupByAlertsCountTableColumns', () => { + test('it returns the expected columns', () => { + expect( + getSingleGroupByAlertsCountTableColumns({ defaultNumberFormat, stackByField0 }).map((x) => + omit('render', x) + ) + ).toEqual([ + { + 'data-test-subj': 'stackByField0Key', + field: 'key', + name: 'kibana.alert.rule.name', + truncateText: false, + }, + { + 'data-test-subj': 'doc_count', + dataType: 'number', + field: 'doc_count', + name: 'Count of records', + sortable: true, + textOnly: true, + }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.tsx new file mode 100644 index 0000000000000..7dfb3170e43e0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/columns.tsx @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import type { EuiBasicTableColumn } from '@elastic/eui'; +import numeral from '@elastic/numeral'; + +import type { FlattenedBucket } from '../../../../common/components/alerts_treemap/types'; +import { DefaultDraggable } from '../../../../common/components/draggables'; +import type { GenericBuckets } from '../../../../../common/search_strategy/common'; +import * as i18n from './translations'; +import { DEFAULT_STACK_BY_FIELD0_SIZE, DEFAULT_STACK_BY_FIELD1_SIZE } from './helpers'; + +export const getSingleGroupByAlertsCountTableColumns = ({ + defaultNumberFormat, + stackByField0, +}: { + defaultNumberFormat: string; + stackByField0: string; +}): Array> => [ + { + 'data-test-subj': 'stackByField0Key', + field: 'key', + name: stackByField0, + render: function DraggableStackOptionField(value: string) { + return ( + + ); + }, + truncateText: false, + }, + { + 'data-test-subj': 'doc_count', + dataType: 'number', + field: 'doc_count', + name: i18n.COUNT_TABLE_COLUMN_TITLE, + render: (item: string) => numeral(item).format(defaultNumberFormat), + sortable: true, + textOnly: true, + }, +]; + +export const getMultiGroupAlertsCountTableColumns = ({ + defaultNumberFormat, + stackByField0, + stackByField1, +}: { + defaultNumberFormat: string; + stackByField0: string; + stackByField1: string | undefined; +}): Array> => [ + { + 'data-test-subj': 'stackByField0Key', + field: 'key', + name: i18n.COLUMN_LABEL({ fieldName: stackByField0, topN: DEFAULT_STACK_BY_FIELD0_SIZE }), + render: function DraggableStackOptionField(value: string) { + return ( + + ); + }, + truncateText: false, + }, + { + 'data-test-subj': 'stackByField1Key', + field: 'stackByField1Key', + name: i18n.COLUMN_LABEL({ fieldName: stackByField1 ?? '', topN: DEFAULT_STACK_BY_FIELD1_SIZE }), + render: function DraggableStackOptionField(value: string) { + return ( + + ); + }, + truncateText: false, + }, + { + 'data-test-subj': 'stackByField1DocCount', + dataType: 'number', + field: 'stackByField1DocCount', + name: i18n.COUNT_TABLE_COLUMN_TITLE, + render: (item: string) => numeral(item).format(defaultNumberFormat), + sortable: true, + textOnly: true, + }, +]; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.test.tsx new file mode 100644 index 0000000000000..e651f17d59157 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.test.tsx @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getAlertsCountQuery } from './helpers'; + +const stackByField0 = 'kibana.alert.rule.name'; +const stackByField1 = 'host.name'; +const from = '2022-07-08T06:00:00.000Z'; +const to = '2022-07-09T05:59:59.999Z'; +const additionalFilters = [ + { + bool: { + must: [], + filter: [ + { + term: { + 'kibana.alert.workflow_status': 'open', + }, + }, + ], + should: [], + must_not: [ + { + exists: { + field: 'kibana.alert.building_block_type', + }, + }, + ], + }, + }, +]; +const runtimeMappings = {}; + +describe('helpers', () => { + describe('getAlertsCountQuery', () => { + test('it returns the expected query when stackByField1 is specified', () => { + expect( + getAlertsCountQuery({ + additionalFilters, + from, + runtimeMappings, + stackByField0, + stackByField1, + to, + }) + ).toEqual({ + size: 0, + aggs: { + stackByField0: { + terms: { field: 'kibana.alert.rule.name', order: { _count: 'desc' }, size: 1000 }, + aggs: { + stackByField1: { + terms: { field: 'host.name', order: { _count: 'desc' }, size: 1000 }, + }, + }, + }, + }, + query: { + bool: { + filter: [ + { + bool: { + must: [], + filter: [{ term: { 'kibana.alert.workflow_status': 'open' } }], + should: [], + must_not: [{ exists: { field: 'kibana.alert.building_block_type' } }], + }, + }, + { + range: { + '@timestamp': { + gte: '2022-07-08T06:00:00.000Z', + lte: '2022-07-09T05:59:59.999Z', + }, + }, + }, + ], + }, + }, + runtime_mappings: {}, + }); + }); + + test('it returns the expected query when stackByField1 is `undefined`', () => { + expect( + getAlertsCountQuery({ + additionalFilters, + from, + runtimeMappings, + stackByField0, + stackByField1: undefined, + to, + }) + ).toEqual({ + size: 0, + aggs: { + stackByField0: { + terms: { field: 'kibana.alert.rule.name', order: { _count: 'desc' }, size: 1000 }, + aggs: {}, + }, + }, + query: { + bool: { + filter: [ + { + bool: { + must: [], + filter: [{ term: { 'kibana.alert.workflow_status': 'open' } }], + should: [], + must_not: [{ exists: { field: 'kibana.alert.building_block_type' } }], + }, + }, + { + range: { + '@timestamp': { + gte: '2022-07-08T06:00:00.000Z', + lte: '2022-07-09T05:59:59.999Z', + }, + }, + }, + ], + }, + }, + runtime_mappings: {}, + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.tsx index 509053e8244fe..d052af0ae0b9e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/helpers.tsx @@ -6,27 +6,44 @@ */ import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../../../common/constants'; +import { getOptionalSubAggregation } from '../../../../common/components/alerts_treemap/query'; -export const getAlertsCountQuery = ( - stackByField: string, - from: string, - to: string, +export const DEFAULT_STACK_BY_FIELD0_SIZE = 1000; +export const DEFAULT_STACK_BY_FIELD1_SIZE = 1000; + +export const getAlertsCountQuery = ({ + additionalFilters = [], + from, + runtimeMappings, + stackByField0, + stackByField1, + to, +}: { additionalFilters: Array<{ bool: { filter: unknown[]; should: unknown[]; must_not: unknown[]; must: unknown[] }; - }> = [], - runtimeMappings?: MappingRuntimeFields -) => { + }>; + from: string; + runtimeMappings?: MappingRuntimeFields; + stackByField0: string; + stackByField1: string | undefined; + to: string; +}) => { return { size: 0, aggs: { - alertsByGroupingCount: { + stackByField0: { terms: { - field: stackByField, + field: stackByField0, order: { _count: 'desc', }, - size: DEFAULT_MAX_TABLE_QUERY_SIZE, + size: DEFAULT_STACK_BY_FIELD0_SIZE, + }, + aggs: { + ...getOptionalSubAggregation({ + stackByField1, + stackByField1Size: DEFAULT_STACK_BY_FIELD1_SIZE, + }), }, }, }, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.test.tsx index 94e009a067fb5..82c9d447879c0 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.test.tsx @@ -7,12 +7,13 @@ import React from 'react'; import { waitFor, act } from '@testing-library/react'; - import { mount } from 'enzyme'; -import { TestProviders } from '../../../../common/mock'; import { AlertsCountPanel } from '.'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { DEFAULT_STACK_BY_FIELD, DEFAULT_STACK_BY_FIELD1 } from '../common/config'; +import { TestProviders } from '../../../../common/mock'; +import { ChartContextMenu } from '../../../pages/detection_engine/chart_panels/chart_context_menu'; jest.mock('../../../../common/containers/query_toggle'); jest.mock('react-router-dom', () => { @@ -20,12 +21,32 @@ jest.mock('react-router-dom', () => { return { ...actual, useLocation: jest.fn().mockReturnValue({ pathname: '' }) }; }); +const defaultUseQueryAlertsReturn = { + loading: false, + data: {}, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, +}; +const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn); +jest.mock('../../../containers/detection_engine/alerts/use_query', () => { + return { + useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props), + }; +}); + describe('AlertsCountPanel', () => { const defaultProps = { signalIndexName: 'signalIndexName', + stackByField0: DEFAULT_STACK_BY_FIELD, + stackByField1: DEFAULT_STACK_BY_FIELD1, + setStackByField0: jest.fn(), + setStackByField1: jest.fn(), }; const mockSetToggle = jest.fn(); const mockUseQueryToggle = useQueryToggle as jest.Mock; + beforeEach(() => { jest.clearAllMocks(); mockUseQueryToggle.mockReturnValue({ toggleStatus: true, setToggleStatus: mockSetToggle }); @@ -43,6 +64,58 @@ describe('AlertsCountPanel', () => { }); }); + it('renders with the specified `alignHeader` alignment', async () => { + await act(async () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="headerSectionInnerFlexGroup"]').first().getDOMNode() + ).toHaveClass('euiFlexGroup--alignItemsFlexEnd'); + }); + }); + + it('renders the inspect button by default', async () => { + await act(async () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('button[data-test-subj="inspect-icon-button"]').first().exists()).toBe( + true + ); + }); + }); + + it('it does NOT render the inspect button when a `chartOptionsContextMenu` is provided', async () => { + const chartOptionsContextMenu = (queryId: string) => ( + + ); + + await act(async () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('button[data-test-subj="inspect-icon-button"]').first().exists()).toBe( + false + ); + }); + }); + describe('Query', () => { it('it render with a illegal KQL', async () => { jest.mock('@kbn/es-query', () => ({ diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx index d70b77a16521a..a609d388f0f7e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/index.tsx @@ -21,27 +21,49 @@ import { getAlertsCountQuery } from './helpers'; import * as i18n from './translations'; import { AlertsCount } from './alerts_count'; import type { AlertsCountAggregation } from './types'; -import { DEFAULT_STACK_BY_FIELD } from '../common/config'; -import { KpiPanel, StackByComboBox } from '../common/components'; +import { KpiPanel } from '../common/components'; import { useInspectButton } from '../common/hooks'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { FieldSelection } from '../../../../common/components/field_selection'; export const DETECTIONS_ALERTS_COUNT_ID = 'detections-alerts-count'; interface AlertsCountPanelProps { + alignHeader?: 'center' | 'baseline' | 'stretch' | 'flexStart' | 'flexEnd'; + chartOptionsContextMenu?: (queryId: string) => React.ReactNode; filters?: Filter[]; + panelHeight?: number; query?: Query; + setStackByField0: (stackBy: string) => void; + setStackByField1: (stackBy: string | undefined) => void; signalIndexName: string | null; + stackByField0: string; + stackByField1: string | undefined; + stackByWidth?: number; + title?: React.ReactNode; runtimeMappings?: MappingRuntimeFields; } export const AlertsCountPanel = memo( - ({ filters, query, signalIndexName, runtimeMappings }) => { + ({ + alignHeader, + chartOptionsContextMenu, + filters, + panelHeight, + query, + runtimeMappings, + setStackByField0, + setStackByField1, + signalIndexName, + stackByField0, + stackByField1, + stackByWidth, + title = i18n.COUNT_TABLE_TITLE, + }) => { const { to, from, deleteQuery, setQuery } = useGlobalTime(); // create a unique, but stable (across re-renders) query id const uniqueQueryId = useMemo(() => `${DETECTIONS_ALERTS_COUNT_ID}-${uuid.v4()}`, []); - const [selectedStackByOption, setSelectedStackByOption] = useState(DEFAULT_STACK_BY_FIELD); // Disabling the fecth method in useQueryAlerts since it is defaulted to the old one // const fetchMethod = fetchQueryRuleRegistryAlerts; @@ -82,22 +104,38 @@ export const AlertsCountPanel = memo( request, refetch, } = useQueryAlerts<{}, AlertsCountAggregation>({ - query: getAlertsCountQuery( - selectedStackByOption, + query: getAlertsCountQuery({ + stackByField0, + stackByField1, from, to, additionalFilters, - runtimeMappings - ), + runtimeMappings, + }), indexName: signalIndexName, skip: querySkip, }); useEffect(() => { setAlertsQuery( - getAlertsCountQuery(selectedStackByOption, from, to, additionalFilters, runtimeMappings) + getAlertsCountQuery({ + additionalFilters, + from, + runtimeMappings, + stackByField0, + stackByField1, + to, + }) ); - }, [setAlertsQuery, selectedStackByOption, from, to, additionalFilters, runtimeMappings]); + }, [ + additionalFilters, + from, + runtimeMappings, + setAlertsQuery, + stackByField0, + stackByField1, + to, + ]); useInspectButton({ setQuery, @@ -111,22 +149,39 @@ export const AlertsCountPanel = memo( return ( - + - + - {toggleStatus && ( + {toggleStatus && alertsData != null && ( )} diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_empty_field0.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_empty_field0.ts new file mode 100644 index 0000000000000..a2d9d92cd75a6 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_empty_field0.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AlertSearchResponse } from '../../../../containers/detection_engine/alerts/types'; +import type { AlertsCountAggregation } from '../types'; + +export const emptyStackByField0Response: AlertSearchResponse = { + took: 0, + timeout: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 87, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + stackByField0: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + }, +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_multi_group.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_multi_group.ts new file mode 100644 index 0000000000000..730fded03f88b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_multi_group.ts @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AlertSearchResponse } from '../../../../containers/detection_engine/alerts/types'; +import type { AlertsCountAggregation } from '../types'; + +export const buckets = [ + { + key: 'matches everything', + doc_count: 34, + }, + { + key: 'EQL process sequence', + doc_count: 28, + }, + { + key: 'Endpoint Security', + doc_count: 19, + }, + { + key: 'mimikatz process started', + doc_count: 5, + }, + { + key: 'Threshold rule', + doc_count: 1, + }, +]; + +/** + * A mock response to a request containing multiple group by fields + */ +export const mockMultiGroupResponse: AlertSearchResponse = { + took: 0, + timeout: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 87, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + stackByField0: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets, + }, + }, +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_single_group.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_single_group.ts new file mode 100644 index 0000000000000..e7c0f982be03b --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/mocks/mock_response_single_group.ts @@ -0,0 +1,146 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AlertSearchResponse } from '../../../../containers/detection_engine/alerts/types'; +import type { AlertsCountAggregation } from '../types'; + +export const buckets = [ + { + key: 'matches everything', + doc_count: 34, + stackByField1: { + buckets: [ + { + key: 'Host-k8iyfzraq9', + doc_count: 12, + }, + { + key: 'Host-ao1a4wu7vn', + doc_count: 10, + }, + { + key: 'Host-3fbljiq8rj', + doc_count: 7, + }, + { + key: 'Host-r4y6xi92ob', + doc_count: 5, + }, + ], + }, + }, + { + key: 'EQL process sequence', + doc_count: 28, + stackByField1: { + sum_other_doc_count: 0, + buckets: [ + { + key: 'Host-k8iyfzraq9', + doc_count: 10, + }, + { + key: 'Host-ao1a4wu7vn', + doc_count: 7, + }, + { + key: 'Host-3fbljiq8rj', + doc_count: 5, + }, + { + key: 'Host-r4y6xi92ob', + doc_count: 3, + }, + ], + }, + }, + { + key: 'Endpoint Security', + doc_count: 19, + stackByField1: { + sum_other_doc_count: 0, + buckets: [ + { + key: 'Host-ao1a4wu7vn', + doc_count: 11, + }, + { + key: 'Host-3fbljiq8rj', + doc_count: 6, + }, + { + key: 'Host-k8iyfzraq9', + doc_count: 1, + }, + { + key: 'Host-r4y6xi92ob', + doc_count: 1, + }, + ], + }, + }, + { + key: 'mimikatz process started', + doc_count: 5, + stackByField1: { + sum_other_doc_count: 0, + buckets: [ + { + key: 'Host-k8iyfzraq9', + doc_count: 3, + }, + { + key: 'Host-3fbljiq8rj', + doc_count: 1, + }, + { + key: 'Host-r4y6xi92ob', + doc_count: 1, + }, + ], + }, + }, + { + key: 'Threshold rule', + doc_count: 1, + stackByField1: { + sum_other_doc_count: 0, + buckets: [ + { + key: 'Host-r4y6xi92ob', + doc_count: 1, + }, + ], + }, + }, +]; + +export const singleGroupResponse: AlertSearchResponse = { + took: 0, + timeout: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 87, + relation: 'eq', + }, + max_score: null, + hits: [], + }, + aggregations: { + stackByField0: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets, + }, + }, +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/translations.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/translations.ts index 6f2e428b6b519..14f4d38003d58 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/translations.ts @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; export const COUNT_TABLE_COLUMN_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.alerts.count.countTableColumnTitle', { - defaultMessage: 'Count', + defaultMessage: 'Count of records', } ); @@ -21,4 +21,10 @@ export const COUNT_TABLE_TITLE = i18n.translate( } ); +export const COLUMN_LABEL = ({ fieldName, topN }: { fieldName: string; topN: number }) => + i18n.translate('xpack.securitySolution.detectionEngine.alerts.count.columnLabel', { + values: { fieldName, topN }, + defaultMessage: 'Top {topN} values of {fieldName}', + }); + export * from '../common/translations'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/types.ts index b541c7234f08e..a26024b80dba7 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_count_panel/types.ts @@ -5,10 +5,12 @@ * 2.0. */ -import type { GenericBuckets } from '../../../../../common/search_strategy'; +import type { RawBucket } from '../../../../common/components/alerts_treemap/types'; export interface AlertsCountAggregation { - alertsByGroupingCount: { - buckets: GenericBuckets[]; + stackByField0: { + buckets: RawBucket[]; + doc_count_error_upper_bound?: number; + sum_other_doc_count?: number; }; } diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.test.tsx index 11ab2c49a5dc0..4b64a214bd02b 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.test.tsx @@ -6,26 +6,73 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { mount, shallow } from 'enzyme'; -import '../../../../common/mock/match_media'; import { AlertsHistogram } from './alerts_histogram'; +import { TestProviders } from '../../../../common/mock'; jest.mock('../../../../common/lib/kibana'); +const legendItems = [ + { + color: '#1EA593', + count: 77, + dataProviderId: + 'draggable-legend-item-2f890398-548e-4604-b2de-525f0eecd124-kibana_alert_rule_name-matches everything', + field: 'kibana.alert.rule.name', + value: 'matches everything', + }, + { + color: '#2B70F7', + count: 56, + dataProviderId: + 'draggable-legend-item-07aca01b-d334-424d-98c0-6d6bc9f8a886-kibana_alert_rule_name-Endpoint Security', + field: 'kibana.alert.rule.name', + value: 'Endpoint Security', + }, +]; + +const defaultProps = { + legendItems, + loading: false, + data: [], + from: '2020-07-07T08:20:18.966Z', + to: '2020-07-08T08:20:18.966Z', + updateDateRange: jest.fn(), +}; + describe('AlertsHistogram', () => { it('renders correctly', () => { - const wrapper = shallow( - - ); + const wrapper = shallow(); expect(wrapper.find('Chart').exists()).toBeTruthy(); }); + + it('renders a legend with the default width', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule( + 'min-width', + '165px' + ); + }); + + it('renders a legend with the specified `legendWidth`', () => { + const legendMinWidth = 1234; + + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule( + 'min-width', + `${legendMinWidth}px` + ); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx index c2c712c718762..3966c9a319582 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx @@ -27,6 +27,7 @@ interface AlertsHistogramProps { from: string; legendItems: LegendItem[]; legendPosition?: Position; + legendMinWidth?: number; loading: boolean; showLegend?: boolean; to: string; @@ -40,6 +41,7 @@ export const AlertsHistogram = React.memo( from, legendItems, legendPosition = Position.Right, + legendMinWidth, loading, showLegend, to, @@ -98,7 +100,11 @@ export const AlertsHistogram = React.memo( {legendItems.length > 0 && ( - + )} diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx index 539728291156f..2288132ffd020 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.test.tsx @@ -5,18 +5,22 @@ * 2.0. */ +import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; import React from 'react'; -import { waitFor, act } from '@testing-library/react'; import { mount } from 'enzyme'; - import type { Filter } from '@kbn/es-query'; -import { TestProviders } from '../../../../common/mock'; + import { SecurityPageName } from '../../../../app/types'; +import { CHART_SETTINGS_POPOVER_ARIA_LABEL } from '../../../../common/components/chart_settings_popover/translations'; +import { DEFAULT_WIDTH } from '../../../../common/components/charts/draggable_legend'; import { MatrixLoader } from '../../../../common/components/matrix_histogram/matrix_loader'; - -import { AlertsHistogramPanel } from '.'; -import * as helpers from './helpers'; +import { DEFAULT_STACK_BY_FIELD, DEFAULT_STACK_BY_FIELD1 } from '../common/config'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { TestProviders } from '../../../../common/mock'; +import * as helpers from './helpers'; +import { mockAlertSearchResponse } from './mock_data'; +import { ChartContextMenu } from '../../../pages/detection_engine/chart_panels/chart_context_menu'; +import { AlertsHistogramPanel, LEGEND_WITH_COUNTS_WIDTH } from '.'; jest.mock('../../../../common/containers/query_toggle'); @@ -74,18 +78,21 @@ jest.mock('../../../../common/lib/kibana', () => { jest.mock('../../../../common/components/navigation/use_get_url_search'); +const defaultUseQueryAlertsReturn = { + loading: true, + setQuery: () => undefined, + data: null, + response: '', + request: '', + refetch: null, +}; +const mockUseQueryAlerts = jest.fn().mockReturnValue(defaultUseQueryAlertsReturn); + jest.mock('../../../containers/detection_engine/alerts/use_query', () => { const original = jest.requireActual('../../../containers/detection_engine/alerts/use_query'); return { ...original, - useQueryAlerts: jest.fn().mockReturnValue({ - loading: true, - setQuery: () => undefined, - data: null, - response: '', - request: '', - refetch: null, - }), + useQueryAlerts: (...props: unknown[]) => mockUseQueryAlerts(...props), }; }); @@ -117,6 +124,311 @@ describe('AlertsHistogramPanel', () => { wrapper.unmount(); }); + describe('legend counts', () => { + beforeEach(() => { + mockUseQueryAlerts.mockReturnValue({ + loading: false, + data: mockAlertSearchResponse, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, + }); + }); + + test('it does NOT render counts in the legend by default', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="legendItemCount"]').exists()).toBe(false); + }); + + test('it renders counts in the legend when `showCountsInLegend` is true', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="legendItemCount"]').exists()).toBe(true); + }); + }); + + test('it renders the header with the specified `alignHeader` alignment', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper.find('[data-test-subj="headerSectionInnerFlexGroup"]').first().getDOMNode() + ).toHaveClass('euiFlexGroup--alignItemsFlexEnd'); + }); + + describe('inspect button', () => { + test('it renders the inspect button by default', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="inspect-icon-button"]').first().exists()).toBe(true); + }); + + test('it does NOT render the inspect button when a `chartOptionsContextMenu` is provided', async () => { + const chartOptionsContextMenu = (queryId: string) => ( + + ); + + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="inspect-icon-button"]').first().exists()).toBe(false); + }); + }); + + test('it aligns the panel flex group at flex start to ensure the context menu is displayed at the top of the panel', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="panelFlexGroup"]').first().getDOMNode()).toHaveClass( + 'euiFlexGroup--alignItemsFlexStart' + ); + }); + + test('it invokes onFieldSelected when a field is selected', async () => { + const onFieldSelected = jest.fn(); + const optionToSelect = 'agent.hostname'; + + mockUseQueryAlerts.mockReturnValue({ + loading: false, + data: mockAlertSearchResponse, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, + }); + + render( + + + + ); + + const comboBox = screen.getByTestId('comboBoxSearchInput'); + comboBox.focus(); // display the combo box options + + const option = await screen.findByText(optionToSelect); + fireEvent.click(option); + + expect(onFieldSelected).toBeCalledWith(optionToSelect); + }); + + describe('stackByLabel', () => { + test('it renders the default stack by label when `stackByLabel` is NOT provided', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('label.euiFormControlLayout__prepend').first().text()).toEqual( + 'Stack by' + ); + }); + + test('it prepends a custom stack by label when `stackByLabel` is provided', () => { + const stackByLabel = 'Group by'; + + const wrapper = mount( + + + + ); + + expect(wrapper.find('label.euiFormControlLayout__prepend').first().text()).toEqual( + stackByLabel + ); + }); + }); + + describe('stackByWidth', () => { + test('it renders the first StackByComboBox with the specified `stackByWidth`', () => { + const stackByWidth = 1234; + + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="stackByComboBox"]').first()).toHaveStyleRule( + 'width', + `${stackByWidth}px` + ); + }); + + test('it renders the placeholder StackByComboBox with the specified `stackByWidth`', () => { + const stackByWidth = 1234; + + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="stackByPlaceholder"]').first()).toHaveStyleRule( + 'width', + `${stackByWidth}px` + ); + }); + }); + + describe('placeholder spacer', () => { + test('it does NOT render the group by placeholder spacer by default', () => { + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="placeholderSpacer"]').exists()).toBe(false); + }); + + test('it renders the placeholder spacer when `showGroupByPlaceholder` is true', () => { + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="placeholderSpacer"]').exists()).toBe(true); + }); + }); + + describe('placeholder tooltip', () => { + test('it does NOT render the placeholder tooltip by default', () => { + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="placeholderTooltip"]').exists()).toBe(false); + }); + + test('it renders the placeholder tooltip when `showGroupByPlaceholder` is true', () => { + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="placeholderTooltip"]').exists()).toBe(true); + }); + }); + + describe('placeholder', () => { + test('it does NOT render the group by placeholder by default', () => { + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="stackByPlaceholder"]').exists()).toBe(false); + }); + + test('it renders the placeholder when `showGroupByPlaceholder` is true', () => { + const wrapper = mount( + + + + ); + expect(wrapper.find('[data-test-subj="stackByPlaceholder"]').exists()).toBe(true); + }); + }); + + test('it renders the chart options context menu when a `chartOptionsContextMenu` is provided', async () => { + const chartOptionsContextMenu = (queryId: string) => ( + + ); + + render( + + + + ); + + expect( + screen.getByRole('button', { name: CHART_SETTINGS_POPOVER_ARIA_LABEL }) + ).toBeInTheDocument(); + }); + + describe('legend width', () => { + beforeEach(() => { + mockUseQueryAlerts.mockReturnValue({ + loading: false, + data: mockAlertSearchResponse, + setQuery: () => {}, + response: '', + request: '', + refetch: () => {}, + }); + }); + + test('it renders the legend with the expected default min-width', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule( + 'min-width', + `${DEFAULT_WIDTH}px` + ); + }); + + test('it renders the legend with the expected min-width when `showCountsInLegend` is true', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule( + 'min-width', + `${LEGEND_WITH_COUNTS_WIDTH}px` + ); + }); + }); + describe('Button view alerts', () => { it('renders correctly', () => { const props = { ...defaultProps, showLinkToAlerts: true }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx index c8ab450bb6f04..7a0896b56c5ec 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/index.tsx @@ -8,11 +8,11 @@ import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import type { Position } from '@elastic/charts'; import type { EuiTitleSize } from '@elastic/eui'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiToolTip } from '@elastic/eui'; import numeral from '@elastic/numeral'; import React, { memo, useCallback, useMemo, useState, useEffect } from 'react'; import styled from 'styled-components'; -import { isEmpty } from 'lodash/fp'; +import { isEmpty, noop } from 'lodash/fp'; import uuid from 'uuid'; import type { Filter, Query } from '@kbn/es-query'; @@ -48,6 +48,7 @@ import { KpiPanel, StackByComboBox } from '../common/components'; import { useInspectButton } from '../common/hooks'; import { useQueryToggle } from '../../../../common/containers/query_toggle'; +import { GROUP_BY_TOP_LABEL } from '../common/translations'; const defaultTotalAlertsObj: AlertsTotal = { value: 0, @@ -60,25 +61,39 @@ const ViewAlertsFlexItem = styled(EuiFlexItem)` margin-left: ${({ theme }) => theme.eui.euiSizeL}; `; +const OptionsFlexItem = styled(EuiFlexItem)` + margin-left: ${({ theme }) => theme.eui.euiSizeS}; +`; + +export const LEGEND_WITH_COUNTS_WIDTH = 300; // px + interface AlertsHistogramPanelProps { + alignHeader?: 'center' | 'baseline' | 'stretch' | 'flexStart' | 'flexEnd'; chartHeight?: number; + chartOptionsContextMenu?: (queryId: string) => React.ReactNode; combinedQueries?: string; - defaultStackByOption?: AlertsStackByField; + defaultStackByOption?: string; filters?: Filter[]; headerChildren?: React.ReactNode; + onFieldSelected?: (field: string) => void; /** Override all defaults, and only display this field */ onlyField?: AlertsStackByField; paddingSize?: 's' | 'm' | 'l' | 'none'; + panelHeight?: number; titleSize?: EuiTitleSize; query?: Query; legendPosition?: Position; signalIndexName: string | null; + showCountsInLegend?: boolean; + showGroupByPlaceholder?: boolean; showLegend?: boolean; showLinkToAlerts?: boolean; showTotalAlertsCount?: boolean; showStackBy?: boolean; + stackByLabel?: string; + stackByWidth?: number; timelineId?: string; - title?: string; + title?: React.ReactNode; updateDateRange: UpdateDateRange; runtimeMappings?: MappingRuntimeFields; } @@ -87,20 +102,28 @@ const NO_LEGEND_DATA: LegendItem[] = []; export const AlertsHistogramPanel = memo( ({ + alignHeader, chartHeight, + chartOptionsContextMenu, combinedQueries, defaultStackByOption = DEFAULT_STACK_BY_FIELD, filters, headerChildren, + onFieldSelected, onlyField, paddingSize = 'm', + panelHeight = PANEL_HEIGHT, query, legendPosition = 'right', signalIndexName, + showCountsInLegend = false, + showGroupByPlaceholder = false, showLegend = true, showLinkToAlerts = false, showTotalAlertsCount = false, showStackBy = true, + stackByLabel, + stackByWidth, timelineId, title = i18n.HISTOGRAM_HEADER, updateDateRange, @@ -118,6 +141,19 @@ export const AlertsHistogramPanel = memo( const [selectedStackByOption, setSelectedStackByOption] = useState( onlyField == null ? defaultStackByOption : onlyField ); + const onSelect = useCallback( + (field: string) => { + setSelectedStackByOption(field); + if (onFieldSelected != null) { + onFieldSelected(field); + } + }, + [onFieldSelected] + ); + + useEffect(() => { + setSelectedStackByOption(onlyField == null ? defaultStackByOption : onlyField); + }, [defaultStackByOption, onlyField]); const { toggleStatus, setToggleStatus } = useQueryToggle(DETECTIONS_HISTOGRAM_ID); const [querySkip, setQuerySkip] = useState(!toggleStatus); @@ -183,6 +219,7 @@ export const AlertsHistogramPanel = memo( showLegend && alertsData?.aggregations?.alertsByGrouping?.buckets != null ? alertsData.aggregations.alertsByGrouping.buckets.map((bucket, i) => ({ color: i < defaultLegendColors.length ? defaultLegendColors[i] : undefined, + count: showCountsInLegend ? bucket.doc_count : undefined, dataProviderId: escapeDataProviderId( `draggable-legend-item-${uuid.v4()}-${selectedStackByOption}-${bucket.key}` ), @@ -194,6 +231,7 @@ export const AlertsHistogramPanel = memo( [ alertsData?.aggregations?.alertsByGrouping.buckets, selectedStackByOption, + showCountsInLegend, showLegend, timelineId, ] @@ -289,34 +327,64 @@ export const AlertsHistogramPanel = memo( return ( - + {showStackBy && ( <> + {showGroupByPlaceholder && ( + <> + + + + + + )} )} {headerChildren != null && headerChildren} + {chartOptionsContextMenu != null && ( + + {chartOptionsContextMenu(uniqueQueryId)} + + )} + {linkButton} @@ -331,6 +399,7 @@ export const AlertsHistogramPanel = memo( from={from} legendItems={legendItems} legendPosition={legendPosition} + legendMinWidth={showCountsInLegend ? LEGEND_WITH_COUNTS_WIDTH : undefined} loading={isLoadingAlerts} to={to} showLegend={showLegend} diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/mock_data.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/mock_data.ts index 6e5551eb69201..e2ab1a3ae9f84 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/mock_data.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/mock_data.ts @@ -81,3 +81,354 @@ export const textResult = [ { x: 1652199588074, y: 0, g: 'MacBook-Pro.local' }, { x: 1652202288073, y: 0, g: 'MacBook-Pro.local' }, ]; + +export const mockAlertSearchResponse = { + took: 1, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: { + value: 1, + relation: 'eq', + }, + max_score: 0, + hits: [ + { + _index: '.internal.alerts-security.alerts-default-000001', + _id: 'cb3bcb63c619cd7f3349d77568cc0bf0406210dce95374b04b9bf1e98b68dcdc', + _score: 0, + _source: { + 'kibana.version': '8.4.0', + 'kibana.alert.rule.category': 'Custom Query Rule', + 'kibana.alert.rule.consumer': 'siem', + 'kibana.alert.rule.execution.uuid': '5240f735-0205-4af4-8e6d-dec17d0f084e', + 'kibana.alert.rule.name': 'matches everything', + 'kibana.alert.rule.producer': 'siem', + 'kibana.alert.rule.rule_type_id': 'siem.queryRule', + 'kibana.alert.rule.uuid': '6a6ecac0-fe4f-11ec-8ccd-258a52cbda02', + 'kibana.space_ids': ['default'], + 'kibana.alert.rule.tags': ['test'], + '@timestamp': '2022-07-08T23:58:11.500Z', + agent: { + id: '4a6c871a-b23e-4e83-9098-5e14e85c3f7b', + type: 'endpoint', + version: '7.6.11', + }, + process: { + Ext: { + ancestry: ['snmviyj5md', '2g4w55131x'], + code_signature: [ + { + trusted: false, + subject_name: 'bad signer', + }, + ], + user: 'SYSTEM', + token: { + integrity_level: 16384, + privileges: [ + { + name: 'SeAssignPrimaryTokenPrivilege', + description: 'Replace a process level token', + enabled: false, + }, + ], + integrity_level_name: 'system', + domain: 'NT AUTHORITY', + type: 'tokenPrimary', + user: 'SYSTEM', + sid: 'S-1-5-18', + }, + }, + parent: { + pid: 1, + entity_id: 'snmviyj5md', + }, + group_leader: { + name: 'fake leader', + pid: 4, + entity_id: 'xq1spmmi2w', + }, + session_leader: { + name: 'fake session', + pid: 26, + entity_id: 'xq1spmmi2w', + }, + entry_leader: { + name: 'fake entry', + pid: 558, + entity_id: 'xq1spmmi2w', + }, + name: 'malware writer', + start: 1657324615198, + pid: 2, + entity_id: 'v6w0s12zn1', + executable: 'C:/malware.exe', + hash: { + sha1: 'fake sha1', + sha256: 'fake sha256', + md5: 'fake md5', + }, + uptime: 0, + }, + file: { + owner: 'SYSTEM', + Ext: { + temp_file_path: 'C:/temp/fake_malware.exe', + code_signature: [ + { + trusted: false, + subject_name: 'bad signer', + }, + ], + quarantine_message: 'fake quarantine message', + quarantine_result: true, + malware_classification: { + identifier: 'endpointpe', + score: 1, + threshold: 0.66, + version: '3.0.33', + }, + }, + path: 'C:/fake_malware.exe', + size: 3456, + created: 1657324615198, + name: 'fake_malware.exe', + accessed: 1657324615198, + mtime: 1657324615198, + hash: { + sha1: 'fake file sha1', + sha256: 'fake file sha256', + md5: 'fake file md5', + }, + }, + Endpoint: { + capabilities: [], + configuration: { + isolation: true, + }, + state: { + isolation: true, + }, + status: 'enrolled', + policy: { + applied: { + name: 'With Eventing', + id: 'C2A9093E-E289-4C0A-AA44-8C32A414FA7A', + endpoint_policy_version: 3, + version: 5, + status: 'failure', + }, + }, + }, + ecs: { + version: '1.4.0', + }, + dll: [ + { + Ext: { + compile_time: 1534424710, + malware_classification: { + identifier: 'Whitelisted', + score: 0, + threshold: 0, + version: '3.0.0', + }, + mapped_address: 5362483200, + mapped_size: 0, + }, + path: 'C:\\Program Files\\Cybereason ActiveProbe\\AmSvc.exe', + code_signature: { + trusted: true, + subject_name: 'Cybereason Inc', + }, + pe: { + architecture: 'x64', + }, + hash: { + sha1: 'ca85243c0af6a6471bdaa560685c51eefd6dbc0d', + sha256: '8ad40c90a611d36eb8f9eb24fa04f7dbca713db383ff55a03aa0f382e92061a2', + md5: '1f2d082566b0fc5f2c238a5180db7451', + }, + }, + ], + data_stream: { + namespace: 'default', + type: 'logs', + dataset: 'endpoint.alerts', + }, + elastic: { + agent: { + id: '4a6c871a-b23e-4e83-9098-5e14e85c3f7b', + }, + }, + host: { + hostname: 'Host-xh6qyoiujf', + os: { + Ext: { + variant: 'Windows Server', + }, + name: 'Windows', + family: 'windows', + version: '6.2', + platform: 'Windows', + full: 'Windows Server 2012', + }, + ip: ['10.74.191.143'], + name: 'Host-xh6qyoiujf', + id: 'b51c2aad-8371-44e5-8dab-cb92a8a32414', + mac: ['86-b5-5e-e7-99-2d'], + architecture: 'rdf4znaej1', + }, + 'event.agent_id_status': 'auth_metadata_missing', + 'event.sequence': 63, + 'event.ingested': '2022-07-08T21:15:43Z', + 'event.code': 'malicious_file', + 'event.kind': 'signal', + 'event.module': 'endpoint', + 'event.action': 'deletion', + 'event.id': 'c6760bef-1c62-4730-848a-1b2d5f8938f9', + 'event.category': 'malware', + 'event.type': 'creation', + 'event.dataset': 'endpoint', + 'kibana.alert.original_time': '2022-07-08T23:56:55.198Z', + 'kibana.alert.ancestors': [ + { + id: 'N2ir34EB_eEmuUQvINry', + type: 'event', + index: '.ds-logs-endpoint.alerts-default-2022.07.07-000001', + depth: 0, + }, + ], + 'kibana.alert.status': 'active', + 'kibana.alert.workflow_status': 'open', + 'kibana.alert.depth': 1, + 'kibana.alert.reason': + 'malware event with process malware writer, file fake_malware.exe, on Host-xh6qyoiujf created low alert matches everything.', + 'kibana.alert.severity': 'low', + 'kibana.alert.risk_score': 21, + 'kibana.alert.rule.parameters': { + description: 'matches almost everything', + risk_score: 21, + severity: 'low', + license: '', + meta: { + from: '1m', + kibana_siem_app_url: 'http://localhost:5601/app/security', + }, + author: [], + false_positives: [], + from: 'now-360s', + rule_id: 'f544e86c-4d83-496f-9e5b-c60965b1eb83', + max_signals: 100, + risk_score_mapping: [], + severity_mapping: [], + threat: [], + to: 'now', + references: [], + version: 1, + exceptions_list: [], + immutable: false, + related_integrations: [], + required_fields: [], + setup: '', + type: 'query', + language: 'kuery', + index: [ + 'apm-*-transaction*', + 'traces-apm*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + query: '_id: *', + filters: [], + }, + 'kibana.alert.rule.actions': [], + 'kibana.alert.rule.author': [], + 'kibana.alert.rule.created_at': '2022-07-07T23:49:18.761Z', + 'kibana.alert.rule.created_by': 'elastic', + 'kibana.alert.rule.description': 'matches almost everything', + 'kibana.alert.rule.enabled': true, + 'kibana.alert.rule.exceptions_list': [], + 'kibana.alert.rule.false_positives': [], + 'kibana.alert.rule.from': 'now-360s', + 'kibana.alert.rule.immutable': false, + 'kibana.alert.rule.interval': '5m', + 'kibana.alert.rule.indices': [ + 'apm-*-transaction*', + 'traces-apm*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + 'kibana.alert.rule.license': '', + 'kibana.alert.rule.max_signals': 100, + 'kibana.alert.rule.references': [], + 'kibana.alert.rule.risk_score_mapping': [], + 'kibana.alert.rule.rule_id': 'f544e86c-4d83-496f-9e5b-c60965b1eb83', + 'kibana.alert.rule.severity_mapping': [], + 'kibana.alert.rule.threat': [], + 'kibana.alert.rule.to': 'now', + 'kibana.alert.rule.type': 'query', + 'kibana.alert.rule.updated_at': '2022-07-07T23:50:01.437Z', + 'kibana.alert.rule.updated_by': 'elastic', + 'kibana.alert.rule.version': 1, + 'kibana.alert.rule.meta.from': '1m', + 'kibana.alert.rule.meta.kibana_siem_app_url': 'http://localhost:5601/app/security', + 'kibana.alert.rule.risk_score': 21, + 'kibana.alert.rule.severity': 'low', + 'kibana.alert.original_event.agent_id_status': 'auth_metadata_missing', + 'kibana.alert.original_event.sequence': 63, + 'kibana.alert.original_event.ingested': '2022-07-08T21:15:43Z', + 'kibana.alert.original_event.code': 'malicious_file', + 'kibana.alert.original_event.kind': 'alert', + 'kibana.alert.original_event.module': 'endpoint', + 'kibana.alert.original_event.action': 'deletion', + 'kibana.alert.original_event.id': 'c6760bef-1c62-4730-848a-1b2d5f8938f9', + 'kibana.alert.original_event.category': 'malware', + 'kibana.alert.original_event.type': 'creation', + 'kibana.alert.original_event.dataset': 'endpoint', + 'kibana.alert.uuid': 'cb3bcb63c619cd7f3349d77568cc0bf0406210dce95374b04b9bf1e98b68dcdc', + }, + }, + ], + }, + aggregations: { + alertsByGrouping: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'matches everything', + doc_count: 2, + alerts: { + buckets: [ + { + key_as_string: '2022-07-08T05:49:46.200Z', + key: 1657259386200, + doc_count: 0, + }, + { + key_as_string: '2022-07-08T06:34:46.199Z', + key: 1657262086199, + doc_count: 0, + }, + ], + }, + }, + ], + }, + }, +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/translations.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/translations.ts index 67150926621ab..0f5e48a2399cc 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/translations.ts @@ -20,6 +20,13 @@ export const HISTOGRAM_HEADER = i18n.translate( } ); +export const NOT_AVAILABLE_TOOLTIP = i18n.translate( + 'xpack.securitySolution.detectionEngine.alerts.histogram.notAvailableTooltip', + { + defaultMessage: 'Not available for trend view', + } +); + export const VIEW_ALERTS = i18n.translate( 'xpack.securitySolution.detectionEngine.alerts.histogram.viewAlertsButtonLabel', { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/types.ts index 433fee1716a47..7b1136d5b11c6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/types.ts @@ -31,6 +31,7 @@ export interface AlertsGroupBucket { alerts: { buckets: AlertsBucket[]; }; + doc_count: number; } export interface AlertsTotal { diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/common/components.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/common/components.test.tsx new file mode 100644 index 0000000000000..3967655d6f089 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/common/components.test.tsx @@ -0,0 +1,205 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { fireEvent, render, screen } from '@testing-library/react'; +import React from 'react'; + +import { TestProviders } from '../../../../common/mock'; +import { KpiPanel, StackByComboBox } from './components'; +import * as i18n from './translations'; + +jest.mock('react-router-dom', () => { + const originalModule = jest.requireActual('react-router-dom'); + return { + ...originalModule, + createHref: jest.fn(), + useHistory: jest.fn(), + useLocation: jest.fn().mockReturnValue({ pathname: '' }), + }; +}); + +const mockNavigateToApp = jest.fn(); +jest.mock('../../../../common/lib/kibana/kibana_react', () => { + const original = jest.requireActual('../../../../common/lib/kibana/kibana_react'); + + return { + ...original, + useKibana: () => ({ + services: { + application: { + navigateToApp: mockNavigateToApp, + getUrlForApp: jest.fn(), + }, + data: { + search: { + search: jest.fn(), + }, + }, + uiSettings: { + get: jest.fn(), + }, + notifications: { + toasts: { + addWarning: jest.fn(), + addError: jest.fn(), + addSuccess: jest.fn(), + remove: jest.fn(), + }, + }, + }, + }), + }; +}); + +describe('components', () => { + describe('KpiPanel', () => { + test('it has a hidden overflow-x', () => { + render( + + + {'test'} + + + ); + + expect(screen.getByTestId('test')).toHaveStyleRule('overflow-x', 'hidden'); + }); + + test('it has a hidden overflow-y by default', () => { + render( + + + {'test'} + + + ); + + expect(screen.getByTestId('test')).toHaveStyleRule('overflow-y', 'hidden'); + }); + + test('it uses the `$overflowY` prop for the value of overflow-y when provided', () => { + render( + + + {'test'} + + + ); + + expect(screen.getByTestId('test')).toHaveStyleRule('overflow-y', 'auto'); + }); + }); + + describe('StackByComboBox', () => { + test('it invokes onSelect when a field is selected', async () => { + const onSelect = jest.fn(); + const optionToSelect = 'agent.hostname'; + + render( + + + + ); + + const comboBox = screen.getByRole('combobox', { name: i18n.STACK_BY_ARIA_LABEL }); + comboBox.focus(); // display the combo box options + + const option = await screen.findByText(optionToSelect); + fireEvent.click(option); + + expect(onSelect).toBeCalledWith(optionToSelect); + }); + + test('it does NOT disable the combo box by default', () => { + render( + + + + ); + + expect(screen.getByRole('combobox', { name: i18n.STACK_BY_ARIA_LABEL })).not.toHaveAttribute( + 'disabled' + ); + }); + + test('it disables the combo box when `isDisabled` is true', () => { + render( + + + + ); + + expect(screen.getByRole('combobox', { name: i18n.STACK_BY_ARIA_LABEL })).toHaveAttribute( + 'disabled' + ); + }); + + test('overrides the default accessible name via the `aria-label` prop when provided', () => { + const customAccessibleName = 'custom'; + + render( + + + + ); + + expect(screen.getByRole('combobox', { name: customAccessibleName })).toBeInTheDocument(); + }); + + test('it renders the default label', () => { + const defaultLabel = 'Stack by'; + + render( + + + + ); + + expect(screen.getByLabelText(defaultLabel)).toBeInTheDocument(); + }); + + test('it overrides the default label when `prepend` is specified', () => { + const prepend = 'Group by'; + + render( + + + + ); + + expect(screen.getByLabelText(prepend)).toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/common/components.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/common/components.tsx index fdc07cb9c91d0..06c6368b2725a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/common/components.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/common/components.tsx @@ -12,16 +12,34 @@ import { PANEL_HEIGHT, MOBILE_PANEL_HEIGHT } from './config'; import { useStackByFields } from './hooks'; import * as i18n from './translations'; -export const KpiPanel = styled(EuiPanel)<{ height?: number; $toggleStatus: boolean }>` +const DEFAULT_WIDTH = 400; + +export const KpiPanel = styled(EuiPanel)<{ + height?: number; + $overflowY?: + | 'auto' + | 'clip' + | 'hidden' + | 'hidden visible' + | 'inherit' + | 'initial' + | 'revert' + | 'revert-layer' + | 'scroll' + | 'unset' + | 'visible'; + $toggleStatus: boolean; +}>` display: flex; flex-direction: column; position: relative; - overflow: hidden; + overflow-x: hidden; + overflow-y: ${({ $overflowY }) => $overflowY ?? 'hidden'}; @media only screen and (min-width: ${(props) => props.theme.eui.euiBreakpoints.m}) { - ${({ $toggleStatus }) => + ${({ height, $toggleStatus }) => $toggleStatus && ` - height: ${PANEL_HEIGHT}px; + height: ${height != null ? height : PANEL_HEIGHT}px; `} } ${({ $toggleStatus }) => @@ -31,15 +49,29 @@ export const KpiPanel = styled(EuiPanel)<{ height?: number; $toggleStatus: boole `} `; interface StackedBySelectProps { + 'aria-label'?: string; + 'data-test-subj'?: string; + isDisabled?: boolean; + prepend?: string; selected: string; onSelect: (selected: string) => void; + width?: number; } -export const StackByComboBoxWrapper = styled.div` +export const StackByComboBoxWrapper = styled.div<{ width: number }>` max-width: 400px; + width: ${({ width }) => width}px; `; -export const StackByComboBox: React.FC = ({ selected, onSelect }) => { +export const StackByComboBox: React.FC = ({ + 'aria-label': ariaLabel = i18n.STACK_BY_ARIA_LABEL, + 'data-test-subj': dataTestSubj, + isDisabled = false, + onSelect, + prepend = i18n.STACK_BY_LABEL, + selected, + width = DEFAULT_WIDTH, +}) => { const onChange = useCallback( (options) => { if (options && options.length > 0) { @@ -58,11 +90,13 @@ export const StackByComboBox: React.FC = ({ selected, onSe return { asPlainText: true }; }, []); return ( - + {I18n.LOW}, + inputDisplay: {I18n.LOW}, }, { value: 'medium', - inputDisplay: ( - {I18n.MEDIUM} - ), + inputDisplay: {I18n.MEDIUM}, }, { value: 'high', - inputDisplay: {I18n.HIGH}, + inputDisplay: {I18n.HIGH}, }, { value: 'critical', - inputDisplay: ( - {I18n.CRITICAL} - ), + inputDisplay: {I18n.CRITICAL}, }, ]; export const defaultRiskScoreBySeverity: Record = { - low: 21, - medium: 47, - high: 73, - critical: 99, + low: RISK_SCORE_LOW, + medium: RISK_SCORE_MEDIUM, + high: RISK_SCORE_HIGH, + critical: RISK_SCORE_CRITICAL, }; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/types.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/types.ts index ffc46610ae06d..db68e700de4ff 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/types.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/alerts/types.ts @@ -33,6 +33,7 @@ export interface AlertSearchResponse value: number; relation: string; }; + max_score?: number | null; hits: Hit[]; }; } diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/constants.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/constants.ts new file mode 100644 index 0000000000000..6df717c6b541e --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/constants.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/** settings for the `Alerts` page are grouped under this logical key */ +export const ALERTS_PAGE = 'alerts'; + +/** This setting persists the value of the view selector, which toggles between chart types */ +export const ALERT_VIEW_SELECTION_SETTING_NAME = 'alert-view-selection'; + +/** settings for the `Count` table visualization are grouped under this category */ +export const TABLE_CATEGORY = 'table'; + +/** This setting persists the expanded / collapsed state of an expandable panel */ +export const EXPAND_SETTING_NAME = 'expand'; + +/** settings for the `Treemap` visualization are grouped under this category */ +export const TREEMAP_CATEGORY = 'treemap'; + +/** This setting persists the value of the `Stack by` field selector */ +export const STACK_BY_SETTING_NAME = 'stack-by'; + +/** This setting persists the value of the first `Stack by` field selector when there are multiple */ +export const STACK_BY_0_SETTING_NAME = 'stack-by-0'; + +/** This setting persists the value of the second `Stack by` field selector when there are multiple */ +export const STACK_BY_1_SETTING_NAME = 'stack-by-1'; + +/** settings for the `Trend` visualization are grouped under this category */ +export const TREND_CHART_CATEGORY = 'trend'; + +/** settings for view selection are grouped under this category */ +export const VIEW_CATEGORY = 'view'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/index.test.tsx new file mode 100644 index 0000000000000..6731bee771a3d --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/index.test.tsx @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import React from 'react'; + +import { useAlertsLocalStorage } from '.'; +import { TestProviders } from '../../../../../common/mock'; + +describe('useAlertsLocalStorage', () => { + const wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + + test('it returns the expected defaults', () => { + const { result } = renderHook(() => useAlertsLocalStorage(), { wrapper }); + + const defaults = Object.fromEntries( + Object.entries(result.current).filter((x) => typeof x[1] !== 'function') + ); + + expect(defaults).toEqual({ + alertViewSelection: 'trend', // default to the trend chart + countTableStackBy0: 'kibana.alert.rule.name', + countTableStackBy1: 'host.name', + isTreemapPanelExpanded: true, + riskChartStackBy0: 'kibana.alert.rule.name', + riskChartStackBy1: 'host.name', + trendChartStackBy: 'kibana.alert.rule.name', + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/index.tsx new file mode 100644 index 0000000000000..5ac129b6368e3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/index.tsx @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useLocalStorage } from '../../../../../common/components/local_storage'; +import { + getSettingKey, + isDefaultWhenEmptyString, +} from '../../../../../common/components/local_storage/helpers'; + +import { + ALERTS_PAGE, + ALERT_VIEW_SELECTION_SETTING_NAME, + TABLE_CATEGORY, + EXPAND_SETTING_NAME, + TREEMAP_CATEGORY, + STACK_BY_0_SETTING_NAME, + STACK_BY_1_SETTING_NAME, + STACK_BY_SETTING_NAME, + TREND_CHART_CATEGORY, + VIEW_CATEGORY, +} from './constants'; +import { + DEFAULT_STACK_BY_FIELD, + DEFAULT_STACK_BY_FIELD1, +} from '../../../../components/alerts_kpis/common/config'; +import type { AlertsSettings } from './types'; +import type { AlertViewSelection } from '../chart_select/helpers'; +import { TREND_ID } from '../chart_select/helpers'; + +export const useAlertsLocalStorage = (): AlertsSettings => { + const [alertViewSelection, setAlertViewSelection] = useLocalStorage({ + defaultValue: TREND_ID, + key: getSettingKey({ + category: VIEW_CATEGORY, + page: ALERTS_PAGE, + setting: ALERT_VIEW_SELECTION_SETTING_NAME, + }), + isInvalidDefault: isDefaultWhenEmptyString, + }); + + const [isTreemapPanelExpanded, setIsTreemapPanelExpanded] = useLocalStorage({ + defaultValue: true, + key: getSettingKey({ + category: TREEMAP_CATEGORY, + page: ALERTS_PAGE, + setting: EXPAND_SETTING_NAME, + }), + }); + + const [riskChartStackBy0, setRiskChartStackBy0] = useLocalStorage({ + defaultValue: DEFAULT_STACK_BY_FIELD, + key: getSettingKey({ + category: TREEMAP_CATEGORY, + page: ALERTS_PAGE, + setting: STACK_BY_0_SETTING_NAME, + }), + isInvalidDefault: isDefaultWhenEmptyString, + }); + + const [riskChartStackBy1, setRiskChartStackBy1] = useLocalStorage({ + defaultValue: DEFAULT_STACK_BY_FIELD1, + key: getSettingKey({ + category: TREEMAP_CATEGORY, + page: ALERTS_PAGE, + setting: STACK_BY_1_SETTING_NAME, + }), + }); + + const [countTableStackBy0, setCountTableStackBy0] = useLocalStorage({ + defaultValue: DEFAULT_STACK_BY_FIELD, + key: getSettingKey({ + category: TABLE_CATEGORY, + page: ALERTS_PAGE, + setting: STACK_BY_0_SETTING_NAME, + }), + isInvalidDefault: isDefaultWhenEmptyString, + }); + + const [countTableStackBy1, setCountTableStackBy1] = useLocalStorage({ + defaultValue: DEFAULT_STACK_BY_FIELD1, + key: getSettingKey({ + category: TABLE_CATEGORY, + page: ALERTS_PAGE, + setting: STACK_BY_1_SETTING_NAME, + }), + }); + + const [trendChartStackBy, setTrendChartStackBy] = useLocalStorage({ + defaultValue: DEFAULT_STACK_BY_FIELD, + key: getSettingKey({ + category: TREND_CHART_CATEGORY, + page: ALERTS_PAGE, + setting: STACK_BY_SETTING_NAME, + }), + isInvalidDefault: isDefaultWhenEmptyString, + }); + + return { + alertViewSelection, + countTableStackBy0, + countTableStackBy1, + isTreemapPanelExpanded, + riskChartStackBy0, + riskChartStackBy1, + setAlertViewSelection, + setCountTableStackBy0, + setCountTableStackBy1, + setIsTreemapPanelExpanded, + setRiskChartStackBy0, + setRiskChartStackBy1, + setTrendChartStackBy, + trendChartStackBy, + }; +}; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/types.ts new file mode 100644 index 0000000000000..e909fc66f11db --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/alerts_local_storage/types.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AlertViewSelection } from '../chart_select/helpers'; + +export interface AlertsSettings { + alertViewSelection: AlertViewSelection; + countTableStackBy0: string; + countTableStackBy1: string | undefined; + isTreemapPanelExpanded: boolean; + riskChartStackBy0: string; + riskChartStackBy1: string | undefined; + setAlertViewSelection: (alertViewSelection: AlertViewSelection) => void; + setCountTableStackBy0: (value: string) => void; + setCountTableStackBy1: (value: string | undefined) => void; + setIsTreemapPanelExpanded: (value: boolean) => void; + setRiskChartStackBy0: (value: string) => void; + setRiskChartStackBy1: (value: string | undefined) => void; + setTrendChartStackBy: (value: string) => void; + trendChartStackBy: string; +} diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_context_menu/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_context_menu/index.test.tsx new file mode 100644 index 0000000000000..fa9ab51d19161 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_context_menu/index.test.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { RESET_GROUP_BY_FIELDS } from '../../../../../common/components/chart_settings_popover/configurations/default/translations'; +import { CHART_SETTINGS_POPOVER_ARIA_LABEL } from '../../../../../common/components/chart_settings_popover/translations'; +import { INSPECT } from '../../../../../common/components/inspect/translations'; +import { + DEFAULT_STACK_BY_FIELD, + DEFAULT_STACK_BY_FIELD1, +} from '../../../../components/alerts_kpis/common/config'; +import { TestProviders } from '../../../../../common/mock'; +import { ChartContextMenu } from '.'; + +describe('ChartContextMenu', () => { + const queryId = 'abcd'; + beforeEach(() => jest.resetAllMocks()); + + test('it renders the chart context menu button', () => { + render( + + + + ); + + expect( + screen.getByRole('button', { name: CHART_SETTINGS_POPOVER_ARIA_LABEL }) + ).toBeInTheDocument(); + }); + + test('it renders the Inspect menu item', () => { + render( + + + + ); + + const menuButton = screen.getByRole('button', { name: CHART_SETTINGS_POPOVER_ARIA_LABEL }); + menuButton.click(); + + expect(screen.getByRole('button', { name: INSPECT })).toBeInTheDocument(); + }); + + test('it invokes `setStackBy` and `setStackByField1` when the Reset group by fields menu item selected', () => { + const setStackBy = jest.fn(); + const setStackByField1 = jest.fn(); + + render( + + + + ); + + const menuButton = screen.getByRole('button', { name: CHART_SETTINGS_POPOVER_ARIA_LABEL }); + menuButton.click(); + + const resetMenuItem = screen.getByRole('button', { name: RESET_GROUP_BY_FIELDS }); + resetMenuItem.click(); + + expect(setStackBy).toBeCalledWith('kibana.alert.rule.name'); + expect(setStackByField1).toBeCalledWith('host.name'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_context_menu/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_context_menu/index.tsx new file mode 100644 index 0000000000000..36ac97de09610 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_context_menu/index.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; + +import { ChartSettingsPopover } from '../../../../../common/components/chart_settings_popover'; +import { useChartSettingsPopoverConfiguration } from '../../../../../common/components/chart_settings_popover/configurations/default'; + +interface Props { + defaultStackByField: string; + defaultStackByField1?: string; + queryId: string; + setStackBy: (value: string) => void; + setStackByField1?: (stackBy: string | undefined) => void; +} + +const ChartContextMenuComponent: React.FC = ({ + defaultStackByField, + defaultStackByField1, + queryId, + setStackBy, + setStackByField1, +}: Props) => { + const onResetStackByFields = useCallback(() => { + setStackBy(defaultStackByField); + + if (setStackByField1 != null) { + setStackByField1(defaultStackByField1); + } + }, [defaultStackByField, defaultStackByField1, setStackBy, setStackByField1]); + + const { defaultInitialPanelId, defaultMenuItems, isPopoverOpen, setIsPopoverOpen } = + useChartSettingsPopoverConfiguration({ + onResetStackByFields, + queryId, + }); + + return ( + + ); +}; + +ChartContextMenuComponent.displayName = 'ChartContextMenuComponent'; + +export const ChartContextMenu = React.memo(ChartContextMenuComponent); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts new file mode 100644 index 0000000000000..7d24cee9b6533 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.test.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AlertViewSelection } from './helpers'; +import { + getButtonProperties, + getContextMenuPanels, + TABLE_ID, + TREEMAP_ID, + TREND_ID, +} from './helpers'; +import * as i18n from './translations'; + +describe('helpers', () => { + beforeEach(() => jest.resetAllMocks()); + + describe('getButtonProperties', () => { + test('it returns the expected properties when alertViewSelection is Trend', () => { + expect(getButtonProperties(TREND_ID)).toEqual({ + 'data-test-subj': TREND_ID, + icon: 'visBarVerticalStacked', + name: i18n.TREND, + }); + }); + + test('it returns the expected properties when alertViewSelection is Table', () => { + expect(getButtonProperties(TABLE_ID)).toEqual({ + 'data-test-subj': TABLE_ID, + icon: 'visTable', + name: i18n.TABLE, + }); + }); + + test('it returns the expected properties when alertViewSelection is Treemap', () => { + expect(getButtonProperties(TREEMAP_ID)).toEqual({ + 'data-test-subj': TREEMAP_ID, + icon: 'grid', + name: i18n.TREEMAP, + }); + }); + }); + + describe('getContextMenuPanels', () => { + const alertViewSelections: AlertViewSelection[] = ['trend', 'table', 'treemap']; + const closePopover = jest.fn(); + const setAlertViewSelection = jest.fn(); + + alertViewSelections.forEach((alertViewSelection) => { + test(`it returns the expected panel id when alertViewSelection is '${alertViewSelection}'`, () => { + const panels = getContextMenuPanels({ + alertViewSelection, + closePopover, + setAlertViewSelection, + }); + + expect(panels[0].id).toEqual(0); + }); + + test(`onClick invokes setAlertViewSelection with '${alertViewSelection}' item when alertViewSelection is '${alertViewSelection}'`, () => { + const panels = getContextMenuPanels({ + alertViewSelection, + closePopover, + setAlertViewSelection, + }); + + const item = panels[0].items?.find((x) => x['data-test-subj'] === alertViewSelection); + (item?.onClick as () => void)(); + + expect(setAlertViewSelection).toBeCalledWith(alertViewSelection); + }); + + test(`onClick invokes closePopover when alertViewSelection is '${alertViewSelection}'`, () => { + const panels = getContextMenuPanels({ + alertViewSelection, + closePopover, + setAlertViewSelection, + }); + + const item = panels[0].items?.find((x) => x['data-test-subj'] === alertViewSelection); + (item?.onClick as () => void)(); + + expect(closePopover).toBeCalled(); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.ts new file mode 100644 index 0000000000000..08507759b375e --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/helpers.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { EuiContextMenuPanelDescriptor } from '@elastic/eui'; + +import * as i18n from './translations'; + +export const TABLE_ID = 'table'; +export const TREND_ID = 'trend'; +export const TREEMAP_ID = 'treemap'; + +export type AlertViewSelection = 'trend' | 'table' | 'treemap'; + +export interface ButtonProperties { + 'data-test-subj': string; + icon: string; + name: string; +} + +export const getButtonProperties = (alertViewSelection: AlertViewSelection): ButtonProperties => { + const table = { 'data-test-subj': alertViewSelection, icon: 'visTable', name: i18n.TABLE }; + + switch (alertViewSelection) { + case TABLE_ID: + return table; + case TREND_ID: + return { + 'data-test-subj': alertViewSelection, + icon: 'visBarVerticalStacked', + name: i18n.TREND, + }; + case TREEMAP_ID: + return { 'data-test-subj': alertViewSelection, icon: 'grid', name: i18n.TREEMAP }; + default: + return table; + } +}; + +export const getContextMenuPanels = ({ + alertViewSelection, + closePopover, + setAlertViewSelection, +}: { + alertViewSelection: AlertViewSelection; + closePopover: () => void; + setAlertViewSelection: (alertViewSelection: AlertViewSelection) => void; +}): EuiContextMenuPanelDescriptor[] => [ + { + id: 0, + items: [ + { + ...getButtonProperties('table'), + onClick: () => { + closePopover(); + setAlertViewSelection('table'); + }, + }, + { + ...getButtonProperties('trend'), + onClick: () => { + closePopover(); + setAlertViewSelection('trend'); + }, + }, + { + ...getButtonProperties('treemap'), + onClick: () => { + closePopover(); + setAlertViewSelection('treemap'); + }, + }, + ], + }, +]; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.test.tsx new file mode 100644 index 0000000000000..82861cf2b9d6e --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.test.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +import { TestProviders } from '../../../../../common/mock'; +import { SELECT_A_CHART_ARIA_LABEL, TREEMAP } from './translations'; +import { ChartSelect } from '.'; + +describe('ChartSelect', () => { + test('it renders the chart select button', () => { + render( + + + + ); + + expect(screen.getByRole('button', { name: SELECT_A_CHART_ARIA_LABEL })).toBeInTheDocument(); + }); + + test('it invokes `setAlertViewSelection` with the expected value when a chart is selected', () => { + const setAlertViewSelection = jest.fn(); + + render( + + + + ); + + const selectButton = screen.getByRole('button', { name: SELECT_A_CHART_ARIA_LABEL }); + selectButton.click(); + + const treemapMenuItem = screen.getByRole('button', { name: TREEMAP }); + treemapMenuItem.click(); + + expect(setAlertViewSelection).toBeCalledWith('treemap'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.tsx new file mode 100644 index 0000000000000..5203eaf77edab --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { EuiContextMenuPanelDescriptor } from '@elastic/eui'; +import { EuiButton, EuiContextMenu, EuiIcon, EuiPopover } from '@elastic/eui'; +import React, { useCallback, useMemo, useState } from 'react'; +import styled from 'styled-components'; + +import type { AlertViewSelection } from './helpers'; +import { getButtonProperties, getContextMenuPanels } from './helpers'; +import * as i18n from './translations'; + +interface Props { + alertViewSelection: AlertViewSelection; + setAlertViewSelection: (alertViewSelection: AlertViewSelection) => void; +} + +const ChartTypeIcon = styled(EuiIcon)` + margin-right: ${({ theme }) => theme.eui.euiSizeS}; +`; + +const ChartSelectComponent: React.FC = ({ + alertViewSelection, + setAlertViewSelection, +}: Props) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const closePopover = useCallback(() => setIsPopoverOpen(false), []); + const onButtonClick = useCallback(() => setIsPopoverOpen((currentVal) => !currentVal), []); + + const button = useMemo(() => { + const buttonProperties = getButtonProperties(alertViewSelection); + + return ( + + + {buttonProperties.name} + + ); + }, [alertViewSelection, onButtonClick]); + + const panels: EuiContextMenuPanelDescriptor[] = useMemo( + () => getContextMenuPanels({ alertViewSelection, closePopover, setAlertViewSelection }), + [alertViewSelection, closePopover, setAlertViewSelection] + ); + + return ( + + + + ); +}; + +ChartSelectComponent.displayName = 'ChartSelectComponent'; + +export const ChartSelect = React.memo(ChartSelectComponent); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/translations.ts new file mode 100644 index 0000000000000..e776b94f3f957 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/translations.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const SELECT_A_CHART_ARIA_LABEL = i18n.translate( + 'xpack.securitySolution.components.chartSelect.selectAChartAriaLabel', + { + defaultMessage: 'Select a chart', + } +); + +export const TABLE = i18n.translate('xpack.securitySolution.components.chartSelect.tableOption', { + defaultMessage: 'Table', +}); + +export const TREND = i18n.translate('xpack.securitySolution.components.chartSelect.trendOption', { + defaultMessage: 'Trend', +}); + +export const TREEMAP = i18n.translate( + 'xpack.securitySolution.components.chartSelect.treemapOption', + { + defaultMessage: 'Treemap', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.test.tsx new file mode 100644 index 0000000000000..dcf77449da7c2 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.test.tsx @@ -0,0 +1,233 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { render, screen, waitFor } from '@testing-library/react'; +import React from 'react'; + +import { useAlertsLocalStorage } from './alerts_local_storage'; +import { mockBrowserFields } from '../../../../common/containers/source/mock'; +import { useSourcererDataView } from '../../../../common/containers/sourcerer'; +import { TestProviders } from '../../../../common/mock'; +import { ChartPanels } from '.'; + +jest.mock('./alerts_local_storage'); + +jest.mock('../../../../common/containers/sourcerer'); + +jest.mock('react-router-dom', () => { + const originalModule = jest.requireActual('react-router-dom'); + + return { + ...originalModule, + useParams: jest.fn(), + useHistory: jest.fn(), + useLocation: () => ({ pathname: '' }), + }; +}); + +jest.mock('../../../../common/lib/kibana', () => { + const original = jest.requireActual('../../../../common/lib/kibana'); + + return { + ...original, + useUiSetting$: () => ['0,0.[000]'], + useKibana: () => ({ + services: { + application: { + navigateToUrl: jest.fn(), + }, + storage: { + get: jest.fn(), + set: jest.fn(), + }, + }, + }), + }; +}); + +const defaultAlertSettings = { + alertViewSelection: 'trend', + countTableStackBy0: 'kibana.alert.rule.name', + countTableStackBy1: 'host.name', + isTreemapPanelExpanded: true, + riskChartStackBy0: 'kibana.alert.rule.name', + riskChartStackBy1: 'host.name', + setAlertViewSelection: jest.fn(), + setCountTableStackBy0: jest.fn(), + setCountTableStackBy1: jest.fn(), + setIsTreemapPanelExpanded: jest.fn(), + setRiskChartStackBy0: jest.fn(), + setRiskChartStackBy1: jest.fn(), + setTrendChartStackBy: jest.fn(), + trendChartStackBy: 'kibana.alert.rule.name', +}; + +const defaultProps = { + addFilter: jest.fn(), + alertsHistogramDefaultFilters: [ + { + meta: { + alias: null, + negate: true, + disabled: false, + type: 'exists', + key: 'kibana.alert.building_block_type', + value: 'exists', + }, + query: { + exists: { + field: 'kibana.alert.building_block_type', + }, + }, + }, + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'kibana.alert.workflow_status', + params: { + query: 'open', + }, + }, + query: { + term: { + 'kibana.alert.workflow_status': 'open', + }, + }, + }, + ], + isLoadingIndexPattern: false, + query: { + query: '', + language: 'kuery', + }, + runtimeMappings: {}, + signalIndexName: '.alerts-security.alerts-default', + updateDateRangeCallback: jest.fn(), +}; + +describe('ChartPanels', () => { + beforeEach(() => { + jest.clearAllMocks(); + + (useSourcererDataView as jest.Mock).mockReturnValue({ + indicesExist: true, + indexPattern: {}, + browserFields: mockBrowserFields, + }); + + (useAlertsLocalStorage as jest.Mock).mockReturnValue({ + ...defaultAlertSettings, + }); + }); + + test('it renders the chart selector', async () => { + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId('chartSelect')).toBeInTheDocument(); + }); + }); + + test('it renders the trend loading spinner when data is loading and `alertViewSelection` is trend', async () => { + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId('trendLoadingSpinner')).toBeInTheDocument(); + }); + }); + + test('it renders the alert histogram panel when `alertViewSelection` is trend', async () => { + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId('alerts-histogram-panel')).toBeInTheDocument(); + }); + }); + + test('it renders the table loading spinner when data is loading and `alertViewSelection` is table', async () => { + (useAlertsLocalStorage as jest.Mock).mockReturnValue({ + ...defaultAlertSettings, + alertViewSelection: 'table', + }); + + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId('tableLoadingSpinner')).toBeInTheDocument(); + }); + }); + + test('it renders the alerts count panel when `alertViewSelection` is table', async () => { + (useAlertsLocalStorage as jest.Mock).mockReturnValue({ + ...defaultAlertSettings, + alertViewSelection: 'table', + }); + + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId('alertsCountPanel')).toBeInTheDocument(); + }); + }); + + test('it renders the treemap loading spinner when data is loading and `alertViewSelection` is treemap', async () => { + (useAlertsLocalStorage as jest.Mock).mockReturnValue({ + ...defaultAlertSettings, + alertViewSelection: 'treemap', + }); + + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId('treemapLoadingSpinner')).toBeInTheDocument(); + }); + }); + + test('it renders the alerts count panel when `alertViewSelection` is treemap', async () => { + (useAlertsLocalStorage as jest.Mock).mockReturnValue({ + ...defaultAlertSettings, + alertViewSelection: 'treemap', + }); + + render( + + + + ); + + await waitFor(() => { + expect(screen.getByTestId('treemapPanel')).toBeInTheDocument(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx new file mode 100644 index 0000000000000..9d57590c72bff --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.tsx @@ -0,0 +1,204 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { Filter, Query } from '@kbn/es-query'; +import { EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; +import React, { useCallback, useMemo } from 'react'; +import styled from 'styled-components'; + +import { useAlertsLocalStorage } from './alerts_local_storage'; +import type { AlertsSettings } from './alerts_local_storage/types'; +import { ChartContextMenu } from './chart_context_menu'; +import { ChartSelect } from './chart_select'; +import { AlertsTreemapPanel } from '../../../../common/components/alerts_treemap_panel'; +import type { UpdateDateRange } from '../../../../common/components/charts/common'; +import { AlertsHistogramPanel } from '../../../components/alerts_kpis/alerts_histogram_panel'; +import { + DEFAULT_STACK_BY_FIELD, + DEFAULT_STACK_BY_FIELD1, +} from '../../../components/alerts_kpis/common/config'; +import { AlertsCountPanel } from '../../../components/alerts_kpis/alerts_count_panel'; +import { GROUP_BY_LABEL } from '../../../components/alerts_kpis/common/translations'; + +const TABLE_PANEL_HEIGHT = 330; // px +const TRENT_CHART_HEIGHT = 127; // px +const TREND_CHART_PANEL_HEIGHT = 256; // px + +const AlertsCountPanelFlexItem = styled(EuiFlexItem)` + margin-left: ${({ theme }) => theme.eui.euiSizeM}; +`; + +const FullHeightFlexItem = styled(EuiFlexItem)` + height: 100%; +`; + +const ChartSelectContainer = styled.div` + margin-left: ${({ theme }) => theme.eui.euiSizeS}; +`; + +export interface Props { + addFilter: ({ field, value }: { field: string; value: string | number }) => void; + alertsHistogramDefaultFilters: Filter[]; + isLoadingIndexPattern: boolean; + query: Query; + runtimeMappings: MappingRuntimeFields; + signalIndexName: string | null; + updateDateRangeCallback: UpdateDateRange; +} + +const ChartPanelsComponent: React.FC = ({ + addFilter, + alertsHistogramDefaultFilters, + isLoadingIndexPattern, + query, + runtimeMappings, + signalIndexName, + updateDateRangeCallback, +}: Props) => { + const { + alertViewSelection, + countTableStackBy0, + countTableStackBy1, + isTreemapPanelExpanded, + riskChartStackBy0, + riskChartStackBy1, + setAlertViewSelection, + setCountTableStackBy0, + setCountTableStackBy1, + setIsTreemapPanelExpanded, + setRiskChartStackBy0, + setRiskChartStackBy1, + setTrendChartStackBy, + trendChartStackBy, + }: AlertsSettings = useAlertsLocalStorage(); + + const updateCommonStackBy0 = useCallback( + (value: string) => { + setTrendChartStackBy(value); + setCountTableStackBy0(value); + setRiskChartStackBy0(value); + }, + [setCountTableStackBy0, setRiskChartStackBy0, setTrendChartStackBy] + ); + + const updateCommonStackBy1 = useCallback( + (value: string | undefined) => { + setCountTableStackBy1(value); + setRiskChartStackBy1(value); + }, + [setCountTableStackBy1, setRiskChartStackBy1] + ); + + const chartOptionsContextMenu = useCallback( + (queryId: string) => ( + + ), + [updateCommonStackBy0, updateCommonStackBy1] + ); + + const title = useMemo( + () => ( + + + + ), + [alertViewSelection, setAlertViewSelection] + ); + + return ( +
    + {alertViewSelection === 'trend' && ( + + {isLoadingIndexPattern ? ( + + ) : ( + + )} + + )} + + {alertViewSelection === 'table' && ( + + {isLoadingIndexPattern ? ( + + ) : ( + + )} + + )} + + {alertViewSelection === 'treemap' && ( + + {isLoadingIndexPattern ? ( + + ) : ( + + )} + + )} +
    + ); +}; + +export const ChartPanels = React.memo(ChartPanelsComponent); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx index 9680692b9da53..0e86d6e972f3d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.test.tsx @@ -93,6 +93,10 @@ jest.mock('../../../common/lib/kibana', () => { }, }, }, + storage: { + get: jest.fn(), + set: jest.fn(), + }, }, }), useToasts: jest.fn().mockReturnValue({ @@ -139,4 +143,18 @@ describe('DetectionEnginePageComponent', () => { expect(wrapper.find('FiltersGlobal').exists()).toBe(true); }); }); + + it('renders the chart panels', async () => { + const wrapper = mount( + + + + + + ); + + await waitFor(() => { + expect(wrapper.find('[data-test-subj="chartPanels"]').exists()).toBe(true); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index 58984e2703521..56555e95e40ef 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -39,7 +39,6 @@ import { inputsSelectors } from '../../../common/store/inputs'; import { setAbsoluteRangeDatePicker } from '../../../common/store/inputs/actions'; import { AlertsTable } from '../../components/alerts_table'; import { NoApiIntegrationKeyCallOut } from '../../components/callouts/no_api_integration_callout'; -import { AlertsHistogramPanel } from '../../components/alerts_kpis/alerts_histogram_panel'; import { useUserData } from '../../components/user_info'; import { DetectionEngineNoIndex } from './detection_engine_no_index'; import { useListsConfig } from '../../containers/detection_engine/lists/use_lists_config'; @@ -62,6 +61,7 @@ import { buildShowBuildingBlockFilter, buildThreatMatchFilter, } from '../../components/alerts_table/default_config'; +import { ChartPanels } from './chart_panels'; import { useSourcererDataView } from '../../../common/containers/sourcerer'; import { useSignalHelpers } from '../../../common/containers/sourcerer/use_signal_helpers'; @@ -69,8 +69,6 @@ import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import { NeedAdminForUpdateRulesCallOut } from '../../components/callouts/need_admin_for_update_callout'; import { MissingPrivilegesCallOut } from '../../components/callouts/missing_privileges_callout'; import { useKibana } from '../../../common/lib/kibana'; -import { AlertsCountPanel } from '../../components/alerts_kpis/alerts_count_panel'; -import { CHART_HEIGHT } from '../../components/alerts_kpis/common/config'; import { AlertsTableFilterGroup, FILTER_OPEN, @@ -78,6 +76,7 @@ import { import { EmptyPage } from '../../../common/components/empty_page'; import { HeaderPage } from '../../../common/components/header_page'; import { LandingPageComponent } from '../../../common/components/landing_page'; + /** * Need a 100% height here to account for the graph/analyze tool, which sets no explicit height parameters, but fills the available space. */ @@ -144,10 +143,29 @@ const DetectionEnginePageComponent: React.FC = ({ const { application: { navigateToUrl }, timelines: timelinesUi, + data, docLinks, } = useKibana().services; const [filterGroup, setFilterGroup] = useState(FILTER_OPEN); + const { filterManager } = data.query; + + const addFilter = useCallback( + ({ field, value }: { field: string; value: string | number }) => { + filterManager.addFilters([ + { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { match_phrase: { [field]: value } }, + }, + ]); + }, + [filterManager] + ); + const showUpdating = useMemo(() => isAlertsLoading || loading, [isAlertsLoading, loading]); const updateDateRangeCallback = useCallback( @@ -330,45 +348,30 @@ const DetectionEnginePageComponent: React.FC = ({ onFilterGroupChanged={onFilterGroupChangedCallback} /> + - {updatedAt && - timelinesUi.getLastUpdated({ - updatedAt: updatedAt || Date.now(), - showUpdating, - })} + + + {updatedAt && + timelinesUi.getLastUpdated({ + updatedAt: updatedAt || Date.now(), + showUpdating, + })} + + - - - {isLoadingIndexPattern ? ( - - ) : ( - - )} - - - {isLoadingIndexPattern ? ( - - ) : ( - - )} - - + + From 978012bf512278d4b702698cdedc98e02575f26b Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 13 Jul 2022 00:47:07 -0400 Subject: [PATCH 37/42] [api-docs] Daily api_docs build (#136249) --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.mdx | 2 +- api_docs/apm.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/controls.mdx | 2 +- api_docs/core.mdx | 2 +- api_docs/core_application.mdx | 2 +- api_docs/core_chrome.mdx | 2 +- api_docs/core_http.mdx | 2 +- api_docs/core_saved_objects.mdx | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.mdx | 2 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.mdx | 2 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/deprecations_by_api.mdx | 2 +- api_docs/deprecations_by_plugin.mdx | 2 +- api_docs/deprecations_by_team.mdx | 2 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/elastic_apm_synthtrace.mdx | 2 +- api_docs/embeddable.devdocs.json | 169 ++++++++++++++++++ api_docs/embeddable.mdx | 4 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.devdocs.json | 28 +++ api_docs/enterprise_search.mdx | 4 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_log.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.devdocs.json | 10 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/fleet.devdocs.json | 19 +- api_docs/fleet.mdx | 4 +- api_docs/global_search.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_utils.mdx | 2 +- api_docs/kbn_alerts.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.mdx | 2 +- ..._analytics_shippers_elastic_v3_browser.mdx | 2 +- ...n_analytics_shippers_elastic_v3_common.mdx | 2 +- ...n_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_bazel_packages.devdocs.json | 2 +- api_docs/kbn_bazel_packages.mdx | 2 +- api_docs/kbn_bazel_runner.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- .../kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- .../kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- .../kbn_core_environment_server_internal.mdx | 2 +- .../kbn_core_environment_server_mocks.mdx | 2 +- .../kbn_core_execution_context_browser.mdx | 2 +- ...ore_execution_context_browser_internal.mdx | 2 +- ...n_core_execution_context_browser_mocks.mdx | 2 +- .../kbn_core_execution_context_common.mdx | 2 +- .../kbn_core_execution_context_server.mdx | 2 +- ...core_execution_context_server_internal.mdx | 2 +- ...bn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- .../kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- .../kbn_core_injected_metadata_browser.mdx | 2 +- ...n_core_injected_metadata_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_internal.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_doc_links.devdocs.json | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_home_sample_data_cards.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_kibana_json_schema.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- ..._performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_discovery.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_pm.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- .../kbn_scalability_simulation_generator.mdx | 2 +- .../kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- ..._securitysolution_io_ts_alerting_types.mdx | 2 +- .../kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- .../kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- .../kbn_shared_ux_components.devdocs.json | 39 +--- api_docs/kbn_shared_ux_components.mdx | 4 +- .../kbn_shared_ux_page_analytics_no_data.mdx | 2 +- .../kbn_shared_ux_page_kibana_no_data.mdx | 2 +- ...n_shared_ux_page_solution_nav.devdocs.json | 131 ++++++++++++++ api_docs/kbn_shared_ux_page_solution_nav.mdx | 30 ++++ .../kbn_shared_ux_prompt_no_data_views.mdx | 2 +- api_docs/kbn_shared_ux_services.mdx | 2 +- api_docs/kbn_shared_ux_storybook.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_sort_package_json.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_type_summarizer.mdx | 2 +- api_docs/kbn_type_summarizer_core.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.devdocs.json | 25 ++- api_docs/lens.mdx | 4 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.devdocs.json | 15 ++ api_docs/maps.mdx | 4 +- api_docs/maps_ems.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/observability.devdocs.json | 27 +++ api_docs/observability.mdx | 4 +- api_docs/osquery.mdx | 2 +- api_docs/plugin_directory.mdx | 23 +-- api_docs/presentation_util.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/shared_u_x.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/task_manager.devdocs.json | 39 +++- api_docs/task_manager.mdx | 4 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/timelines.devdocs.json | 4 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.mdx | 2 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.devdocs.json | 4 +- api_docs/visualizations.mdx | 2 +- 301 files changed, 790 insertions(+), 363 deletions(-) create mode 100644 api_docs/kbn_shared_ux_page_solution_nav.devdocs.json create mode 100644 api_docs/kbn_shared_ux_page_solution_nav.mdx diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 6a3c68ad48d58..b37fe3df21ad2 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github summary: API docs for the actions plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 9bb5fe3d5b4cc..d7284bf2ba87a 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github summary: API docs for the advancedSettings plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index aeab63cb6ec32..618ea5e1eb78d 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github summary: API docs for the aiops plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 6de83d9c2f0ae..7b86f41380cf8 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github summary: API docs for the alerting plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 1174fcd7766b5..1e52586d92225 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github summary: API docs for the apm plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index a9128dcfbe534..60a50ae742061 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github summary: API docs for the banners plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index a55e23a53a276..d11bfb0ab67a5 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github summary: API docs for the bfetch plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 7225e5901480f..e647d472c91f9 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github summary: API docs for the canvas plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index 8130986f8f37c..2aa643e48d15b 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github summary: API docs for the cases plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index 94d2a09976d0d..9671ccbe8af2f 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github summary: API docs for the charts plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index c930bf640a69f..01ec58337accf 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github summary: API docs for the cloud plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 66d945d6270ae..21e18763c07cc 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github summary: API docs for the cloudSecurityPosture plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 8cc43448bdab2..bf2db61245112 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github summary: API docs for the console plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index ee8efdd416512..fc1869da56022 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github summary: API docs for the controls plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/core.mdx b/api_docs/core.mdx index 437959d2b9df7..1ca2a7bd7646b 100644 --- a/api_docs/core.mdx +++ b/api_docs/core.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core title: "core" image: https://source.unsplash.com/400x175/?github summary: API docs for the core plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/core_application.mdx b/api_docs/core_application.mdx index 7435423973865..73e1cc4df37c8 100644 --- a/api_docs/core_application.mdx +++ b/api_docs/core_application.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-application title: "core.application" image: https://source.unsplash.com/400x175/?github summary: API docs for the core.application plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.application'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/core_chrome.mdx b/api_docs/core_chrome.mdx index 9472473f24b9d..38935d4adbf2c 100644 --- a/api_docs/core_chrome.mdx +++ b/api_docs/core_chrome.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-chrome title: "core.chrome" image: https://source.unsplash.com/400x175/?github summary: API docs for the core.chrome plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.chrome'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/core_http.mdx b/api_docs/core_http.mdx index 079c924163450..3dca29910f474 100644 --- a/api_docs/core_http.mdx +++ b/api_docs/core_http.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-http title: "core.http" image: https://source.unsplash.com/400x175/?github summary: API docs for the core.http plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.http'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/core_saved_objects.mdx b/api_docs/core_saved_objects.mdx index 87e86a6d4dff9..ef05af39d188d 100644 --- a/api_docs/core_saved_objects.mdx +++ b/api_docs/core_saved_objects.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/core-savedObjects title: "core.savedObjects" image: https://source.unsplash.com/400x175/?github summary: API docs for the core.savedObjects plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'core.savedObjects'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 93aed293adba1..ee0b3ef00b9be 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github summary: API docs for the customIntegrations plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index bb93d6c8ea90c..17a5f089230cd 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github summary: API docs for the dashboard plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 6d9fedd99c3d4..cb8a65c67c0e3 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github summary: API docs for the dashboardEnhanced plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 264d88e0a80a7..f6c01d22f9c87 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github summary: API docs for the data plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index ee01b530880cf..50c49ba0251eb 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github summary: API docs for the data.query plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 46644a6ce3a47..b141bc3f7fa82 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github summary: API docs for the data.search plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 6b36aa2a4838f..a0dfcb172f2c8 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataViewEditor plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index ea9c70bc46ca1..59bf59eefe9bd 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataViewFieldEditor plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index 5ae395f21c8f1..d1f57d8a709de 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataViewManagement plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index d23739df18cd4..affa6f0e564e2 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataViews plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index a89043f6918c0..7c3260f17f381 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github summary: API docs for the dataVisualizer plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index bea1e416fbb83..671d479bbccc2 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -3,7 +3,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API summary: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index a1a9497c770b5..fab0384a628df 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -3,7 +3,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin summary: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 2acb315e3c738..8d5fe5e1eae5d 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -3,7 +3,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team summary: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index d0bd126888036..aa54cbe4f050e 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github summary: API docs for the devTools plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 63ef2090d99be..f65c671bed195 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github summary: API docs for the discover plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index 32c9ef7baaad4..0e9c5ec48c255 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github summary: API docs for the discoverEnhanced plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/elastic_apm_synthtrace.mdx b/api_docs/elastic_apm_synthtrace.mdx index f3a52beef90e5..5ca22a26a6e36 100644 --- a/api_docs/elastic_apm_synthtrace.mdx +++ b/api_docs/elastic_apm_synthtrace.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/elastic-apm-synthtrace title: "@elastic/apm-synthtrace" image: https://source.unsplash.com/400x175/?github summary: API docs for the @elastic/apm-synthtrace plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@elastic/apm-synthtrace'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/embeddable.devdocs.json b/api_docs/embeddable.devdocs.json index c04fd527e5e5f..80b1999d682a5 100644 --- a/api_docs/embeddable.devdocs.json +++ b/api_docs/embeddable.devdocs.json @@ -2513,6 +2513,21 @@ ], "returnComment": [] }, + { + "parentPluginId": "embeddable", + "id": "def-public.Embeddable.reportsEmbeddableLoad", + "type": "Function", + "tags": [], + "label": "reportsEmbeddableLoad", + "description": [], + "signature": [ + "() => boolean" + ], + "path": "src/plugins/embeddable/public/lib/embeddables/embeddable.tsx", + "deprecated": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "embeddable", "id": "def-public.Embeddable.refreshInputFromParent", @@ -6200,6 +6215,50 @@ "deprecated": false } ] + }, + { + "parentPluginId": "embeddable", + "id": "def-public.EmbeddableChildPanelProps.onPanelStatusChange", + "type": "Function", + "tags": [], + "label": "onPanelStatusChange", + "description": [], + "signature": [ + "((info: ", + { + "pluginId": "embeddable", + "scope": "public", + "docId": "kibEmbeddablePluginApi", + "section": "def-public.EmbeddablePhaseEvent", + "text": "EmbeddablePhaseEvent" + }, + ") => void) | undefined" + ], + "path": "src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "embeddable", + "id": "def-public.EmbeddableChildPanelProps.onPanelStatusChange.$1", + "type": "Object", + "tags": [], + "label": "info", + "description": [], + "signature": [ + { + "pluginId": "embeddable", + "scope": "public", + "docId": "kibEmbeddablePluginApi", + "section": "def-public.EmbeddablePhaseEvent", + "text": "EmbeddablePhaseEvent" + } + ], + "path": "src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [] } ], "initialIsOpen": false @@ -6917,6 +6976,19 @@ "path": "src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts", "deprecated": false }, + { + "parentPluginId": "embeddable", + "id": "def-public.EmbeddableOutput.rendered", + "type": "CompoundType", + "tags": [], + "label": "rendered", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts", + "deprecated": false + }, { "parentPluginId": "embeddable", "id": "def-public.EmbeddableOutput.error", @@ -7116,6 +7188,72 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "embeddable", + "id": "def-public.EmbeddablePhaseEvent", + "type": "Interface", + "tags": [], + "label": "EmbeddablePhaseEvent", + "description": [], + "path": "src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "embeddable", + "id": "def-public.EmbeddablePhaseEvent.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx", + "deprecated": false + }, + { + "parentPluginId": "embeddable", + "id": "def-public.EmbeddablePhaseEvent.status", + "type": "CompoundType", + "tags": [], + "label": "status", + "description": [], + "signature": [ + "\"loading\" | \"error\" | \"loaded\" | \"rendered\"" + ], + "path": "src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx", + "deprecated": false + }, + { + "parentPluginId": "embeddable", + "id": "def-public.EmbeddablePhaseEvent.error", + "type": "CompoundType", + "tags": [], + "label": "error", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ErrorLike", + "text": "ErrorLike" + }, + " | undefined" + ], + "path": "src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx", + "deprecated": false + }, + { + "parentPluginId": "embeddable", + "id": "def-public.EmbeddablePhaseEvent.timeToEvent", + "type": "number", + "tags": [], + "label": "timeToEvent", + "description": [], + "path": "src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx", + "deprecated": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "embeddable", "id": "def-public.EmbeddableSetupDependencies", @@ -8082,6 +8220,23 @@ "path": "src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts", "deprecated": false }, + { + "parentPluginId": "embeddable", + "id": "def-public.IEmbeddable.reportsEmbeddableLoad", + "type": "Function", + "tags": [], + "label": "reportsEmbeddableLoad", + "description": [ + "\nThis method returns false by default.\nIt should be set to true for any embeddable type that utilizes the `loading` and `rendered`\noutput variables to notify a container of their loading progress. If set to false, a container should assume\nthe embeddable is loaded immediately." + ], + "signature": [ + "() => boolean" + ], + "path": "src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts", + "deprecated": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "embeddable", "id": "def-public.IEmbeddable.getIsContainer", @@ -9197,6 +9352,20 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "embeddable", + "id": "def-public.EmbeddablePhase", + "type": "Type", + "tags": [], + "label": "EmbeddablePhase", + "description": [], + "signature": [ + "\"loading\" | \"error\" | \"loaded\" | \"rendered\"" + ], + "path": "src/plugins/embeddable/public/lib/containers/embeddable_child_panel.tsx", + "deprecated": false, + "initialIsOpen": false + }, { "parentPluginId": "embeddable", "id": "def-public.EmbeddableRendererProps", diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 6823ec57019c5..e4131ab27841b 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github summary: API docs for the embeddable plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [App Services](https://github.com/orgs/elastic/teams/kibana-app-services | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 494 | 0 | 403 | 3 | +| 505 | 0 | 413 | 3 | ## Client diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index eafa8561e09f3..57a314069ec80 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github summary: API docs for the embeddableEnhanced plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index 85d2f0d185835..6d81ac8710d6d 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github summary: API docs for the encryptedSavedObjects plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/enterprise_search.devdocs.json b/api_docs/enterprise_search.devdocs.json index fdbc357cfd7a5..18f9b884e77d7 100644 --- a/api_docs/enterprise_search.devdocs.json +++ b/api_docs/enterprise_search.devdocs.json @@ -41,6 +41,34 @@ "path": "x-pack/plugins/enterprise_search/server/index.ts", "deprecated": false, "initialIsOpen": false + }, + { + "parentPluginId": "enterpriseSearch", + "id": "def-server.CONNECTORS_JOBS_INDEX", + "type": "string", + "tags": [], + "label": "CONNECTORS_JOBS_INDEX", + "description": [], + "signature": [ + "\".elastic-connectors-sync-jobs\"" + ], + "path": "x-pack/plugins/enterprise_search/server/index.ts", + "deprecated": false, + "initialIsOpen": false + }, + { + "parentPluginId": "enterpriseSearch", + "id": "def-server.CONNECTORS_VERSION", + "type": "string", + "tags": [], + "label": "CONNECTORS_VERSION", + "description": [], + "signature": [ + "\"1\"" + ], + "path": "x-pack/plugins/enterprise_search/server/index.ts", + "deprecated": false, + "initialIsOpen": false } ], "objects": [ diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index d5564539277ec..e63cd6c903d56 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github summary: API docs for the enterpriseSearch plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Enterprise Search](https://github.com/orgs/elastic/teams/enterprise-sea | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 3 | 0 | 3 | 0 | +| 5 | 0 | 5 | 0 | ## Server diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 12156e65690e1..9af8efc8273f8 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github summary: API docs for the esUiShared plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index 267cea8167c2a..ef1af364265bd 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github summary: API docs for the eventAnnotation plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index 7fbf7901119f2..8f4e382d5481b 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github summary: API docs for the eventLog plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index ff4b2a24ecb2f..51094be114475 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionError plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index db52c844525a6..e50484b266e4c 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionGauge plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 1b2331648d3cb..685f99a7ba9e0 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionHeatmap plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index 43ad3c539dc2c..2e1f71c8bdd5c 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionImage plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index 5cff7dee72072..9e6300966a37f 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionLegacyMetricVis plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index 4684cb5b973fd..e8985aa1d6ed7 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionMetric plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index b1e2f1471edda..ed1d2c1b8385d 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionMetricVis plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index 3924654b91170..0dc3ea7c83883 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionPartitionVis plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 030403bcbbdaa..fff23cee1715b 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionRepeatImage plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index cdb11e20cfb8b..03329e37a283c 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionRevealImage plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 369245d5ac060..e9b3fd2668ae8 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionShape plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index 14b9723a63d1a..1cf371bedce39 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionTagcloud plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expression_x_y.devdocs.json b/api_docs/expression_x_y.devdocs.json index befebac94be4d..8e4323a0f0114 100644 --- a/api_docs/expression_x_y.devdocs.json +++ b/api_docs/expression_x_y.devdocs.json @@ -595,13 +595,13 @@ }, { "parentPluginId": "expressionXY", - "id": "def-common.DataLayerArgs.splitAccessor", - "type": "CompoundType", + "id": "def-common.DataLayerArgs.splitAccessors", + "type": "Array", "tags": [], - "label": "splitAccessor", + "label": "splitAccessors", "description": [], "signature": [ - "string | ", + "(string | ", { "pluginId": "visualizations", "scope": "common", @@ -609,7 +609,7 @@ "section": "def-common.ExpressionValueVisDimension", "text": "ExpressionValueVisDimension" }, - " | undefined" + ")[] | undefined" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", "deprecated": false diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index b80f2be3cc15d..07590478a7e0e 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressionXY plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 011781bbddfaf..f8c304a1aeab4 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github summary: API docs for the expressions plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 9ac804b9c7f97..d6353777ae421 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github summary: API docs for the features plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index 636a253a8f9c5..3f308b1e45891 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github summary: API docs for the fieldFormats plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index b66cb5c8b9b8f..0a0984a7c1b61 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github summary: API docs for the fileUpload plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/fleet.devdocs.json b/api_docs/fleet.devdocs.json index a450ab9b36268..0e8db05a8eb7a 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -12195,7 +12195,7 @@ "label": "developer", "description": [], "signature": [ - "{ disableRegistryVersionCheck?: boolean | undefined; allowAgentUpgradeSourceUri?: boolean | undefined; bundledPackageLocation?: string | undefined; } | undefined" + "{ disableRegistryVersionCheck?: boolean | undefined; bundledPackageLocation?: string | undefined; } | undefined" ], "path": "x-pack/plugins/fleet/common/types/index.ts", "deprecated": false @@ -13263,7 +13263,7 @@ "label": "agent", "description": [], "signature": [ - "{ monitoring: { namespace?: string | undefined; use_output?: string | undefined; enabled: boolean; metrics: boolean; logs: boolean; }; } | undefined" + "{ monitoring: { namespace?: string | undefined; use_output?: string | undefined; enabled: boolean; metrics: boolean; logs: boolean; }; download: { source_uri: string; }; } | undefined" ], "path": "x-pack/plugins/fleet/common/types/models/agent_policy.ts", "deprecated": false @@ -16030,6 +16030,19 @@ ], "path": "x-pack/plugins/fleet/common/types/models/agent.ts", "deprecated": false + }, + { + "parentPluginId": "fleet", + "id": "def-common.NewAgentAction.source_uri", + "type": "string", + "tags": [], + "label": "source_uri", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/fleet/common/types/models/agent.ts", + "deprecated": false } ], "initialIsOpen": false @@ -20597,7 +20610,7 @@ "label": "DEFAULT_DOWNLOAD_SOURCE", "description": [], "signature": [ - "\"artifactory.elastic.co\"" + "\"https://artifacts.elastic.co\"" ], "path": "x-pack/plugins/fleet/common/constants/download_source.ts", "deprecated": false, diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 1d5d8cf4433d3..9e4d8abbf4299 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github summary: API docs for the fleet plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Fleet](https://github.com/orgs/elastic/teams/fleet) for questions regar | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 1521 | 8 | 1389 | 10 | +| 1522 | 8 | 1390 | 10 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 4375bd831943c..dd60d001c83c5 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github summary: API docs for the globalSearch plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/home.mdx b/api_docs/home.mdx index f29525b9660c6..dcf20af6a1d11 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github summary: API docs for the home plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 67947a561bb54..c806e66c7e64b 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the indexLifecycleManagement plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 73c9bfe87d846..05e767cf2fc4c 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the indexManagement plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index 222c23bdb6509..d2c9347b374b5 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github summary: API docs for the infra plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index c0a93d711a7f7..db6eb55ddad01 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github summary: API docs for the inspector plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index b4b19e9562ba3..632870059bc83 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github summary: API docs for the interactiveSetup plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 778ed11e62f8b..d33da0ffedfeb 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ace plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index 96576bb02deb9..67908b73aaf91 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/aiops-components plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index 09f193d8a9537..16b93b3725319 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/aiops-utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_alerts.mdx b/api_docs/kbn_alerts.mdx index 91a14011423e6..50b568cc1982a 100644 --- a/api_docs/kbn_alerts.mdx +++ b/api_docs/kbn_alerts.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-alerts title: "@kbn/alerts" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/alerts plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 0f7755bfc8062..13aa2734bd079 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/analytics plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 332fdb73fd3a4..276c72a842dfc 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/analytics-client plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 529dfc5acacab..4b9a8759b2b1b 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index 9c3967a0fecb5..7e0390fc7d255 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index 667256e28f64d..8e9c1a191211b 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 54ae769be4ae3..17813d56492b2 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 14852dc700709..5c00309bd8842 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/apm-config-loader plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 1ff4abd0f55ce..5203af444072a 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/apm-utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index b202a9a9cfdef..c94abfadcbe05 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/axe-config plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_bazel_packages.devdocs.json b/api_docs/kbn_bazel_packages.devdocs.json index 993554aca7636..23cf46d140b55 100644 --- a/api_docs/kbn_bazel_packages.devdocs.json +++ b/api_docs/kbn_bazel_packages.devdocs.json @@ -273,7 +273,7 @@ "\nResolve all the BAZEL_PACKAGE_DIRS to absolute paths" ], "signature": [ - "() => EntryInternal[] & string[]" + "() => string[]" ], "path": "packages/kbn-bazel-packages/src/bazel_package_dirs.ts", "deprecated": false, diff --git a/api_docs/kbn_bazel_packages.mdx b/api_docs/kbn_bazel_packages.mdx index d19ab27d92791..99c31ede3a39d 100644 --- a/api_docs/kbn_bazel_packages.mdx +++ b/api_docs/kbn_bazel_packages.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-bazel-packages title: "@kbn/bazel-packages" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/bazel-packages plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bazel-packages'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_bazel_runner.mdx b/api_docs/kbn_bazel_runner.mdx index b1fd1876c09ba..6d99f6c5f5299 100644 --- a/api_docs/kbn_bazel_runner.mdx +++ b/api_docs/kbn_bazel_runner.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-bazel-runner title: "@kbn/bazel-runner" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/bazel-runner plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bazel-runner'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index 2facf632dafac..dc14fb459eb97 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ci-stats-core plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index b2874a4da8d9f..38dc0a15f5175 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index 7439499dff91b..869643ad84802 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ci-stats-reporter plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index 37279e889ea88..15587ab667343 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/cli-dev-mode plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index d2dc78fec048b..471a883e4db00 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/coloring plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index d4a5758d97d36..e3f717c4d980a 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/config plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index f57c62bce7588..c8d686e8deda8 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/config-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index c34ce93917eca..a9387c456838b 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/config-schema plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index 54e272ba8f24a..2fc459ffbbe1a 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-analytics-browser plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index 238466b0bc836..c28cc9230262a 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index 4ee730630133b..c3dea4bb4fec1 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index 23f971a7fa394..f2f99d527dd31 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-analytics-server plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index d8449d6fb77ba..e6e247e4970ab 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-analytics-server-internal plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index 5f5424903ca16..92e3d24c1bedb 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 13a20f7bf2651..ad6288df80ee0 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-base-browser-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index 3d12c39483629..ba1e4abd15ae2 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-base-common plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index 949707ddb21cd..c7d4e0c80c19e 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-base-server-internal plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 14c0ed2fbdd9d..d2b99c2f95faf 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-base-server-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index 43c40203cab67..9c7741208effb 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-config-server-internal plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index 267a92638519d..1a969e948111d 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-doc-links-browser plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index 7d08334721f27..cd9441faafeae 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index d5899796f92a5..e960e0a78e180 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-doc-links-server plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index 914b5e628d8fe..6fcdb8e176c21 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index b7f620d289b32..51a204698e524 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-environment-server-internal plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index f92206181f315..d430edc47a08a 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-environment-server-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index 67180407420af..83698a759bb1b 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-execution-context-browser plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index 9a47c6234f5f5..ea919a39c9a6d 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index 7b070bb886e74..05abf8327f857 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index b3029382d72f3..959a2dbc027ba 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-execution-context-common plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index 85a04d37b8516..a88ed431d022f 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-execution-context-server plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index c90863c336653..22d3cdf2819ec 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index cbb0cf2b71184..368b325e127a5 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index 3f5f0b109b441..a7b2329ec2369 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index a6477204a3a97..bfea5fb0095d6 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index 291d5de25b786..742d0816a7548 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-http-browser plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index d6a148344a269..c6859f2ef93e6 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-http-browser-internal plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 0f45a7c9b362c..df680225c6251 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-http-browser-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index 7919a429d38d1..97ec4742c8089 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-http-common plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 7e649339c0607..80871a81a9d25 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-http-server plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index a91021ebc1dfc..fd5dc5578ec45 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-i18n-browser plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index fbce78c64125f..451a717d03e00 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_injected_metadata_browser.mdx b/api_docs/kbn_core_injected_metadata_browser.mdx index ccc520c13af17..e1277bff51595 100644 --- a/api_docs/kbn_core_injected_metadata_browser.mdx +++ b/api_docs/kbn_core_injected_metadata_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser title: "@kbn/core-injected-metadata-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-injected-metadata-browser plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 36fef0f5ed33e..315b6ccb8dd93 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 916fd50bd6dcc..1e2f604a6658a 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-logging-server plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 2e9b6efde4e40..6c2e940096f8a 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-logging-server-internal plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index 69f605b770090..41a4a645ccbb6 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-logging-server-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index 226cef67aac46..1b2f3c80de467 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-node-server plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index 55bdfa7ae888f..5885b29b2c8c8 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-node-server-internal plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index 0ab1f3154b7c6..9e667be321173 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-node-server-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index 8ae587b311f54..b109431d1dbad 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-preboot-server plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index 7cf1640f3a81e..ed3173fb57216 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index 2696e5095f1d9..09957ca3e6854 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-theme-browser plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_theme_browser_internal.mdx b/api_docs/kbn_core_theme_browser_internal.mdx index a74da07fc7a1b..c3a60618f7845 100644 --- a/api_docs/kbn_core_theme_browser_internal.mdx +++ b/api_docs/kbn_core_theme_browser_internal.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-internal title: "@kbn/core-theme-browser-internal" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-theme-browser-internal plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-internal'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index df4a00590b746..3a7174fc1b2e3 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index f4ccbc6836131..7c0e6e7648ffa 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/crypto plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 88264483fc9a8..6017c04753893 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/crypto-browser plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index e023e9079e2bd..87f44ac0da128 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/datemath plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 9d1c3ffc9df7b..0889027b5ed49 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/dev-cli-errors plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index 81266da8d2371..759f8039981f8 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/dev-cli-runner plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index 46ef619d15bad..2d5e8095c3ffd 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/dev-proc-runner plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index a303d0746a4b7..2a578260588bf 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/dev-utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json index c50316d8d36b3..75ecd04cd953b 100644 --- a/api_docs/kbn_doc_links.devdocs.json +++ b/api_docs/kbn_doc_links.devdocs.json @@ -510,7 +510,7 @@ "label": "securitySolution", "description": [], "signature": [ - "{ readonly trustedApps: string; readonly eventFilters: string; readonly blocklist: string; readonly policyResponseTroubleshooting: { full_disk_access: string; }; }" + "{ readonly trustedApps: string; readonly eventFilters: string; readonly blocklist: string; readonly policyResponseTroubleshooting: { full_disk_access: string; macos_system_ext: string; }; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index b14e9bc396e94..00ef15c27f6c6 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/doc-links plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index 795ed22797a5a..1745e95ece2ba 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/docs-utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index fe26cd2e11499..59a8a0413c64c 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/es-archiver plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index 48142c6196171..65d64b1d89a75 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/es-errors plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 2d8bae60b8638..9084761693e28 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/es-query plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 1cdf11398ec25..46381bd6e4ed9 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/eslint-plugin-imports plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index 11fe0ce3bfe4c..851a8217b16ca 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/field-types plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index f575ed9e96acf..c1e107406b828 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/find-used-node-modules plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index 0774a78983edf..b77f7771f716b 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/generate plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 1a82a8e4b4e05..c9dfd16d549fe 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/handlebars plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index 9883c92e94510..74193484aaa01 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/hapi-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_home_sample_data_cards.mdx b/api_docs/kbn_home_sample_data_cards.mdx index 03317518065c2..ff0d667da085f 100644 --- a/api_docs/kbn_home_sample_data_cards.mdx +++ b/api_docs/kbn_home_sample_data_cards.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-cards title: "@kbn/home-sample-data-cards" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/home-sample-data-cards plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-cards'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index c9e59b4d88f57..c8164c7778e15 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/i18n plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index 8bf9d49d6575e..95f220bd9fb5b 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/import-resolver plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 96b22326b2c91..ea07bac765030 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/interpreter plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 39cdff5e68ec7..2b5ce56a5d60f 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/io-ts-utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index 893c265de79fb..a994586c8a74e 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/jest-serializers plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_kibana_json_schema.mdx b/api_docs/kbn_kibana_json_schema.mdx index 79d914b9c4185..e351775e76cee 100644 --- a/api_docs/kbn_kibana_json_schema.mdx +++ b/api_docs/kbn_kibana_json_schema.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-json-schema title: "@kbn/kibana-json-schema" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/kibana-json-schema plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-json-schema'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index a6d3a89102cab..d2b10fc180773 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/logging plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index 1eb5f32f3d4e6..e6ebec63e1632 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/logging-mocks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index cb2f5e70cdbd0..28115f56adc24 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/mapbox-gl plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index 571327db19dae..d9073ce0d303d 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ml-agg-utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index cb6bb04de04ac..a9e31ddb35448 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ml-is-populated-object plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index cbf44d612dba3..f9f2be975e065 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ml-string-hash plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 00c57bd021c12..c51c233e79489 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/monaco plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 362dc2c3a5978..26d65a3ead693 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/optimizer plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index af2b4599c64e9..401301b348c9e 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 0bfc7e8a1d076..f57e25cf322cb 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_plugin_discovery.mdx b/api_docs/kbn_plugin_discovery.mdx index cb9dc8fd3de57..55e9a6b95abc0 100644 --- a/api_docs/kbn_plugin_discovery.mdx +++ b/api_docs/kbn_plugin_discovery.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-discovery title: "@kbn/plugin-discovery" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/plugin-discovery plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-discovery'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index e342972b344dc..e4578b1f4a432 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/plugin-generator plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index 2d2c6844989a3..4374d72505a63 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/plugin-helpers plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_pm.mdx b/api_docs/kbn_pm.mdx index 87b48361f3fda..da5a74d30c957 100644 --- a/api_docs/kbn_pm.mdx +++ b/api_docs/kbn_pm.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-pm title: "@kbn/pm" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/pm plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/pm'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 34861771b09ba..ca8e694697cd0 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/react-field plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 801fc84fe142a..5482e0bedb1a2 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/rule-data-utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_scalability_simulation_generator.mdx b/api_docs/kbn_scalability_simulation_generator.mdx index c9819fefe3b25..e79d76f12349e 100644 --- a/api_docs/kbn_scalability_simulation_generator.mdx +++ b/api_docs/kbn_scalability_simulation_generator.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-scalability-simulation-generator title: "@kbn/scalability-simulation-generator" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/scalability-simulation-generator plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/scalability-simulation-generator'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index d1729ee7ab846..e8ccd05d48c9a 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index 6f3ce2b2f2a98..6c944bcd5e19e 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-es-utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 39bad06541b21..6728b6ba14dee 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 5f574350fb617..347d01b914dbc 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index 324b498fc24af..833b86fa341b0 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index 83c2de04f5b8b..762606a0b72b1 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index ebd3da11ca2ef..71a19be9283bf 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index 5c6f7b44f0bec..b85e37444435f 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-list-api plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index e31530908c658..fe27d37be1ed7 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-list-constants plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index cb690f98e6b4e..36797c388c072 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index d923ad7f51457..192b8ec9d7554 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-list-utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index c3de139828b82..26020f321d906 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-rules plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index 013e6d2f23c68..39c4712d09079 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-t-grid plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 6a69e5117bdc5..bb30474236d0c 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/securitysolution-utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index a116923d595c8..2ae808a2f4544 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/server-http-tools plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index d3d8592d3a56c..a8d94e51fd268 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/server-route-repository plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index b970f9d128768..4fc6f96dc869a 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 940fe818df34d..3120a76d22182 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_components.devdocs.json b/api_docs/kbn_shared_ux_components.devdocs.json index 36b5a90b05801..52bb9bcd5d8b0 100644 --- a/api_docs/kbn_shared_ux_components.devdocs.json +++ b/api_docs/kbn_shared_ux_components.devdocs.json @@ -66,43 +66,6 @@ "returnComment": [], "initialIsOpen": false }, - { - "parentPluginId": "@kbn/shared-ux-components", - "id": "def-common.KibanaPageTemplateSolutionNav", - "type": "Function", - "tags": [], - "label": "KibanaPageTemplateSolutionNav", - "description": [ - "\nA wrapper around EuiSideNav but also creates the appropriate title with optional solution logo" - ], - "signature": [ - "({ children, headingProps, icon, isOpenOnDesktop, items, mobileBreakpoints, closeFlyoutButtonPosition, name, onCollapse, canBeCollapsed, ...rest }: React.PropsWithChildren<", - "KibanaPageTemplateSolutionNavProps", - ">) => JSX.Element" - ], - "path": "packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.tsx", - "deprecated": false, - "children": [ - { - "parentPluginId": "@kbn/shared-ux-components", - "id": "def-common.KibanaPageTemplateSolutionNav.$1", - "type": "CompoundType", - "tags": [], - "label": "{\n children,\n headingProps,\n icon,\n isOpenOnDesktop = false,\n items,\n mobileBreakpoints = ['xs', 's'],\n closeFlyoutButtonPosition = 'outside',\n name,\n onCollapse,\n canBeCollapsed = true,\n ...rest\n}", - "description": [], - "signature": [ - "React.PropsWithChildren<", - "KibanaPageTemplateSolutionNavProps", - ">" - ], - "path": "packages/kbn-shared-ux-components/src/page_template/solution_nav/solution_nav.tsx", - "deprecated": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/shared-ux-components", "id": "def-common.NoDataConfigPage", @@ -273,7 +236,7 @@ " | undefined; fullHeight?: boolean | \"noscroll\" | undefined; minHeight?: ", "Property", ".MinHeight | undefined; } & { isEmptyState?: boolean | undefined; solutionNav?: ", - "KibanaPageTemplateSolutionNavProps", + "SolutionNavProps", " | undefined; noDataConfig?: ", { "pluginId": "@kbn/shared-ux-components", diff --git a/api_docs/kbn_shared_ux_components.mdx b/api_docs/kbn_shared_ux_components.mdx index f460e297eb49f..55b064588053a 100644 --- a/api_docs/kbn_shared_ux_components.mdx +++ b/api_docs/kbn_shared_ux_components.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-components title: "@kbn/shared-ux-components" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-components plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-components'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Owner missing] for questions regarding this plugin. | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 13 | 0 | 7 | 1 | +| 11 | 0 | 6 | 0 | ## Common diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index d6fdca0a23148..b2c92e8913396 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index 3328f0bcac453..d1c4b518a2b54 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_page_solution_nav.devdocs.json b/api_docs/kbn_shared_ux_page_solution_nav.devdocs.json new file mode 100644 index 0000000000000..7331e1f6ac1e0 --- /dev/null +++ b/api_docs/kbn_shared_ux_page_solution_nav.devdocs.json @@ -0,0 +1,131 @@ +{ + "id": "@kbn/shared-ux-page-solution-nav", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/shared-ux-page-solution-nav", + "id": "def-common.SolutionNav", + "type": "Function", + "tags": [], + "label": "SolutionNav", + "description": [ + "\nA wrapper around `EuiSideNav` that includes the appropriate title with optional solution logo." + ], + "signature": [ + "({ children, headingProps, icon, isOpenOnDesktop, items, mobileBreakpoints, closeFlyoutButtonPosition, name, onCollapse, canBeCollapsed, ...rest }: React.PropsWithChildren<", + { + "pluginId": "@kbn/shared-ux-page-solution-nav", + "scope": "common", + "docId": "kibKbnSharedUxPageSolutionNavPluginApi", + "section": "def-common.SolutionNavProps", + "text": "SolutionNavProps" + }, + ">) => JSX.Element" + ], + "path": "packages/shared-ux/page/solution_nav/src/solution_nav.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-page-solution-nav", + "id": "def-common.SolutionNav.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n children,\n headingProps,\n icon,\n isOpenOnDesktop = false,\n items,\n mobileBreakpoints = ['xs', 's'],\n closeFlyoutButtonPosition = 'outside',\n name,\n onCollapse,\n canBeCollapsed = true,\n ...rest\n}", + "description": [], + "signature": [ + "React.PropsWithChildren<", + { + "pluginId": "@kbn/shared-ux-page-solution-nav", + "scope": "common", + "docId": "kibKbnSharedUxPageSolutionNavPluginApi", + "section": "def-common.SolutionNavProps", + "text": "SolutionNavProps" + }, + ">" + ], + "path": "packages/shared-ux/page/solution_nav/src/solution_nav.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/shared-ux-page-solution-nav", + "id": "def-common.withSolutionNav", + "type": "Function", + "tags": [], + "label": "withSolutionNav", + "description": [], + "signature": [ + "

    (WrappedComponent: React.ComponentType

    ) => { (props: Props

    ): JSX.Element; displayName: string; }" + ], + "path": "packages/shared-ux/page/solution_nav/src/with_solution_nav.tsx", + "deprecated": false, + "children": [ + { + "parentPluginId": "@kbn/shared-ux-page-solution-nav", + "id": "def-common.withSolutionNav.$1", + "type": "CompoundType", + "tags": [], + "label": "WrappedComponent", + "description": [], + "signature": [ + "React.ComponentType

    " + ], + "path": "packages/shared-ux/page/solution_nav/src/with_solution_nav.tsx", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/shared-ux-page-solution-nav", + "id": "def-common.SolutionNavProps", + "type": "Type", + "tags": [], + "label": "SolutionNavProps", + "description": [ + "\nProps for the `SolutionNav` component." + ], + "signature": [ + "Omit<", + "EuiSideNavProps", + "<{}>, \"children\" | \"heading\" | \"items\"> & { name: string; icon?: ", + "IconType", + " | undefined; items?: ", + "EuiSideNavItemType", + "<{}>[] | undefined; children?: React.ReactNode; closeFlyoutButtonPosition?: \"outside\" | \"inside\" | undefined; isOpenOnDesktop?: boolean | undefined; onCollapse?: (() => void) | undefined; canBeCollapsed?: boolean | undefined; }" + ], + "path": "packages/shared-ux/page/solution_nav/src/solution_nav.tsx", + "deprecated": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx new file mode 100644 index 0000000000000..ec5eb2ca715e7 --- /dev/null +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -0,0 +1,30 @@ +--- +id: kibKbnSharedUxPageSolutionNavPluginApi +slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav +title: "@kbn/shared-ux-page-solution-nav" +image: https://source.unsplash.com/400x175/?github +summary: API docs for the @kbn/shared-ux-page-solution-nav plugin +date: 2022-07-13 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- +import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; + + + +Contact [Owner missing] for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 5 | 0 | 3 | 0 | + +## Common + +### Functions + + +### Consts, variables and types + + diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index 10146ed75c83d..3cd347e515853 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_services.mdx b/api_docs/kbn_shared_ux_services.mdx index 65e7140afe28c..0d2aefce71fdc 100644 --- a/api_docs/kbn_shared_ux_services.mdx +++ b/api_docs/kbn_shared_ux_services.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-services title: "@kbn/shared-ux-services" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-services plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-services'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_storybook.mdx b/api_docs/kbn_shared_ux_storybook.mdx index 3f1d95b263164..42b4f931b1d85 100644 --- a/api_docs/kbn_shared_ux_storybook.mdx +++ b/api_docs/kbn_shared_ux_storybook.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook title: "@kbn/shared-ux-storybook" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-storybook plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index d5c7f5e56b8e8..711fb0e0cd990 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/shared-ux-utility plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_sort_package_json.mdx b/api_docs/kbn_sort_package_json.mdx index 8830a177ce2b6..5ae42cf4f2d29 100644 --- a/api_docs/kbn_sort_package_json.mdx +++ b/api_docs/kbn_sort_package_json.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-sort-package-json title: "@kbn/sort-package-json" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/sort-package-json plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/sort-package-json'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index b472b5fbd8f4a..00e47f058bb0e 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/std plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index 35ae6b8cef450..a3912ad4534f4 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/stdio-dev-helpers plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index 1b12c02aec7e8..6bd0a35d7781a 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/storybook plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index b235a059a2d8b..01f7ecfa62fa7 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/telemetry-tools plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 651f4786f0156..e02a0e7631088 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/test plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 20835dd2e1608..e5a7fbdfff162 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/test-jest-helpers plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index 349f171f5b3be..f5328706fcf4b 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/tooling-log plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_type_summarizer.mdx b/api_docs/kbn_type_summarizer.mdx index 3055d6d304109..f243d4dc40658 100644 --- a/api_docs/kbn_type_summarizer.mdx +++ b/api_docs/kbn_type_summarizer.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer title: "@kbn/type-summarizer" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/type-summarizer plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_type_summarizer_core.mdx b/api_docs/kbn_type_summarizer_core.mdx index 1aebf107aa27b..82bdca5e9d8b2 100644 --- a/api_docs/kbn_type_summarizer_core.mdx +++ b/api_docs/kbn_type_summarizer_core.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-type-summarizer-core title: "@kbn/type-summarizer-core" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/type-summarizer-core plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/type-summarizer-core'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index 52e8ddec81689..3bb61421b625d 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/typed-react-router-config plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 062a5b9078aca..1b007f581dd8f 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/ui-theme plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index f53d7e1be0ea1..6464c4216069e 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/utility-types plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index 4d2355b63a94b..f1ca30178f685 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/utility-types-jest plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index 6eafcf2fc3a70..66520bf1db066 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github summary: API docs for the @kbn/utils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index 91aac1a61eb61..ca0fd397bc94c 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github summary: API docs for the kibanaOverview plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 687eae60179b7..3c4b6ed0309fc 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github summary: API docs for the kibanaReact plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index 92603a4ebea38..e57d0e2d3b29e 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github summary: API docs for the kibanaUtils plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index 60c14c122031d..736bf513869f4 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github summary: API docs for the kubernetesSecurity plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index 08678f653942c..990dbbafbda78 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -159,6 +159,21 @@ ], "returnComment": [] }, + { + "parentPluginId": "lens", + "id": "def-public.Embeddable.reportsEmbeddableLoad", + "type": "Function", + "tags": [], + "label": "reportsEmbeddableLoad", + "description": [], + "signature": [ + "() => boolean" + ], + "path": "x-pack/plugins/lens/public/embeddable/embeddable.tsx", + "deprecated": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "lens", "id": "def-public.Embeddable.supportedTriggers", @@ -1005,13 +1020,13 @@ }, { "parentPluginId": "lens", - "id": "def-public.DataLayerArgs.splitAccessor", - "type": "CompoundType", + "id": "def-public.DataLayerArgs.splitAccessors", + "type": "Array", "tags": [], - "label": "splitAccessor", + "label": "splitAccessors", "description": [], "signature": [ - "string | ", + "(string | ", { "pluginId": "visualizations", "scope": "common", @@ -1019,7 +1034,7 @@ "section": "def-common.ExpressionValueVisDimension", "text": "ExpressionValueVisDimension" }, - " | undefined" + ")[] | undefined" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", "deprecated": false diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index e0450f1abf271..1c49503e62394 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github summary: API docs for the lens plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 600 | 0 | 522 | 40 | +| 601 | 0 | 523 | 40 | ## Client diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 327ab57345a40..331b37523bb0b 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github summary: API docs for the licenseApiGuard plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index 66839078a7b6a..7d70dd7118c5b 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the licenseManagement plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index 82ff85966ce25..dce399078a361 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github summary: API docs for the licensing plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index bf69a3a893d20..c5bf01ce7e4e4 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github summary: API docs for the lists plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/management.mdx b/api_docs/management.mdx index 16ecc45408de0..d0fe3a0d4d7c6 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github summary: API docs for the management plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/maps.devdocs.json b/api_docs/maps.devdocs.json index 8e94db12e16f0..f25dab745052a 100644 --- a/api_docs/maps.devdocs.json +++ b/api_docs/maps.devdocs.json @@ -314,6 +314,21 @@ ], "returnComment": [] }, + { + "parentPluginId": "maps", + "id": "def-public.MapEmbeddable.reportsEmbeddableLoad", + "type": "Function", + "tags": [], + "label": "reportsEmbeddableLoad", + "description": [], + "signature": [ + "() => boolean" + ], + "path": "x-pack/plugins/maps/public/embeddable/map_embeddable.tsx", + "deprecated": false, + "children": [], + "returnComment": [] + }, { "parentPluginId": "maps", "id": "def-public.MapEmbeddable.inputIsRefType", diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 72f42307dbf9c..c3af9f2b2b8ce 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github summary: API docs for the maps plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [GIS](https://github.com/orgs/elastic/teams/kibana-gis) for questions re | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 248 | 0 | 247 | 24 | +| 249 | 0 | 248 | 24 | ## Client diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index 8ad30bc5269f3..8d4fa80184619 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github summary: API docs for the mapsEms plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 301159f8b1fde..7cf5e6f08934f 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github summary: API docs for the ml plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index c45fee1dc8e79..9a96f5fb71459 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github summary: API docs for the monitoring plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index 743a4c1367da1..dd904b036cb13 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github summary: API docs for the monitoringCollection plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index d24a5dac3371e..c34729aae4333 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github summary: API docs for the navigation plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index 04cda4bfcf52a..9fd49bf331ce3 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github summary: API docs for the newsfeed plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index cf452389bd2e9..b7ed67de04c43 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -2069,6 +2069,20 @@ "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx", "deprecated": false }, + { + "parentPluginId": "observability", + "id": "def-public.ExploratoryEmbeddableProps.legendPosition", + "type": "CompoundType", + "tags": [], + "label": "legendPosition", + "description": [], + "signature": [ + "Position", + " | undefined" + ], + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx", + "deprecated": false + }, { "parentPluginId": "observability", "id": "def-public.ExploratoryEmbeddableProps.onBrushEnd", @@ -2204,6 +2218,19 @@ ], "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx", "deprecated": false + }, + { + "parentPluginId": "observability", + "id": "def-public.ExploratoryEmbeddableProps.align", + "type": "CompoundType", + "tags": [], + "label": "align", + "description": [], + "signature": [ + "\"left\" | \"right\" | \"center\" | undefined" + ], + "path": "x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx", + "deprecated": false } ], "initialIsOpen": false diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 25ab9bd4e98c2..71cfee396b9d1 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github summary: API docs for the observability plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Observability UI](https://github.com/orgs/elastic/teams/observability-u | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 384 | 2 | 381 | 31 | +| 386 | 2 | 383 | 31 | ## Client diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index d6f51adf2a555..52aac69572b83 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github summary: API docs for the osquery plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index aab05ea9dbb1c..80e714e48ffa2 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -3,7 +3,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory summary: Directory of public APIs available through plugins or packages. -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -12,13 +12,13 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | Count | Plugins or Packages with a
    public API | Number of teams | |--------------|----------|------------------------| -| 334 | 275 | 36 | +| 335 | 276 | 36 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 27391 | 172 | 19498 | 1433 | +| 27414 | 172 | 19519 | 1433 | ## Plugin Directory @@ -52,10 +52,10 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 10 | 0 | 8 | 2 | | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 79 | 0 | 63 | 7 | | | [Data Discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 37 | 0 | 35 | 2 | -| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds embeddables service to Kibana | 494 | 0 | 403 | 3 | +| | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Adds embeddables service to Kibana | 505 | 0 | 413 | 3 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Extends embeddable plugin with more functionality | 14 | 0 | 14 | 0 | | | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides encryption and decryption utilities for saved objects containing sensitive information. | 51 | 0 | 44 | 0 | -| | [Enterprise Search](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | Adds dashboards for discovering and managing Enterprise Search products. | 3 | 0 | 3 | 0 | +| | [Enterprise Search](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | Adds dashboards for discovering and managing Enterprise Search products. | 5 | 0 | 5 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 114 | 3 | 110 | 3 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | The Event Annotation service contains expressions for event annotations | 90 | 0 | 90 | 5 | | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 100 | 0 | 100 | 9 | @@ -76,7 +76,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 222 | 0 | 95 | 2 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | Index pattern fields and ambiguous values formatters | 288 | 5 | 249 | 3 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 62 | 0 | 62 | 2 | -| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1521 | 8 | 1389 | 10 | +| | [Fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1522 | 8 | 1390 | 10 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 68 | 0 | 14 | 5 | | globalSearchBar | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | globalSearchProviders | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | @@ -95,21 +95,21 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | kibanaUsageCollection | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 0 | 0 | 0 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 615 | 3 | 420 | 9 | | | [Security Team](https://github.com/orgs/elastic/teams/security-team) | - | 3 | 0 | 3 | 1 | -| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 600 | 0 | 522 | 40 | +| | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 601 | 0 | 523 | 40 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 8 | 0 | 8 | 0 | | | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 3 | 0 | 3 | 0 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | | | [Security detections response](https://github.com/orgs/elastic/teams/security-detections-response) | - | 198 | 0 | 90 | 49 | | logstash | [Logstash](https://github.com/orgs/elastic/teams/logstash) | - | 0 | 0 | 0 | 0 | | | [Vis Editors](https://github.com/orgs/elastic/teams/kibana-vis-editors) | - | 41 | 0 | 41 | 6 | -| | [GIS](https://github.com/orgs/elastic/teams/kibana-gis) | - | 248 | 0 | 247 | 24 | +| | [GIS](https://github.com/orgs/elastic/teams/kibana-gis) | - | 249 | 0 | 248 | 24 | | | [GIS](https://github.com/orgs/elastic/teams/kibana-gis) | - | 67 | 0 | 67 | 0 | | | [Machine Learning UI](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the machine learning features provided by Elastic. | 251 | 10 | 78 | 31 | | | [Stack Monitoring](https://github.com/orgs/elastic/teams/stack-monitoring-ui) | - | 11 | 0 | 9 | 1 | | | [Stack Monitoring](https://github.com/orgs/elastic/teams/stack-monitoring-ui) | - | 9 | 0 | 9 | 0 | | | [App Services](https://github.com/orgs/elastic/teams/kibana-app-services) | - | 34 | 0 | 34 | 2 | | | [Kibana Core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | -| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 384 | 2 | 381 | 31 | +| | [Observability UI](https://github.com/orgs/elastic/teams/observability-ui) | - | 386 | 2 | 383 | 31 | | | [Security asset management](https://github.com/orgs/elastic/teams/security-asset-management) | - | 13 | 0 | 13 | 0 | | painlessLab | [Stack Management](https://github.com/orgs/elastic/teams/kibana-stack-management) | - | 0 | 0 | 0 | 0 | | | [Kibana Presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | The Presentation Utility Plugin is a set of common, shared components and toolkits for solutions within the Presentation space, (e.g. Dashboards, Canvas). | 231 | 2 | 180 | 11 | @@ -134,7 +134,7 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) | This plugin provides the Spaces feature, which allows saved objects to be organized into meaningful categories. | 260 | 0 | 64 | 0 | | | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 4 | 0 | 4 | 0 | | synthetics | [Uptime](https://github.com/orgs/elastic/teams/uptime) | This plugin visualizes data from Synthetics and Heartbeat, and integrates with other Observability solutions. | 0 | 0 | 0 | 0 | -| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 80 | 0 | 39 | 7 | +| | [Response Ops](https://github.com/orgs/elastic/teams/response-ops) | - | 82 | 0 | 41 | 7 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 43 | 0 | 1 | 0 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 32 | 0 | 32 | 6 | | | [Kibana Telemetry](https://github.com/orgs/elastic/teams/kibana-telemetry) | - | 1 | 0 | 1 | 0 | @@ -301,9 +301,10 @@ warning: This document is auto-generated and is meant to be viewed inside our ex | | [Owner missing] | - | 25 | 0 | 24 | 1 | | | [Owner missing] | - | 20 | 0 | 6 | 0 | | | [Owner missing] | - | 15 | 0 | 8 | 1 | -| | [Owner missing] | - | 13 | 0 | 7 | 1 | +| | [Owner missing] | - | 11 | 0 | 6 | 0 | | | [Owner missing] | - | 12 | 0 | 8 | 5 | | | [Owner missing] | - | 31 | 0 | 12 | 2 | +| | [Owner missing] | - | 5 | 0 | 3 | 0 | | | [Owner missing] | - | 22 | 0 | 13 | 2 | | | [Owner missing] | - | 80 | 0 | 51 | 1 | | | [Owner missing] | - | 16 | 0 | 7 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index fc208c81091bc..c7b08aab4bd73 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github summary: API docs for the presentationUtil plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index 2d1fb67d26de5..59c7c219a1602 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github summary: API docs for the remoteClusters plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index 80cf32fd0ed90..8d73fbaa1f36a 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github summary: API docs for the reporting plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index e9d9288cb3e7f..deb418b833fe3 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github summary: API docs for the rollup plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 307825975a7ca..db25b33fb89f9 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github summary: API docs for the ruleRegistry plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index 51f7aa3f08761..e0a004928b4b2 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github summary: API docs for the runtimeFields plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 3f81ae82637ff..2792e2107d074 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github summary: API docs for the savedObjects plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index b5e91aa4161e8..4d25d3bf28f50 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github summary: API docs for the savedObjectsManagement plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index bdbd11196165d..5b61e650b0976 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github summary: API docs for the savedObjectsTagging plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index a33056823fe45..8f7e57b1e2718 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github summary: API docs for the savedObjectsTaggingOss plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 4ee75e67b0da4..7b042ea1f93c6 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github summary: API docs for the screenshotMode plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index b6d32d285b9b9..51a7e1a61919f 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github summary: API docs for the screenshotting plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/security.mdx b/api_docs/security.mdx index 8b42b557c4116..434d136388f5e 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github summary: API docs for the security plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index ff130491b6ec5..d1d77eae3d5fc 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github summary: API docs for the securitySolution plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index 6b8833becf436..da2c2cf09a878 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github summary: API docs for the sessionView plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 3721470c5f914..b6a02d17aecc4 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github summary: API docs for the share plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/shared_u_x.mdx b/api_docs/shared_u_x.mdx index d396395dcef19..de326e69e0fe2 100644 --- a/api_docs/shared_u_x.mdx +++ b/api_docs/shared_u_x.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/sharedUX title: "sharedUX" image: https://source.unsplash.com/400x175/?github summary: API docs for the sharedUX plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sharedUX'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 66174b12e48c4..2d533df972c65 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github summary: API docs for the snapshotRestore plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index aa56e7d9431f0..2082447ea3258 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github summary: API docs for the spaces plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index baea07d62e2cc..e5cce413d231b 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github summary: API docs for the stackAlerts plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/task_manager.devdocs.json b/api_docs/task_manager.devdocs.json index 9b015ce7264be..49c9fd818c3dd 100644 --- a/api_docs/task_manager.devdocs.json +++ b/api_docs/task_manager.devdocs.json @@ -239,6 +239,43 @@ } ], "functions": [ + { + "parentPluginId": "taskManager", + "id": "def-server.aggregateTaskOverduePercentilesForType", + "type": "Function", + "tags": [], + "label": "aggregateTaskOverduePercentilesForType", + "description": [], + "signature": [ + "(type: string) => { aggs: Record; query: ", + "QueryDslQueryContainer", + "; runtime_mappings: ", + "MappingRuntimeFields", + "; }" + ], + "path": "x-pack/plugins/task_manager/server/queries/aggregate_task_overdue_percentiles_for_type.ts", + "deprecated": false, + "children": [ + { + "parentPluginId": "taskManager", + "id": "def-server.aggregateTaskOverduePercentilesForType.$1", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + "string" + ], + "path": "x-pack/plugins/task_manager/server/queries/aggregate_task_overdue_percentiles_for_type.ts", + "deprecated": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "taskManager", "id": "def-server.asInterval", @@ -1400,7 +1437,7 @@ "TaskScheduling", ", \"schedule\" | \"runSoon\" | \"ephemeralRunNow\" | \"ensureScheduled\" | \"bulkUpdateSchedules\"> & Pick<", "TaskStore", - ", \"fetch\" | \"get\" | \"remove\"> & { removeIfExists: (id: string) => Promise; } & { supportsEphemeralTasks: () => boolean; }" + ", \"aggregate\" | \"fetch\" | \"get\" | \"remove\"> & { removeIfExists: (id: string) => Promise; } & { supportsEphemeralTasks: () => boolean; }" ], "path": "x-pack/plugins/task_manager/server/plugin.ts", "deprecated": false, diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 8e15af839a4b5..2ca6c69bacca9 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github summary: API docs for the taskManager plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- @@ -18,7 +18,7 @@ Contact [Response Ops](https://github.com/orgs/elastic/teams/response-ops) for q | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 80 | 0 | 39 | 7 | +| 82 | 0 | 41 | 7 | ## Server diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index 8d4627b0a0e67..b0a927943f987 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github summary: API docs for the telemetry plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index e3df7e229abfe..5ed1647e3fd4e 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github summary: API docs for the telemetryCollectionManager plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index 3e1f0c17b8241..421ef5b38c9a8 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github summary: API docs for the telemetryCollectionXpack plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 863ce108c48e1..3d33d343a8620 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github summary: API docs for the telemetryManagementSection plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/timelines.devdocs.json b/api_docs/timelines.devdocs.json index 38c0c9653f999..1ef3a094339f2 100644 --- a/api_docs/timelines.devdocs.json +++ b/api_docs/timelines.devdocs.json @@ -6968,9 +6968,7 @@ }, "; filterQuery: string | ", "ESQuery", - " | undefined; docValueFields?: ", - "QueryDslFieldAndFormat", - "[] | undefined; factoryQueryType?: ", + " | undefined; factoryQueryType?: ", "TimelineEventsQueries", " | undefined; entityType?: ", { diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index 319ee00cff216..c384f9da142fc 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github summary: API docs for the timelines plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index a6c2fb6f90011..a8e0315881b2d 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github summary: API docs for the transform plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index 0c679bdb8020c..c5522864a1c75 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github summary: API docs for the triggersActionsUi plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 0099e4cf7eaaf..49db9908f5e09 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github summary: API docs for the uiActions plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 3427b9f4a8ef3..176c8a902aa9c 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github summary: API docs for the uiActionsEnhanced plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 4db559f27d120..31a4820f9da14 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github summary: API docs for the unifiedSearch plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index b5df7764e5fc3..40f91935ccaeb 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github summary: API docs for the unifiedSearch.autocomplete plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 5bc965e0f2e1b..257045a1a7cd8 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github summary: API docs for the urlForwarding plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index 407ae7fe20787..68a4cf6640fa6 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github summary: API docs for the usageCollection plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index d25bfb3aa53ab..c59920507b2c0 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github summary: API docs for the ux plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index a18709b0a133a..643412cbea7b5 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github summary: API docs for the visDefaultEditor plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index baaef94a15d1d..73b3fd805469b 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeGauge plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index 74f232b1fb3f6..8fced74534107 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeHeatmap plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index c409286bfda39..6f2df2634dae4 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypePie plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 0931024d8cd1e..399fdf3d04609 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeTable plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index 6ed4ddce7daac..35ceba01e9d4c 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeTimelion plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index 766372be1f7ac..0e79bc87dad38 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeTimeseries plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index 38f97af77566f..9c7e5004768fd 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeVega plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 050a29ac598ff..63b6b57590f6d 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeVislib plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index de103e3f2b8d2..7ddcc5d5423f9 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github summary: API docs for the visTypeXy plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- diff --git a/api_docs/visualizations.devdocs.json b/api_docs/visualizations.devdocs.json index e6d13ebfd5089..07a10571b64cb 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -5163,7 +5163,7 @@ "section": "def-public.VisualizeInput", "text": "VisualizeInput" }, - ">) => void; getInspectorAdapters: () => ", + ">) => void; reportsEmbeddableLoad: () => boolean; getInspectorAdapters: () => ", { "pluginId": "inspector", "scope": "common", @@ -5179,7 +5179,7 @@ "section": "def-public.OverlayRef", "text": "OverlayRef" }, - " | undefined; transferCustomizationsToUiState: () => void; hasInspector: () => boolean; onContainerLoading: () => void; onContainerRender: () => void; onContainerError: (error: ", + " | undefined; transferCustomizationsToUiState: () => void; hasInspector: () => boolean; onContainerLoading: () => void; onContainerData: () => void; onContainerRender: () => void; onContainerError: (error: ", { "pluginId": "expressions", "scope": "public", diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 427639052a2bd..13b8faa002999 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -4,7 +4,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github summary: API docs for the visualizations plugin -date: 2022-07-12 +date: 2022-07-13 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. --- From 6f9d771714d31a9bff2ae382e69bddd7bb2ef98e Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Wed, 13 Jul 2022 06:49:21 +0200 Subject: [PATCH 38/42] data view side panel explore button (#135224) --- .../public/components/data_view_editor.tsx | 2 + .../data_view_editor_flyout_content.tsx | 17 ++++-- .../data_view_flyout_content_container.tsx | 8 ++- .../public/components/footer/footer.tsx | 56 +++++++++++++++---- .../public/components/form_schema.ts | 7 +++ .../data_view_editor/public/open_editor.tsx | 2 + src/plugins/data_view_editor/public/types.ts | 5 ++ 7 files changed, 79 insertions(+), 18 deletions(-) diff --git a/src/plugins/data_view_editor/public/components/data_view_editor.tsx b/src/plugins/data_view_editor/public/components/data_view_editor.tsx index f984b8451b061..ff236e804c2b7 100644 --- a/src/plugins/data_view_editor/public/components/data_view_editor.tsx +++ b/src/plugins/data_view_editor/public/components/data_view_editor.tsx @@ -24,6 +24,7 @@ export const DataViewEditor = ({ defaultTypeIsRollup = false, requireTimestampField = false, editData, + allowAdHocDataView, }: DataViewEditorPropsWithServices) => { const { Provider: KibanaReactContextProvider } = createKibanaReactContext(services); @@ -37,6 +38,7 @@ export const DataViewEditor = ({ defaultTypeIsRollup={defaultTypeIsRollup} requireTimestampField={requireTimestampField} editData={editData} + allowAdHocDataView={allowAdHocDataView} /> diff --git a/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx b/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx index dc000de1e20fd..744e3824e3c68 100644 --- a/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx +++ b/src/plugins/data_view_editor/public/components/data_view_editor_flyout_content.tsx @@ -20,6 +20,7 @@ import { useFormData, useKibana, GetFieldsOptions, + UseField, } from '../shared_imports'; import { ensureMinimumTime, getIndices, extractTimeFields, getMatchedIndices } from '../lib'; @@ -55,7 +56,7 @@ export interface Props { /** * Handler for the "save" footer button */ - onSave: (dataViewSpec: DataViewSpec) => void; + onSave: (dataViewSpec: DataViewSpec, persist: boolean) => void; /** * Handler for the "cancel" footer button */ @@ -63,6 +64,7 @@ export interface Props { defaultTypeIsRollup?: boolean; requireTimestampField?: boolean; editData?: DataView; + allowAdHoc: boolean; } const editorTitle = i18n.translate('indexPatternEditor.title', { @@ -79,6 +81,7 @@ const IndexPatternEditorFlyoutContentComponent = ({ defaultTypeIsRollup, requireTimestampField = false, editData, + allowAdHoc, }: Props) => { const { services: { http, dataViews, uiSettings, overlays }, @@ -88,6 +91,7 @@ const IndexPatternEditorFlyoutContentComponent = ({ // Prefill with data if editData exists defaultValue: { type: defaultTypeIsRollup ? INDEX_PATTERN_TYPE.ROLLUP : INDEX_PATTERN_TYPE.DEFAULT, + isAdHoc: false, ...(editData ? { title: editData.title, @@ -129,11 +133,11 @@ const IndexPatternEditorFlyoutContentComponent = ({ dataViewName: formData.name || formData.title, overlays, onEdit: async () => { - await onSave(indexPatternStub); + await onSave(indexPatternStub, !formData.isAdHoc); }, }); } else { - await onSave(indexPatternStub); + await onSave(indexPatternStub, !formData.isAdHoc); } }, }); @@ -371,6 +375,7 @@ const IndexPatternEditorFlyoutContentComponent = ({

    {editData ? editorTitleEditMode : editorTitle}

    + {indexPatternTypeSelect} @@ -409,9 +414,13 @@ const IndexPatternEditorFlyoutContentComponent = ({