From 6f2e49d82b7ba91743e72abcd4afbf77f51cea81 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Thu, 24 Feb 2022 11:25:54 -0700 Subject: [PATCH 01/28] [maps] give data plugin control of filter migrations (#124729) * convert saved_object_migrations to TS * wire up filter migrations for saved objects * cleanup * implement filter migration * update embeddable migration Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../migrate_data_persisted_state.test.ts | 30 ++++++ .../migrate_data_persisted_state.ts | 35 ++++++ .../embeddable_migrations.test.ts | 3 +- .../{ => embeddable}/embeddable_migrations.ts | 10 +- .../plugins/maps/server/embeddable/index.ts | 8 ++ .../server/embeddable/setup_embeddable.ts | 33 ++++++ x-pack/plugins/maps/server/plugin.ts | 19 ++-- .../maps/server/saved_objects/index.ts | 3 +- .../plugins/maps/server/saved_objects/map.ts | 44 -------- .../server/saved_objects/maps_telemetry.ts | 25 ----- ...grations.js => saved_object_migrations.ts} | 73 ++++++++++--- .../saved_objects/setup_saved_objects.ts | 100 ++++++++++++++++++ x-pack/plugins/maps/server/types.ts | 6 +- 13 files changed, 286 insertions(+), 103 deletions(-) create mode 100644 x-pack/plugins/maps/common/migrations/migrate_data_persisted_state.test.ts create mode 100644 x-pack/plugins/maps/common/migrations/migrate_data_persisted_state.ts rename x-pack/plugins/maps/server/{ => embeddable}/embeddable_migrations.test.ts (90%) rename x-pack/plugins/maps/server/{ => embeddable}/embeddable_migrations.ts (84%) create mode 100644 x-pack/plugins/maps/server/embeddable/index.ts create mode 100644 x-pack/plugins/maps/server/embeddable/setup_embeddable.ts delete mode 100644 x-pack/plugins/maps/server/saved_objects/map.ts delete mode 100644 x-pack/plugins/maps/server/saved_objects/maps_telemetry.ts rename x-pack/plugins/maps/server/saved_objects/{saved_object_migrations.js => saved_object_migrations.ts} (71%) create mode 100644 x-pack/plugins/maps/server/saved_objects/setup_saved_objects.ts diff --git a/x-pack/plugins/maps/common/migrations/migrate_data_persisted_state.test.ts b/x-pack/plugins/maps/common/migrations/migrate_data_persisted_state.test.ts new file mode 100644 index 00000000000000..51d7a41f797eb5 --- /dev/null +++ b/x-pack/plugins/maps/common/migrations/migrate_data_persisted_state.test.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 { Filter } from '@kbn/es-query'; +import { migrateDataPersistedState } from './migrate_data_persisted_state'; + +const attributes = { + title: 'My map', + mapStateJSON: + '{"filters":[{"meta":{"index":"90943e30-9a47-11e8-b64d-95841ca0b247","params":{"lt":10000,"gte":2000},"field":"bytes","alias":null,"negate":false,"disabled":false,"type":"range","key":"bytes"},"query":{"range":{"bytes":{"lt":10000,"gte":2000}}},"$state":{"store":"appState"}}]}', +}; + +const filterMigrationMock = (filters: Filter[]): Filter[] => { + return filters.map((filter) => { + return { + ...filter, + alias: 'filter_has_been_migrated', + }; + }); +}; + +test('should apply data migrations to data peristed data', () => { + const { mapStateJSON } = migrateDataPersistedState({ attributes }, filterMigrationMock); + const mapState = JSON.parse(mapStateJSON!); + expect(mapState.filters[0].alias).toEqual('filter_has_been_migrated'); +}); diff --git a/x-pack/plugins/maps/common/migrations/migrate_data_persisted_state.ts b/x-pack/plugins/maps/common/migrations/migrate_data_persisted_state.ts new file mode 100644 index 00000000000000..7a933663a60f24 --- /dev/null +++ b/x-pack/plugins/maps/common/migrations/migrate_data_persisted_state.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 { Filter } from '@kbn/es-query'; +import { MapSavedObjectAttributes } from '../map_saved_object_type'; +import { MigrateFunction } from '../../../../../src/plugins/kibana_utils/common'; + +export function migrateDataPersistedState( + { + attributes, + }: { + attributes: MapSavedObjectAttributes; + }, + filterMigration: MigrateFunction +): MapSavedObjectAttributes { + let mapState: { filters: Filter[] } = { filters: [] }; + if (attributes.mapStateJSON) { + try { + mapState = JSON.parse(attributes.mapStateJSON); + } catch (e) { + throw new Error('Unable to parse attribute mapStateJSON'); + } + + mapState.filters = filterMigration(mapState.filters); + } + + return { + ...attributes, + mapStateJSON: JSON.stringify(mapState), + }; +} diff --git a/x-pack/plugins/maps/server/embeddable_migrations.test.ts b/x-pack/plugins/maps/server/embeddable/embeddable_migrations.test.ts similarity index 90% rename from x-pack/plugins/maps/server/embeddable_migrations.test.ts rename to x-pack/plugins/maps/server/embeddable/embeddable_migrations.test.ts index 4cf2642bb545c1..58a6716c517a9f 100644 --- a/x-pack/plugins/maps/server/embeddable_migrations.test.ts +++ b/x-pack/plugins/maps/server/embeddable/embeddable_migrations.test.ts @@ -7,8 +7,7 @@ import semverGte from 'semver/functions/gte'; import { embeddableMigrations } from './embeddable_migrations'; -// @ts-ignore -import { savedObjectMigrations } from './saved_objects/saved_object_migrations'; +import { savedObjectMigrations } from '../saved_objects/saved_object_migrations'; describe('saved object migrations and embeddable migrations', () => { test('should have same versions registered (>7.12)', () => { diff --git a/x-pack/plugins/maps/server/embeddable_migrations.ts b/x-pack/plugins/maps/server/embeddable/embeddable_migrations.ts similarity index 84% rename from x-pack/plugins/maps/server/embeddable_migrations.ts rename to x-pack/plugins/maps/server/embeddable/embeddable_migrations.ts index 9c17889e0c33c0..951877a31f8287 100644 --- a/x-pack/plugins/maps/server/embeddable_migrations.ts +++ b/x-pack/plugins/maps/server/embeddable/embeddable_migrations.ts @@ -6,11 +6,11 @@ */ import type { SerializableRecord } from '@kbn/utility-types'; -import { MapSavedObjectAttributes } from '../common/map_saved_object_type'; -import { moveAttribution } from '../common/migrations/move_attribution'; -import { setEmsTmsDefaultModes } from '../common/migrations/set_ems_tms_default_modes'; -import { renameLayerTypes } from '../common/migrations/rename_layer_types'; -import { extractReferences } from '../common/migrations/references'; +import { MapSavedObjectAttributes } from '../../common/map_saved_object_type'; +import { moveAttribution } from '../../common/migrations/move_attribution'; +import { setEmsTmsDefaultModes } from '../../common/migrations/set_ems_tms_default_modes'; +import { renameLayerTypes } from '../../common/migrations/rename_layer_types'; +import { extractReferences } from '../../common/migrations/references'; /* * Embeddables such as Maps, Lens, and Visualize can be embedded by value or by reference on a dashboard. diff --git a/x-pack/plugins/maps/server/embeddable/index.ts b/x-pack/plugins/maps/server/embeddable/index.ts new file mode 100644 index 00000000000000..5061fc03dcfc14 --- /dev/null +++ b/x-pack/plugins/maps/server/embeddable/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 { setupEmbeddable } from './setup_embeddable'; diff --git a/x-pack/plugins/maps/server/embeddable/setup_embeddable.ts b/x-pack/plugins/maps/server/embeddable/setup_embeddable.ts new file mode 100644 index 00000000000000..9411869c5ad11a --- /dev/null +++ b/x-pack/plugins/maps/server/embeddable/setup_embeddable.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 { EmbeddableSetup } from '../../../../../src/plugins/embeddable/server'; +import { + mergeMigrationFunctionMaps, + MigrateFunctionsObject, +} from '../../../../../src/plugins/kibana_utils/common'; +import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; +import { extract, inject } from '../../common/embeddable'; +import { embeddableMigrations } from './embeddable_migrations'; +import { getPersistedStateMigrations } from '../saved_objects'; + +export function setupEmbeddable( + embeddable: EmbeddableSetup, + getFilterMigrations: () => MigrateFunctionsObject +) { + embeddable.registerEmbeddableFactory({ + id: MAP_SAVED_OBJECT_TYPE, + migrations: () => { + return mergeMigrationFunctionMaps( + embeddableMigrations, + getPersistedStateMigrations(getFilterMigrations()) + ); + }, + inject, + extract, + }); +} diff --git a/x-pack/plugins/maps/server/plugin.ts b/x-pack/plugins/maps/server/plugin.ts index 92d0f08fb51abb..05051a861d5956 100644 --- a/x-pack/plugins/maps/server/plugin.ts +++ b/x-pack/plugins/maps/server/plugin.ts @@ -22,15 +22,14 @@ import { getFlightsSavedObjects } from './sample_data/flights_saved_objects.js'; import { getWebLogsSavedObjects } from './sample_data/web_logs_saved_objects.js'; import { registerMapsUsageCollector } from './maps_telemetry/collectors/register'; import { APP_ID, APP_ICON, MAP_SAVED_OBJECT_TYPE, getFullPath } from '../common/constants'; -import { extract, inject } from '../common/embeddable'; -import { mapSavedObjects, mapsTelemetrySavedObjects } from './saved_objects'; import { MapsXPackConfig } from '../config'; import { setStartServices } from './kibana_server_services'; import { emsBoundariesSpecProvider } from './tutorials/ems'; import { initRoutes } from './routes'; import { HomeServerPluginSetup } from '../../../../src/plugins/home/server'; import type { EMSSettings } from '../../../../src/plugins/maps_ems/server'; -import { embeddableMigrations } from './embeddable_migrations'; +import { setupEmbeddable } from './embeddable'; +import { setupSavedObjects } from './saved_objects'; import { registerIntegrations } from './register_integrations'; import { StartDeps, SetupDeps } from './types'; @@ -146,6 +145,10 @@ export class MapsPlugin implements Plugin { } setup(core: CoreSetup, plugins: SetupDeps) { + const getFilterMigrations = plugins.data.query.filterManager.getAllMigrations.bind( + plugins.data.query.filterManager + ); + const { usageCollection, home, features, customIntegrations } = plugins; const config$ = this._initializerContext.config.create(); @@ -192,16 +195,10 @@ export class MapsPlugin implements Plugin { }, }); - core.savedObjects.registerType(mapsTelemetrySavedObjects); - core.savedObjects.registerType(mapSavedObjects); + setupSavedObjects(core, getFilterMigrations); registerMapsUsageCollector(usageCollection); - plugins.embeddable.registerEmbeddableFactory({ - id: MAP_SAVED_OBJECT_TYPE, - migrations: embeddableMigrations, - inject, - extract, - }); + setupEmbeddable(plugins.embeddable, getFilterMigrations); return { config: config$, diff --git a/x-pack/plugins/maps/server/saved_objects/index.ts b/x-pack/plugins/maps/server/saved_objects/index.ts index f34e20becd4372..1b0b129a292993 100644 --- a/x-pack/plugins/maps/server/saved_objects/index.ts +++ b/x-pack/plugins/maps/server/saved_objects/index.ts @@ -5,5 +5,4 @@ * 2.0. */ -export { mapsTelemetrySavedObjects } from './maps_telemetry'; -export { mapSavedObjects } from './map'; +export { getPersistedStateMigrations, setupSavedObjects } from './setup_saved_objects'; diff --git a/x-pack/plugins/maps/server/saved_objects/map.ts b/x-pack/plugins/maps/server/saved_objects/map.ts deleted file mode 100644 index b13f24fc6ba1cb..00000000000000 --- a/x-pack/plugins/maps/server/saved_objects/map.ts +++ /dev/null @@ -1,44 +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 { SavedObjectsType } from 'src/core/server'; -import { APP_ICON, getFullPath } from '../../common/constants'; -// @ts-ignore -import { savedObjectMigrations } from './saved_object_migrations'; - -export const mapSavedObjects: SavedObjectsType = { - name: 'map', - hidden: false, - namespaceType: 'multiple-isolated', - convertToMultiNamespaceTypeVersion: '8.0.0', - mappings: { - properties: { - description: { type: 'text' }, - title: { type: 'text' }, - version: { type: 'integer' }, - mapStateJSON: { type: 'text' }, - layerListJSON: { type: 'text' }, - uiStateJSON: { type: 'text' }, - bounds: { dynamic: false, properties: {} }, // Disable removed field - }, - }, - management: { - icon: APP_ICON, - defaultSearchField: 'title', - importableAndExportable: true, - getTitle(obj) { - return obj.attributes.title; - }, - getInAppUrl(obj) { - return { - path: getFullPath(obj.id), - uiCapabilitiesPath: 'maps.show', - }; - }, - }, - migrations: savedObjectMigrations, -}; diff --git a/x-pack/plugins/maps/server/saved_objects/maps_telemetry.ts b/x-pack/plugins/maps/server/saved_objects/maps_telemetry.ts deleted file mode 100644 index 35366188f909de..00000000000000 --- a/x-pack/plugins/maps/server/saved_objects/maps_telemetry.ts +++ /dev/null @@ -1,25 +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 { SavedObjectsType } from 'src/core/server'; - -/* - * The maps-telemetry saved object type isn't used, but in order to remove these fields from - * the mappings we register this type with `type: 'object', enabled: true` to remove all - * previous fields from the mappings until https://github.com/elastic/kibana/issues/67086 is - * solved. - */ -export const mapsTelemetrySavedObjects: SavedObjectsType = { - name: 'maps-telemetry', - hidden: false, - namespaceType: 'agnostic', - mappings: { - // @ts-ignore Core types don't support this since it's only really valid when removing a previously registered type - type: 'object', - enabled: false, - }, -}; diff --git a/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js b/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.ts similarity index 71% rename from x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js rename to x-pack/plugins/maps/server/saved_objects/saved_object_migrations.ts index 986878e65eb8be..f8cd06cf60bfc3 100644 --- a/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.js +++ b/x-pack/plugins/maps/server/saved_objects/saved_object_migrations.ts @@ -5,11 +5,17 @@ * 2.0. */ +import type { SavedObjectMigrationContext, SavedObjectUnsanitizedDoc } from 'kibana/server'; import { extractReferences } from '../../common/migrations/references'; +// @ts-expect-error import { emsRasterTileToEmsVectorTile } from '../../common/migrations/ems_raster_tile_to_ems_vector_tile'; +// @ts-expect-error import { topHitsTimeToSort } from '../../common/migrations/top_hits_time_to_sort'; +// @ts-expect-error import { moveApplyGlobalQueryToSources } from '../../common/migrations/move_apply_global_query'; +// @ts-expect-error import { addFieldMetaOptions } from '../../common/migrations/add_field_meta_options'; +// @ts-expect-error import { migrateSymbolStyleDescriptor } from '../../common/migrations/migrate_symbol_style_descriptor'; import { migrateUseTopHitsToScalingType } from '../../common/migrations/scaling_type'; import { migrateJoinAggKey } from '../../common/migrations/join_agg_key'; @@ -19,8 +25,13 @@ import { addTypeToTermJoin } from '../../common/migrations/add_type_to_termjoin' import { moveAttribution } from '../../common/migrations/move_attribution'; import { setEmsTmsDefaultModes } from '../../common/migrations/set_ems_tms_default_modes'; import { renameLayerTypes } from '../../common/migrations/rename_layer_types'; +import type { MapSavedObjectAttributes } from '../../common/map_saved_object_type'; -function logMigrationWarning(context, errorMsg, doc) { +function logMigrationWarning( + context: SavedObjectMigrationContext, + errorMsg: string, + doc: SavedObjectUnsanitizedDoc +) { context.log.warning( `map migration failed (${context.migrationVersion}). ${errorMsg}. attributes: ${JSON.stringify( doc @@ -36,7 +47,10 @@ function logMigrationWarning(context, errorMsg, doc) { * This is the saved object migration registry. */ export const savedObjectMigrations = { - '7.2.0': (doc, context) => { + '7.2.0': ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ) => { try { const { attributes, references } = extractReferences(doc); @@ -50,7 +64,10 @@ export const savedObjectMigrations = { return doc; } }, - '7.4.0': (doc, context) => { + '7.4.0': ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ) => { try { const attributes = emsRasterTileToEmsVectorTile(doc); @@ -63,7 +80,10 @@ export const savedObjectMigrations = { return doc; } }, - '7.5.0': (doc, context) => { + '7.5.0': ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ) => { try { const attributes = topHitsTimeToSort(doc); @@ -76,7 +96,10 @@ export const savedObjectMigrations = { return doc; } }, - '7.6.0': (doc, context) => { + '7.6.0': ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ) => { try { const attributesPhase1 = moveApplyGlobalQueryToSources(doc); const attributesPhase2 = addFieldMetaOptions({ attributes: attributesPhase1 }); @@ -90,7 +113,10 @@ export const savedObjectMigrations = { return doc; } }, - '7.7.0': (doc, context) => { + '7.7.0': ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ) => { try { const attributesPhase1 = migrateSymbolStyleDescriptor(doc); const attributesPhase2 = migrateUseTopHitsToScalingType({ attributes: attributesPhase1 }); @@ -104,7 +130,10 @@ export const savedObjectMigrations = { return doc; } }, - '7.8.0': (doc, context) => { + '7.8.0': ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ) => { try { const attributes = migrateJoinAggKey(doc); @@ -117,7 +146,10 @@ export const savedObjectMigrations = { return doc; } }, - '7.9.0': (doc, context) => { + '7.9.0': ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ) => { try { const attributes = removeBoundsFromSavedObject(doc); @@ -130,7 +162,10 @@ export const savedObjectMigrations = { return doc; } }, - '7.10.0': (doc, context) => { + '7.10.0': ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ) => { try { const attributes = setDefaultAutoFitToBounds(doc); @@ -143,7 +178,10 @@ export const savedObjectMigrations = { return doc; } }, - '7.12.0': (doc, context) => { + '7.12.0': ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ) => { try { const attributes = addTypeToTermJoin(doc); @@ -156,7 +194,10 @@ export const savedObjectMigrations = { return doc; } }, - '7.14.0': (doc, context) => { + '7.14.0': ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ) => { try { const attributes = moveAttribution(doc); @@ -169,7 +210,10 @@ export const savedObjectMigrations = { return doc; } }, - '8.0.0': (doc, context) => { + '8.0.0': ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ) => { try { const attributes = setEmsTmsDefaultModes(doc); @@ -182,7 +226,10 @@ export const savedObjectMigrations = { return doc; } }, - '8.1.0': (doc, context) => { + '8.1.0': ( + doc: SavedObjectUnsanitizedDoc, + context: SavedObjectMigrationContext + ) => { try { const attributes = renameLayerTypes(doc); diff --git a/x-pack/plugins/maps/server/saved_objects/setup_saved_objects.ts b/x-pack/plugins/maps/server/saved_objects/setup_saved_objects.ts new file mode 100644 index 00000000000000..d5c0c0fa6dcf33 --- /dev/null +++ b/x-pack/plugins/maps/server/saved_objects/setup_saved_objects.ts @@ -0,0 +1,100 @@ +/* + * 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 { mapValues } from 'lodash'; +import type { CoreSetup, SavedObjectUnsanitizedDoc } from 'kibana/server'; +import type { SavedObjectMigrationMap } from 'src/core/server'; +import { MigrateFunctionsObject } from '../../../../../src/plugins/kibana_utils/common'; +import { mergeSavedObjectMigrationMaps } from '../../../../../src/core/server'; +import { APP_ICON, getFullPath } from '../../common/constants'; +import { migrateDataPersistedState } from '../../common/migrations/migrate_data_persisted_state'; +import type { MapSavedObjectAttributes } from '../../common/map_saved_object_type'; +import { savedObjectMigrations } from './saved_object_migrations'; + +export function setupSavedObjects( + core: CoreSetup, + getFilterMigrations: () => MigrateFunctionsObject +) { + core.savedObjects.registerType({ + name: 'map', + hidden: false, + namespaceType: 'multiple-isolated', + convertToMultiNamespaceTypeVersion: '8.0.0', + mappings: { + properties: { + description: { type: 'text' }, + title: { type: 'text' }, + version: { type: 'integer' }, + mapStateJSON: { type: 'text' }, + layerListJSON: { type: 'text' }, + uiStateJSON: { type: 'text' }, + bounds: { dynamic: false, properties: {} }, // Disable removed field + }, + }, + management: { + icon: APP_ICON, + defaultSearchField: 'title', + importableAndExportable: true, + getTitle(obj) { + return obj.attributes.title; + }, + getInAppUrl(obj) { + return { + path: getFullPath(obj.id), + uiCapabilitiesPath: 'maps.show', + }; + }, + }, + migrations: () => { + return mergeSavedObjectMigrationMaps( + savedObjectMigrations, + getPersistedStateMigrations(getFilterMigrations()) as unknown as SavedObjectMigrationMap + ); + }, + }); + + /* + * The maps-telemetry saved object type isn't used, but in order to remove these fields from + * the mappings we register this type with `type: 'object', enabled: true` to remove all + * previous fields from the mappings until https://github.com/elastic/kibana/issues/67086 is + * solved. + */ + core.savedObjects.registerType({ + name: 'maps-telemetry', + hidden: false, + namespaceType: 'agnostic', + mappings: { + // @ts-ignore Core types don't support this since it's only really valid when removing a previously registered type + type: 'object', + enabled: false, + }, + }); +} + +/** + * This creates a migration map that applies external plugin migrations to persisted state stored in Maps + */ +export const getPersistedStateMigrations = ( + filterMigrations: MigrateFunctionsObject +): MigrateFunctionsObject => + mapValues( + filterMigrations, + (filterMigration) => (doc: SavedObjectUnsanitizedDoc) => { + try { + const attributes = migrateDataPersistedState(doc, filterMigration); + + return { + ...doc, + attributes, + }; + } catch (e) { + // Do not fail migration + // Maps application can display error when saved object is viewed + return doc; + } + } + ); diff --git a/x-pack/plugins/maps/server/types.ts b/x-pack/plugins/maps/server/types.ts index 293964fdb6fee4..597b826eee56d8 100644 --- a/x-pack/plugins/maps/server/types.ts +++ b/x-pack/plugins/maps/server/types.ts @@ -11,10 +11,14 @@ import { HomeServerPluginSetup } from '../../../../src/plugins/home/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { MapsEmsPluginServerSetup } from '../../../../src/plugins/maps_ems/server'; import { EmbeddableSetup } from '../../../../src/plugins/embeddable/server'; -import { PluginStart as DataPluginStart } from '../../../../src/plugins/data/server'; +import { + PluginSetup as DataPluginSetup, + PluginStart as DataPluginStart, +} from '../../../../src/plugins/data/server'; import { CustomIntegrationsPluginSetup } from '../../../../src/plugins/custom_integrations/server'; export interface SetupDeps { + data: DataPluginSetup; features: FeaturesPluginSetupContract; usageCollection?: UsageCollectionSetup; home?: HomeServerPluginSetup; From 614139b8e5a38c60918586c2282eb75c88fedb80 Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 24 Feb 2022 12:43:12 -0600 Subject: [PATCH 02/28] [bazel] avoid a little boilerplate in packages (#126309) * [bazel] avoid a little boilerplate for @types packages * [bazel/ts] stop building sourcemaps since they're ignored --- packages/elastic-apm-synthtrace/BUILD.bazel | 5 +---- packages/elastic-apm-synthtrace/tsconfig.json | 3 --- packages/elastic-datemath/BUILD.bazel | 5 +---- packages/elastic-datemath/tsconfig.json | 3 --- packages/kbn-ace/BUILD.bazel | 5 +---- packages/kbn-ace/tsconfig.json | 3 --- packages/kbn-alerts/BUILD.bazel | 5 +---- packages/kbn-alerts/tsconfig.json | 3 --- packages/kbn-analytics/BUILD.bazel | 5 +---- packages/kbn-analytics/tsconfig.json | 3 --- packages/kbn-apm-config-loader/BUILD.bazel | 5 +---- packages/kbn-apm-config-loader/tsconfig.json | 3 --- packages/kbn-apm-utils/BUILD.bazel | 5 +---- packages/kbn-apm-utils/tsconfig.json | 3 --- packages/kbn-cli-dev-mode/BUILD.bazel | 5 +---- packages/kbn-cli-dev-mode/tsconfig.json | 3 --- packages/kbn-config-schema/BUILD.bazel | 5 +---- packages/kbn-config-schema/tsconfig.json | 3 --- packages/kbn-config/BUILD.bazel | 5 +---- packages/kbn-config/tsconfig.json | 3 --- packages/kbn-crypto/BUILD.bazel | 7 ++----- packages/kbn-crypto/tsconfig.json | 3 --- packages/kbn-dev-utils/BUILD.bazel | 5 +---- packages/kbn-dev-utils/tsconfig.json | 3 --- packages/kbn-doc-links/BUILD.bazel | 5 +---- packages/kbn-doc-links/tsconfig.json | 3 --- packages/kbn-docs-utils/BUILD.bazel | 5 +---- packages/kbn-docs-utils/tsconfig.json | 3 --- packages/kbn-es-archiver/BUILD.bazel | 5 +---- packages/kbn-es-archiver/tsconfig.json | 3 --- packages/kbn-es-query/BUILD.bazel | 5 +---- packages/kbn-es-query/tsconfig.json | 3 --- packages/kbn-field-types/BUILD.bazel | 5 +---- packages/kbn-field-types/tsconfig.json | 3 --- packages/kbn-i18n-react/BUILD.bazel | 5 +---- packages/kbn-i18n-react/tsconfig.json | 3 --- packages/kbn-i18n/BUILD.bazel | 5 +---- packages/kbn-i18n/tsconfig.json | 3 --- packages/kbn-interpreter/BUILD.bazel | 5 +---- packages/kbn-interpreter/tsconfig.json | 3 --- packages/kbn-io-ts-utils/BUILD.bazel | 5 +---- packages/kbn-io-ts-utils/tsconfig.json | 3 --- packages/kbn-logging-mocks/BUILD.bazel | 5 +---- packages/kbn-logging-mocks/tsconfig.json | 3 --- packages/kbn-logging/BUILD.bazel | 5 +---- packages/kbn-logging/tsconfig.json | 3 --- packages/kbn-mapbox-gl/BUILD.bazel | 5 +---- packages/kbn-mapbox-gl/tsconfig.json | 3 --- packages/kbn-monaco/BUILD.bazel | 5 +---- packages/kbn-monaco/tsconfig.json | 3 --- packages/kbn-optimizer/BUILD.bazel | 5 +---- packages/kbn-optimizer/tsconfig.json | 3 --- packages/kbn-plugin-generator/BUILD.bazel | 5 +---- packages/kbn-plugin-generator/tsconfig.json | 3 --- packages/kbn-plugin-helpers/BUILD.bazel | 5 +---- packages/kbn-plugin-helpers/tsconfig.json | 3 --- packages/kbn-react-field/BUILD.bazel | 5 +---- packages/kbn-react-field/tsconfig.json | 3 --- packages/kbn-rule-data-utils/BUILD.bazel | 5 +---- packages/kbn-rule-data-utils/tsconfig.json | 3 --- packages/kbn-securitysolution-autocomplete/BUILD.bazel | 5 +---- packages/kbn-securitysolution-autocomplete/tsconfig.json | 3 --- packages/kbn-securitysolution-es-utils/BUILD.bazel | 5 +---- packages/kbn-securitysolution-es-utils/tsconfig.json | 3 --- packages/kbn-securitysolution-hook-utils/BUILD.bazel | 5 +---- packages/kbn-securitysolution-hook-utils/tsconfig.json | 3 --- .../kbn-securitysolution-io-ts-alerting-types/BUILD.bazel | 5 +---- .../tsconfig.json | 3 --- packages/kbn-securitysolution-io-ts-list-types/BUILD.bazel | 5 +---- .../kbn-securitysolution-io-ts-list-types/tsconfig.json | 3 --- packages/kbn-securitysolution-io-ts-types/BUILD.bazel | 5 +---- packages/kbn-securitysolution-io-ts-types/tsconfig.json | 3 --- packages/kbn-securitysolution-io-ts-utils/BUILD.bazel | 5 +---- packages/kbn-securitysolution-io-ts-utils/tsconfig.json | 3 --- packages/kbn-securitysolution-list-api/BUILD.bazel | 5 +---- packages/kbn-securitysolution-list-api/tsconfig.json | 3 --- packages/kbn-securitysolution-list-constants/BUILD.bazel | 5 +---- packages/kbn-securitysolution-list-constants/tsconfig.json | 3 --- packages/kbn-securitysolution-list-hooks/BUILD.bazel | 5 +---- packages/kbn-securitysolution-list-hooks/tsconfig.json | 3 --- packages/kbn-securitysolution-list-utils/BUILD.bazel | 5 +---- packages/kbn-securitysolution-list-utils/tsconfig.json | 3 --- packages/kbn-securitysolution-rules/BUILD.bazel | 5 +---- packages/kbn-securitysolution-rules/tsconfig.json | 3 --- packages/kbn-securitysolution-t-grid/BUILD.bazel | 5 +---- packages/kbn-securitysolution-t-grid/tsconfig.json | 3 --- packages/kbn-securitysolution-utils/BUILD.bazel | 5 +---- packages/kbn-securitysolution-utils/tsconfig.json | 3 --- packages/kbn-server-http-tools/BUILD.bazel | 5 +---- packages/kbn-server-http-tools/tsconfig.json | 3 --- packages/kbn-server-route-repository/BUILD.bazel | 5 +---- packages/kbn-server-route-repository/tsconfig.json | 3 --- packages/kbn-std/BUILD.bazel | 5 +---- packages/kbn-std/tsconfig.json | 3 --- packages/kbn-storybook/BUILD.bazel | 5 +---- packages/kbn-storybook/tsconfig.json | 3 --- packages/kbn-telemetry-tools/BUILD.bazel | 5 +---- packages/kbn-telemetry-tools/tsconfig.json | 3 --- packages/kbn-test-jest-helpers/BUILD.bazel | 5 +---- packages/kbn-test-jest-helpers/tsconfig.json | 3 --- packages/kbn-test/BUILD.bazel | 5 +---- packages/kbn-test/tsconfig.json | 3 --- packages/kbn-typed-react-router-config/BUILD.bazel | 5 +---- packages/kbn-typed-react-router-config/tsconfig.json | 3 --- packages/kbn-ui-shared-deps-npm/BUILD.bazel | 5 +---- packages/kbn-ui-shared-deps-npm/tsconfig.json | 3 --- packages/kbn-ui-shared-deps-src/BUILD.bazel | 5 +---- packages/kbn-ui-shared-deps-src/tsconfig.json | 3 --- packages/kbn-ui-theme/BUILD.bazel | 5 +---- packages/kbn-ui-theme/tsconfig.json | 3 --- packages/kbn-utility-types/BUILD.bazel | 5 +---- packages/kbn-utility-types/tsconfig.json | 3 --- packages/kbn-utils/BUILD.bazel | 5 +---- packages/kbn-utils/tsconfig.json | 3 --- src/dev/bazel/pkg_npm_types/pkg_npm_types.bzl | 7 +++++-- 115 files changed, 63 insertions(+), 402 deletions(-) diff --git a/packages/elastic-apm-synthtrace/BUILD.bazel b/packages/elastic-apm-synthtrace/BUILD.bazel index 7fb188de435b61..09406644f44b23 100644 --- a/packages/elastic-apm-synthtrace/BUILD.bazel +++ b/packages/elastic-apm-synthtrace/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "elastic-apm-synthtrace" PKG_REQUIRE_NAME = "@elastic/apm-synthtrace" -TYPES_PKG_REQUIRE_NAME = "@types/elastic__apm-synthtrace" SOURCE_FILES = glob( [ @@ -67,11 +66,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", validate = False, ) @@ -103,7 +100,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/elastic-apm-synthtrace/tsconfig.json b/packages/elastic-apm-synthtrace/tsconfig.json index 6ae9c20b4387ba..9d9a6ecc6b2b6c 100644 --- a/packages/elastic-apm-synthtrace/tsconfig.json +++ b/packages/elastic-apm-synthtrace/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "./src", - "sourceMap": true, - "sourceRoot": "../../../../packages/elastic-apm-synthtrace/src", "types": ["node", "jest"] }, "include": ["./src/**/*.ts"] diff --git a/packages/elastic-datemath/BUILD.bazel b/packages/elastic-datemath/BUILD.bazel index b4ed018fe9d04f..1ee7f8582cb7e6 100644 --- a/packages/elastic-datemath/BUILD.bazel +++ b/packages/elastic-datemath/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "ts_project", "pkg_npm", "p PKG_BASE_NAME = "elastic-datemath" PKG_REQUIRE_NAME = "@elastic/datemath" -TYPES_PKG_REQUIRE_NAME = "@types/elastic__datemath" SOURCE_FILES = glob([ "src/index.ts", @@ -50,10 +49,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig" ) @@ -85,7 +82,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/elastic-datemath/tsconfig.json b/packages/elastic-datemath/tsconfig.json index 02465bebe519ab..3f81ea4e68516b 100644 --- a/packages/elastic-datemath/tsconfig.json +++ b/packages/elastic-datemath/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/elastic-datemath/src", "types": [ "node" ] diff --git a/packages/kbn-ace/BUILD.bazel b/packages/kbn-ace/BUILD.bazel index c674f9ae8e00fb..583d81ce847bdb 100644 --- a/packages/kbn-ace/BUILD.bazel +++ b/packages/kbn-ace/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-ace" PKG_REQUIRE_NAME = "@kbn/ace" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__ace" SOURCE_FILES = glob( [ @@ -68,10 +67,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -103,7 +100,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-ace/tsconfig.json b/packages/kbn-ace/tsconfig.json index 4a132efa091184..0fd9a15b5dbf88 100644 --- a/packages/kbn-ace/tsconfig.json +++ b/packages/kbn-ace/tsconfig.json @@ -2,11 +2,8 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-ace/src", "stripInternal": true, "types": ["node"] }, diff --git a/packages/kbn-alerts/BUILD.bazel b/packages/kbn-alerts/BUILD.bazel index a6e5f167735c01..e6ebdaa2027012 100644 --- a/packages/kbn-alerts/BUILD.bazel +++ b/packages/kbn-alerts/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-alerts" PKG_REQUIRE_NAME = "@kbn/alerts" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__alerts" SOURCE_FILES = glob( [ @@ -74,11 +73,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -109,7 +106,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-alerts/tsconfig.json b/packages/kbn-alerts/tsconfig.json index ac523fb77a9e1a..dfe59c663d3e15 100644 --- a/packages/kbn-alerts/tsconfig.json +++ b/packages/kbn-alerts/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-alerts/src", "types": ["jest", "node"] }, "include": ["src/**/*"], diff --git a/packages/kbn-analytics/BUILD.bazel b/packages/kbn-analytics/BUILD.bazel index 94e65b2e35ba35..d144ab186a6a11 100644 --- a/packages/kbn-analytics/BUILD.bazel +++ b/packages/kbn-analytics/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-analytics" PKG_REQUIRE_NAME = "@kbn/analytics" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__analytics" SOURCE_FILES = glob( [ @@ -71,11 +70,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -106,7 +103,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-analytics/tsconfig.json b/packages/kbn-analytics/tsconfig.json index dfae54ad91c8e4..de4301e2a2ac05 100644 --- a/packages/kbn-analytics/tsconfig.json +++ b/packages/kbn-analytics/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "isolatedModules": true, "outDir": "./target_types", - "sourceMap": true, - "sourceRoot": "../../../../../packages/kbn-analytics/src", "stripInternal": true, "types": [ "node" diff --git a/packages/kbn-apm-config-loader/BUILD.bazel b/packages/kbn-apm-config-loader/BUILD.bazel index a18a5e973d3a0f..bcdbefb132aa6d 100644 --- a/packages/kbn-apm-config-loader/BUILD.bazel +++ b/packages/kbn-apm-config-loader/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-apm-config-loader" PKG_REQUIRE_NAME = "@kbn/apm-config-loader" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__apm-config-loader" SOURCE_FILES = glob( [ @@ -66,10 +65,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -101,7 +98,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-apm-config-loader/tsconfig.json b/packages/kbn-apm-config-loader/tsconfig.json index 2f6da800d9dd20..7d2597d318b310 100644 --- a/packages/kbn-apm-config-loader/tsconfig.json +++ b/packages/kbn-apm-config-loader/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "./src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-apm-config-loader/src", "stripInternal": false, "types": [ "jest", diff --git a/packages/kbn-apm-utils/BUILD.bazel b/packages/kbn-apm-utils/BUILD.bazel index c8ad4d1a09625f..9ca9009bb7186b 100644 --- a/packages/kbn-apm-utils/BUILD.bazel +++ b/packages/kbn-apm-utils/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-apm-utils" PKG_REQUIRE_NAME = "@kbn/apm-utils" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__apm-utils" SOURCE_FILES = glob([ "src/index.ts", @@ -51,10 +50,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -86,7 +83,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-apm-utils/tsconfig.json b/packages/kbn-apm-utils/tsconfig.json index d7056cda6d71ab..9c8c443436ce5c 100644 --- a/packages/kbn-apm-utils/tsconfig.json +++ b/packages/kbn-apm-utils/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-apm-utils/src", "types": [ "node" ] diff --git a/packages/kbn-cli-dev-mode/BUILD.bazel b/packages/kbn-cli-dev-mode/BUILD.bazel index 133474a3aefa61..4b45e34b7e9fa3 100644 --- a/packages/kbn-cli-dev-mode/BUILD.bazel +++ b/packages/kbn-cli-dev-mode/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-cli-dev-mode" PKG_REQUIRE_NAME = "@kbn/cli-dev-mode" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__cli-dev-mode" SOURCE_FILES = glob( [ @@ -93,11 +92,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -128,7 +125,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-cli-dev-mode/tsconfig.json b/packages/kbn-cli-dev-mode/tsconfig.json index 6ce2e21b846741..4ba84d786cb4b1 100644 --- a/packages/kbn-cli-dev-mode/tsconfig.json +++ b/packages/kbn-cli-dev-mode/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "./src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-cli-dev-mode/src", "types": [ "jest", "node" diff --git a/packages/kbn-config-schema/BUILD.bazel b/packages/kbn-config-schema/BUILD.bazel index ed6082527bab92..e496aa31b49d33 100644 --- a/packages/kbn-config-schema/BUILD.bazel +++ b/packages/kbn-config-schema/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-config-schema" PKG_REQUIRE_NAME = "@kbn/config-schema" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__config-schema" SOURCE_FILES = glob([ "src/**/*.ts", @@ -62,10 +61,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -97,7 +94,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-config-schema/tsconfig.json b/packages/kbn-config-schema/tsconfig.json index 79b652daf7ec1d..e1125366bc3900 100644 --- a/packages/kbn-config-schema/tsconfig.json +++ b/packages/kbn-config-schema/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-config-schema/src", "stripInternal": true, "types": [ "jest", diff --git a/packages/kbn-config/BUILD.bazel b/packages/kbn-config/BUILD.bazel index 0577014768d4ca..e242cf5c926229 100644 --- a/packages/kbn-config/BUILD.bazel +++ b/packages/kbn-config/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-config" PKG_REQUIRE_NAME = "@kbn/config" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__config" SOURCE_FILES = glob( [ @@ -83,10 +82,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -118,7 +115,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-config/tsconfig.json b/packages/kbn-config/tsconfig.json index 0971923a11a0f6..a684de9502b5e5 100644 --- a/packages/kbn-config/tsconfig.json +++ b/packages/kbn-config/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-config/src", "stripInternal": false, "types": [ "jest", diff --git a/packages/kbn-crypto/BUILD.bazel b/packages/kbn-crypto/BUILD.bazel index f71c8b866fd5d9..de8c97ed3b713a 100644 --- a/packages/kbn-crypto/BUILD.bazel +++ b/packages/kbn-crypto/BUILD.bazel @@ -5,7 +5,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-crypto" PKG_REQUIRE_NAME = "@kbn/crypto" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__crypto" SOURCE_FILES = glob( [ @@ -29,7 +28,7 @@ NPM_MODULE_EXTRA_FILES = [ ] RUNTIME_DEPS = [ - "//packages/kbn-dev-utils", + "//packages/kbn-dev-utils:build", "@npm//node-forge", ] @@ -62,10 +61,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -97,7 +94,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-crypto/tsconfig.json b/packages/kbn-crypto/tsconfig.json index 0863fc3f530def..272363e976ba1e 100644 --- a/packages/kbn-crypto/tsconfig.json +++ b/packages/kbn-crypto/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-crypto/src", "types": [ "jest", "node" diff --git a/packages/kbn-dev-utils/BUILD.bazel b/packages/kbn-dev-utils/BUILD.bazel index 65e0957fe5d90f..7be527c65a06c4 100644 --- a/packages/kbn-dev-utils/BUILD.bazel +++ b/packages/kbn-dev-utils/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-dev-utils" PKG_REQUIRE_NAME = "@kbn/dev-utils" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__dev-utils" SOURCE_FILES = glob( [ @@ -114,10 +113,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -149,7 +146,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-dev-utils/tsconfig.json b/packages/kbn-dev-utils/tsconfig.json index 3c22642edfaa1e..a8cfc2cceb08b8 100644 --- a/packages/kbn-dev-utils/tsconfig.json +++ b/packages/kbn-dev-utils/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-dev-utils/src", "stripInternal": false, "types": [ "jest", diff --git a/packages/kbn-doc-links/BUILD.bazel b/packages/kbn-doc-links/BUILD.bazel index b4ae92aa8050b5..13b68935c43261 100644 --- a/packages/kbn-doc-links/BUILD.bazel +++ b/packages/kbn-doc-links/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-doc-links" PKG_REQUIRE_NAME = "@kbn/doc-links" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__doc-links" SOURCE_FILES = glob( [ @@ -61,10 +60,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -96,7 +93,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-doc-links/tsconfig.json b/packages/kbn-doc-links/tsconfig.json index e2178f72072e98..a684de9502b5e5 100644 --- a/packages/kbn-doc-links/tsconfig.json +++ b/packages/kbn-doc-links/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-doc-links/src", "stripInternal": false, "types": [ "jest", diff --git a/packages/kbn-docs-utils/BUILD.bazel b/packages/kbn-docs-utils/BUILD.bazel index ad6b6687b7e1af..30baa9dccb7f78 100644 --- a/packages/kbn-docs-utils/BUILD.bazel +++ b/packages/kbn-docs-utils/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-docs-utils" PKG_REQUIRE_NAME = "@kbn/docs-utils" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__docs-utils" SOURCE_FILES = glob( [ @@ -67,10 +66,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -102,7 +99,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-docs-utils/tsconfig.json b/packages/kbn-docs-utils/tsconfig.json index fa20d6f4be398e..9a68b2e81940cf 100644 --- a/packages/kbn-docs-utils/tsconfig.json +++ b/packages/kbn-docs-utils/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-docs-utils/src", "types": [ "jest", "node" diff --git a/packages/kbn-es-archiver/BUILD.bazel b/packages/kbn-es-archiver/BUILD.bazel index fed3f248c09957..70bda4c67106f2 100644 --- a/packages/kbn-es-archiver/BUILD.bazel +++ b/packages/kbn-es-archiver/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-es-archiver" PKG_REQUIRE_NAME = "@kbn/es-archiver" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__es-archiver" SOURCE_FILES = glob( [ @@ -80,10 +79,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -115,7 +112,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-es-archiver/tsconfig.json b/packages/kbn-es-archiver/tsconfig.json index 15c846f052b473..92bc23ab6616b8 100644 --- a/packages/kbn-es-archiver/tsconfig.json +++ b/packages/kbn-es-archiver/tsconfig.json @@ -2,11 +2,8 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-es-archiver/src", "types": [ "jest", "node" diff --git a/packages/kbn-es-query/BUILD.bazel b/packages/kbn-es-query/BUILD.bazel index 84ee5584ceabe7..c7d8ad33cf7f66 100644 --- a/packages/kbn-es-query/BUILD.bazel +++ b/packages/kbn-es-query/BUILD.bazel @@ -5,7 +5,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-es-query" PKG_REQUIRE_NAME = "@kbn/es-query" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__es-query" SOURCE_FILES = glob( [ @@ -94,10 +93,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -129,7 +126,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-es-query/tsconfig.json b/packages/kbn-es-query/tsconfig.json index 5b1f3be263138b..4b2faade0d50a8 100644 --- a/packages/kbn-es-query/tsconfig.json +++ b/packages/kbn-es-query/tsconfig.json @@ -2,11 +2,8 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-es-query/src", "types": [ "jest", "node" diff --git a/packages/kbn-field-types/BUILD.bazel b/packages/kbn-field-types/BUILD.bazel index 0398d4d9b9ba62..77a4acaedb2359 100644 --- a/packages/kbn-field-types/BUILD.bazel +++ b/packages/kbn-field-types/BUILD.bazel @@ -5,7 +5,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-field-types" PKG_REQUIRE_NAME = "@kbn/field-types" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__field-types" SOURCE_FILES = glob( [ @@ -64,10 +63,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -99,7 +96,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-field-types/tsconfig.json b/packages/kbn-field-types/tsconfig.json index 150854076bbf82..f4dd1662f832f7 100644 --- a/packages/kbn-field-types/tsconfig.json +++ b/packages/kbn-field-types/tsconfig.json @@ -3,11 +3,8 @@ "compilerOptions": { "outDir": "./target_types", "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-field-types/src" }, "include": ["src/**/*"] } diff --git a/packages/kbn-i18n-react/BUILD.bazel b/packages/kbn-i18n-react/BUILD.bazel index 505afabfa860d7..0ba73353903804 100644 --- a/packages/kbn-i18n-react/BUILD.bazel +++ b/packages/kbn-i18n-react/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-i18n-react" PKG_REQUIRE_NAME = "@kbn/i18n-react" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__i18n-react" SOURCE_FILES = glob( [ @@ -74,10 +73,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -109,7 +106,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-i18n-react/tsconfig.json b/packages/kbn-i18n-react/tsconfig.json index d2707938041d2a..5f0c08bace6d33 100644 --- a/packages/kbn-i18n-react/tsconfig.json +++ b/packages/kbn-i18n-react/tsconfig.json @@ -2,11 +2,8 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", - "sourceMap": true, - "sourceRoot": "../../../../../packages/kbn-i18n-react/src", "types": [ "jest", "node" diff --git a/packages/kbn-i18n/BUILD.bazel b/packages/kbn-i18n/BUILD.bazel index 385bdafb7c8ee4..07b2c0b4d773ae 100644 --- a/packages/kbn-i18n/BUILD.bazel +++ b/packages/kbn-i18n/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-i18n" PKG_REQUIRE_NAME = "@kbn/i18n" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__i18n" SOURCE_FILES = glob( [ @@ -75,10 +74,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -110,7 +107,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-i18n/tsconfig.json b/packages/kbn-i18n/tsconfig.json index 2ac0b081b76ddb..0fd24d62281b6a 100644 --- a/packages/kbn-i18n/tsconfig.json +++ b/packages/kbn-i18n/tsconfig.json @@ -2,11 +2,8 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", - "sourceMap": true, - "sourceRoot": "../../../../../packages/kbn-i18n/src", "types": [ "jest", "node" diff --git a/packages/kbn-interpreter/BUILD.bazel b/packages/kbn-interpreter/BUILD.bazel index fd19413116f8d5..d390ccbc2ebde7 100644 --- a/packages/kbn-interpreter/BUILD.bazel +++ b/packages/kbn-interpreter/BUILD.bazel @@ -5,7 +5,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-interpreter" PKG_REQUIRE_NAME = "@kbn/interpreter" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__interpreter" SOURCE_FILES = glob( [ @@ -74,10 +73,8 @@ ts_project( deps = TYPES_DEPS, allow_js = True, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -109,7 +106,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-interpreter/tsconfig.json b/packages/kbn-interpreter/tsconfig.json index 60f8c76cf8809b..de30741c59a60c 100644 --- a/packages/kbn-interpreter/tsconfig.json +++ b/packages/kbn-interpreter/tsconfig.json @@ -3,12 +3,9 @@ "compilerOptions": { "allowJs": true, "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-interpreter/src", "stripInternal": true, "types": [ "jest", diff --git a/packages/kbn-io-ts-utils/BUILD.bazel b/packages/kbn-io-ts-utils/BUILD.bazel index 5ecfc0acc55e8e..aa0116b81efe68 100644 --- a/packages/kbn-io-ts-utils/BUILD.bazel +++ b/packages/kbn-io-ts-utils/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-io-ts-utils" PKG_REQUIRE_NAME = "@kbn/io-ts-utils" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__io-ts-utils" SOURCE_FILES = glob( [ @@ -65,10 +64,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -100,7 +97,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-io-ts-utils/tsconfig.json b/packages/kbn-io-ts-utils/tsconfig.json index 72d14796213455..1998f132ffd246 100644 --- a/packages/kbn-io-ts-utils/tsconfig.json +++ b/packages/kbn-io-ts-utils/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-io-ts-utils/src", "stripInternal": false, "types": [ "jest", diff --git a/packages/kbn-logging-mocks/BUILD.bazel b/packages/kbn-logging-mocks/BUILD.bazel index 74fb9c2651e5d3..90a20464442914 100644 --- a/packages/kbn-logging-mocks/BUILD.bazel +++ b/packages/kbn-logging-mocks/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-logging-mocks" PKG_REQUIRE_NAME = "@kbn/logging-mocks" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__logging-mocks" SOURCE_FILES = glob( [ @@ -57,10 +56,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -92,7 +89,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-logging-mocks/tsconfig.json b/packages/kbn-logging-mocks/tsconfig.json index ce53e016c2830e..ee25c507e7f562 100644 --- a/packages/kbn-logging-mocks/tsconfig.json +++ b/packages/kbn-logging-mocks/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-logging-mocks/src", "stripInternal": false, "types": [ "jest", diff --git a/packages/kbn-logging/BUILD.bazel b/packages/kbn-logging/BUILD.bazel index 09ff3f0d83b2de..ec25b5cd3ae88c 100644 --- a/packages/kbn-logging/BUILD.bazel +++ b/packages/kbn-logging/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-logging" PKG_REQUIRE_NAME = "@kbn/logging" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__logging" SOURCE_FILES = glob( [ @@ -58,10 +57,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -93,7 +90,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-logging/tsconfig.json b/packages/kbn-logging/tsconfig.json index a6fb0f2f731871..ee25c507e7f562 100644 --- a/packages/kbn-logging/tsconfig.json +++ b/packages/kbn-logging/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-logging/src", "stripInternal": false, "types": [ "jest", diff --git a/packages/kbn-mapbox-gl/BUILD.bazel b/packages/kbn-mapbox-gl/BUILD.bazel index b09b783a0aebf3..89cbeb4c431ae0 100644 --- a/packages/kbn-mapbox-gl/BUILD.bazel +++ b/packages/kbn-mapbox-gl/BUILD.bazel @@ -5,7 +5,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-mapbox-gl" PKG_REQUIRE_NAME = "@kbn/mapbox-gl" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__mapbox-gl" SOURCE_FILES = glob( [ @@ -61,10 +60,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -96,7 +93,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-mapbox-gl/tsconfig.json b/packages/kbn-mapbox-gl/tsconfig.json index e935276e917623..ee063bd30933e7 100644 --- a/packages/kbn-mapbox-gl/tsconfig.json +++ b/packages/kbn-mapbox-gl/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-mapbox-gl/src", "types": [] }, "include": [ diff --git a/packages/kbn-monaco/BUILD.bazel b/packages/kbn-monaco/BUILD.bazel index c72c46f8ed2020..c615196f5c1823 100644 --- a/packages/kbn-monaco/BUILD.bazel +++ b/packages/kbn-monaco/BUILD.bazel @@ -5,7 +5,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-monaco" PKG_REQUIRE_NAME = "@kbn/monaco" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__monaco" SOURCE_FILES = glob( [ @@ -96,10 +95,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -131,7 +128,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-monaco/tsconfig.json b/packages/kbn-monaco/tsconfig.json index 959051b17b782b..e55d786c41bc33 100644 --- a/packages/kbn-monaco/tsconfig.json +++ b/packages/kbn-monaco/tsconfig.json @@ -2,11 +2,8 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-monaco/src", "types": [ "jest", "node" diff --git a/packages/kbn-optimizer/BUILD.bazel b/packages/kbn-optimizer/BUILD.bazel index 9486f309bd0f3d..680ca0e58b8eb2 100644 --- a/packages/kbn-optimizer/BUILD.bazel +++ b/packages/kbn-optimizer/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-optimizer" PKG_REQUIRE_NAME = "@kbn/optimizer" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__optimizer" SOURCE_FILES = glob( [ @@ -119,10 +118,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -154,7 +151,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-optimizer/tsconfig.json b/packages/kbn-optimizer/tsconfig.json index 5fbd02106e777e..72de52e593cf72 100644 --- a/packages/kbn-optimizer/tsconfig.json +++ b/packages/kbn-optimizer/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "./src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-optimizer/src", "types": [ "jest", "node" diff --git a/packages/kbn-plugin-generator/BUILD.bazel b/packages/kbn-plugin-generator/BUILD.bazel index 0578842a7509ba..3e1565e86fe0d9 100644 --- a/packages/kbn-plugin-generator/BUILD.bazel +++ b/packages/kbn-plugin-generator/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-plugin-generator" PKG_REQUIRE_NAME = "@kbn/plugin-generator" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__plugin-generator" SOURCE_FILES = glob( [ @@ -86,10 +85,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -121,7 +118,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-plugin-generator/tsconfig.json b/packages/kbn-plugin-generator/tsconfig.json index 5b666cf801da63..519de9b703140c 100644 --- a/packages/kbn-plugin-generator/tsconfig.json +++ b/packages/kbn-plugin-generator/tsconfig.json @@ -2,11 +2,8 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-plugin-generator/src", "target": "ES2019", "types": [ "jest", diff --git a/packages/kbn-plugin-helpers/BUILD.bazel b/packages/kbn-plugin-helpers/BUILD.bazel index 7112c497f6ff8c..cda9dce927e357 100644 --- a/packages/kbn-plugin-helpers/BUILD.bazel +++ b/packages/kbn-plugin-helpers/BUILD.bazel @@ -5,7 +5,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-plugin-helpers" PKG_REQUIRE_NAME = "@kbn/plugin-helpers" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__plugin-helpers" SOURCE_FILES = glob( [ @@ -79,10 +78,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -114,7 +111,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-plugin-helpers/tsconfig.json b/packages/kbn-plugin-helpers/tsconfig.json index 34f3ec5e675038..0bff4cdbb85e1d 100644 --- a/packages/kbn-plugin-helpers/tsconfig.json +++ b/packages/kbn-plugin-helpers/tsconfig.json @@ -2,11 +2,8 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-plugin-helpers/src", "target": "ES2018", "types": [ "jest", diff --git a/packages/kbn-react-field/BUILD.bazel b/packages/kbn-react-field/BUILD.bazel index 36ab9d7f38c560..07ecbcb61db26f 100644 --- a/packages/kbn-react-field/BUILD.bazel +++ b/packages/kbn-react-field/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-react-field" PKG_REQUIRE_NAME = "@kbn/react-field" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__react-field" SOURCE_FILES = glob( [ @@ -84,10 +83,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -119,7 +116,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-react-field/tsconfig.json b/packages/kbn-react-field/tsconfig.json index 4d37e1825c85a5..f91c1f4c54d747 100644 --- a/packages/kbn-react-field/tsconfig.json +++ b/packages/kbn-react-field/tsconfig.json @@ -2,11 +2,8 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", - "sourceMap": true, - "sourceRoot": "../../../../../packages/kbn-react-field/src", "types": [ "jest", "node", diff --git a/packages/kbn-rule-data-utils/BUILD.bazel b/packages/kbn-rule-data-utils/BUILD.bazel index 1e71947566722b..6477b558db9cb3 100644 --- a/packages/kbn-rule-data-utils/BUILD.bazel +++ b/packages/kbn-rule-data-utils/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-rule-data-utils" PKG_REQUIRE_NAME = "@kbn/rule-data-utils" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__rule-data-utils" SOURCE_FILES = glob( [ @@ -60,11 +59,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, incremental = False, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -96,7 +93,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-rule-data-utils/tsconfig.json b/packages/kbn-rule-data-utils/tsconfig.json index be6095d187ef31..05947e9286a740 100644 --- a/packages/kbn-rule-data-utils/tsconfig.json +++ b/packages/kbn-rule-data-utils/tsconfig.json @@ -2,13 +2,10 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "incremental": false, "outDir": "./target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-rule-data-utils/src", "stripInternal": false, "types": [ "jest", diff --git a/packages/kbn-securitysolution-autocomplete/BUILD.bazel b/packages/kbn-securitysolution-autocomplete/BUILD.bazel index bc29f400f4d5b2..16aa28e79b45be 100644 --- a/packages/kbn-securitysolution-autocomplete/BUILD.bazel +++ b/packages/kbn-securitysolution-autocomplete/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-autocomplete" PKG_REQUIRE_NAME = "@kbn/securitysolution-autocomplete" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-autocomplete" SOURCE_FILES = glob( [ @@ -93,11 +92,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -128,7 +125,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-autocomplete/tsconfig.json b/packages/kbn-securitysolution-autocomplete/tsconfig.json index b2e24676cdbd41..dfe59c663d3e15 100644 --- a/packages/kbn-securitysolution-autocomplete/tsconfig.json +++ b/packages/kbn-securitysolution-autocomplete/tsconfig.json @@ -2,11 +2,8 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-autocomplete/src", "rootDir": "src", "types": ["jest", "node"] }, diff --git a/packages/kbn-securitysolution-es-utils/BUILD.bazel b/packages/kbn-securitysolution-es-utils/BUILD.bazel index ceb643246249ea..e36409f464b3e0 100644 --- a/packages/kbn-securitysolution-es-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-es-utils/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-es-utils" PKG_REQUIRE_NAME = "@kbn/securitysolution-es-utils" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-es-utils" SOURCE_FILES = glob( [ @@ -65,11 +64,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -100,7 +97,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-es-utils/tsconfig.json b/packages/kbn-securitysolution-es-utils/tsconfig.json index e5b1c99d3f598c..305954d669b758 100644 --- a/packages/kbn-securitysolution-es-utils/tsconfig.json +++ b/packages/kbn-securitysolution-es-utils/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-es-utils/src", "types": [ "jest", "node" diff --git a/packages/kbn-securitysolution-hook-utils/BUILD.bazel b/packages/kbn-securitysolution-hook-utils/BUILD.bazel index 4f46992ad13d6f..363d4f688783da 100644 --- a/packages/kbn-securitysolution-hook-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-hook-utils/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-hook-utils" PKG_REQUIRE_NAME = "@kbn/securitysolution-hook-utils" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-hook-utils" SOURCE_FILES = glob( [ @@ -72,11 +71,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -107,7 +104,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-hook-utils/tsconfig.json b/packages/kbn-securitysolution-hook-utils/tsconfig.json index 4782331e31c440..6a1981335d68ea 100644 --- a/packages/kbn-securitysolution-hook-utils/tsconfig.json +++ b/packages/kbn-securitysolution-hook-utils/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-hook-utils/src", "types": ["jest", "node"] }, "include": ["src/**/*"] diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/BUILD.bazel b/packages/kbn-securitysolution-io-ts-alerting-types/BUILD.bazel index 61cdb9cccd2929..0a06de87e83038 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/BUILD.bazel +++ b/packages/kbn-securitysolution-io-ts-alerting-types/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-io-ts-alerting-types" PKG_REQUIRE_NAME = "@kbn/securitysolution-io-ts-alerting-types" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-io-ts-alerting-types" SOURCE_FILES = glob( [ @@ -75,11 +74,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -110,7 +107,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/tsconfig.json b/packages/kbn-securitysolution-io-ts-alerting-types/tsconfig.json index 0e58572c1b82bf..305954d669b758 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/tsconfig.json +++ b/packages/kbn-securitysolution-io-ts-alerting-types/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-io-ts-alerting-types/src", "types": [ "jest", "node" diff --git a/packages/kbn-securitysolution-io-ts-list-types/BUILD.bazel b/packages/kbn-securitysolution-io-ts-list-types/BUILD.bazel index 66e52c1f509b44..d4ba51713d7e34 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/BUILD.bazel +++ b/packages/kbn-securitysolution-io-ts-list-types/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-io-ts-list-types" PKG_REQUIRE_NAME = "@kbn/securitysolution-io-ts-list-types" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-io-ts-list-types" SOURCE_FILES = glob( [ @@ -75,11 +74,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -110,7 +107,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-io-ts-list-types/tsconfig.json b/packages/kbn-securitysolution-io-ts-list-types/tsconfig.json index ca0ea969f89d8a..305954d669b758 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/tsconfig.json +++ b/packages/kbn-securitysolution-io-ts-list-types/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-io-ts-list-types/src", "types": [ "jest", "node" diff --git a/packages/kbn-securitysolution-io-ts-types/BUILD.bazel b/packages/kbn-securitysolution-io-ts-types/BUILD.bazel index a4f817ac55f092..794eab1635b73d 100644 --- a/packages/kbn-securitysolution-io-ts-types/BUILD.bazel +++ b/packages/kbn-securitysolution-io-ts-types/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-io-ts-types" PKG_REQUIRE_NAME = "@kbn/securitysolution-io-ts-types" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-io-ts-types" SOURCE_FILES = glob( [ @@ -73,11 +72,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -108,7 +105,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-io-ts-types/tsconfig.json b/packages/kbn-securitysolution-io-ts-types/tsconfig.json index c640181145be84..305954d669b758 100644 --- a/packages/kbn-securitysolution-io-ts-types/tsconfig.json +++ b/packages/kbn-securitysolution-io-ts-types/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-io-ts-types/src", "types": [ "jest", "node" diff --git a/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel b/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel index 46afff1a6affbd..0229acdb474e4f 100644 --- a/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-io-ts-utils/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-io-ts-utils" PKG_REQUIRE_NAME = "@kbn/securitysolution-io-ts-utils" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-io-ts-utils" SOURCE_FILES = glob( [ @@ -76,11 +75,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -111,7 +108,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-io-ts-utils/tsconfig.json b/packages/kbn-securitysolution-io-ts-utils/tsconfig.json index 7c71143083d4d4..305954d669b758 100644 --- a/packages/kbn-securitysolution-io-ts-utils/tsconfig.json +++ b/packages/kbn-securitysolution-io-ts-utils/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-io-ts-utils/src", "types": [ "jest", "node" diff --git a/packages/kbn-securitysolution-list-api/BUILD.bazel b/packages/kbn-securitysolution-list-api/BUILD.bazel index 587b564ab91926..c73b9b4ea7503c 100644 --- a/packages/kbn-securitysolution-list-api/BUILD.bazel +++ b/packages/kbn-securitysolution-list-api/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-list-api" PKG_REQUIRE_NAME = "@kbn/securitysolution-list-api" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-list-api" SOURCE_FILES = glob( [ @@ -75,11 +74,9 @@ ts_project( deps = TYPES_DEPS, args = ["--pretty"], declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -110,7 +107,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-list-api/tsconfig.json b/packages/kbn-securitysolution-list-api/tsconfig.json index d51cd3ac71d8d9..305954d669b758 100644 --- a/packages/kbn-securitysolution-list-api/tsconfig.json +++ b/packages/kbn-securitysolution-list-api/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-list-api/src", "types": [ "jest", "node" diff --git a/packages/kbn-securitysolution-list-constants/BUILD.bazel b/packages/kbn-securitysolution-list-constants/BUILD.bazel index 3802e3a5819697..339743721bc84b 100644 --- a/packages/kbn-securitysolution-list-constants/BUILD.bazel +++ b/packages/kbn-securitysolution-list-constants/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-list-constants" PKG_REQUIRE_NAME = "@kbn/securitysolution-list-constants" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-list-constants" SOURCE_FILES = glob( [ @@ -63,11 +62,9 @@ ts_project( deps = TYPES_DEPS, args = ["--pretty"], declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -98,7 +95,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-list-constants/tsconfig.json b/packages/kbn-securitysolution-list-constants/tsconfig.json index 8697cbd61580a8..305954d669b758 100644 --- a/packages/kbn-securitysolution-list-constants/tsconfig.json +++ b/packages/kbn-securitysolution-list-constants/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-list-constants/src", "types": [ "jest", "node" diff --git a/packages/kbn-securitysolution-list-hooks/BUILD.bazel b/packages/kbn-securitysolution-list-hooks/BUILD.bazel index 7b3fc87b6f87e1..d709a0ddfbb80e 100644 --- a/packages/kbn-securitysolution-list-hooks/BUILD.bazel +++ b/packages/kbn-securitysolution-list-hooks/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-list-hooks" PKG_REQUIRE_NAME = "@kbn/securitysolution-list-hooks" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-list-hooks" SOURCE_FILES = glob( [ @@ -83,11 +82,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -118,7 +115,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-list-hooks/tsconfig.json b/packages/kbn-securitysolution-list-hooks/tsconfig.json index 41ec03f2ebf352..305954d669b758 100644 --- a/packages/kbn-securitysolution-list-hooks/tsconfig.json +++ b/packages/kbn-securitysolution-list-hooks/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-list-hooks/src", "types": [ "jest", "node" diff --git a/packages/kbn-securitysolution-list-utils/BUILD.bazel b/packages/kbn-securitysolution-list-utils/BUILD.bazel index 87e546a1fff089..642e4a970aca2a 100644 --- a/packages/kbn-securitysolution-list-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-list-utils/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-list-utils" PKG_REQUIRE_NAME = "@kbn/securitysolution-list-utils" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-list-utils" SOURCE_FILES = glob( [ @@ -82,11 +81,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -118,7 +115,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-list-utils/tsconfig.json b/packages/kbn-securitysolution-list-utils/tsconfig.json index fa50bd7981214f..305954d669b758 100644 --- a/packages/kbn-securitysolution-list-utils/tsconfig.json +++ b/packages/kbn-securitysolution-list-utils/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-list-utils/src", "types": [ "jest", "node" diff --git a/packages/kbn-securitysolution-rules/BUILD.bazel b/packages/kbn-securitysolution-rules/BUILD.bazel index 7158f759f4466d..80a27a426fbb26 100644 --- a/packages/kbn-securitysolution-rules/BUILD.bazel +++ b/packages/kbn-securitysolution-rules/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-rules" PKG_REQUIRE_NAME = "@kbn/securitysolution-rules" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-rules" SOURCE_FILES = glob( [ @@ -63,11 +62,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -98,7 +95,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-rules/tsconfig.json b/packages/kbn-securitysolution-rules/tsconfig.json index 3895e13ad28ede..305954d669b758 100644 --- a/packages/kbn-securitysolution-rules/tsconfig.json +++ b/packages/kbn-securitysolution-rules/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-rules/src", "types": [ "jest", "node" diff --git a/packages/kbn-securitysolution-t-grid/BUILD.bazel b/packages/kbn-securitysolution-t-grid/BUILD.bazel index e0898f90d7f870..6ee620199a6e6e 100644 --- a/packages/kbn-securitysolution-t-grid/BUILD.bazel +++ b/packages/kbn-securitysolution-t-grid/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-t-grid" PKG_REQUIRE_NAME = "@kbn/securitysolution-t-grid" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-t-grid" SOURCE_FILES = glob( [ @@ -72,11 +71,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -107,7 +104,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-t-grid/tsconfig.json b/packages/kbn-securitysolution-t-grid/tsconfig.json index 3c701d149ab2e4..305954d669b758 100644 --- a/packages/kbn-securitysolution-t-grid/tsconfig.json +++ b/packages/kbn-securitysolution-t-grid/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-t-grid/src", "types": [ "jest", "node" diff --git a/packages/kbn-securitysolution-utils/BUILD.bazel b/packages/kbn-securitysolution-utils/BUILD.bazel index d22e31daacd55b..cfb6b722ea2e63 100644 --- a/packages/kbn-securitysolution-utils/BUILD.bazel +++ b/packages/kbn-securitysolution-utils/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-securitysolution-utils" PKG_REQUIRE_NAME = "@kbn/securitysolution-utils" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__securitysolution-utils" SOURCE_FILES = glob( [ @@ -61,11 +60,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -96,7 +93,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-securitysolution-utils/tsconfig.json b/packages/kbn-securitysolution-utils/tsconfig.json index 23fdf3178e174c..305954d669b758 100644 --- a/packages/kbn-securitysolution-utils/tsconfig.json +++ b/packages/kbn-securitysolution-utils/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-securitysolution-utils/src", "types": [ "jest", "node" diff --git a/packages/kbn-server-http-tools/BUILD.bazel b/packages/kbn-server-http-tools/BUILD.bazel index e524078a8ba89e..29ca48adc566e2 100644 --- a/packages/kbn-server-http-tools/BUILD.bazel +++ b/packages/kbn-server-http-tools/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-server-http-tools" PKG_REQUIRE_NAME = "@kbn/server-http-tools" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__server-http-tools" SOURCE_FILES = glob( [ @@ -72,10 +71,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -107,7 +104,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-server-http-tools/tsconfig.json b/packages/kbn-server-http-tools/tsconfig.json index e378e41c3828bf..c89835eada8189 100644 --- a/packages/kbn-server-http-tools/tsconfig.json +++ b/packages/kbn-server-http-tools/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target/types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-server-http-tools/src", "types": [ "jest", "node" diff --git a/packages/kbn-server-route-repository/BUILD.bazel b/packages/kbn-server-route-repository/BUILD.bazel index a0e1cf41dcf8f1..06c09260e2fa60 100644 --- a/packages/kbn-server-route-repository/BUILD.bazel +++ b/packages/kbn-server-route-repository/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-server-route-repository" PKG_REQUIRE_NAME = "@kbn/server-route-repository" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__server-route-repository" SOURCE_FILES = glob( [ @@ -68,10 +67,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -103,7 +100,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-server-route-repository/tsconfig.json b/packages/kbn-server-route-repository/tsconfig.json index 447a2084926c69..db908eacc6c35c 100644 --- a/packages/kbn-server-route-repository/tsconfig.json +++ b/packages/kbn-server-route-repository/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-server-route-repository/src", "stripInternal": false, "types": [ "jest", diff --git a/packages/kbn-std/BUILD.bazel b/packages/kbn-std/BUILD.bazel index 1e45803dbdcf17..08cfa2a2a1308e 100644 --- a/packages/kbn-std/BUILD.bazel +++ b/packages/kbn-std/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-std" PKG_REQUIRE_NAME = "@kbn/std" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__std" SOURCE_FILES = glob( [ @@ -67,10 +66,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -102,7 +99,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-std/tsconfig.json b/packages/kbn-std/tsconfig.json index 2674ca26e96d52..04d1a54cc39517 100644 --- a/packages/kbn-std/tsconfig.json +++ b/packages/kbn-std/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-std/src", "stripInternal": true, "types": [ "jest", diff --git a/packages/kbn-storybook/BUILD.bazel b/packages/kbn-storybook/BUILD.bazel index 686de744b656f7..ed7b167a30078e 100644 --- a/packages/kbn-storybook/BUILD.bazel +++ b/packages/kbn-storybook/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-storybook" PKG_REQUIRE_NAME = "@kbn/storybook" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__storybook" SOURCE_FILES = glob( [ @@ -93,11 +92,9 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -128,7 +125,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-storybook/tsconfig.json b/packages/kbn-storybook/tsconfig.json index 0ccf3e78c82880..53e689b569e5e5 100644 --- a/packages/kbn-storybook/tsconfig.json +++ b/packages/kbn-storybook/tsconfig.json @@ -2,14 +2,11 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "incremental": false, "outDir": "target_types", "rootDir": "src", "skipLibCheck": true, - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-storybook", "target": "es2015", "types": ["node"] }, diff --git a/packages/kbn-telemetry-tools/BUILD.bazel b/packages/kbn-telemetry-tools/BUILD.bazel index 1f53e4b71ae212..50336dc44da005 100644 --- a/packages/kbn-telemetry-tools/BUILD.bazel +++ b/packages/kbn-telemetry-tools/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-telemetry-tools" PKG_REQUIRE_NAME = "@kbn/telemetry-tools" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__telemetry-tools" SOURCE_FILES = glob( [ @@ -71,10 +70,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -106,7 +103,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-telemetry-tools/tsconfig.json b/packages/kbn-telemetry-tools/tsconfig.json index 034d7c0c6e745a..25e1d341ac07b3 100644 --- a/packages/kbn-telemetry-tools/tsconfig.json +++ b/packages/kbn-telemetry-tools/tsconfig.json @@ -2,13 +2,10 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "isolatedModules": true, "outDir": "./target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-telemetry-tools/src", "types": [ "jest", "node" diff --git a/packages/kbn-test-jest-helpers/BUILD.bazel b/packages/kbn-test-jest-helpers/BUILD.bazel index c713e245929449..d910fab5295d58 100644 --- a/packages/kbn-test-jest-helpers/BUILD.bazel +++ b/packages/kbn-test-jest-helpers/BUILD.bazel @@ -5,7 +5,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-test-jest-helpers" PKG_REQUIRE_NAME = "@kbn/test-jest-helpers" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__test-jest-helpers" SOURCE_FILES = glob( [ @@ -133,10 +132,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -168,7 +165,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-test-jest-helpers/tsconfig.json b/packages/kbn-test-jest-helpers/tsconfig.json index 7a1121c9e91f13..72d996c69e2dac 100644 --- a/packages/kbn-test-jest-helpers/tsconfig.json +++ b/packages/kbn-test-jest-helpers/tsconfig.json @@ -2,13 +2,10 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "stripInternal": true, "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../../../packages/kbn-test-jest-helpers/src", "types": ["jest", "node"] }, "include": ["src/**/*"], diff --git a/packages/kbn-test/BUILD.bazel b/packages/kbn-test/BUILD.bazel index 233aeab6250b19..6732b08d8bc72a 100644 --- a/packages/kbn-test/BUILD.bazel +++ b/packages/kbn-test/BUILD.bazel @@ -5,7 +5,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-test" PKG_REQUIRE_NAME = "@kbn/test" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__test" SOURCE_FILES = glob( [ @@ -139,10 +138,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -174,7 +171,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-test/tsconfig.json b/packages/kbn-test/tsconfig.json index 7ba83019b00756..aa9fb4f04135d1 100644 --- a/packages/kbn-test/tsconfig.json +++ b/packages/kbn-test/tsconfig.json @@ -2,13 +2,10 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "stripInternal": true, "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../../../packages/kbn-test/src", "types": ["jest", "node"] }, "include": ["src/**/*", "index.d.ts"], diff --git a/packages/kbn-typed-react-router-config/BUILD.bazel b/packages/kbn-typed-react-router-config/BUILD.bazel index 62fd6adf5bb269..c2a5aa84dbb2d9 100644 --- a/packages/kbn-typed-react-router-config/BUILD.bazel +++ b/packages/kbn-typed-react-router-config/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-typed-react-router-config" PKG_REQUIRE_NAME = "@kbn/typed-react-router-config" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__typed-react-router-config" SOURCE_FILES = glob( [ @@ -83,10 +82,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -118,7 +115,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-typed-react-router-config/tsconfig.json b/packages/kbn-typed-react-router-config/tsconfig.json index 8e17781119ee9e..2619b0ff8f9d3c 100644 --- a/packages/kbn-typed-react-router-config/tsconfig.json +++ b/packages/kbn-typed-react-router-config/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "isolatedModules": true, "outDir": "./target_types", - "sourceMap": true, - "sourceRoot": "../../../../../packages/kbn-typed-react-router-config/src", "stripInternal": true, "types": [ "node", diff --git a/packages/kbn-ui-shared-deps-npm/BUILD.bazel b/packages/kbn-ui-shared-deps-npm/BUILD.bazel index 22d51e260bcc07..17bbb09bd36e32 100644 --- a/packages/kbn-ui-shared-deps-npm/BUILD.bazel +++ b/packages/kbn-ui-shared-deps-npm/BUILD.bazel @@ -5,7 +5,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-ui-shared-deps-npm" PKG_REQUIRE_NAME = "@kbn/ui-shared-deps-npm" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__ui-shared-deps-npm" SOURCE_FILES = glob( [ @@ -121,11 +120,9 @@ ts_project( deps = TYPES_DEPS, allow_js = True, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -174,7 +171,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-ui-shared-deps-npm/tsconfig.json b/packages/kbn-ui-shared-deps-npm/tsconfig.json index 107d82aa59ee8c..a8a821708d036f 100644 --- a/packages/kbn-ui-shared-deps-npm/tsconfig.json +++ b/packages/kbn-ui-shared-deps-npm/tsconfig.json @@ -3,12 +3,9 @@ "compilerOptions": { "allowJs": true, "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-ui-shared-deps-npm/src", "types": [ "node", ] diff --git a/packages/kbn-ui-shared-deps-src/BUILD.bazel b/packages/kbn-ui-shared-deps-src/BUILD.bazel index 3617956b15c4a6..295f6fa0594edb 100644 --- a/packages/kbn-ui-shared-deps-src/BUILD.bazel +++ b/packages/kbn-ui-shared-deps-src/BUILD.bazel @@ -5,7 +5,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-ui-shared-deps-src" PKG_REQUIRE_NAME = "@kbn/ui-shared-deps-src" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__ui-shared-deps-src" SOURCE_FILES = glob( [ @@ -77,11 +76,9 @@ ts_project( deps = TYPES_DEPS, allow_js = True, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", root_dir = "src", - source_map = True, tsconfig = ":tsconfig", ) @@ -130,7 +127,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-ui-shared-deps-src/tsconfig.json b/packages/kbn-ui-shared-deps-src/tsconfig.json index 521fb122e4659d..a8a821708d036f 100644 --- a/packages/kbn-ui-shared-deps-src/tsconfig.json +++ b/packages/kbn-ui-shared-deps-src/tsconfig.json @@ -3,12 +3,9 @@ "compilerOptions": { "allowJs": true, "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-ui-shared-deps-src/src", "types": [ "node", ] diff --git a/packages/kbn-ui-theme/BUILD.bazel b/packages/kbn-ui-theme/BUILD.bazel index 8e388efe23757d..5a848ddcc838f8 100644 --- a/packages/kbn-ui-theme/BUILD.bazel +++ b/packages/kbn-ui-theme/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-ui-theme" PKG_REQUIRE_NAME = "@kbn/ui-theme" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__ui-theme" SOURCE_FILES = glob( [ @@ -61,10 +60,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -96,7 +93,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-ui-theme/tsconfig.json b/packages/kbn-ui-theme/tsconfig.json index e1c27e88f1c91f..0fd9a15b5dbf88 100644 --- a/packages/kbn-ui-theme/tsconfig.json +++ b/packages/kbn-ui-theme/tsconfig.json @@ -2,11 +2,8 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-ui-theme/src", "stripInternal": true, "types": ["node"] }, diff --git a/packages/kbn-utility-types/BUILD.bazel b/packages/kbn-utility-types/BUILD.bazel index 159ab134684f88..c556751d7550e5 100644 --- a/packages/kbn-utility-types/BUILD.bazel +++ b/packages/kbn-utility-types/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-utility-types" PKG_REQUIRE_NAME = "@kbn/utility-types" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__utility-types" SOURCE_FILES = glob([ "src/jest/index.ts", @@ -56,10 +55,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -91,7 +88,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-utility-types/tsconfig.json b/packages/kbn-utility-types/tsconfig.json index 997bcf9e0c45bd..1d7104a6fc2546 100644 --- a/packages/kbn-utility-types/tsconfig.json +++ b/packages/kbn-utility-types/tsconfig.json @@ -2,12 +2,9 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "./target_types", "rootDir": "./src", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-utility-types", "stripInternal": true, "types": [ "jest", diff --git a/packages/kbn-utils/BUILD.bazel b/packages/kbn-utils/BUILD.bazel index 6ac23129a1d03d..b60c60af43c25a 100644 --- a/packages/kbn-utils/BUILD.bazel +++ b/packages/kbn-utils/BUILD.bazel @@ -4,7 +4,6 @@ load("//src/dev/bazel:index.bzl", "jsts_transpiler", "pkg_npm", "pkg_npm_types", PKG_BASE_NAME = "kbn-utils" PKG_REQUIRE_NAME = "@kbn/utils" -TYPES_PKG_REQUIRE_NAME = "@types/kbn__utils" SOURCE_FILES = glob( [ @@ -60,10 +59,8 @@ ts_project( srcs = SRCS, deps = TYPES_DEPS, declaration = True, - declaration_map = True, emit_declaration_only = True, out_dir = "target_types", - source_map = True, root_dir = "src", tsconfig = ":tsconfig", ) @@ -95,7 +92,7 @@ pkg_npm_types( name = "npm_module_types", srcs = SRCS, deps = [":tsc_types"], - package_name = TYPES_PKG_REQUIRE_NAME, + package_name = PKG_REQUIRE_NAME, tsconfig = ":tsconfig", visibility = ["//visibility:public"], ) diff --git a/packages/kbn-utils/tsconfig.json b/packages/kbn-utils/tsconfig.json index 85c26f42a695cd..a2851d55c0a456 100644 --- a/packages/kbn-utils/tsconfig.json +++ b/packages/kbn-utils/tsconfig.json @@ -2,11 +2,8 @@ "extends": "../../tsconfig.bazel.json", "compilerOptions": { "declaration": true, - "declarationMap": true, "emitDeclarationOnly": true, "outDir": "target_types", - "sourceMap": true, - "sourceRoot": "../../../../packages/kbn-utils/src", "types": [ "jest", "node" diff --git a/src/dev/bazel/pkg_npm_types/pkg_npm_types.bzl b/src/dev/bazel/pkg_npm_types/pkg_npm_types.bzl index 4651e9264ef501..ed48228bc95871 100644 --- a/src/dev/bazel/pkg_npm_types/pkg_npm_types.bzl +++ b/src/dev/bazel/pkg_npm_types/pkg_npm_types.bzl @@ -33,6 +33,9 @@ def _collect_inputs_deps_and_transitive_types_deps(ctx): deps_files = depset(transitive = deps_files_depsets).to_list() return [deps_files, transitive_types_deps] +def _get_type_package_name(actualName): + return "@types/" + actualName.replace("@", "").replace("/", "__") + def _calculate_entrypoint_path(ctx): return _join(ctx.bin_dir.path, ctx.label.package, _get_types_outdir_name(ctx), ctx.attr.entrypoint_name) @@ -78,7 +81,7 @@ def _pkg_npm_types_impl(ctx): # gathering template args template_args = [ - "NAME", ctx.attr.package_name + "NAME", _get_type_package_name(ctx.attr.package_name) ] # layout api extractor arguments @@ -119,7 +122,7 @@ def _pkg_npm_types_impl(ctx): deps = transitive_types_deps, ), LinkablePackageInfo( - package_name = ctx.attr.package_name, + package_name = _get_type_package_name(ctx.attr.package_name), package_path = "", path = package_dir.path, files = package_dir_depset, From f0803c9a3b7f815248a675046cd76cef1b3f33d3 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 24 Feb 2022 20:58:47 +0200 Subject: [PATCH 03/28] [Cases] Show release phase badges to page titles (#126152) --- .../public/common/mock/test_providers.tsx | 18 +++-- ..._cases_add_to_existing_case_modal.test.tsx | 1 + .../public/components/cases_context/index.tsx | 6 +- .../use_cases_add_to_new_case_flyout.test.tsx | 1 + .../editable_title.test.tsx.snap | 51 ++++++------ .../__snapshots__/index.test.tsx.snap | 60 +++++++------- .../__snapshots__/title.test.tsx.snap | 35 -------- .../header_page/editable_title.test.tsx | 62 ++++++++++++--- .../components/header_page/editable_title.tsx | 29 +++---- .../components/header_page/index.test.tsx | 48 ++++++++--- .../public/components/header_page/index.tsx | 21 ++--- .../components/header_page/title.test.tsx | 55 +++++++------ .../public/components/header_page/title.tsx | 79 ++++++++++--------- .../components/header_page/translations.ts | 18 +++++ .../public/components/header_page/types.ts | 20 ----- .../plugins/cases/public/components/types.ts | 2 + .../cases/public/methods/get_cases.tsx | 3 +- .../public/methods/get_cases_context.tsx | 5 +- 18 files changed, 277 insertions(+), 237 deletions(-) delete mode 100644 x-pack/plugins/cases/public/components/header_page/__snapshots__/title.test.tsx.snap delete mode 100644 x-pack/plugins/cases/public/components/header_page/types.ts diff --git a/x-pack/plugins/cases/public/common/mock/test_providers.tsx b/x-pack/plugins/cases/public/common/mock/test_providers.tsx index 314796bdaa0ed9..0bd08e348c3bdc 100644 --- a/x-pack/plugins/cases/public/common/mock/test_providers.tsx +++ b/x-pack/plugins/cases/public/common/mock/test_providers.tsx @@ -21,12 +21,14 @@ import { } from '../lib/kibana/kibana_react.mock'; import { FieldHook } from '../shared_imports'; import { StartServices } from '../../types'; +import { ReleasePhase } from '../../components/types'; -interface Props { +interface TestProviderProps { children: React.ReactNode; userCanCrud?: boolean; features?: CasesFeatures; owner?: string[]; + releasePhase?: ReleasePhase; } type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult; @@ -34,11 +36,12 @@ window.scrollTo = jest.fn(); const MockKibanaContextProvider = createKibanaContextProviderMock(); /** A utility for wrapping children in the providers required to run most tests */ -const TestProvidersComponent: React.FC = ({ +const TestProvidersComponent: React.FC = ({ children, features, owner = [SECURITY_SOLUTION_OWNER], userCanCrud = true, + releasePhase = 'ga', }) => { return ( @@ -63,18 +66,17 @@ export const createAppMockRenderer = ({ features, owner = [SECURITY_SOLUTION_OWNER], userCanCrud = true, -}: { - features?: CasesFeatures; - owner?: string[]; - userCanCrud?: boolean; -} = {}): AppMockRenderer => { + releasePhase = 'ga', +}: Omit = {}): AppMockRenderer => { const services = createStartServicesMock(); const AppWrapper: React.FC<{ children: React.ReactElement }> = ({ children }) => ( ({ eui: euiDarkVars, darkMode: true })}> - {children} + + {children} + diff --git a/x-pack/plugins/cases/public/components/all_cases/selector_modal/uses_cases_add_to_existing_case_modal.test.tsx b/x-pack/plugins/cases/public/components/all_cases/selector_modal/uses_cases_add_to_existing_case_modal.test.tsx index 954284a670fd2c..6eeff6102ae6a3 100644 --- a/x-pack/plugins/cases/public/components/all_cases/selector_modal/uses_cases_add_to_existing_case_modal.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/selector_modal/uses_cases_add_to_existing_case_modal.test.tsx @@ -32,6 +32,7 @@ describe('use cases add to existing case modal hook', () => { basePath: '/jest', dispatch, features: { alerts: { sync: true }, metrics: [] }, + releasePhase: 'ga', }} > {children} diff --git a/x-pack/plugins/cases/public/components/cases_context/index.tsx b/x-pack/plugins/cases/public/components/cases_context/index.tsx index 1f1da31595a041..70490882d31b3e 100644 --- a/x-pack/plugins/cases/public/components/cases_context/index.tsx +++ b/x-pack/plugins/cases/public/components/cases_context/index.tsx @@ -17,6 +17,7 @@ import { } from './cases_context_reducer'; import { CasesContextFeatures, CasesFeatures } from '../../containers/types'; import { CasesGlobalComponents } from './cases_global_components'; +import { ReleasePhase } from '../types'; export type CasesContextValueDispatch = Dispatch; @@ -27,12 +28,14 @@ export interface CasesContextValue { userCanCrud: boolean; basePath: string; features: CasesContextFeatures; + releasePhase: ReleasePhase; dispatch: CasesContextValueDispatch; } export interface CasesContextProps extends Pick { basePath?: string; features?: CasesFeatures; + releasePhase?: ReleasePhase; } export const CasesContext = React.createContext(undefined); @@ -44,7 +47,7 @@ export interface CasesContextStateValue extends Omit = ({ children, - value: { owner, userCanCrud, basePath = DEFAULT_BASE_PATH, features = {} }, + value: { owner, userCanCrud, basePath = DEFAULT_BASE_PATH, features = {}, releasePhase = 'ga' }, }) => { const { appId, appTitle } = useApplication(); const [state, dispatch] = useReducer(casesContextReducer, getInitialCasesContextState()); @@ -57,6 +60,7 @@ export const CasesProvider: React.FC<{ value: CasesContextProps }> = ({ * of the DEFAULT_FEATURES object */ features: merge({}, DEFAULT_FEATURES, features), + releasePhase, dispatch, })); diff --git a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.test.tsx b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.test.tsx index 2c3750887cb1de..103e24c4b7a656 100644 --- a/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.test.tsx +++ b/x-pack/plugins/cases/public/components/create/flyout/use_cases_add_to_new_case_flyout.test.tsx @@ -29,6 +29,7 @@ describe('use cases add to new case flyout hook', () => { basePath: '/jest', dispatch, features: { alerts: { sync: true }, metrics: [] }, + releasePhase: 'ga', }} > {children} diff --git a/x-pack/plugins/cases/public/components/header_page/__snapshots__/editable_title.test.tsx.snap b/x-pack/plugins/cases/public/components/header_page/__snapshots__/editable_title.test.tsx.snap index 86d752f84a8b38..ae50f4fd81cb69 100644 --- a/x-pack/plugins/cases/public/components/header_page/__snapshots__/editable_title.test.tsx.snap +++ b/x-pack/plugins/cases/public/components/header_page/__snapshots__/editable_title.test.tsx.snap @@ -1,27 +1,30 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`EditableTitle it renders 1`] = ` - - - - - - - - +exports[`EditableTitle renders 1`] = ` + + + + + + + + + `; diff --git a/x-pack/plugins/cases/public/components/header_page/__snapshots__/index.test.tsx.snap b/x-pack/plugins/cases/public/components/header_page/__snapshots__/index.test.tsx.snap index 17517b1d05f197..b5175e7e8c1160 100644 --- a/x-pack/plugins/cases/public/components/header_page/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/cases/public/components/header_page/__snapshots__/index.test.tsx.snap @@ -1,40 +1,34 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`HeaderPage it renders 1`] = ` -
- - - + + + - - - - -

- Test supplement -

-
-
-
+ > + +

+ Test supplement +

+
+ + + +
`; diff --git a/x-pack/plugins/cases/public/components/header_page/__snapshots__/title.test.tsx.snap b/x-pack/plugins/cases/public/components/header_page/__snapshots__/title.test.tsx.snap deleted file mode 100644 index 60714d6e7bb29c..00000000000000 --- a/x-pack/plugins/cases/public/components/header_page/__snapshots__/title.test.tsx.snap +++ /dev/null @@ -1,35 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Title it renders 1`] = ` - -

- - - -

-
-`; - -exports[`Title it renders the title if is not a string 1`] = ` - -

- - Test title - -

-
-`; diff --git a/x-pack/plugins/cases/public/components/header_page/editable_title.test.tsx b/x-pack/plugins/cases/public/components/header_page/editable_title.test.tsx index 19aea39f1f793b..9cf956c78fe726 100644 --- a/x-pack/plugins/cases/public/components/header_page/editable_title.test.tsx +++ b/x-pack/plugins/cases/public/components/header_page/editable_title.test.tsx @@ -9,7 +9,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import '../../common/mock/match_media'; -import { TestProviders } from '../../common/mock'; +import { AppMockRenderer, createAppMockRenderer, TestProviders } from '../../common/mock'; import { EditableTitle, EditableTitleProps } from './editable_title'; import { useMountAppended } from '../../utils/use_mount_appended'; @@ -27,13 +27,17 @@ describe('EditableTitle', () => { jest.clearAllMocks(); }); - test('it renders', () => { - const wrapper = shallow(); + it('renders', () => { + const wrapper = shallow( + + + + ); expect(wrapper).toMatchSnapshot(); }); - test('it does not show the edit icon when the user does not have edit permissions', () => { + it('does not show the edit icon when the user does not have edit permissions', () => { const wrapper = mount( @@ -43,7 +47,7 @@ describe('EditableTitle', () => { expect(wrapper.find('[data-test-subj="editable-title-edit-icon"]').exists()).toBeFalsy(); }); - test('it shows the edit title input field', () => { + it('shows the edit title input field', () => { const wrapper = mount( @@ -58,7 +62,7 @@ describe('EditableTitle', () => { ); }); - test('it shows the submit button', () => { + it('shows the submit button', () => { const wrapper = mount( @@ -73,7 +77,7 @@ describe('EditableTitle', () => { ); }); - test('it shows the cancel button', () => { + it('shows the cancel button', () => { const wrapper = mount( @@ -88,7 +92,7 @@ describe('EditableTitle', () => { ); }); - test('it DOES NOT shows the edit icon when in edit mode', () => { + it('DOES NOT shows the edit icon when in edit mode', () => { const wrapper = mount( @@ -103,7 +107,7 @@ describe('EditableTitle', () => { ); }); - test('it switch to non edit mode when canceled', () => { + it('switch to non edit mode when canceled', () => { const wrapper = mount( @@ -117,7 +121,7 @@ describe('EditableTitle', () => { expect(wrapper.find('[data-test-subj="editable-title-edit-icon"]').first().exists()).toBe(true); }); - test('it should change the title', () => { + it('should change the title', () => { const newTitle = 'new test title'; const wrapper = mount( @@ -140,7 +144,7 @@ describe('EditableTitle', () => { ).toEqual(newTitle); }); - test('it should NOT change the title when cancel', () => { + it('should NOT change the title when cancel', () => { const title = 'Test title'; const newTitle = 'new test title'; @@ -164,7 +168,7 @@ describe('EditableTitle', () => { expect(wrapper.find('h1[data-test-subj="header-page-title"]').text()).toEqual(title); }); - test('it submits the title', () => { + it('submits the title', () => { const newTitle = 'new test title'; const wrapper = mount( @@ -188,7 +192,7 @@ describe('EditableTitle', () => { expect(wrapper.find('[data-test-subj="editable-title-edit-icon"]').first().exists()).toBe(true); }); - test('it does not submits the title when the length is longer than 64 characters', () => { + it('does not submit the title when the length is longer than 64 characters', () => { const longTitle = 'This is a title that should not be saved as it is longer than 64 characters.'; @@ -216,4 +220,36 @@ describe('EditableTitle', () => { false ); }); + + describe('Badges', () => { + let appMock: AppMockRenderer; + + beforeEach(() => { + appMock = createAppMockRenderer(); + }); + + it('does not render the badge if the release is ga', () => { + const renderResult = appMock.render(); + + expect(renderResult.getByText('Test title')).toBeInTheDocument(); + expect(renderResult.queryByText('Beta')).toBeFalsy(); + expect(renderResult.queryByText('Technical preview')).toBeFalsy(); + }); + + it('does render the beta badge', () => { + appMock = createAppMockRenderer({ releasePhase: 'beta' }); + const renderResult = appMock.render(); + + expect(renderResult.getByText('Test title')).toBeInTheDocument(); + expect(renderResult.getByText('Beta')).toBeInTheDocument(); + }); + + it('does render the experimental badge', () => { + appMock = createAppMockRenderer({ releasePhase: 'experimental' }); + const renderResult = appMock.render(); + + expect(renderResult.getByText('Test title')).toBeInTheDocument(); + expect(renderResult.getByText('Technical preview')).toBeInTheDocument(); + }); + }); }); diff --git a/x-pack/plugins/cases/public/components/header_page/editable_title.tsx b/x-pack/plugins/cases/public/components/header_page/editable_title.tsx index 674a31122d9837..95e3f6f4a4bcb3 100644 --- a/x-pack/plugins/cases/public/components/header_page/editable_title.tsx +++ b/x-pack/plugins/cases/public/components/header_page/editable_title.tsx @@ -22,6 +22,7 @@ import { import { MAX_TITLE_LENGTH } from '../../../common/constants'; import * as i18n from './translations'; import { Title } from './title'; +import { useCasesContext } from '../cases_context/use_cases_context'; const MyEuiButtonIcon = styled(EuiButtonIcon)` ${({ theme }) => css` @@ -48,6 +49,7 @@ const EditableTitleComponent: React.FC = ({ isLoading, title, }) => { + const { releasePhase } = useCasesContext(); const [editMode, setEditMode] = useState(false); const [errors, setErrors] = useState([]); const [newTitle, setNewTitle] = useState(title); @@ -116,22 +118,17 @@ const EditableTitleComponent: React.FC = ({ ) : ( - - - - </EuiFlexItem> - <EuiFlexItem grow={false}> - {isLoading && <MySpinner data-test-subj="editable-title-loading" />} - {!isLoading && userCanCrud && ( - <MyEuiButtonIcon - aria-label={i18n.EDIT_TITLE_ARIA(title as string)} - iconType="pencil" - onClick={onClickEditIcon} - data-test-subj="editable-title-edit-icon" - /> - )} - </EuiFlexItem> - </EuiFlexGroup> + <Title title={title} releasePhase={releasePhase}> + {isLoading && <MySpinner data-test-subj="editable-title-loading" />} + {!isLoading && userCanCrud && ( + <MyEuiButtonIcon + aria-label={i18n.EDIT_TITLE_ARIA(title as string)} + iconType="pencil" + onClick={onClickEditIcon} + data-test-subj="editable-title-edit-icon" + /> + )} + ); }; EditableTitleComponent.displayName = 'EditableTitle'; diff --git a/x-pack/plugins/cases/public/components/header_page/index.test.tsx b/x-pack/plugins/cases/public/components/header_page/index.test.tsx index 4f0da554f3d1b2..bd4069cd11679a 100644 --- a/x-pack/plugins/cases/public/components/header_page/index.test.tsx +++ b/x-pack/plugins/cases/public/components/header_page/index.test.tsx @@ -10,7 +10,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import '../../common/mock/match_media'; -import { TestProviders } from '../../common/mock'; +import { AppMockRenderer, createAppMockRenderer, TestProviders } from '../../common/mock'; import { HeaderPage } from './index'; import { useMountAppended } from '../../utils/use_mount_appended'; @@ -21,15 +21,11 @@ describe('HeaderPage', () => { test('it renders', () => { const wrapper = shallow( - -

{'Test supplement'}

-
+ + +

{'Test supplement'}

+
+
); expect(wrapper).toMatchSnapshot(); @@ -142,4 +138,36 @@ describe('HeaderPage', () => { expect(casesHeaderPage).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin); expect(casesHeaderPage).not.toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l); }); + + describe('Badges', () => { + let appMock: AppMockRenderer; + + beforeEach(() => { + appMock = createAppMockRenderer(); + }); + + it('does not render the badge if the release is ga', () => { + const renderResult = appMock.render(); + + expect(renderResult.getByText('Test title')).toBeInTheDocument(); + expect(renderResult.queryByText('Beta')).toBeFalsy(); + expect(renderResult.queryByText('Technical preview')).toBeFalsy(); + }); + + it('does render the beta badge', () => { + appMock = createAppMockRenderer({ releasePhase: 'beta' }); + const renderResult = appMock.render(); + + expect(renderResult.getByText('Test title')).toBeInTheDocument(); + expect(renderResult.getByText('Beta')).toBeInTheDocument(); + }); + + it('does render the experimental badge', () => { + appMock = createAppMockRenderer({ releasePhase: 'experimental' }); + const renderResult = appMock.render(); + + expect(renderResult.getByText('Test title')).toBeInTheDocument(); + expect(renderResult.getByText('Technical preview')).toBeInTheDocument(); + }); + }); }); diff --git a/x-pack/plugins/cases/public/components/header_page/index.tsx b/x-pack/plugins/cases/public/components/header_page/index.tsx index 3afcd15bfa817f..db0c9bb3011c72 100644 --- a/x-pack/plugins/cases/public/components/header_page/index.tsx +++ b/x-pack/plugins/cases/public/components/header_page/index.tsx @@ -6,15 +6,15 @@ */ import React, { useCallback } from 'react'; -import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiProgress } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiProgress } from '@elastic/eui'; import styled, { css } from 'styled-components'; -import { useAllCasesNavigation } from '../../common/navigation'; +import { useAllCasesNavigation } from '../../common/navigation'; import { LinkIcon } from '../link_icon'; import { Subtitle, SubtitleProps } from '../subtitle'; import { Title } from './title'; -import { BadgeOptions, TitleProp } from './types'; import * as i18n from './translations'; +import { useCasesContext } from '../cases_context/use_cases_context'; interface HeaderProps { border?: boolean; @@ -55,24 +55,17 @@ const LinkBack = styled.div.attrs({ `; LinkBack.displayName = 'LinkBack'; -const Badge = styled(EuiBadge)` - letter-spacing: 0; -` as unknown as typeof EuiBadge; -Badge.displayName = 'Badge'; - export interface HeaderPageProps extends HeaderProps { showBackButton?: boolean; - badgeOptions?: BadgeOptions; children?: React.ReactNode; subtitle?: SubtitleProps['items']; subtitle2?: SubtitleProps['items']; - title: TitleProp; + title: string | React.ReactNode; titleNode?: React.ReactElement; } const HeaderPageComponent: React.FC = ({ showBackButton = false, - badgeOptions, border, children, isLoading, @@ -80,8 +73,8 @@ const HeaderPageComponent: React.FC = ({ subtitle2, title, titleNode, - ...rest }) => { + const { releasePhase } = useCasesContext(); const { getAllCasesUrl, navigateToAllCases } = useAllCasesNavigation(); const navigateToAllCasesClick = useCallback( @@ -95,7 +88,7 @@ const HeaderPageComponent: React.FC = ({ ); return ( -
+
{showBackButton && ( @@ -111,7 +104,7 @@ const HeaderPageComponent: React.FC = ({ )} - {titleNode || } + {titleNode || <Title title={title} releasePhase={releasePhase} />} {subtitle && <Subtitle data-test-subj="header-page-subtitle" items={subtitle} />} {subtitle2 && <Subtitle data-test-subj="header-page-subtitle-2" items={subtitle2} />} diff --git a/x-pack/plugins/cases/public/components/header_page/title.test.tsx b/x-pack/plugins/cases/public/components/header_page/title.test.tsx index 063b21e4d89066..bd26b37956e65b 100644 --- a/x-pack/plugins/cases/public/components/header_page/title.test.tsx +++ b/x-pack/plugins/cases/public/components/header_page/title.test.tsx @@ -5,41 +5,50 @@ * 2.0. */ -import { shallow } from 'enzyme'; import React from 'react'; +import { render, screen } from '@testing-library/react'; import '../../common/mock/match_media'; -import { TestProviders } from '../../common/mock'; import { Title } from './title'; -import { useMountAppended } from '../../utils/use_mount_appended'; describe('Title', () => { - const mount = useMountAppended(); - - test('it renders', () => { - const wrapper = shallow( - <Title - badgeOptions={{ beta: true, text: 'Beta', tooltip: 'Test tooltip' }} - title="Test title" - /> - ); + it('does not render the badge if the release is ga', () => { + render(<Title title="Test title" releasePhase="ga" />); - expect(wrapper).toMatchSnapshot(); + expect(screen.getByText('Test title')).toBeInTheDocument(); + expect(screen.queryByText('Beta')).toBeFalsy(); + expect(screen.queryByText('Technical preview')).toBeFalsy(); }); - test('it renders the title', () => { - const wrapper = mount( - <TestProviders> - <Title title="Test title" /> - </TestProviders> - ); + it('does render the beta badge', () => { + render(<Title title="Test title" releasePhase="beta" />); + + expect(screen.getByText('Test title')).toBeInTheDocument(); + expect(screen.getByText('Beta')).toBeInTheDocument(); + }); + + it('does render the experimental badge', () => { + render(<Title title="Test title" releasePhase="experimental" />); - expect(wrapper.find('[data-test-subj="header-page-title"]').first().exists()).toBe(true); + expect(screen.getByText('Test title')).toBeInTheDocument(); + expect(screen.getByText('Technical preview')).toBeInTheDocument(); }); - test('it renders the title if is not a string', () => { - const wrapper = shallow(<Title title={<span>{'Test title'}</span>} />); + it('renders the title if is not a string', () => { + render(<Title title={<span>{'Test title'}</span>} releasePhase="experimental" />); + + expect(screen.getByText('Test title')).toBeInTheDocument(); + expect(screen.getByText('Technical preview')).toBeInTheDocument(); + }); + + it('renders the children if provided', () => { + render( + <Title title="Test title" releasePhase="ga"> + <span>{'children'}</span> + + ); - expect(wrapper).toMatchSnapshot(); + expect(screen.getByText('Test title')).toBeInTheDocument(); + expect(screen.getByText('children')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cases/public/components/header_page/title.tsx b/x-pack/plugins/cases/public/components/header_page/title.tsx index 9ccf13b8d83a93..c6d2bf97e1cf16 100644 --- a/x-pack/plugins/cases/public/components/header_page/title.tsx +++ b/x-pack/plugins/cases/public/components/header_page/title.tsx @@ -7,51 +7,54 @@ import React from 'react'; import { isString } from 'lodash'; -import { EuiBetaBadge, EuiBadge, EuiTitle } from '@elastic/eui'; -import styled from 'styled-components'; +import { EuiBetaBadge, EuiTitle, EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; -import { BadgeOptions, TitleProp } from './types'; import { TruncatedText } from '../truncated_text'; +import { ReleasePhase } from '../types'; +import * as i18n from './translations'; -const StyledEuiBetaBadge = styled(EuiBetaBadge)` - vertical-align: middle; -`; +interface Props { + title: string | React.ReactNode; + releasePhase: ReleasePhase; + children?: React.ReactNode; +} -StyledEuiBetaBadge.displayName = 'StyledEuiBetaBadge'; +const ExperimentalBadge: React.FC = () => ( + +); -const Badge = styled(EuiBadge)` - letter-spacing: 0; -` as unknown as typeof EuiBadge; -Badge.displayName = 'Badge'; +ExperimentalBadge.displayName = 'ExperimentalBadge'; -interface Props { - badgeOptions?: BadgeOptions; - title: TitleProp; -} +const BetaBadge: React.FC = () => ( + +); -const TitleComponent: React.FC = ({ title, badgeOptions }) => ( - -

- {isString(title) ? : title} - {badgeOptions && ( - <> - {' '} - {badgeOptions.beta ? ( - - ) : ( - - {badgeOptions.text} - - )} - - )} -

-
+BetaBadge.displayName = 'BetaBadge'; + +const TitleComponent: React.FC = ({ title, releasePhase, children }) => ( + + + + + +

+ {isString(title) ? : title} +

+
+
+ {children} +
+
+ + {releasePhase === 'experimental' && } + {releasePhase === 'beta' && } + +
); -TitleComponent.displayName = 'Title'; +TitleComponent.displayName = 'Title'; export const Title = React.memo(TitleComponent); diff --git a/x-pack/plugins/cases/public/components/header_page/translations.ts b/x-pack/plugins/cases/public/components/header_page/translations.ts index ba987d1f45f157..358f667bba367e 100644 --- a/x-pack/plugins/cases/public/components/header_page/translations.ts +++ b/x-pack/plugins/cases/public/components/header_page/translations.ts @@ -22,3 +22,21 @@ export const EDIT_TITLE_ARIA = (title: string) => values: { title }, defaultMessage: 'You can edit {title} by clicking', }); + +export const EXPERIMENTAL_LABEL = i18n.translate('xpack.cases.header.badge.experimentalLabel', { + defaultMessage: 'Technical preview', +}); + +export const EXPERIMENTAL_DESC = i18n.translate('xpack.cases.header.badge.experimentalDesc', { + defaultMessage: + 'This functionality is in technical preview and may be changed or removed completely in a future release. Elastic will take a best effort approach to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.', +}); + +export const BETA_LABEL = i18n.translate('xpack.cases.header.badge.betaLabel', { + defaultMessage: 'Beta', +}); + +export const BETA_DESC = i18n.translate('xpack.cases.header.badge.betaDesc', { + defaultMessage: + 'This feature is currently in beta. If you encounter any bugs or have feedback, please open an issue or visit our discussion forum.', +}); diff --git a/x-pack/plugins/cases/public/components/header_page/types.ts b/x-pack/plugins/cases/public/components/header_page/types.ts deleted file mode 100644 index e95d0c8e1e69c4..00000000000000 --- a/x-pack/plugins/cases/public/components/header_page/types.ts +++ /dev/null @@ -1,20 +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 type React from 'react'; -export type TitleProp = string | React.ReactNode; - -export interface DraggableArguments { - field: string; - value: string; -} - -export interface BadgeOptions { - beta?: boolean; - text: string; - tooltip?: string; -} diff --git a/x-pack/plugins/cases/public/components/types.ts b/x-pack/plugins/cases/public/components/types.ts index 6d72a74fa5d818..d31c297d18b1c1 100644 --- a/x-pack/plugins/cases/public/components/types.ts +++ b/x-pack/plugins/cases/public/components/types.ts @@ -6,3 +6,5 @@ */ export type { CaseActionConnector } from '../../common/ui/types'; + +export type ReleasePhase = 'experimental' | 'beta' | 'ga'; diff --git a/x-pack/plugins/cases/public/methods/get_cases.tsx b/x-pack/plugins/cases/public/methods/get_cases.tsx index 94e7d321840a87..3c1d3294d38ce0 100644 --- a/x-pack/plugins/cases/public/methods/get_cases.tsx +++ b/x-pack/plugins/cases/public/methods/get_cases.tsx @@ -25,8 +25,9 @@ export const getCasesLazy = ({ refreshRef, timelineIntegration, features, + releasePhase, }: GetCasesProps) => ( - + }> { return ( }> - {children} + + {children} + ); }; From f4a13a883b71c53aba8663ee52c33091375d5d83 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Thu, 24 Feb 2022 13:59:40 -0500 Subject: [PATCH 04/28] [Security Solution][Endpoint] Common Artifact List page for use across all artifacts (#124580) * Generic Artifact List Page component * re-usable `useIsMounted()` hook * Initial Blocklist page (using new artifact list component) --- .../exceptions_list_item_generator.ts | 9 +- .../endpoint/service/artifacts/constants.ts | 8 + .../endpoint/service/artifacts/utils.test.ts | 93 +++++ .../endpoint/service/artifacts/utils.ts | 24 +- .../public/management/common/utils.ts | 7 +- .../components/artifact_entry_card/index.ts | 1 + .../artifact_list_page/artifact_list_page.tsx | 290 ++++++++++++++ .../components/artifact_delete_modal.tsx | 151 ++++++++ .../components/artifact_flyout.tsx | 354 ++++++++++++++++++ .../components/no_data_empty_state.tsx | 65 ++++ .../hooks/use_artifact_card_props_provider.ts | 106 ++++++ .../hooks/use_artifact_create_item.ts | 46 +++ .../hooks/use_artifact_delete_item.ts | 89 +++++ .../hooks/use_artifact_get_item.ts | 28 ++ .../hooks/use_artifact_update_item.ts | 49 +++ ...se_is_artifact_allowed_per_policy_usage.ts | 23 ++ .../hooks/use_is_flyout_opened.ts | 17 + ...use_kuery_from_exceptions_search_filter.ts | 23 ++ .../hooks/use_set_url_params.ts | 36 ++ .../hooks/use_url_params.ts | 25 ++ .../hooks/use_with_artifact_list_data.ts | 171 +++++++++ .../hooks/use_with_artifact_submit_data.ts | 21 ++ .../components/artifact_list_page/index.ts | 10 + .../artifact_list_page/translations.ts | 123 ++++++ .../components/artifact_list_page/types.ts | 40 ++ .../components/hooks/use_is_mounted.ts | 26 ++ .../components/management_page_loader.tsx | 2 +- .../search_exceptions.test.tsx | 4 +- .../search_exceptions/search_exceptions.tsx | 15 +- .../use_bulk_delete_artifact.test.tsx | 8 +- .../artifacts/use_bulk_delete_artifact.tsx | 37 +- .../artifacts/use_bulk_update_artifact.tsx | 4 +- .../hooks/artifacts/use_create_artifact.tsx | 4 +- .../artifacts/use_delete_artifact.test.tsx | 4 +- .../hooks/artifacts/use_delete_artifact.tsx | 20 +- .../hooks/artifacts/use_get_artifact.test.tsx | 6 +- .../hooks/artifacts/use_get_artifact.tsx | 11 +- .../hooks/artifacts/use_update_artifact.tsx | 4 +- .../pages/blocklist/view/blocklist.test.tsx | 15 +- .../pages/blocklist/view/blocklist.tsx | 184 +++++---- .../pages/blocklist/view/components/empty.tsx | 58 --- .../pages/mocks/trusted_apps_http_mocks.ts | 17 +- .../policy_trusted_apps_flyout.test.tsx | 13 +- ...ove_trusted_app_from_policy_modal.test.tsx | 2 +- .../view/trusted_apps_page.test.tsx | 10 +- .../exceptions_list_api_client.test.ts | 33 +- .../exceptions_list_api_client.ts | 31 +- .../scripts/endpoint/event_filters/index.ts | 10 +- .../host_isolation_exceptions.ts | 20 +- .../apis/endpoint_artifacts/trusted_apps.ts | 10 +- 50 files changed, 2150 insertions(+), 207 deletions(-) create mode 100644 x-pack/plugins/security_solution/common/endpoint/service/artifacts/utils.test.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.tsx create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_delete_modal.tsx create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.tsx create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/no_data_empty_state.tsx create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_card_props_provider.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_create_item.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_delete_item.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_get_item.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_update_item.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_is_artifact_allowed_per_policy_usage.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_is_flyout_opened.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_kuery_from_exceptions_search_filter.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_set_url_params.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_url_params.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_list_data.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_submit_data.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/index.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/translations.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/artifact_list_page/types.ts create mode 100644 x-pack/plugins/security_solution/public/management/components/hooks/use_is_mounted.ts delete mode 100644 x-pack/plugins/security_solution/public/management/pages/blocklist/view/components/empty.tsx diff --git a/x-pack/plugins/security_solution/common/endpoint/data_generators/exceptions_list_item_generator.ts b/x-pack/plugins/security_solution/common/endpoint/data_generators/exceptions_list_item_generator.ts index f15e3f418427a9..90bd928cbd1fe3 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_generators/exceptions_list_item_generator.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_generators/exceptions_list_item_generator.ts @@ -17,7 +17,7 @@ import { } from '@kbn/securitysolution-list-constants'; import { BaseDataGenerator } from './base_data_generator'; import { ConditionEntryField } from '../types'; -import { BY_POLICY_ARTIFACT_TAG_PREFIX } from '../service/artifacts/constants'; +import { BY_POLICY_ARTIFACT_TAG_PREFIX, GLOBAL_ARTIFACT_TAG } from '../service/artifacts/constants'; /** Utility that removes null and undefined from a Type's property value */ type NonNullableTypeProperties = { @@ -87,6 +87,11 @@ const exceptionItemToUpdateExceptionItem = ( }; }; +const EFFECTIVE_SCOPE: readonly string[] = [ + `${BY_POLICY_ARTIFACT_TAG_PREFIX}123-456`, // Policy Specific + GLOBAL_ARTIFACT_TAG, +]; + export class ExceptionsListItemGenerator extends BaseDataGenerator { generate(overrides: Partial = {}): ExceptionListItemSchema { const exceptionItem: ExceptionListItemSchema = { @@ -110,7 +115,7 @@ export class ExceptionsListItemGenerator extends BaseDataGenerator = [ + `name`, + `description`, + `entries.value`, + `entries.entries.value`, + `item_id`, +]; diff --git a/x-pack/plugins/security_solution/common/endpoint/service/artifacts/utils.test.ts b/x-pack/plugins/security_solution/common/endpoint/service/artifacts/utils.test.ts new file mode 100644 index 00000000000000..75076e191dcdc9 --- /dev/null +++ b/x-pack/plugins/security_solution/common/endpoint/service/artifacts/utils.test.ts @@ -0,0 +1,93 @@ +/* + * 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 { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { BY_POLICY_ARTIFACT_TAG_PREFIX, GLOBAL_ARTIFACT_TAG } from './constants'; +import { + createExceptionListItemForCreate, + getPolicyIdsFromArtifact, + isArtifactByPolicy, + isArtifactGlobal, +} from './utils'; + +describe('Endpoint artifact utilities', () => { + let globalEntry: Pick; + let perPolicyWithPolicy: Pick; + let perPolicyNoPolicies: Pick; + + beforeEach(() => { + globalEntry = { + tags: [GLOBAL_ARTIFACT_TAG], + }; + + perPolicyWithPolicy = { + tags: [`${BY_POLICY_ARTIFACT_TAG_PREFIX}123`, `${BY_POLICY_ARTIFACT_TAG_PREFIX}456`], + }; + + perPolicyNoPolicies = { + tags: [], + }; + }); + + describe('when using `isArtifactGlobal()', () => { + it('should return `true` if artifact is global', () => { + expect(isArtifactGlobal(globalEntry)).toBe(true); + }); + + it('should return `false` if artifact is per-policy', () => { + expect(isArtifactGlobal(perPolicyWithPolicy)).toBe(false); + }); + + it('should return `false` if artifact is per-policy but not assigned to any policy', () => { + expect(isArtifactGlobal(perPolicyNoPolicies)).toBe(false); + }); + }); + + describe('when using `isArtifactByPolicy()', () => { + it('should return `true` if artifact is per-policy', () => { + expect(isArtifactByPolicy(perPolicyWithPolicy)).toBe(true); + }); + + it('should return `true` if artifact is per-policy but not assigned to any policy', () => { + expect(isArtifactByPolicy(perPolicyNoPolicies)).toBe(true); + }); + + it('should return `false` if artifact is global', () => { + expect(isArtifactByPolicy(globalEntry)).toBe(false); + }); + }); + + describe('when using `getPolicyIdsFromArtifact()`', () => { + it('should return array of policies', () => { + expect(getPolicyIdsFromArtifact(perPolicyWithPolicy)).toEqual(['123', '456']); + }); + + it('should return empty array if there are none', () => { + expect(getPolicyIdsFromArtifact(perPolicyNoPolicies)).toEqual([]); + }); + }); + + describe('when using `createExceptionListItemForCreate()`', () => { + it('should return an empty exception list ready for create', () => { + expect(createExceptionListItemForCreate('abc')).toEqual({ + comments: [], + description: '', + entries: [], + item_id: undefined, + list_id: 'abc', + meta: { + temporaryUuid: expect.any(String), + }, + name: '', + namespace_type: 'agnostic', + tags: [GLOBAL_ARTIFACT_TAG], + type: 'simple', + os_types: ['windows'], + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/common/endpoint/service/artifacts/utils.ts b/x-pack/plugins/security_solution/common/endpoint/service/artifacts/utils.ts index 4cc39e9fb89802..332667064a605d 100644 --- a/x-pack/plugins/security_solution/common/endpoint/service/artifacts/utils.ts +++ b/x-pack/plugins/security_solution/common/endpoint/service/artifacts/utils.ts @@ -5,7 +5,11 @@ * 2.0. */ -import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { + ExceptionListItemSchema, + CreateExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import uuid from 'uuid'; import { BY_POLICY_ARTIFACT_TAG_PREFIX, GLOBAL_ARTIFACT_TAG } from './constants'; const POLICY_ID_START_POSITION = BY_POLICY_ARTIFACT_TAG_PREFIX.length; @@ -30,3 +34,21 @@ export const getPolicyIdsFromArtifact = (item: Pick { + return { + comments: [], + description: '', + entries: [], + item_id: undefined, + list_id: listId, + meta: { + temporaryUuid: uuid.v4(), + }, + name: '', + namespace_type: 'agnostic', + tags: [GLOBAL_ARTIFACT_TAG], + type: 'simple', + os_types: ['windows'], + }; +}; diff --git a/x-pack/plugins/security_solution/public/management/common/utils.ts b/x-pack/plugins/security_solution/public/management/common/utils.ts index 12da54a992becb..8b88fcaff8a780 100644 --- a/x-pack/plugins/security_solution/public/management/common/utils.ts +++ b/x-pack/plugins/security_solution/public/management/common/utils.ts @@ -7,7 +7,10 @@ import { isEmpty } from 'lodash/fp'; -export const parseQueryFilterToKQL = (filter: string, fields: Readonly): string => { +export const parseQueryFilterToKQL = ( + filter: string | undefined, + fields: Readonly +): string => { if (!filter) return ''; const kuery = fields .map( @@ -66,7 +69,7 @@ export const parsePoliciesAndFilterToKql = ({ kuery?: string; }): string | undefined => { if (policies?.length === 0 && excludedPolicies?.length === 0) { - return kuery; + return kuery ? kuery : undefined; } const policiesKQL = parsePoliciesToKQL(policies, excludedPolicies); diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/index.ts b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/index.ts index 71a12308895598..d8e2eeb956c11f 100644 --- a/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/index.ts +++ b/x-pack/plugins/security_solution/public/management/components/artifact_entry_card/index.ts @@ -11,3 +11,4 @@ export * from './artifact_entry_collapsible_card'; export * from './components/card_section_panel'; export * from './types'; export { CardCompressedHeaderLayout } from './components/card_compressed_header'; +export { useEndpointPoliciesToArtifactPolicies } from './hooks/use_endpoint_policies_to_artifact_policies'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.tsx new file mode 100644 index 00000000000000..87673cf5c1e47d --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/artifact_list_page.tsx @@ -0,0 +1,290 @@ +/* + * 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, { memo, useCallback, useMemo, useState } from 'react'; + +import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { EuiButton, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiFlyoutSize } from '@elastic/eui/src/components/flyout/flyout'; +import { useLocation } from 'react-router-dom'; +import { AdministrationListPage } from '../administration_list_page'; + +import { PaginatedContent, PaginatedContentProps } from '../paginated_content'; + +import { ArtifactEntryCard } from '../artifact_entry_card'; + +import { ArtifactListPageLabels, artifactListPageLabels } from './translations'; +import { useTestIdGenerator } from '../hooks/use_test_id_generator'; +import { ManagementPageLoader } from '../management_page_loader'; +import { SearchExceptions } from '../search_exceptions'; +import { + useArtifactCardPropsProvider, + UseArtifactCardPropsProviderProps, +} from './hooks/use_artifact_card_props_provider'; +import { NoDataEmptyState } from './components/no_data_empty_state'; +import { ArtifactFlyoutProps, MaybeArtifactFlyout } from './components/artifact_flyout'; +import { useIsFlyoutOpened } from './hooks/use_is_flyout_opened'; +import { useSetUrlParams } from './hooks/use_set_url_params'; +import { useWithArtifactListData } from './hooks/use_with_artifact_list_data'; +import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; +import { ArtifactListPageUrlParams } from './types'; +import { useUrlParams } from './hooks/use_url_params'; +import { ListPageRouteState, MaybeImmutable } from '../../../../common/endpoint/types'; +import { DEFAULT_EXCEPTION_LIST_ITEM_SEARCHABLE_FIELDS } from '../../../../common/endpoint/service/artifacts/constants'; +import { ArtifactDeleteModal } from './components/artifact_delete_modal'; +import { useGetEndpointSpecificPolicies } from '../../services/policies/hooks'; +import { getLoadPoliciesError } from '../../common/translations'; +import { useToasts } from '../../../common/lib/kibana'; +import { useMemoizedRouteState } from '../../common/hooks'; +import { BackToExternalAppSecondaryButton } from '../back_to_external_app_secondary_button'; +import { BackToExternalAppButton } from '../back_to_external_app_button'; + +type ArtifactEntryCardType = typeof ArtifactEntryCard; + +type ArtifactListPagePaginatedContentComponent = PaginatedContentProps< + ExceptionListItemSchema, + ArtifactEntryCardType +>; + +export interface ArtifactListPageProps { + apiClient: ExceptionsListApiClient; + /** The artifact Component that will be displayed in the Flyout for Create and Edit flows */ + ArtifactFormComponent: ArtifactFlyoutProps['FormComponent']; + /** A list of labels for the given artifact page. Not all have to be defined, only those that should override the defaults */ + labels: ArtifactListPageLabels; + /** A list of fields that will be used by the search functionality when a user enters a value in the searchbar */ + searchableFields?: MaybeImmutable; + flyoutSize?: EuiFlyoutSize; + 'data-test-subj'?: string; +} + +export const ArtifactListPage = memo( + ({ + apiClient, + ArtifactFormComponent, + searchableFields = DEFAULT_EXCEPTION_LIST_ITEM_SEARCHABLE_FIELDS, + labels: _labels = {}, + 'data-test-subj': dataTestSubj, + }) => { + const { state: routeState } = useLocation(); + const getTestId = useTestIdGenerator(dataTestSubj); + const toasts = useToasts(); + const isFlyoutOpened = useIsFlyoutOpened(); + const setUrlParams = useSetUrlParams(); + const { + urlParams: { filter, includedPolicies }, + } = useUrlParams(); + + const { + isPageInitializing, + isFetching: isLoading, + data: listDataResponse, + uiPagination, + doesDataExist, + error, + refetch: refetchListData, + } = useWithArtifactListData(apiClient, searchableFields); + + const items = useMemo(() => { + return listDataResponse?.data ?? []; + }, [listDataResponse?.data]); + + const [selectedItemForDelete, setSelectedItemForDelete] = useState< + undefined | ExceptionListItemSchema + >(undefined); + + const [selectedItemForEdit, setSelectedItemForEdit] = useState< + undefined | ExceptionListItemSchema + >(undefined); + + const labels = useMemo(() => { + return { + ...artifactListPageLabels, + ..._labels, + }; + }, [_labels]); + + const handleOnCardActionClick = useCallback( + ({ type, item }) => { + switch (type) { + case 'edit': + setSelectedItemForEdit(item); + setUrlParams({ show: 'edit', itemId: item.item_id }); + break; + + case 'delete': + setSelectedItemForDelete(item); + break; + } + }, + [setUrlParams] + ); + + const handleCardProps = useArtifactCardPropsProvider({ + items, + onAction: handleOnCardActionClick, + cardActionDeleteLabel: labels.cardActionDeleteLabel, + cardActionEditLabel: labels.cardActionEditLabel, + dataTestSubj: getTestId('card'), + }); + + const policiesRequest = useGetEndpointSpecificPolicies({ + onError: (err) => { + toasts.addWarning(getLoadPoliciesError(err)); + }, + }); + + const memoizedRouteState = useMemoizedRouteState(routeState); + + const backButtonEmptyComponent = useMemo(() => { + if (memoizedRouteState && memoizedRouteState.onBackButtonNavigateTo) { + return ; + } + }, [memoizedRouteState]); + + const backButtonHeaderComponent = useMemo(() => { + if (memoizedRouteState && memoizedRouteState.onBackButtonNavigateTo) { + return ; + } + }, [memoizedRouteState]); + + const handleOpenCreateFlyoutClick = useCallback(() => { + setUrlParams({ show: 'create' }); + }, [setUrlParams]); + + const handlePaginationChange: ArtifactListPagePaginatedContentComponent['onChange'] = + useCallback( + ({ pageIndex, pageSize }) => { + setUrlParams({ + page: pageIndex + 1, + pageSize, + }); + + // Scroll to the top to ensure that when new set of data is received and list updated, + // the user is back at the top of the list + window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }); + }, + [setUrlParams] + ); + + const handleOnSearch = useCallback( + (filterValue: string, selectedPolicies: string, doHardRefresh) => { + setUrlParams({ + // `undefined` will drop the param from the url + filter: filterValue.trim() === '' ? undefined : filterValue, + includedPolicies: selectedPolicies.trim() === '' ? undefined : selectedPolicies, + }); + + if (doHardRefresh) { + refetchListData(); + } + }, + [refetchListData, setUrlParams] + ); + + const handleArtifactDeleteModalOnSuccess = useCallback(() => { + setSelectedItemForDelete(undefined); + refetchListData(); + }, [refetchListData]); + + const handleArtifactDeleteModalOnCancel = useCallback(() => { + setSelectedItemForDelete(undefined); + }, []); + + const handleArtifactFlyoutOnSuccess = useCallback(() => { + refetchListData(); + }, [refetchListData]); + + if (isPageInitializing) { + return ; + } + + return ( + + {labels.pageAddButtonTitle} + + } + > + {/* Flyout component is driven by URL params and may or may not be displayed based on those */} + + + {selectedItemForDelete && ( + + )} + + {!doesDataExist ? ( + + ) : ( + <> + + + + + + {labels.getShowingCountLabel(uiPagination.totalItemCount)} + + + + + + items={items} + ItemComponent={ArtifactEntryCard} + itemComponentProps={handleCardProps} + onChange={handlePaginationChange} + error={error} + loading={isLoading} + pagination={uiPagination} + contentClassName="card-container" + data-test-subj={getTestId('cardContent')} + /> + + )} + + ); + } +); +ArtifactListPage.displayName = 'ArtifactListPage'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_delete_modal.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_delete_modal.tsx new file mode 100644 index 00000000000000..4228d923a9ab30 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_delete_modal.tsx @@ -0,0 +1,151 @@ +/* + * 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, { memo, useCallback } from 'react'; +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { i18n } from '@kbn/i18n'; +import { + EuiButtonEmpty, + EuiCallOut, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { AutoFocusButton } from '../../../../common/components/autofocus_button/autofocus_button'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; +import { + getPolicyIdsFromArtifact, + isArtifactGlobal, +} from '../../../../../common/endpoint/service/artifacts'; +import { + ARTIFACT_DELETE_ACTION_LABELS, + useArtifactDeleteItem, +} from '../hooks/use_artifact_delete_item'; +import { ExceptionsListApiClient } from '../../../services/exceptions_list/exceptions_list_api_client'; + +export const ARTIFACT_DELETE_LABELS = Object.freeze({ + deleteModalTitle: (itemName: string): string => + i18n.translate('xpack.securitySolution.artifactListPage.deleteModalTitle', { + defaultMessage: 'Delete {itemName}', + values: { itemName }, + }), + + deleteModalImpactTitle: i18n.translate( + 'xpack.securitySolution.artifactListPage.deleteModalImpactTitle', + { + defaultMessage: 'Warning', + } + ), + + deleteModalImpactInfo: (item: ExceptionListItemSchema): string => { + return i18n.translate('xpack.securitySolution.artifactListPage.deleteModalImpactInfo', { + defaultMessage: + 'Deleting this entry will remove it from {count} associated {count, plural, one {policy} other {policies}}.', + values: { + count: isArtifactGlobal(item) + ? i18n.translate('xpack.securitySolution.artifactListPage.deleteModalImpactInfoAll', { + defaultMessage: 'all', + }) + : getPolicyIdsFromArtifact(item).length, + }, + }); + }, + + deleteModalConfirmInfo: i18n.translate( + 'xpack.securitySolution.artifactListPage.deleteModalConfirmInfo', + { + defaultMessage: 'This action cannot be undone. Are you sure you wish to continue?', + } + ), + + deleteModalSubmitButtonTitle: i18n.translate( + 'xpack.securitySolution.artifactListPage.deleteModalSubmitButtonTitle', + { defaultMessage: 'Delete' } + ), + + deleteModalCancelButtonTitle: i18n.translate( + 'xpack.securitySolution.artifactListPage.deleteModalCancelButtonTitle', + { defaultMessage: 'Cancel' } + ), +}); + +interface DeleteArtifactModalProps { + apiClient: ExceptionsListApiClient; + item: ExceptionListItemSchema; + onCancel: () => void; + onSuccess: () => void; + labels: typeof ARTIFACT_DELETE_LABELS & typeof ARTIFACT_DELETE_ACTION_LABELS; + 'data-test-subj'?: string; +} + +export const ArtifactDeleteModal = memo( + ({ apiClient, item, onCancel, onSuccess, 'data-test-subj': dataTestSubj, labels }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + const { deleteArtifactItem, isLoading: isDeleting } = useArtifactDeleteItem(apiClient, labels); + + const onConfirm = useCallback(() => { + deleteArtifactItem(item).then(() => onSuccess()); + }, [deleteArtifactItem, item, onSuccess]); + + const handleOnCancel = useCallback(() => { + if (!isDeleting) { + onCancel(); + } + }, [isDeleting, onCancel]); + + return ( + + + {labels.deleteModalTitle(item.name)} + + + + + +

+ {labels.deleteModalImpactInfo(item)} +

+
+ +

{labels.deleteModalConfirmInfo}

+
+
+ + + + {labels.deleteModalCancelButtonTitle} + + + + {labels.deleteModalSubmitButtonTitle} + + +
+ ); + } +); +ArtifactDeleteModal.displayName = 'ArtifactDeleteModal'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.tsx new file mode 100644 index 00000000000000..483695de738249 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/artifact_flyout.tsx @@ -0,0 +1,354 @@ +/* + * 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, { memo, useCallback, useEffect, useMemo, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { + EuiButton, + EuiButtonEmpty, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiTitle, +} from '@elastic/eui'; +import { EuiFlyoutSize } from '@elastic/eui/src/components/flyout/flyout'; +import { useUrlParams } from '../hooks/use_url_params'; +import { useIsFlyoutOpened } from '../hooks/use_is_flyout_opened'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; +import { useSetUrlParams } from '../hooks/use_set_url_params'; +import { useArtifactGetItem } from '../hooks/use_artifact_get_item'; +import { + ArtifactFormComponentOnChangeCallbackProps, + ArtifactFormComponentProps, + ArtifactListPageUrlParams, +} from '../types'; +import { ManagementPageLoader } from '../../management_page_loader'; +import { ExceptionsListApiClient } from '../../../services/exceptions_list/exceptions_list_api_client'; +import { useToasts } from '../../../../common/lib/kibana'; +import { createExceptionListItemForCreate } from '../../../../../common/endpoint/service/artifacts/utils'; +import { useWithArtifactSubmitData } from '../hooks/use_with_artifact_submit_data'; +import { useIsArtifactAllowedPerPolicyUsage } from '../hooks/use_is_artifact_allowed_per_policy_usage'; + +export const ARTIFACT_FLYOUT_LABELS = Object.freeze({ + flyoutEditTitle: i18n.translate('xpack.securitySolution.artifactListPage.flyoutEditTitle', { + defaultMessage: 'Add artifact', + }), + + flyoutCreateTitle: i18n.translate('xpack.securitySolution.artifactListPage.flyoutCreateTitle', { + defaultMessage: 'Create artifact', + }), + flyoutCancelButtonLabel: i18n.translate( + 'xpack.securitySolution.artifactListPage.flyoutCancelButtonLabel', + { + defaultMessage: 'Cancel', + } + ), + flyoutCreateSubmitButtonLabel: i18n.translate( + 'xpack.securitySolution.artifactListPage.flyoutCreateSubmitButtonLabel', + { defaultMessage: 'Add' } + ), + flyoutEditSubmitButtonLabel: i18n.translate( + 'xpack.securitySolution.artifactListPage.flyoutEditSubmitButtonLabel', + { defaultMessage: 'Save' } + ), + flyoutDowngradedLicenseTitle: i18n.translate( + 'xpack.securitySolution.artifactListPage.expiredLicenseTitle', + { + defaultMessage: 'Expired License', + } + ), + flyoutDowngradedLicenseInfo: i18n.translate( + 'xpack.securitySolution.artifactListPage.flyoutDowngradedLicenseInfo', + { + defaultMessage: + 'Your Kibana license has been downgraded. Future policy configurations will now be globally assigned to all policies.', + } + ), + /** + * This should be set to a sentence that includes a link to the documentation page for this specific artifact type. + * + * @example + * // in a component + * () => { + * const { docLinks } = useKibana().services; + * return ( + * + * + * + * }} + * /> + * ); + * } + */ + flyoutDowngradedLicenseDocsInfo: (): React.ReactNode => + i18n.translate('xpack.securitySolution.artifactListPage.flyoutDowngradedLicenseDocsInfo', { + defaultMessage: 'For more information, see our documentation.', + }), + + flyoutEditItemLoadFailure: (errorMessage: string) => + i18n.translate('xpack.securitySolution.artifactListPage.flyoutEditItemLoadFailure', { + defaultMessage: 'Failed to retrieve item for edit. Reason: {errorMessage}', + values: { errorMessage }, + }), + + /** + * A function returning the label for the success message toast + * @param itemName + * @example + * ({ name }) => i18n.translate('xpack.securitySolution.some_page.flyoutCreateSubmitSuccess', { + * defaultMessage: '"{name}" has been added.', + * values: { name }, + * }) + */ + flyoutCreateSubmitSuccess: ({ name }: ExceptionListItemSchema) => + i18n.translate('xpack.securitySolution.some_page.flyoutCreateSubmitSuccess', { + defaultMessage: '"{name}" has been added to your event filters.', + values: { name }, + }), + + /** + * Returns the edit success message for the toast + * @param item + * @example + * ({ name }) => + * i18n.translate('xpack.securitySolution.some_page.flyoutEditSubmitSuccess', { + * defaultMessage: '"{name}" has been updated.', + * values: { name }, + * }) + */ + flyoutEditSubmitSuccess: ({ name }: ExceptionListItemSchema) => + i18n.translate('xpack.securitySolution.artifactListPage.flyoutEditSubmitSuccess', { + defaultMessage: '"{name}" has been updated.', + values: { name }, + }), +}); + +const createFormInitialState = ( + listId: string, + item: ArtifactFormComponentOnChangeCallbackProps['item'] | undefined +): ArtifactFormComponentOnChangeCallbackProps => { + return { + isValid: false, + item: item ?? createExceptionListItemForCreate(listId), + }; +}; + +export interface ArtifactFlyoutProps { + apiClient: ExceptionsListApiClient; + FormComponent: React.ComponentType; + onSuccess(): void; + /** + * If the artifact data is provided and it matches the id in the URL, then it will not be + * retrieved again via the API + */ + item?: ExceptionListItemSchema; + /** Any label overrides */ + labels?: Partial; + 'data-test-subj'?: string; + size?: EuiFlyoutSize; +} + +/** + * Show the flyout based on URL params + */ +export const MaybeArtifactFlyout = memo( + ({ + apiClient, + item, + FormComponent, + onSuccess, + labels: _labels = {}, + 'data-test-subj': dataTestSubj, + size = 'm', + }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + const toasts = useToasts(); + const isFlyoutOpened = useIsFlyoutOpened(); + const setUrlParams = useSetUrlParams(); + const { urlParams } = useUrlParams(); + const labels = useMemo(() => { + return { + ...ARTIFACT_FLYOUT_LABELS, + ..._labels, + }; + }, [_labels]); + + const isEditFlow = urlParams.show === 'edit'; + const formMode: ArtifactFormComponentProps['mode'] = isEditFlow ? 'edit' : 'create'; + + const { + isLoading: isSubmittingData, + mutateAsync: submitData, + error: submitError, + } = useWithArtifactSubmitData(apiClient, formMode); + + const { + isLoading: isLoadingItemForEdit, + error, + refetch: fetchItemForEdit, + } = useArtifactGetItem(apiClient, urlParams.itemId ?? '', false); + + const [formState, setFormState] = useState( + createFormInitialState.bind(null, apiClient.listId, item) + ); + const showExpiredLicenseBanner = useIsArtifactAllowedPerPolicyUsage( + { tags: formState.item.tags ?? [] }, + formMode + ); + + const hasItemDataForEdit = useMemo(() => { + // `item_id` will not be defined for a `create` flow, so we use it below to determine if we + // are still attempting to load the item for edit from the api + return !!item || !!formState.item.item_id; + }, [formState.item.item_id, item]); + + const isInitializing = useMemo(() => { + return isEditFlow && !hasItemDataForEdit; + }, [hasItemDataForEdit, isEditFlow]); + + const handleFlyoutClose = useCallback(() => { + if (isSubmittingData) { + return; + } + + // `undefined` will cause params to be dropped from url + setUrlParams({ id: undefined, show: undefined }, true); + }, [isSubmittingData, setUrlParams]); + + const handleFormComponentOnChange: ArtifactFormComponentProps['onChange'] = useCallback( + ({ item: updatedItem, isValid }) => { + setFormState({ + item: updatedItem, + isValid, + }); + }, + [] + ); + + const handleSubmitClick = useCallback(() => { + submitData(formState.item).then((result) => { + toasts.addSuccess( + isEditFlow + ? labels.flyoutEditSubmitSuccess(result) + : labels.flyoutCreateSubmitSuccess(result) + ); + + // Close the flyout + // `undefined` will cause params to be dropped from url + setUrlParams({ id: undefined, show: undefined }, true); + }); + }, [formState.item, isEditFlow, labels, setUrlParams, submitData, toasts]); + + // If we don't have the actual Artifact data yet for edit (in initialization phase - ex. came in with an + // ID in the url that was not in the list), then retrieve it now + useEffect(() => { + if (isEditFlow && !hasItemDataForEdit && !error && isInitializing && !isLoadingItemForEdit) { + fetchItemForEdit().then(({ data: editItemData }) => { + if (editItemData) { + setFormState(createFormInitialState(apiClient.listId, editItemData)); + } + }); + } + }, [ + apiClient.listId, + error, + fetchItemForEdit, + isEditFlow, + isInitializing, + isLoadingItemForEdit, + hasItemDataForEdit, + ]); + + // If we got an error while trying ot retrieve the item for edit, then show a toast message + useEffect(() => { + if (isEditFlow && error) { + toasts.addWarning(labels.flyoutEditItemLoadFailure(error?.body?.message || error.message)); + + // Blank out the url params for id and show (will close out the flyout) + setUrlParams({ id: undefined, show: undefined }); + } + }, [error, isEditFlow, labels, setUrlParams, toasts, urlParams.itemId]); + + if (!isFlyoutOpened || error) { + return null; + } + + return ( + + + +

{isEditFlow ? labels.flyoutEditTitle : labels.flyoutCreateTitle}

+
+
+ + {!isInitializing && showExpiredLicenseBanner && ( + + {`${labels.flyoutDowngradedLicenseInfo} ${labels.flyoutDowngradedLicenseDocsInfo()}`} + + )} + + + {isInitializing && } + + {!isInitializing && ( + + )} + + + {!isInitializing && ( + + + + + {labels.flyoutCancelButtonLabel} + + + + + {isEditFlow + ? labels.flyoutEditSubmitButtonLabel + : labels.flyoutCreateSubmitButtonLabel} + + + + + )} +
+ ); + } +); +MaybeArtifactFlyout.displayName = 'MaybeArtifactFlyout'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/no_data_empty_state.tsx b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/no_data_empty_state.tsx new file mode 100644 index 00000000000000..cbb6bd8c5454e5 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/components/no_data_empty_state.tsx @@ -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 React, { memo } from 'react'; +import styled, { css } from 'styled-components'; +import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; +import { ManagementEmptyStateWrapper } from '../../management_empty_state_wrapper'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; + +const EmptyPrompt = styled(EuiEmptyPrompt)` + ${() => css` + max-width: 100%; + `} +`; + +export const NoDataEmptyState = memo<{ + onAdd: () => void; + titleLabel: string; + aboutInfo: string; + primaryButtonLabel: string; + /** Should the Add button be disabled */ + isAddDisabled?: boolean; + backComponent?: React.ReactNode; + 'data-test-subj'?: string; +}>( + ({ + onAdd, + isAddDisabled = false, + backComponent, + 'data-test-subj': dataTestSubj, + titleLabel, + aboutInfo, + primaryButtonLabel, + }) => { + const getTestId = useTestIdGenerator(dataTestSubj); + + return ( + + {titleLabel}} + body={
{aboutInfo}
} + actions={[ + + {primaryButtonLabel} + , + ...(backComponent ? [backComponent] : []), + ]} + /> +
+ ); + } +); + +NoDataEmptyState.displayName = 'NoDataEmptyState'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_card_props_provider.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_card_props_provider.ts new file mode 100644 index 00000000000000..58a0f59feaa38b --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_card_props_provider.ts @@ -0,0 +1,106 @@ +/* + * 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 { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { useCallback, useMemo } from 'react'; +import { + AnyArtifact, + ArtifactEntryCardProps, + useEndpointPoliciesToArtifactPolicies, +} from '../../artifact_entry_card'; +import { useTestIdGenerator } from '../../hooks/use_test_id_generator'; +import { useGetEndpointSpecificPolicies } from '../../../services/policies/hooks'; +import { getLoadPoliciesError } from '../../../common/translations'; +import { useToasts } from '../../../../common/lib/kibana'; + +type CardActionType = 'edit' | 'delete'; + +export interface UseArtifactCardPropsProviderProps { + items: ExceptionListItemSchema[]; + onAction: (action: { type: CardActionType; item: ExceptionListItemSchema }) => void; + cardActionEditLabel: string; + cardActionDeleteLabel: string; + dataTestSubj?: string; +} + +type ArtifactCardPropsProvider = (artifactItem: ExceptionListItemSchema) => ArtifactEntryCardProps; + +/** + * Return a function that can be used to retrieve props for an `ArtifactCardEntry` component given an + * `ExceptionListItemSchema` on input + */ +export const useArtifactCardPropsProvider = ({ + items, + onAction, + cardActionDeleteLabel, + cardActionEditLabel, + dataTestSubj, +}: UseArtifactCardPropsProviderProps): ArtifactCardPropsProvider => { + const getTestId = useTestIdGenerator(dataTestSubj); + const toasts = useToasts(); + + const { data: policyData } = useGetEndpointSpecificPolicies({ + onError: (error) => { + toasts.addDanger(getLoadPoliciesError(error)); + }, + }); + + const policies: ArtifactEntryCardProps['policies'] = useEndpointPoliciesToArtifactPolicies( + policyData?.items + ); + + const artifactCardPropsPerItem = useMemo(() => { + const cachedCardProps: Record = {}; + + // Casting `listItems` below to remove the `Immutable<>` from it in order to prevent errors + // with common component's props + for (const artifactItem of items as ExceptionListItemSchema[]) { + cachedCardProps[artifactItem.id] = { + item: artifactItem as AnyArtifact, + policies, + 'data-test-subj': dataTestSubj, + actions: [ + { + icon: 'controlsHorizontal', + onClick: () => { + onAction({ type: 'edit', item: artifactItem }); + }, + 'data-test-subj': getTestId('cardEditAction'), + children: cardActionEditLabel, + }, + { + icon: 'trash', + onClick: () => { + onAction({ type: 'delete', item: artifactItem }); + }, + 'data-test-subj': getTestId('cardDeleteAction'), + children: cardActionDeleteLabel, + }, + ], + hideDescription: !artifactItem.description, + hideComments: !artifactItem.comments.length, + }; + } + + return cachedCardProps; + }, [ + items, + policies, + dataTestSubj, + getTestId, + cardActionEditLabel, + cardActionDeleteLabel, + onAction, + ]); + + return useCallback( + (artifactItem: ExceptionListItemSchema) => { + return artifactCardPropsPerItem[artifactItem.id]; + }, + [artifactCardPropsPerItem] + ); +}; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_create_item.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_create_item.ts new file mode 100644 index 00000000000000..4252d66f2a510d --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_create_item.ts @@ -0,0 +1,46 @@ +/* + * 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 { + CreateExceptionListItemSchema, + ExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { useMutation } from 'react-query'; +import { HttpFetchError } from 'kibana/public'; +import { ExceptionsListApiClient } from '../../../services/exceptions_list/exceptions_list_api_client'; + +// FIXME: delete entire file once PR# 125198 is merged. This entire file was copied from that pr + +export interface CallbackTypes { + onSuccess?: (updatedException: ExceptionListItemSchema) => void; + onError?: (error?: HttpFetchError) => void; + onSettled?: () => void; +} + +export function useCreateArtifact( + exceptionListApiClient: ExceptionsListApiClient, + callbacks: CallbackTypes = {} +) { + const { onSuccess = () => {}, onError = () => {}, onSettled = () => {} } = callbacks; + + return useMutation< + ExceptionListItemSchema, + HttpFetchError, + CreateExceptionListItemSchema, + () => void + >( + async (exception: CreateExceptionListItemSchema) => { + return exceptionListApiClient.create(exception); + }, + { + onSuccess, + onError, + onSettled: () => { + onSettled(); + }, + } + ); +} diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_delete_item.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_delete_item.ts new file mode 100644 index 00000000000000..feac0c2b0c5997 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_delete_item.ts @@ -0,0 +1,89 @@ +/* + * 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 { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { useMutation, UseMutationResult } from 'react-query'; +import { i18n } from '@kbn/i18n'; +import { useMemo } from 'react'; +import type { HttpFetchError } from 'kibana/public'; +import { useToasts } from '../../../../common/lib/kibana'; +import { ExceptionsListApiClient } from '../../../services/exceptions_list/exceptions_list_api_client'; + +export const ARTIFACT_DELETE_ACTION_LABELS = Object.freeze({ + /** + * Message to be displayed in toast when deletion fails + * @param itemName + * @param errorMessage + * @example + * (itemsName, errorMessage) => i18n.translate( + * 'xpack.securitySolution.artifactListPage.deleteActionFailure', + * { + * defaultMessage: 'Unable to remove "{itemName}" . Reason: {errorMessage}', + * values: { itemName, errorMessage }, + * }) + */ + deleteActionFailure: (itemName: string, errorMessage: string) => + i18n.translate('xpack.securitySolution.artifactListPage.deleteActionFailure', { + defaultMessage: 'Unable to remove "{itemName}" . Reason: {errorMessage}', + values: { itemName, errorMessage }, + }), + + /** + * Message to be displayed in the toast after a successful delete + * @param itemName + * @example + * (itemName) => i18n.translate('xpack.securitySolution.some_page.deleteSuccess', { + * defaultMessage: '"{itemName}" has been removed', + * values: { itemName }, + * }) + */ + deleteActionSuccess: (itemName: string) => + i18n.translate('xpack.securitySolution.artifactListPage.deleteActionSuccess', { + defaultMessage: '"{itemName}" has been removed', + values: { itemName }, + }), +}); + +type UseArtifactDeleteItemMutationResult = UseMutationResult< + ExceptionListItemSchema, + HttpFetchError, + ExceptionListItemSchema +>; + +export type UseArtifactDeleteItemInterface = UseArtifactDeleteItemMutationResult & { + deleteArtifactItem: UseArtifactDeleteItemMutationResult['mutateAsync']; +}; + +export const useArtifactDeleteItem = ( + apiClient: ExceptionsListApiClient, + labels: typeof ARTIFACT_DELETE_ACTION_LABELS +): UseArtifactDeleteItemInterface => { + const toasts = useToasts(); + + const mutation = useMutation( + async (item: ExceptionListItemSchema) => { + return apiClient.delete(item.item_id); + }, + { + onError: (error: HttpFetchError, item) => { + toasts.addDanger( + labels.deleteActionFailure(item.name, error.body?.message || error.message) + ); + }, + onSuccess: (response) => { + toasts.addSuccess(labels.deleteActionSuccess(response.name)); + }, + } + ); + + return useMemo(() => { + return { + ...mutation, + deleteArtifactItem: mutation.mutateAsync, + }; + }, [mutation]); +}; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_get_item.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_get_item.ts new file mode 100644 index 00000000000000..21b13aa285376f --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_get_item.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 { useQuery } from 'react-query'; +import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { HttpFetchError } from 'kibana/public'; +import { ExceptionsListApiClient } from '../../../services/exceptions_list/exceptions_list_api_client'; + +export const useArtifactGetItem = ( + apiClient: ExceptionsListApiClient, + itemId: string, + enabled: boolean = true +) => { + return useQuery( + ['item', apiClient, itemId], + () => apiClient.get(itemId), + { + enabled, + refetchOnWindowFocus: false, + keepPreviousData: true, + retry: false, + } + ); +}; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_update_item.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_update_item.ts new file mode 100644 index 00000000000000..a217da0159ed87 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_artifact_update_item.ts @@ -0,0 +1,49 @@ +/* + * 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 { + UpdateExceptionListItemSchema, + ExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; +import { useQueryClient, useMutation } from 'react-query'; +import { HttpFetchError } from 'kibana/public'; +import { ExceptionsListApiClient } from '../../../services/exceptions_list/exceptions_list_api_client'; + +// FIXME: delete entire file once PR# 125198 is merged. This entire file was copied from that pr + +export interface CallbackTypes { + onSuccess?: (updatedException: ExceptionListItemSchema) => void; + onError?: (error?: HttpFetchError) => void; + onSettled?: () => void; +} + +export function useUpdateArtifact( + exceptionListApiClient: ExceptionsListApiClient, + callbacks: CallbackTypes = {} +) { + const queryClient = useQueryClient(); + const { onSuccess = () => {}, onError = () => {}, onSettled = () => {} } = callbacks; + + return useMutation< + ExceptionListItemSchema, + HttpFetchError, + UpdateExceptionListItemSchema, + () => void + >( + async (exception: UpdateExceptionListItemSchema) => { + return exceptionListApiClient.update(exception); + }, + { + onSuccess, + onError, + onSettled: () => { + queryClient.invalidateQueries(['list', exceptionListApiClient]); + queryClient.invalidateQueries(['get', exceptionListApiClient]); + onSettled(); + }, + } + ); +} diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_is_artifact_allowed_per_policy_usage.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_is_artifact_allowed_per_policy_usage.ts new file mode 100644 index 00000000000000..08a51ca061fe0c --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_is_artifact_allowed_per_policy_usage.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 { useMemo } from 'react'; +import type { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { ArtifactFormComponentProps } from '../types'; +import { useUserPrivileges } from '../../../../common/components/user_privileges'; +import { isArtifactByPolicy } from '../../../../../common/endpoint/service/artifacts'; + +export const useIsArtifactAllowedPerPolicyUsage = ( + item: Pick, + mode: ArtifactFormComponentProps['mode'] +): boolean => { + const endpointAuthz = useUserPrivileges().endpointPrivileges; + + return useMemo(() => { + return mode === 'edit' && !endpointAuthz.canCreateArtifactsByPolicy && isArtifactByPolicy(item); + }, [endpointAuthz.canCreateArtifactsByPolicy, item, mode]); +}; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_is_flyout_opened.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_is_flyout_opened.ts new file mode 100644 index 00000000000000..dc53a58924e83d --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_is_flyout_opened.ts @@ -0,0 +1,17 @@ +/* + * 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 { useUrlParams } from './use_url_params'; +import { ArtifactListPageUrlParams } from '../types'; + +const SHOW_VALUES: readonly string[] = ['create', 'edit']; + +export const useIsFlyoutOpened = (): boolean => { + const showUrlParamValue = useUrlParams().urlParams.show ?? ''; + return useMemo(() => SHOW_VALUES.includes(showUrlParamValue), [showUrlParamValue]); +}; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_kuery_from_exceptions_search_filter.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_kuery_from_exceptions_search_filter.ts new file mode 100644 index 00000000000000..60923a26c694fa --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_kuery_from_exceptions_search_filter.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 { useMemo } from 'react'; +import { parsePoliciesAndFilterToKql, parseQueryFilterToKQL } from '../../../common/utils'; +import { MaybeImmutable } from '../../../../../common/endpoint/types'; + +export const useKueryFromExceptionsSearchFilter = ( + filter: string | undefined, + fields: MaybeImmutable, + policies: string | undefined +): string | undefined => { + return useMemo(() => { + return parsePoliciesAndFilterToKql({ + kuery: parseQueryFilterToKQL(filter, fields), + policies: policies ? policies.split(',') : [], + }); + }, [fields, filter, policies]); +}; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_set_url_params.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_set_url_params.ts new file mode 100644 index 00000000000000..80ffdeb2539469 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_set_url_params.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. + */ + +import { useHistory, useLocation } from 'react-router-dom'; +import { useCallback } from 'react'; +import { pickBy } from 'lodash'; +import { useUrlParams } from './use_url_params'; + +// FIXME:PT delete/change once we get the common hook from @parkiino PR +export const useSetUrlParams = (): (( + /** Any param whose value is `undefined` will be removed from the URl when in append mode */ + params: Record, + replace?: boolean +) => void) => { + const location = useLocation(); + const history = useHistory(); + const { toUrlParams, urlParams: currentUrlParams } = useUrlParams(); + + return useCallback( + (params, replace = false) => { + history.push({ + ...location, + search: toUrlParams( + replace + ? params + : pickBy({ ...currentUrlParams, ...params }, (value) => value !== undefined) + ), + }); + }, + [currentUrlParams, history, location, toUrlParams] + ); +}; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_url_params.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_url_params.ts new file mode 100644 index 00000000000000..7e1b8d16b37713 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_url_params.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 { useMemo } from 'react'; +import { useLocation } from 'react-router-dom'; +import { parse, stringify } from 'query-string'; + +// FIXME:PT delete and use common hook once @parkiino merges +export function useUrlParams>(): { + urlParams: T; + toUrlParams: (params?: T) => string; +} { + const { search } = useLocation(); + return useMemo(() => { + const urlParams = parse(search) as unknown as T; + return { + urlParams, + toUrlParams: (params: T = urlParams) => stringify(params as unknown as object), + }; + }, [search]); +} diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_list_data.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_list_data.ts new file mode 100644 index 00000000000000..3eca6c60bc711d --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_list_data.ts @@ -0,0 +1,171 @@ +/* + * 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 { QueryObserverResult } from 'react-query'; +import type { FoundExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types'; +import { useEffect, useMemo, useState } from 'react'; +import { Pagination } from '@elastic/eui'; +import { useQuery } from 'react-query'; +import type { ServerApiError } from '../../../../common/types'; +import { useIsMounted } from '../../hooks/use_is_mounted'; +import { + MANAGEMENT_DEFAULT_PAGE_SIZE, + MANAGEMENT_PAGE_SIZE_OPTIONS, +} from '../../../common/constants'; +import { useUrlParams } from './use_url_params'; +import { ExceptionsListApiClient } from '../../../services/exceptions_list/exceptions_list_api_client'; +import { ArtifactListPageUrlParams } from '../types'; +import { MaybeImmutable } from '../../../../../common/endpoint/types'; +import { useKueryFromExceptionsSearchFilter } from './use_kuery_from_exceptions_search_filter'; + +type WithArtifactListDataInterface = QueryObserverResult< + FoundExceptionListItemSchema, + ServerApiError +> & { + /** + * Set to true during initialization of the page until it can be determined if either data exists. + * This should drive the showing of the overall page loading state if set to `true` + */ + isPageInitializing: boolean; + + /** + * Indicates if the exception list has any data at all (regardless of filters the user might have used) + */ + doesDataExist: boolean; + + /** + * The UI pagination data based on the data retrieved for the list + */ + uiPagination: Pagination; +}; + +export const useWithArtifactListData = ( + apiClient: ExceptionsListApiClient, + searchableFields: MaybeImmutable +): WithArtifactListDataInterface => { + const isMounted = useIsMounted(); + + const { + urlParams: { + page = 1, + pageSize = MANAGEMENT_DEFAULT_PAGE_SIZE, + sortOrder, + sortField, + filter, + includedPolicies, + }, + } = useUrlParams(); + + const kuery = useKueryFromExceptionsSearchFilter(filter, searchableFields, includedPolicies); + + const { + data: doesDataExist, + isFetching: isLoadingDataExists, + refetch: checkIfDataExists, + } = useQuery( + ['does-data-exists', apiClient], + async () => apiClient.hasData(), + { + enabled: true, + keepPreviousData: true, + refetchOnWindowFocus: false, + } + ); + + const [uiPagination, setUiPagination] = useState({ + totalItemCount: 0, + pageSize, + pageSizeOptions: [...MANAGEMENT_PAGE_SIZE_OPTIONS], + pageIndex: page - 1, + }); + + const [isPageInitializing, setIsPageInitializing] = useState(true); + + const listDataRequest = useQuery( + ['list', apiClient, page, pageSize, sortField, sortField, kuery], + async () => apiClient.find({ page, perPage: pageSize, filter: kuery, sortField, sortOrder }), + { + enabled: true, + keepPreviousData: true, + refetchOnWindowFocus: false, + } + ); + + const { + data: listData, + isFetching: isLoadingListData, + error: listDataError, + isSuccess: isSuccessListData, + } = listDataRequest; + + // Once we know if data exists, update the page initializing state. + // This should only ever happen at most once; + useEffect(() => { + if (isMounted) { + if (isPageInitializing === true && !isLoadingDataExists) { + setIsPageInitializing(false); + } + } + }, [isLoadingDataExists, isMounted, isPageInitializing]); + + // Update the uiPagination once the query succeeds + useEffect(() => { + if (isMounted && listData && !isLoadingListData && isSuccessListData) { + setUiPagination((prevState) => { + return { + ...prevState, + pageIndex: listData.page - 1, + pageSize: listData.per_page, + totalItemCount: listData.total, + }; + }); + } + }, [isLoadingListData, isMounted, isSuccessListData, listData]); + + // Keep the `doesDataExist` updated if we detect that list data result total is zero. + // Anytime: + // 1. the list data total is 0 + // 2. and page is 1 + // 3. and filter is empty + // 4. and doesDataExists is currently set to true + // check if data exists again + useEffect(() => { + if ( + isMounted && + !isLoadingListData && + !listDataError && + listData && + listData.total === 0 && + String(page) === '1' && + !kuery && + doesDataExist + ) { + checkIfDataExists(); + } + }, [ + checkIfDataExists, + doesDataExist, + filter, + includedPolicies, + isLoadingListData, + isMounted, + kuery, + listData, + listDataError, + page, + ]); + + return useMemo( + () => ({ + isPageInitializing, + doesDataExist: doesDataExist ?? false, + uiPagination, + ...listDataRequest, + }), + [doesDataExist, isPageInitializing, listDataRequest, uiPagination] + ); +}; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_submit_data.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_submit_data.ts new file mode 100644 index 00000000000000..59a2739c9d3afe --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/hooks/use_with_artifact_submit_data.ts @@ -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 { ExceptionsListApiClient } from '../../../services/exceptions_list/exceptions_list_api_client'; +import { ArtifactFormComponentProps } from '../types'; +import { useUpdateArtifact } from './use_artifact_update_item'; +import { useCreateArtifact } from './use_artifact_create_item'; + +export const useWithArtifactSubmitData = ( + apiClient: ExceptionsListApiClient, + mode: ArtifactFormComponentProps['mode'] +) => { + const artifactUpdater = useUpdateArtifact(apiClient); + const artifactCreator = useCreateArtifact(apiClient); + + return mode === 'create' ? artifactCreator : artifactUpdater; +}; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/index.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/index.ts new file mode 100644 index 00000000000000..ba26a44259021f --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/index.ts @@ -0,0 +1,10 @@ +/* + * 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 { ArtifactListPage } from './artifact_list_page'; +export type { ArtifactListPageProps } from './artifact_list_page'; +export * from './types'; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/translations.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/translations.ts new file mode 100644 index 00000000000000..ba6acf8a359aab --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/translations.ts @@ -0,0 +1,123 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; +import { ARTIFACT_FLYOUT_LABELS } from './components/artifact_flyout'; +import { ARTIFACT_DELETE_LABELS } from './components/artifact_delete_modal'; +import { ARTIFACT_DELETE_ACTION_LABELS } from './hooks/use_artifact_delete_item'; + +export const artifactListPageLabels = Object.freeze({ + // ------------------------------ + // PAGE labels + // ------------------------------ + pageTitle: i18n.translate('xpack.securitySolution.artifactListPage.pageTitle', { + defaultMessage: 'Artifact', + }), + pageAboutInfo: i18n.translate('xpack.securitySolution.artifactListPage.aboutInfo', { + defaultMessage: 'A list of artifacts for endpoint', + }), + pageAddButtonTitle: i18n.translate('xpack.securitySolution.artifactListPage.addButtonTitle', { + defaultMessage: 'Add artifact', + }), + + // ------------------------------ + // EMPTY state labels + // ------------------------------ + emptyStateTitle: i18n.translate('xpack.securitySolution.artifactListPage.emptyStateTitle', { + defaultMessage: 'Add your first artifact', + }), + emptyStateInfo: i18n.translate('xpack.securitySolution.artifactListPage.emptyStateInfo', { + defaultMessage: 'Add an artifact', + }), + emptyStatePrimaryButtonLabel: i18n.translate( + 'xpack.securitySolution.artifactListPage.emptyStatePrimaryButtonLabel', + { defaultMessage: 'Add' } + ), + + // ------------------------------ + // SEARCH BAR labels + // ------------------------------ + searchPlaceholderInfo: i18n.translate( + 'xpack.securitySolution.artifactListPage.searchPlaceholderInfo', + { + defaultMessage: 'Search on the fields below: name, description, comments, value', + } + ), + /** + * Return the label to show under the search bar with the total number of items that match the current filter (or all) + * @param total + * + * @example: + * (total) => i18n.translate('xpack.securitySolution.somepage.showingTotal', { + * defaultMessage: 'Showing {total} {total, plural, one {event filter} other {event filters}}', + * values: { total }, + * }) + */ + getShowingCountLabel: (total: number) => { + return i18n.translate('xpack.securitySolution.artifactListPage.showingTotal', { + defaultMessage: 'Showing {total, plural, one {# artifact} other {# artifacts}}', + values: { total }, + }); + }, + + // ------------------------------ + // CARD ACTIONS labels + // ------------------------------ + cardActionEditLabel: i18n.translate( + 'xpack.securitySolution.artifactListPage.cardActionEditLabel', + { + defaultMessage: 'Edit artifact', + } + ), + cardActionDeleteLabel: i18n.translate( + 'xpack.securitySolution.artifactListPage.cardActionDeleteLabel', + { + defaultMessage: 'Delete event filter', + } + ), + + // ------------------------------ + // ARTIFACT FLYOUT + // ------------------------------ + ...ARTIFACT_FLYOUT_LABELS, + + // ------------------------------ + // ARTIFACT DELETE MODAL + // ------------------------------ + ...ARTIFACT_DELETE_LABELS, + ...ARTIFACT_DELETE_ACTION_LABELS, +}); + +type IAllLabels = typeof artifactListPageLabels; + +/** + * The set of labels that normally have the artifact specific name in it, thus must be set for every page + */ +export type ArtifactListPageRequiredLabels = Pick< + IAllLabels, + | 'pageTitle' + | 'pageAboutInfo' + | 'pageAddButtonTitle' + | 'getShowingCountLabel' + | 'cardActionEditLabel' + | 'cardActionDeleteLabel' + | 'flyoutCreateTitle' + | 'flyoutEditTitle' + | 'flyoutCreateSubmitButtonLabel' + | 'flyoutCreateSubmitSuccess' + | 'flyoutEditSubmitSuccess' + | 'flyoutDowngradedLicenseDocsInfo' + | 'deleteActionSuccess' + | 'emptyStateTitle' + | 'emptyStateInfo' + | 'emptyStatePrimaryButtonLabel' +>; + +export type ArtifactListPageOptionalLabels = Omit; + +export type ArtifactListPageLabels = ArtifactListPageRequiredLabels & + Partial; diff --git a/x-pack/plugins/security_solution/public/management/components/artifact_list_page/types.ts b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/types.ts new file mode 100644 index 00000000000000..fa63ebb863ce5c --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/artifact_list_page/types.ts @@ -0,0 +1,40 @@ +/* + * 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 { HttpFetchError } from 'kibana/public'; +import type { + ExceptionListItemSchema, + CreateExceptionListItemSchema, +} from '@kbn/securitysolution-io-ts-list-types'; + +export interface ArtifactListPageUrlParams { + page?: number; + pageSize?: number; + filter?: string; + includedPolicies?: string; + show?: 'create' | 'edit'; + itemId?: string; + sortField?: string; + sortOrder?: string; +} + +export interface ArtifactFormComponentProps { + item: ExceptionListItemSchema | CreateExceptionListItemSchema; + mode: 'edit' | 'create'; + /** signals that the form should be made disabled (ex. while an update/create api call is in flight) */ + disabled: boolean; + /** Error will be set if the submission of the form to the api results in an API error. Form can use it to provide feedback to the user */ + error: HttpFetchError | undefined; + + /** reports the state of the form data and the current updated item */ + onChange(formStatus: ArtifactFormComponentOnChangeCallbackProps): void; +} + +export interface ArtifactFormComponentOnChangeCallbackProps { + isValid: boolean; + item: ExceptionListItemSchema | CreateExceptionListItemSchema; +} diff --git a/x-pack/plugins/security_solution/public/management/components/hooks/use_is_mounted.ts b/x-pack/plugins/security_solution/public/management/components/hooks/use_is_mounted.ts new file mode 100644 index 00000000000000..c3ab4472cf4294 --- /dev/null +++ b/x-pack/plugins/security_solution/public/management/components/hooks/use_is_mounted.ts @@ -0,0 +1,26 @@ +/* + * 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, useRef } from 'react'; + +/** + * Track when a comonent is mounted/unmounted. Good for use in async processing that may update + * a component's internal state. + */ +export const useIsMounted = (): boolean => { + const isMounted = useRef(false); + + useEffect(() => { + isMounted.current = true; + + return () => { + isMounted.current = false; + }; + }, []); + + return isMounted.current; +}; diff --git a/x-pack/plugins/security_solution/public/management/components/management_page_loader.tsx b/x-pack/plugins/security_solution/public/management/components/management_page_loader.tsx index cc1104127871fa..20941c11b1593a 100644 --- a/x-pack/plugins/security_solution/public/management/components/management_page_loader.tsx +++ b/x-pack/plugins/security_solution/public/management/components/management_page_loader.tsx @@ -9,7 +9,7 @@ import React, { memo } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; import { ManagementEmptyStateWrapper } from './management_empty_state_wrapper'; -export const ManagementPageLoader = memo<{ 'data-test-subj': string }>( +export const ManagementPageLoader = memo<{ 'data-test-subj'?: string }>( ({ 'data-test-subj': dataTestSubj }) => { return ( diff --git a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx index b6a15c04b3b064..6e86c69c497509 100644 --- a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.test.tsx @@ -83,7 +83,7 @@ describe('Search exceptions', () => { }); expect(onSearchMock).toHaveBeenCalledTimes(1); - expect(onSearchMock).toHaveBeenCalledWith(expectedDefaultValue, ''); + expect(onSearchMock).toHaveBeenCalledWith(expectedDefaultValue, '', false); }); it('should dispatch search action when click on button', () => { @@ -96,7 +96,7 @@ describe('Search exceptions', () => { }); expect(onSearchMock).toHaveBeenCalledTimes(1); - expect(onSearchMock).toHaveBeenCalledWith(expectedDefaultValue, ''); + expect(onSearchMock).toHaveBeenCalledWith(expectedDefaultValue, '', true); }); it('should hide refresh button', () => { diff --git a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx index 52eb8d1e7d4f11..7a7a28b4b0647b 100644 --- a/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx +++ b/x-pack/plugins/security_solution/public/management/components/search_exceptions/search_exceptions.tsx @@ -19,7 +19,14 @@ export interface SearchExceptionsProps { policyList?: ImmutableArray; defaultIncludedPolicies?: string; hideRefreshButton?: boolean; - onSearch(query: string, includedPolicies?: string): void; + onSearch( + /** The query string the user entered into the text field */ + query: string, + /** A list of policy id's comma delimited */ + includedPolicies: string | undefined, + /** Will be `true` if the user clicked the `refresh` button */ + refresh: boolean + ): void; } export const SearchExceptions = memo( @@ -45,7 +52,7 @@ export const SearchExceptions = memo( setIncludedPolicies(includePoliciesNew); - onSearch(query, includePoliciesNew); + onSearch(query, includePoliciesNew, false); }, [onSearch, query] ); @@ -55,13 +62,13 @@ export const SearchExceptions = memo( [setQuery] ); const handleOnSearch = useCallback( - () => onSearch(query, includedPolicies), + () => onSearch(query, includedPolicies, true), [onSearch, query, includedPolicies] ); const handleOnSearchQuery = useCallback( (value) => { - onSearch(value, includedPolicies); + onSearch(value, includedPolicies, false); }, [onSearch, includedPolicies] ); diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.test.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.test.tsx index a7bae8c1f37d60..152b2c3f0d6906 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.test.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.test.tsx @@ -51,19 +51,21 @@ describe('Bulk delete artifact hook', () => { expect(fakeHttpServices.delete).toHaveBeenCalledTimes(0); await act(async () => { - const res = await result.mutateAsync(['fakeId-1', 'fakeId-2']); + const res = await result.mutateAsync([{ id: 'fakeId-1' }, { itemId: 'fakeId-2' }]); expect(res).toEqual([exceptionItem1, exceptionItem2]); expect(onSuccessMock).toHaveBeenCalledTimes(1); expect(fakeHttpServices.delete).toHaveBeenCalledTimes(2); expect(fakeHttpServices.delete).toHaveBeenNthCalledWith(1, '/api/exception_lists/items', { query: { id: 'fakeId-1', + item_id: undefined, namespace_type: 'agnostic', }, }); expect(fakeHttpServices.delete).toHaveBeenNthCalledWith(2, '/api/exception_lists/items', { query: { - id: 'fakeId-2', + id: undefined, + item_id: 'fakeId-2', namespace_type: 'agnostic', }, }); @@ -90,7 +92,7 @@ describe('Bulk delete artifact hook', () => { await act(async () => { try { - await result.mutateAsync(['fakeId-1', 'fakeId-2']); + await result.mutateAsync([{ id: 'fakeId-1' }, { id: 'fakeId-2' }]); } catch (err) { expect(err).toBe(error); expect(fakeHttpServices.delete).toHaveBeenCalledTimes(2); diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.tsx index 5feda65996ea1b..9957ff27d4bf93 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_delete_artifact.tsx @@ -10,25 +10,34 @@ import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types' import { useMutation, UseMutationResult, UseQueryOptions } from 'react-query'; import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; +const DEFAULT_OPTIONS = Object.freeze({}); + export function useBulkDeleteArtifact( exceptionListApiClient: ExceptionsListApiClient, - customOptions: UseQueryOptions, + customOptions: UseQueryOptions = DEFAULT_OPTIONS, options: { concurrency: number; } = { concurrency: 5, } -): UseMutationResult void> { - return useMutation void>( - (exceptionIds: string[]) => { - return pMap( - exceptionIds, - (id) => { - return exceptionListApiClient.delete(id); - }, - options - ); - }, - customOptions - ); +): UseMutationResult< + ExceptionListItemSchema[], + HttpFetchError, + Array<{ itemId?: string; id?: string }>, + () => void +> { + return useMutation< + ExceptionListItemSchema[], + HttpFetchError, + Array<{ itemId?: string; id?: string }>, + () => void + >((exceptionIds: Array<{ itemId?: string; id?: string }>) => { + return pMap( + exceptionIds, + ({ itemId, id }) => { + return exceptionListApiClient.delete(itemId, id); + }, + options + ); + }, customOptions); } diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_update_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_update_artifact.tsx index 181fe6dc9d7d5c..b4854209fa07be 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_update_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_bulk_update_artifact.tsx @@ -13,9 +13,11 @@ import { import { useMutation, UseMutationResult, UseQueryOptions } from 'react-query'; import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; +const DEFAULT_OPTIONS = Object.freeze({}); + export function useBulkUpdateArtifact( exceptionListApiClient: ExceptionsListApiClient, - customOptions: UseQueryOptions, + customOptions: UseQueryOptions = DEFAULT_OPTIONS, options: { concurrency: number; } = { diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_create_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_create_artifact.tsx index 346c4f7b42a87e..74aa6752f371cc 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_create_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_create_artifact.tsx @@ -12,9 +12,11 @@ import { HttpFetchError } from 'kibana/public'; import { useMutation, UseMutationResult, UseQueryOptions } from 'react-query'; import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; +const DEFAULT_OPTIONS = Object.freeze({}); + export function useCreateArtifact( exceptionListApiClient: ExceptionsListApiClient, - customOptions: UseQueryOptions + customOptions: UseQueryOptions = DEFAULT_OPTIONS ): UseMutationResult< ExceptionListItemSchema, HttpFetchError, diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.test.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.test.tsx index 8d31ce8f059bb5..bed3faf237b9f3 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.test.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.test.tsx @@ -49,7 +49,7 @@ describe('Delete artifact hook', () => { expect(fakeHttpServices.delete).toHaveBeenCalledTimes(0); await act(async () => { - const res = await result.mutateAsync('fakeId'); + const res = await result.mutateAsync({ id: 'fakeId' }); expect(res).toBe(exceptionItem); expect(onSuccessMock).toHaveBeenCalledTimes(1); expect(fakeHttpServices.delete).toHaveBeenCalledTimes(1); @@ -82,7 +82,7 @@ describe('Delete artifact hook', () => { await act(async () => { try { - await result.mutateAsync('fakeId'); + await result.mutateAsync({ itemId: 'fakeId' }); } catch (err) { expect(err).toBe(error); expect(fakeHttpServices.delete).toHaveBeenCalledTimes(1); diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.tsx index 27820c73b740ae..e072eecb061307 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_delete_artifact.tsx @@ -9,11 +9,23 @@ import { HttpFetchError } from 'kibana/public'; import { useMutation, UseMutationResult, UseQueryOptions } from 'react-query'; import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; +const DEFAULT_OPTIONS = Object.freeze({}); + export function useDeleteArtifact( exceptionListApiClient: ExceptionsListApiClient, - customOptions: UseQueryOptions -): UseMutationResult void> { - return useMutation void>((id: string) => { - return exceptionListApiClient.delete(id); + customOptions: UseQueryOptions = DEFAULT_OPTIONS +): UseMutationResult< + ExceptionListItemSchema, + HttpFetchError, + { itemId?: string; id?: string }, + () => void +> { + return useMutation< + ExceptionListItemSchema, + HttpFetchError, + { itemId?: string; id?: string }, + () => void + >(({ itemId, id }) => { + return exceptionListApiClient.delete(itemId, id); }, customOptions); } diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.test.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.test.tsx index 70f9620c6edc99..470f29b563f675 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.test.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.test.tsx @@ -39,7 +39,7 @@ describe('Get artifact hook', () => { result = await renderQuery( () => - useGetArtifact(instance, 'fakeId', { + useGetArtifact(instance, 'fakeId', undefined, { onSuccess: onSuccessMock, retry: false, }), @@ -50,7 +50,7 @@ describe('Get artifact hook', () => { expect(fakeHttpServices.get).toHaveBeenCalledTimes(1); expect(fakeHttpServices.get).toHaveBeenCalledWith('/api/exception_lists/items', { query: { - id: 'fakeId', + item_id: 'fakeId', namespace_type: 'agnostic', }, }); @@ -69,7 +69,7 @@ describe('Get artifact hook', () => { result = await renderQuery( () => - useGetArtifact(instance, 'fakeId', { + useGetArtifact(instance, undefined, 'fakeId', { onError: onErrorMock, retry: false, }), diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.tsx index bc27ba285ccb38..676c424e1a2789 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_get_artifact.tsx @@ -9,15 +9,18 @@ import { HttpFetchError } from 'kibana/public'; import { QueryObserverResult, useQuery, UseQueryOptions } from 'react-query'; import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; +const DEFAULT_OPTIONS = Object.freeze({}); + export function useGetArtifact( exceptionListApiClient: ExceptionsListApiClient, - id: string, - customQueryOptions: UseQueryOptions + itemId?: string, + id?: string, + customQueryOptions: UseQueryOptions = DEFAULT_OPTIONS ): QueryObserverResult { return useQuery( - ['get', exceptionListApiClient, id], + ['get', exceptionListApiClient, itemId, id], () => { - return exceptionListApiClient.get(id); + return exceptionListApiClient.get(itemId, id); }, { refetchIntervalInBackground: false, diff --git a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_update_artifact.tsx b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_update_artifact.tsx index a972096bb600ce..54acea6489574b 100644 --- a/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_update_artifact.tsx +++ b/x-pack/plugins/security_solution/public/management/hooks/artifacts/use_update_artifact.tsx @@ -12,9 +12,11 @@ import { HttpFetchError } from 'kibana/public'; import { useMutation, UseMutationResult, UseQueryOptions } from 'react-query'; import { ExceptionsListApiClient } from '../../services/exceptions_list/exceptions_list_api_client'; +const DEFAULT_OPTIONS = Object.freeze({}); + export function useUpdateArtifact( exceptionListApiClient: ExceptionsListApiClient, - customQueryOptions: UseQueryOptions + customQueryOptions: UseQueryOptions = DEFAULT_OPTIONS ): UseMutationResult< ExceptionListItemSchema, HttpFetchError, diff --git a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.test.tsx b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.test.tsx index 37f4004ba91744..2dab6a8fd497a8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.test.tsx @@ -5,10 +5,9 @@ * 2.0. */ -import { act } from '@testing-library/react'; +import { act, waitFor } from '@testing-library/react'; import React from 'react'; import { BLOCKLIST_PATH } from '../../../../../common/constants'; -import { useUserPrivileges as _useUserPrivileges } from '../../../../common/components/user_privileges'; import { AppContextTestRender, createAppRootMockRenderer } from '../../../../common/mock/endpoint'; import { Blocklist } from './blocklist'; @@ -28,12 +27,12 @@ describe('When on the blocklist page', () => { }); }); - describe('When on the blocklist list page', () => { - describe('And no data exists', () => { - it('should show the Empty message', async () => { - render(); - expect(renderResult.getByTestId('blocklistEmpty')).toBeTruthy(); - }); + describe('And no data exists', () => { + it('should show the Empty message', async () => { + render(); + await waitFor(() => + expect(renderResult.getByTestId('blocklistPage-emptyState')).toBeTruthy() + ); }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.tsx b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.tsx index ab96451f0cd25a..a48d6c5bd83774 100644 --- a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/blocklist.tsx @@ -5,80 +5,130 @@ * 2.0. */ -import React, { memo, useMemo } from 'react'; -import { useLocation } from 'react-router-dom'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { EuiButton } from '@elastic/eui'; -import { AdministrationListPage } from '../../../components/administration_list_page'; -import { ListPageRouteState } from '../../../../../common/endpoint/types'; -import { useMemoizedRouteState } from '../../../common/hooks'; -import { BackToExternalAppButton } from '../../../components/back_to_external_app_button'; -import { BlocklistEmptyState } from './components/empty'; -import { BackToExternalAppSecondaryButton } from '../../../components/back_to_external_app_secondary_button'; +import React, { memo } from 'react'; +import { i18n } from '@kbn/i18n'; +import { useHttp } from '../../../../common/lib/kibana'; +import { ArtifactListPage, ArtifactListPageProps } from '../../../components/artifact_list_page'; +import { HostIsolationExceptionsApiClient } from '../../host_isolation_exceptions/host_isolation_exceptions_api_client'; -export const Blocklist = memo(() => { - const { state: routeState } = useLocation(); - const memoizedRouteState = useMemoizedRouteState(routeState); +// FIXME:PT delete this when real component is implemented +const TempDevFormComponent: ArtifactListPageProps['ArtifactFormComponent'] = (props) => { + // For Dev. Delete once we implement this component + // @ts-ignore + if (!window._dev_artifact_form_props) { + // @ts-ignore + window._dev_artifact_form_props = []; + // @ts-ignore + window.console.log(window._dev_artifact_form_props); + } + // @ts-ignore + window._dev_artifact_form_props.push(props); - const backButtonEmptyComponent = useMemo(() => { - if (memoizedRouteState && memoizedRouteState.onBackButtonNavigateTo) { - return ; - } - }, [memoizedRouteState]); + return ( +
+
+ {props.error ? props.error?.body?.message || props.error : ''} +
+ {`TODO: ${props.mode} Form here`} +
+ ); +}; - const backButtonHeaderComponent = useMemo(() => { - if (memoizedRouteState && memoizedRouteState.onBackButtonNavigateTo) { - return ; - } - }, [memoizedRouteState]); +const BLOCKLIST_PAGE_LABELS: ArtifactListPageProps['labels'] = { + pageTitle: i18n.translate('xpack.securitySolution.blocklist.pageTitle', { + defaultMessage: 'Blocklist', + }), + pageAboutInfo: i18n.translate('xpack.securitySolution.blocklist.pageAboutInfo', { + defaultMessage: '(DEV: temporarily using isolation exception api)', // FIXME: need wording from PM + }), + pageAddButtonTitle: i18n.translate('xpack.securitySolution.blocklist.pageAddButtonTitle', { + defaultMessage: 'Add blocklist entry', + }), + getShowingCountLabel: (total) => + i18n.translate('xpack.securitySolution.blocklist.showingTotal', { + defaultMessage: 'Showing {total} {total, plural, one {blocklist} other {blocklists}}', + values: { total }, + }), + cardActionEditLabel: i18n.translate('xpack.securitySolution.blocklist.cardActionEditLabel', { + defaultMessage: 'Edit blocklist', + }), + cardActionDeleteLabel: i18n.translate('xpack.securitySolution.blocklist.cardActionDeleteLabel', { + defaultMessage: 'Delete blocklist', + }), + flyoutCreateTitle: i18n.translate('xpack.securitySolution.blocklist.flyoutCreateTitle', { + defaultMessage: 'Add blocklist', + }), + flyoutEditTitle: i18n.translate('xpack.securitySolution.blocklist.flyoutEditTitle', { + defaultMessage: 'Edit blocklist', + }), + flyoutCreateSubmitButtonLabel: i18n.translate( + 'xpack.securitySolution.blocklist.flyoutCreateSubmitButtonLabel', + { defaultMessage: 'Add blocklist' } + ), + flyoutCreateSubmitSuccess: ({ name }) => + i18n.translate('xpack.securitySolution.blocklist.flyoutCreateSubmitSuccess', { + defaultMessage: '"{name}" has been added to your blocklist.', // FIXME: match this to design (needs count of items) + values: { name }, + }), + flyoutEditSubmitSuccess: ({ name }) => + i18n.translate('xpack.securitySolution.blocklist.flyoutEditSubmitSuccess', { + defaultMessage: '"{name}" has been updated.', + values: { name }, + }), + flyoutDowngradedLicenseDocsInfo: () => { + return 'tbd...'; + // FIXME: define docs link for license downgrade message. sample code below - const hasDataToShow = false; + // const { docLinks } = useKibana().services; + // return ( + // + // {' '} + // {' '} + // + // ), + // }} + // /> + // ); + }, + deleteActionSuccess: (itemName) => + i18n.translate('xpack.securitySolution.blocklist.deleteSuccess', { + defaultMessage: '"{itemName}" has been removed from blocklist.', + values: { itemName }, + }), + emptyStateTitle: i18n.translate('xpack.securitySolution.blocklist.emptyStateTitle', { + defaultMessage: 'Add your first blocklist', + }), + emptyStateInfo: i18n.translate( + 'xpack.securitySolution.blocklist.emptyStateInfo', + { defaultMessage: 'Add a blocklist to prevent execution on the endpoint' } // FIXME: need wording here form PM + ), + emptyStatePrimaryButtonLabel: i18n.translate( + 'xpack.securitySolution.blocklist.emptyStatePrimaryButtonLabel', + { defaultMessage: 'Add blocklist' } + ), +}; - const handleAddButtonClick = () => {}; +export const Blocklist = memo(() => { + const http = useHttp(); + // FIXME: Implement Blocklist API client and define list + // for now, just using Event Filters + const eventFiltersApiClient = HostIsolationExceptionsApiClient.getInstance(http); return ( - - } - subtitle={ - - } - actions={ - hasDataToShow ? ( - - - - ) : ( - [] - ) - } - hideHeader={!hasDataToShow} - > - {hasDataToShow ? ( -

{'Data, search bar, etc here'}

- ) : ( - - )} -
+ ); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/components/empty.tsx b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/components/empty.tsx deleted file mode 100644 index bd1d01b73ec8da..00000000000000 --- a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/components/empty.tsx +++ /dev/null @@ -1,58 +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, { memo } from 'react'; -import styled, { css } from 'styled-components'; -import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { ManagementEmptyStateWrapper } from '../../../../components/management_empty_state_wrapper'; - -const EmptyPrompt = styled(EuiEmptyPrompt)` - ${() => css` - max-width: 100%; - `} -`; - -export const BlocklistEmptyState = memo<{ - onAdd: () => void; - backComponent?: React.ReactNode; -}>(({ onAdd, backComponent }) => { - return ( - - - - - } - body={ - - } - actions={[ - - - , - - ...(backComponent ? [backComponent] : []), - ]} - /> - - ); -}); - -BlocklistEmptyState.displayName = 'BlocklistEmptyState'; diff --git a/x-pack/plugins/security_solution/public/management/pages/mocks/trusted_apps_http_mocks.ts b/x-pack/plugins/security_solution/public/management/pages/mocks/trusted_apps_http_mocks.ts index 347f1dc088c10a..c92dcc0bd7cc47 100644 --- a/x-pack/plugins/security_solution/public/management/pages/mocks/trusted_apps_http_mocks.ts +++ b/x-pack/plugins/security_solution/public/management/pages/mocks/trusted_apps_http_mocks.ts @@ -33,7 +33,10 @@ import { fleetGetEndpointPackagePolicyListHttpMock, FleetGetEndpointPackagePolicyListHttpMockInterface, } from './fleet_mocks'; -import { BY_POLICY_ARTIFACT_TAG_PREFIX } from '../../../../common/endpoint/service/artifacts/constants'; +import { + BY_POLICY_ARTIFACT_TAG_PREFIX, + GLOBAL_ARTIFACT_TAG, +} from '../../../../common/endpoint/service/artifacts/constants'; interface FindExceptionListItemSchemaQueryParams extends Omit { @@ -58,7 +61,11 @@ export const trustedAppsGetListHttpMocks = const generator = new ExceptionsListItemGenerator('seed'); const perPage = apiQueryParams.per_page ?? 10; const data = Array.from({ length: Math.min(perPage, 50) }, () => - generator.generate({ list_id: ENDPOINT_TRUSTED_APPS_LIST_ID, os_types: ['windows'] }) + generator.generate({ + list_id: ENDPOINT_TRUSTED_APPS_LIST_ID, + os_types: ['windows'], + tags: [GLOBAL_ARTIFACT_TAG], + }) ); // FIXME: remove hard-coded IDs below adn get them from the new FleetPackagePolicyGenerator (#2262) @@ -130,6 +137,7 @@ export const trustedAppsGetOneHttpMocks = const apiQueryParams = query as ReadExceptionListItemSchema; const exceptionItem = new ExceptionsListItemGenerator('seed').generate({ os_types: ['windows'], + tags: [GLOBAL_ARTIFACT_TAG], }); exceptionItem.item_id = apiQueryParams.item_id ?? exceptionItem.item_id; @@ -157,7 +165,10 @@ export const trustedAppPostHttpMocks = httpHandlerMockFactory { mockedApis = trustedAppsAllHttpMocks(mockedContext.coreStart.http); getState = () => mockedContext.store.getState().management.policyDetails; render = () => mockedContext.render(); + + const getTaListApiResponseMock = + mockedApis.responseProvider.trustedAppsList.getMockImplementation(); + mockedApis.responseProvider.trustedAppsList.mockImplementation((options) => { + const response = getTaListApiResponseMock!(options); + response.data = response.data.filter((ta) => isArtifactByPolicy(ta)); + return response; + }); }); afterEach(() => reactTestingLibrary.cleanup()); @@ -97,7 +106,7 @@ describe('Policy trusted apps flyout', () => { }); expect(component.getByTestId('confirmPolicyTrustedAppsFlyout')).not.toBeNull(); - expect(component.getByTestId('Generated Exception (u6kh2)_checkbox')).not.toBeNull(); + expect(component.getByTestId('Generated Exception (nng74)_checkbox')).not.toBeNull(); }); it('should confirm flyout action', async () => { @@ -111,7 +120,7 @@ describe('Policy trusted apps flyout', () => { }); // TA name below in the selector matches the 3rd generated trusted app which is policy specific - const tACardCheckbox = component.getByTestId('Generated Exception (3xnng)_checkbox'); + const tACardCheckbox = component.getByTestId('Generated Exception (nng74)_checkbox'); act(() => { fireEvent.click(tACardCheckbox); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/remove_trusted_app_from_policy_modal.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/remove_trusted_app_from_policy_modal.test.tsx index 172b5218188c61..676080d180a6ae 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/remove_trusted_app_from_policy_modal.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/trusted_apps/list/remove_trusted_app_from_policy_modal.test.tsx @@ -214,7 +214,7 @@ describe('When using the RemoveTrustedAppFromPolicyModal component', () => { await clickConfirmButton(true, true); expect(appTestContext.coreStart.notifications.toasts.addSuccess).toHaveBeenCalledWith({ - text: '"Generated Exception (3xnng)" has been removed from Endpoint Policy policy', + text: '"Generated Exception (nng74)" has been removed from Endpoint Policy policy', title: 'Successfully removed', }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx index 73d85077e9579e..82169fcd19c104 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.test.tsx @@ -219,7 +219,7 @@ describe('When on the Trusted Apps Page', () => { it('should persist edit params to url', () => { expect(history.location.search).toEqual( - '?show=edit&id=2d95bec3-b48f-4db7-9622-a2b061cc031d' + '?show=edit&id=bec3b48f-ddb7-4622-a2b0-61cc031d17eb' ); }); @@ -251,7 +251,7 @@ describe('When on the Trusted Apps Page', () => { 'addTrustedAppFlyout-createForm-descriptionField' ) as HTMLTextAreaElement; - expect(formNameInput.value).toEqual('Generated Exception (3xnng)'); + expect(formNameInput.value).toEqual('Generated Exception (nng74)'); expect(formDescriptionInput.value).toEqual('created by ExceptionListItemGenerator'); }); @@ -276,8 +276,8 @@ describe('When on the Trusted Apps Page', () => { expect(lastCallToPut[0]).toEqual('/api/exception_lists/items'); expect(JSON.parse(lastCallToPut[1].body as string)).toEqual({ - _version: '3o9za', - name: 'Generated Exception (3xnng)', + _version: '9zawi', + name: 'Generated Exception (nng74)', description: 'created by ExceptionListItemGenerator', entries: [ { @@ -300,7 +300,7 @@ describe('When on the Trusted Apps Page', () => { ], id: '05b5e350-0cad-4dc3-a61d-6e6796b0af39', comments: [], - item_id: '2d95bec3-b48f-4db7-9622-a2b061cc031d', + item_id: 'bec3b48f-ddb7-4622-a2b0-61cc031d17eb', namespace_type: 'agnostic', type: 'simple', }); diff --git a/x-pack/plugins/security_solution/public/management/services/exceptions_list/exceptions_list_api_client.test.ts b/x-pack/plugins/security_solution/public/management/services/exceptions_list/exceptions_list_api_client.test.ts index 21a50079a1f1fe..e5c8d110b63f27 100644 --- a/x-pack/plugins/security_solution/public/management/services/exceptions_list/exceptions_list_api_client.test.ts +++ b/x-pack/plugins/security_solution/public/management/services/exceptions_list/exceptions_list_api_client.test.ts @@ -165,7 +165,8 @@ describe('Exceptions List Api Client', () => { expect(fakeHttpServices.get).toHaveBeenCalledTimes(1); expect(fakeHttpServices.get).toHaveBeenCalledWith(EXCEPTION_LIST_ITEM_URL, { query: { - id: fakeItemId, + item_id: fakeItemId, + id: undefined, namespace_type: 'agnostic', }, }); @@ -222,7 +223,8 @@ describe('Exceptions List Api Client', () => { expect(fakeHttpServices.delete).toHaveBeenCalledTimes(1); expect(fakeHttpServices.delete).toHaveBeenCalledWith(EXCEPTION_LIST_ITEM_URL, { query: { - id: fakeItemId, + item_id: fakeItemId, + id: undefined, namespace_type: 'agnostic', }, }); @@ -243,5 +245,32 @@ describe('Exceptions List Api Client', () => { }, }); }); + + it('hasData method returns true when list has data', async () => { + fakeHttpServices.get.mockResolvedValue({ + total: 1, + }); + + const exceptionsListApiClientInstance = getInstance(); + + await expect(exceptionsListApiClientInstance.hasData()).resolves.toBe(true); + + expect(fakeHttpServices.get).toHaveBeenCalledWith(`${EXCEPTION_LIST_ITEM_URL}/_find`, { + query: expect.objectContaining({ + page: 1, + per_page: 1, + }), + }); + }); + + it('hasData method returns false when list has no data', async () => { + fakeHttpServices.get.mockResolvedValue({ + total: 0, + }); + + const exceptionsListApiClientInstance = getInstance(); + + await expect(exceptionsListApiClientInstance.hasData()).resolves.toBe(false); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/services/exceptions_list/exceptions_list_api_client.ts b/x-pack/plugins/security_solution/public/management/services/exceptions_list/exceptions_list_api_client.ts index 6edf55e569d355..c995754dd1907c 100644 --- a/x-pack/plugins/security_solution/public/management/services/exceptions_list/exceptions_list_api_client.ts +++ b/x-pack/plugins/security_solution/public/management/services/exceptions_list/exceptions_list_api_client.ts @@ -30,7 +30,7 @@ export class ExceptionsListApiClient { constructor( private readonly http: HttpStart, - private readonly listId: ListId, + public readonly listId: ListId, private readonly listDefinition: CreateExceptionListSchema ) { this.ensureListExists = this.createExceptionList(); @@ -166,14 +166,19 @@ export class ExceptionsListApiClient { } /** - * Returns an item filtered by id - * It requires an id in order to get the desired item + * Returns an item for the given `itemId` or `id`. Exception List Items have both an `item_id` + * and `id`, and at least one of these two is required to be provided. */ - async get(id: string): Promise { + async get(itemId?: string, id?: string): Promise { + if (!itemId && !id) { + throw TypeError('either `itemId` or `id` argument must be set'); + } + await this.ensureListExists; return this.http.get(EXCEPTION_LIST_ITEM_URL, { query: { id, + item_id: itemId, namespace_type: 'agnostic', }, }); @@ -204,14 +209,19 @@ export class ExceptionsListApiClient { } /** - * It deletes an existing item. - * It requires a valid item id. + * It deletes an existing item by `itemId` or `id`. Exception List Items have both an `item_id` + * and `id`, and at least one of these two is required to be provided. */ - async delete(id: string): Promise { + async delete(itemId?: string, id?: string): Promise { + if (!itemId && !id) { + throw TypeError('either `itemId` or `id` argument must be set'); + } + await this.ensureListExists; return this.http.delete(EXCEPTION_LIST_ITEM_URL, { query: { id, + item_id: itemId, namespace_type: 'agnostic', }, }); @@ -231,4 +241,11 @@ export class ExceptionsListApiClient { }, }); } + + /** + * Checks if the given list has any data in it + */ + async hasData(): Promise { + return (await this.find({ perPage: 1, page: 1 })).total > 0; + } } diff --git a/x-pack/plugins/security_solution/scripts/endpoint/event_filters/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/event_filters/index.ts index 70801e08ea335d..7f18c0b40fed7c 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/event_filters/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/event_filters/index.ts @@ -17,8 +17,9 @@ import { EXCEPTION_LIST_ITEM_URL, EXCEPTION_LIST_URL, } from '@kbn/securitysolution-list-constants'; -import { EventFilterGenerator } from '../../../common/endpoint/data_generators/event_filter_generator'; import { randomPolicyIdGenerator } from '../common/random_policy_id_generator'; +import { ExceptionsListItemGenerator } from '../../../common/endpoint/data_generators/exceptions_list_item_generator'; +import { isArtifactByPolicy } from '../../../common/endpoint/service/artifacts'; export const cli = () => { run( @@ -66,7 +67,7 @@ const handleThrowAxiosHttpError = (err: AxiosError): never => { }; const createEventFilters: RunFn = async ({ flags, log }) => { - const eventGenerator = new EventFilterGenerator(); + const eventGenerator = new ExceptionsListItemGenerator(); const kbn = new KbnClient({ log, url: flags.kibana as string }); await ensureCreateEndpointEventFiltersList(kbn); @@ -76,8 +77,9 @@ const createEventFilters: RunFn = async ({ flags, log }) => { await pMap( Array.from({ length: flags.count as unknown as number }), () => { - const body = eventGenerator.generate(); - if (body.tags?.length && body.tags[0] !== 'policy:all') { + const body = eventGenerator.generateEventFilterForCreate(); + + if (isArtifactByPolicy(body)) { const nmExceptions = Math.floor(Math.random() * 3) || 1; body.tags = Array.from({ length: nmExceptions }, () => { return `policy:${randomPolicyId()}`; diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/host_isolation_exceptions.ts b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/host_isolation_exceptions.ts index ffa7473e954164..5e1166d03f0d55 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/host_isolation_exceptions.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/host_isolation_exceptions.ts @@ -11,7 +11,10 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; import { PolicyTestResourceInfo } from '../../../security_solution_endpoint/services/endpoint_policy'; import { ArtifactTestData } from '../../../security_solution_endpoint/services/endpoint_artifacts'; -import { BY_POLICY_ARTIFACT_TAG_PREFIX } from '../../../../plugins/security_solution/common/endpoint/service/artifacts'; +import { + BY_POLICY_ARTIFACT_TAG_PREFIX, + GLOBAL_ARTIFACT_TAG, +} from '../../../../plugins/security_solution/common/endpoint/service/artifacts'; import { createUserAndRole, deleteUserAndRole, @@ -29,6 +32,7 @@ export default function ({ getService }: FtrProviderContext) { const supertestWithoutAuth = getService('supertestWithoutAuth'); const endpointPolicyTestResources = getService('endpointPolicyTestResources'); const endpointArtifactTestResources = getService('endpointArtifactTestResources'); + const log = getService('log'); type ApiCallsInterface = Array<{ method: keyof Pick; @@ -67,7 +71,10 @@ export default function ({ getService }: FtrProviderContext) { { method: 'post', path: EXCEPTION_LIST_ITEM_URL, - getBody: () => exceptionsGenerator.generateHostIsolationExceptionForCreate(), + getBody: () => + exceptionsGenerator.generateHostIsolationExceptionForCreate({ + tags: [GLOBAL_ARTIFACT_TAG], + }), }, { method: 'put', @@ -77,6 +84,7 @@ export default function ({ getService }: FtrProviderContext) { id: existingExceptionData.artifact.id, item_id: existingExceptionData.artifact.item_id, _version: existingExceptionData.artifact._version, + tags: [GLOBAL_ARTIFACT_TAG], }), }, ]; @@ -214,11 +222,17 @@ export default function ({ getService }: FtrProviderContext) { await supertest[apiCall.method](apiCall.path) .set('kbn-xsrf', 'true') + .on('error', (error) => { + log.error(JSON.stringify(error?.response?.body ?? error, null, 2)); + }) .send(body) .expect(200); const deleteUrl = `${EXCEPTION_LIST_ITEM_URL}?item_id=${body.item_id}&namespace_type=${body.namespace_type}`; - await supertest.delete(deleteUrl).set('kbn-xsrf', 'true'); + + log.info(`cleaning up: ${deleteUrl}`); + + await supertest.delete(deleteUrl).set('kbn-xsrf', 'true').expect(200); }); } }); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/trusted_apps.ts b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/trusted_apps.ts index 7caf4f085694a7..60b479c5b37d9e 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/trusted_apps.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/endpoint_artifacts/trusted_apps.ts @@ -11,7 +11,10 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; import { PolicyTestResourceInfo } from '../../../security_solution_endpoint/services/endpoint_policy'; import { ArtifactTestData } from '../../../security_solution_endpoint/services//endpoint_artifacts'; -import { BY_POLICY_ARTIFACT_TAG_PREFIX } from '../../../../plugins/security_solution/common/endpoint/service/artifacts'; +import { + BY_POLICY_ARTIFACT_TAG_PREFIX, + GLOBAL_ARTIFACT_TAG, +} from '../../../../plugins/security_solution/common/endpoint/service/artifacts'; import { ExceptionsListItemGenerator } from '../../../../plugins/security_solution/common/endpoint/data_generators/exceptions_list_item_generator'; import { createUserAndRole, @@ -91,7 +94,9 @@ export default function ({ getService }: FtrProviderContext) { { method: 'post', path: EXCEPTION_LIST_ITEM_URL, - getBody: () => exceptionsGenerator.generateTrustedAppForCreate(), + getBody: () => { + return exceptionsGenerator.generateTrustedAppForCreate({ tags: [GLOBAL_ARTIFACT_TAG] }); + }, }, { method: 'put', @@ -100,6 +105,7 @@ export default function ({ getService }: FtrProviderContext) { exceptionsGenerator.generateTrustedAppForUpdate({ id: trustedAppData.artifact.id, item_id: trustedAppData.artifact.item_id, + tags: [GLOBAL_ARTIFACT_TAG], }), }, ]; From d746bd4c5f9e57114cb61749c7a878d87d83bdff Mon Sep 17 00:00:00 2001 From: John Dorlus Date: Thu, 24 Feb 2022 14:09:20 -0500 Subject: [PATCH 05/28] Migrated Snapshot and Restore tests to use test user (#126011) * Migrated Snapshot and Restore tests to use test user. * added another permission to address unauthorized failure. * Added metadata perms for kibana administration. * Testing out different permissions. * Edited test to not refresh page after role is set. Causing errors. * Adjusted perms per documentation. * Addded link to the documentation specifying the permissions needed for snapshot restore. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../apps/snapshot_restore/home_page.ts | 5 +++-- x-pack/test/functional/config.js | 19 +++++++++++++++++++ .../page_objects/snapshot_restore_page.ts | 5 ++++- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/x-pack/test/functional/apps/snapshot_restore/home_page.ts b/x-pack/test/functional/apps/snapshot_restore/home_page.ts index b2893ace7b20aa..c29135e089bfa2 100644 --- a/x-pack/test/functional/apps/snapshot_restore/home_page.ts +++ b/x-pack/test/functional/apps/snapshot_restore/home_page.ts @@ -12,9 +12,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const pageObjects = getPageObjects(['common', 'snapshotRestore']); const log = getService('log'); const es = getService('es'); + const security = getService('security'); describe('Home page', function () { before(async () => { + await security.testUser.setRoles(['snapshot_restore_user'], false); await pageObjects.common.navigateToApp('snapshotRestore'); }); @@ -46,9 +48,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('cleanup repository', async () => { await pageObjects.snapshotRestore.viewRepositoryDetails('my-repository'); - await pageObjects.common.sleep(25000); const cleanupResponse = await pageObjects.snapshotRestore.performRepositoryCleanup(); - await pageObjects.common.sleep(25000); expect(cleanupResponse).to.contain('results'); expect(cleanupResponse).to.contain('deleted_bytes'); expect(cleanupResponse).to.contain('deleted_blobs'); @@ -57,6 +57,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await es.snapshot.deleteRepository({ name: 'my-repository', }); + await security.testUser.restoreDefaults(); }); }); }); diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 2d26bc6391d9dd..66b32dac8e4b7b 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -551,6 +551,25 @@ export default async function ({ readConfigFile }) { }, ], }, + // https://www.elastic.co/guide/en/elasticsearch/reference/master/snapshots-register-repository.html#snapshot-repo-prereqs + snapshot_restore_user: { + elasticsearch: { + cluster: [ + 'monitor', + 'manage_slm', + 'cluster:admin/snapshot', + 'cluster:admin/repository', + ], + }, + kibana: [ + { + feature: { + advancedSettings: ['read'], + }, + spaces: ['*'], + }, + ], + }, ingest_pipelines_user: { elasticsearch: { diff --git a/x-pack/test/functional/page_objects/snapshot_restore_page.ts b/x-pack/test/functional/page_objects/snapshot_restore_page.ts index 216b2bfff9d7ed..e1fc50ed86fa2d 100644 --- a/x-pack/test/functional/page_objects/snapshot_restore_page.ts +++ b/x-pack/test/functional/page_objects/snapshot_restore_page.ts @@ -49,12 +49,15 @@ export function SnapshotRestorePageProvider({ getService }: FtrProviderContext) const repoToView = repos.filter((r) => (r.repoName = name))[0]; await repoToView.repoLink.click(); } - await retry.waitForWithTimeout(`Repo title should be ${name}`, 10000, async () => { + await retry.waitForWithTimeout(`Repo title should be ${name}`, 25000, async () => { return (await testSubjects.getVisibleText('title')) === name; }); }, async performRepositoryCleanup() { await testSubjects.click('cleanupRepositoryButton'); + await retry.waitForWithTimeout(`wait for code block to be visible`, 25000, async () => { + return await testSubjects.isDisplayed('cleanupCodeBlock'); + }); return await testSubjects.getVisibleText('cleanupCodeBlock'); }, }; From febb41c3e60a6c3ad9928726890ed37a5cf5ef99 Mon Sep 17 00:00:00 2001 From: Maja Grubic Date: Thu, 24 Feb 2022 20:39:59 +0100 Subject: [PATCH 06/28] [Shared UX] No Data Views Component (#125403) * Adding illustration * Add components * Add fake data view editor * [Shared UX] Adding No data views component * updating failing button snapshot * Fix style name * Update test * Change folder structure * Refactoring service * Fixing comments * Minor fixes * Fix failing snapshot * Change the layout of the component * Polish * Fix missing export * Fix docLinks initialization * Fix failing test * Remove unnecessary div Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> * Use calc function Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> * Rename onClick prop * Updated to use EuiEmptyPrompt * Fixing tests * Applying Clint's comments * Add documentation * Update string * Feedback for PR #125403 * Add missing comment * Fix import issue Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> Co-authored-by: cchaos Co-authored-by: Clint Andrew Hall --- src/plugins/shared_ux/kibana.json | 2 +- .../assets/data_view_illustration.tsx | 552 ++++++++++++++++++ .../components/empty_state/assets/index.tsx | 24 + .../public/components/empty_state/index.tsx | 9 + .../documentation_link.test.tsx.snap | 35 ++ .../no_data_views/documentation_link.test.tsx | 28 + .../no_data_views/documentation_link.tsx | 39 ++ .../empty_state/no_data_views/index.tsx | 9 + .../no_data_views.component.test.tsx | 53 ++ .../no_data_views/no_data_views.component.tsx | 90 +++ .../no_data_views/no_data_views.mdx | 14 + .../no_data_views/no_data_views.stories.tsx | 52 ++ .../no_data_views/no_data_views.test.tsx | 48 ++ .../no_data_views/no_data_views.tsx | 72 +++ .../exit_full_screen_button.test.tsx.snap | 15 + .../shared_ux/public/components/index.ts | 17 + src/plugins/shared_ux/public/index.ts | 1 + .../shared_ux/public/services/doc_links.ts | 10 + .../shared_ux/public/services/editors.ts | 15 + .../shared_ux/public/services/index.tsx | 12 + .../public/services/kibana/doc_links.ts | 23 + .../public/services/kibana/editors.ts | 23 + .../shared_ux/public/services/kibana/index.ts | 6 + .../public/services/kibana/permissions.ts | 23 + .../public/services/mocks/doc_links.mock.ts | 19 + .../public/services/mocks/editors.mock.ts | 19 + .../shared_ux/public/services/mocks/index.ts | 6 + .../public/services/mocks/permissions.mock.ts | 20 + .../shared_ux/public/services/permissions.ts | 11 + .../public/services/storybook/doc_links.ts | 19 + .../public/services/storybook/editors.ts | 22 + .../public/services/storybook/index.ts | 6 + .../public/services/storybook/permissions.ts | 20 + .../public/services/stub/doc_links.ts | 19 + .../shared_ux/public/services/stub/editors.ts | 22 + .../shared_ux/public/services/stub/index.ts | 6 + .../public/services/stub/permissions.ts | 22 + src/plugins/shared_ux/public/types/index.ts | 5 +- src/plugins/shared_ux/tsconfig.json | 6 + 39 files changed, 1392 insertions(+), 2 deletions(-) create mode 100644 src/plugins/shared_ux/public/components/empty_state/assets/data_view_illustration.tsx create mode 100644 src/plugins/shared_ux/public/components/empty_state/assets/index.tsx create mode 100644 src/plugins/shared_ux/public/components/empty_state/index.tsx create mode 100644 src/plugins/shared_ux/public/components/empty_state/no_data_views/__snapshots__/documentation_link.test.tsx.snap create mode 100644 src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.test.tsx create mode 100644 src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.tsx create mode 100644 src/plugins/shared_ux/public/components/empty_state/no_data_views/index.tsx create mode 100644 src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.test.tsx create mode 100644 src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.tsx create mode 100644 src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.mdx create mode 100644 src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.stories.tsx create mode 100644 src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.test.tsx create mode 100644 src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.tsx create mode 100644 src/plugins/shared_ux/public/services/doc_links.ts create mode 100644 src/plugins/shared_ux/public/services/editors.ts create mode 100644 src/plugins/shared_ux/public/services/kibana/doc_links.ts create mode 100644 src/plugins/shared_ux/public/services/kibana/editors.ts create mode 100644 src/plugins/shared_ux/public/services/kibana/permissions.ts create mode 100644 src/plugins/shared_ux/public/services/mocks/doc_links.mock.ts create mode 100644 src/plugins/shared_ux/public/services/mocks/editors.mock.ts create mode 100644 src/plugins/shared_ux/public/services/mocks/permissions.mock.ts create mode 100644 src/plugins/shared_ux/public/services/permissions.ts create mode 100644 src/plugins/shared_ux/public/services/storybook/doc_links.ts create mode 100644 src/plugins/shared_ux/public/services/storybook/editors.ts create mode 100644 src/plugins/shared_ux/public/services/storybook/permissions.ts create mode 100644 src/plugins/shared_ux/public/services/stub/doc_links.ts create mode 100644 src/plugins/shared_ux/public/services/stub/editors.ts create mode 100644 src/plugins/shared_ux/public/services/stub/permissions.ts diff --git a/src/plugins/shared_ux/kibana.json b/src/plugins/shared_ux/kibana.json index 44aeeb9cf80fcd..308a252f70b549 100755 --- a/src/plugins/shared_ux/kibana.json +++ b/src/plugins/shared_ux/kibana.json @@ -9,6 +9,6 @@ "description": "A plugin providing components and services for shared user experiences in Kibana.", "server": true, "ui": true, - "requiredPlugins": [], + "requiredPlugins": ["dataViewEditor", "dataViews"], "optionalPlugins": [] } diff --git a/src/plugins/shared_ux/public/components/empty_state/assets/data_view_illustration.tsx b/src/plugins/shared_ux/public/components/empty_state/assets/data_view_illustration.tsx new file mode 100644 index 00000000000000..8a889a9267dee4 --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/assets/data_view_illustration.tsx @@ -0,0 +1,552 @@ +/* + * 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. + */ + +import React from 'react'; +import { useEuiTheme } from '@elastic/eui'; +import { css } from '@emotion/css'; + +export const DataViewIllustration = () => { + const { euiTheme } = useEuiTheme(); + const { colors } = euiTheme; + + const dataViewIllustrationVerticalStripes = css` + fill: ${colors.fullShade}; + `; + + const dataViewIllustrationDots = css` + fill: ${colors.lightShade}; + `; + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/src/plugins/shared_ux/public/components/empty_state/assets/index.tsx b/src/plugins/shared_ux/public/components/empty_state/assets/index.tsx new file mode 100644 index 00000000000000..c234cdf40055b7 --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/assets/index.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 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. + */ + +import React from 'react'; + +import { EuiLoadingSpinner } from '@elastic/eui'; + +import { withSuspense } from '../../utility'; + +export const LazyDataViewIllustration = React.lazy(() => + import('../assets/data_view_illustration').then(({ DataViewIllustration }) => ({ + default: DataViewIllustration, + })) +); + +export const DataViewIllustration = withSuspense( + LazyDataViewIllustration, + +); diff --git a/src/plugins/shared_ux/public/components/empty_state/index.tsx b/src/plugins/shared_ux/public/components/empty_state/index.tsx new file mode 100644 index 00000000000000..fc05199aae2073 --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/index.tsx @@ -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 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. + */ + +export { NoDataViews } from './no_data_views'; diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/__snapshots__/documentation_link.test.tsx.snap b/src/plugins/shared_ux/public/components/empty_state/no_data_views/__snapshots__/documentation_link.test.tsx.snap new file mode 100644 index 00000000000000..5526b78bceb730 --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/no_data_views/__snapshots__/documentation_link.test.tsx.snap @@ -0,0 +1,35 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` is rendered correctly 1`] = ` +
+ +
+ +
+
+   +
+ + + +
+
+`; diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.test.tsx b/src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.test.tsx new file mode 100644 index 00000000000000..fd963dfaa8e1cf --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.test.tsx @@ -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 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. + */ + +import React from 'react'; + +import { EuiLink, EuiTitle } from '@elastic/eui'; +import { shallowWithIntl } from '@kbn/test-jest-helpers'; + +import { DocumentationLink } from './documentation_link'; + +describe('', () => { + test('is rendered correctly', () => { + const component = shallowWithIntl(); + expect(component).toMatchSnapshot(); + + expect(component.find('dl').length).toBe(1); + expect(component.find(EuiTitle).length).toBe(1); + expect(component.find(EuiLink).length).toBe(1); + + const link = component.find(EuiLink).at(0); + expect(link.prop('href')).toBe('dummy'); + }); +}); diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.tsx b/src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.tsx new file mode 100644 index 00000000000000..4ac07899fef5fe --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/no_data_views/documentation_link.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 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. + */ + +import React from 'react'; +import { EuiLink, EuiTitle } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; + +interface Props { + href: string; +} + +export function DocumentationLink({ href }: Props) { + return ( +
+ +
+ +
+
+   +
+ + + +
+
+ ); +} diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/index.tsx b/src/plugins/shared_ux/public/components/empty_state/no_data_views/index.tsx new file mode 100644 index 00000000000000..fc05199aae2073 --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/no_data_views/index.tsx @@ -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 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. + */ + +export { NoDataViews } from './no_data_views'; diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.test.tsx b/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.test.tsx new file mode 100644 index 00000000000000..1d8028d4889a04 --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.test.tsx @@ -0,0 +1,53 @@ +/* + * 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. + */ + +import React from 'react'; +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { EuiButton, EuiPanel } from '@elastic/eui'; +import { NoDataViews } from './no_data_views.component'; +import { DocumentationLink } from './documentation_link'; + +describe('', () => { + test('is rendered correctly', () => { + const component = mountWithIntl( + + ); + expect(component.find(EuiPanel).length).toBe(1); + expect(component.find(EuiButton).length).toBe(1); + expect(component.find(DocumentationLink).length).toBe(1); + }); + + test('does not render button if canCreateNewDataViews is false', () => { + const component = mountWithIntl(); + + expect(component.find(EuiButton).length).toBe(0); + }); + + test('does not documentation link if linkToDocumentation is not provided', () => { + const component = mountWithIntl( + + ); + + expect(component.find(DocumentationLink).length).toBe(0); + }); + + test('onClickCreate', () => { + const onClickCreate = jest.fn(); + const component = mountWithIntl( + + ); + + component.find('button').simulate('click'); + + expect(onClickCreate).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.tsx b/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.tsx new file mode 100644 index 00000000000000..bfab91ef03b264 --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.component.tsx @@ -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 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. + */ + +import React from 'react'; +import { css } from '@emotion/react'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiButton, EuiEmptyPrompt, EuiEmptyPromptProps } from '@elastic/eui'; + +import { DataViewIllustration } from '../assets'; +import { DocumentationLink } from './documentation_link'; + +export interface Props { + canCreateNewDataView: boolean; + onClickCreate?: () => void; + dataViewsDocLink?: string; + emptyPromptColor?: EuiEmptyPromptProps['color']; +} + +const createDataViewText = i18n.translate('sharedUX.noDataViewsPage.addDataViewText', { + defaultMessage: 'Create Data View', +}); + +// Using raw value because it is content dependent +const MAX_WIDTH = 830; + +/** + * A presentational component that is shown in cases when there are no data views created yet. + */ +export const NoDataViews = ({ + onClickCreate, + canCreateNewDataView, + dataViewsDocLink, + emptyPromptColor = 'plain', +}: Props) => { + const createNewButton = canCreateNewDataView && ( + + {createDataViewText} + + ); + + return ( + } + title={ +

+ +
+ +

+ } + body={ +

+ +

+ } + actions={createNewButton} + footer={dataViewsDocLink && } + /> + ); +}; diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.mdx b/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.mdx new file mode 100644 index 00000000000000..ef8812c565a9f3 --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.mdx @@ -0,0 +1,14 @@ +**id:** sharedUX/Components/NoDataViewsPage +**slug:** /shared-ux/components/no-data-views-page +**title:** No Data Views Page +**summary:** A page to be displayed when there is data in Elasticsearch, but no data views +**tags:** ['shared-ux', 'component'] +**date:** 2022-02-09 + +--- + +When there is data in Elasticsearch, but there haven't been any data views created yet, we want to display an appropriate message to the user and facilitate creation of data views (if appropriate permissions are in place). + +The pure component, `no_data_views.component.tsx`, is a pre-configured **EuiEmptyPrompt**. + +The connected component, `no_data_views.tsx`, uses services from the `shared_ux` plugin to open Data View Editor. You must wrap your plugin app in the `ServicesContext` provided by the start contract of the `shared_ux` plugin to use it. diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.stories.tsx b/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.stories.tsx new file mode 100644 index 00000000000000..3f9ae1958cad7f --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.stories.tsx @@ -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 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. + */ +import React from 'react'; +import { action } from '@storybook/addon-actions'; + +import { docLinksServiceFactory } from '../../../services/storybook/doc_links'; + +import { NoDataViews as NoDataViewsComponent, Props } from './no_data_views.component'; +import { NoDataViews } from './no_data_views'; + +import mdx from './no_data_views.mdx'; + +export default { + title: 'No Data Views', + description: 'A component to display when there are no user-created data views available.', + parameters: { + docs: { + page: mdx, + }, + }, +}; + +export const ConnectedComponent = () => { + return ( + + ); +}; + +type Params = Pick; + +export const PureComponent = (params: Params) => { + return ; +}; + +PureComponent.argTypes = { + canCreateNewDataView: { + control: 'boolean', + defaultValue: true, + }, + dataViewsDocLink: { + options: [docLinksServiceFactory().dataViewsDocsLink, undefined], + control: { type: 'radio' }, + }, +}; diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.test.tsx b/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.test.tsx new file mode 100644 index 00000000000000..650d78fa64a036 --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.test.tsx @@ -0,0 +1,48 @@ +/* + * 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. + */ + +import React from 'react'; +import { ReactWrapper } from 'enzyme'; + +import { mountWithIntl } from '@kbn/test-jest-helpers'; +import { EuiButton } from '@elastic/eui'; + +import { ServicesProvider, SharedUXServices } from '../../../services'; +import { servicesFactory } from '../../../services/mocks'; +import { NoDataViews } from './no_data_views'; + +describe('', () => { + let services: SharedUXServices; + let mount: (element: JSX.Element) => ReactWrapper; + + beforeEach(() => { + services = servicesFactory(); + mount = (element: JSX.Element) => + mountWithIntl({element}); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + test('on dataView created', () => { + const component = mount( + + ); + + expect(services.editors.openDataViewEditor).not.toHaveBeenCalled(); + component.find(EuiButton).simulate('click'); + + component.unmount(); + + expect(services.editors.openDataViewEditor).toHaveBeenCalled(); + }); +}); diff --git a/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.tsx b/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.tsx new file mode 100644 index 00000000000000..01494d2c8a5b31 --- /dev/null +++ b/src/plugins/shared_ux/public/components/empty_state/no_data_views/no_data_views.tsx @@ -0,0 +1,72 @@ +/* + * 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. + */ + +import React, { useCallback, useEffect, useRef } from 'react'; + +import { DataView } from '../../../../../data_views/public'; +import { useEditors, usePermissions } from '../../../services'; +import type { SharedUXEditorsService } from '../../../services/editors'; + +import { NoDataViews as NoDataViewsComponent } from './no_data_views.component'; + +export interface Props { + onDataViewCreated: (dataView: DataView) => void; + dataViewsDocLink: string; +} + +type CloseDataViewEditorFn = ReturnType; + +/** + * A service-enabled component that provides Kibana-specific functionality to the `NoDataViews` + * component. + * + * Use of this component requires both the `EuiTheme` context as well as either a configured Shared UX + * `ServicesProvider` or the `ServicesContext` provided by the Shared UX public plugin contract. + * + * See shared-ux/public/services for information. + */ +export const NoDataViews = ({ onDataViewCreated, dataViewsDocLink }: Props) => { + const { canCreateNewDataView } = usePermissions(); + const { openDataViewEditor } = useEditors(); + const closeDataViewEditor = useRef(); + + useEffect(() => { + const cleanup = () => { + if (closeDataViewEditor?.current) { + closeDataViewEditor?.current(); + } + }; + + return () => { + // Make sure to close the editor when unmounting + cleanup(); + }; + }, []); + + const setDataViewEditorRef = useCallback((ref: CloseDataViewEditorFn) => { + closeDataViewEditor.current = ref; + }, []); + + const onClickCreate = useCallback(() => { + if (!canCreateNewDataView) { + return; + } + + const ref = openDataViewEditor({ + onSave: (dataView) => { + onDataViewCreated(dataView); + }, + }); + + if (setDataViewEditorRef) { + setDataViewEditorRef(ref); + } + }, [canCreateNewDataView, openDataViewEditor, setDataViewEditorRef, onDataViewCreated]); + + return ; +}; diff --git a/src/plugins/shared_ux/public/components/exit_full_screen_button/__snapshots__/exit_full_screen_button.test.tsx.snap b/src/plugins/shared_ux/public/components/exit_full_screen_button/__snapshots__/exit_full_screen_button.test.tsx.snap index abf76f9da48ce5..d2609e6b3c7a64 100644 --- a/src/plugins/shared_ux/public/components/exit_full_screen_button/__snapshots__/exit_full_screen_button.test.tsx.snap +++ b/src/plugins/shared_ux/public/components/exit_full_screen_button/__snapshots__/exit_full_screen_button.test.tsx.snap @@ -2,6 +2,21 @@ exports[` is rendered 1`] = ` * a predefined fallback and error boundary. */ export const ExitFullScreenButton = withSuspense(LazyExitFullScreenButton); + +/** + * The Lazily-loaded `NoDataViews` component. Consumers should use `React.Suspennse` or the + * `withSuspense` HOC to load this component. + */ +export const LazyNoDataViewsPage = React.lazy(() => + import('./empty_state/no_data_views').then(({ NoDataViews }) => ({ + default: NoDataViews, + })) +); + +/** + * A `NoDataViewsPage` component that is wrapped by the `withSuspense` HOC. This component can + * be used directly by consumers and will load the `LazyNoDataViewsPage` component lazily with + * a predefined fallback and error boundary. + */ +export const NoDataViewsPage = withSuspense(LazyNoDataViewsPage); diff --git a/src/plugins/shared_ux/public/index.ts b/src/plugins/shared_ux/public/index.ts index e6a2f925c51206..a196a60db847b6 100755 --- a/src/plugins/shared_ux/public/index.ts +++ b/src/plugins/shared_ux/public/index.ts @@ -17,3 +17,4 @@ export function plugin() { export type { SharedUXPluginSetup, SharedUXPluginStart } from './types'; export { ExitFullScreenButton, LazyExitFullScreenButton } from './components'; +export { NoDataViewsPage, LazyNoDataViewsPage } from './components'; diff --git a/src/plugins/shared_ux/public/services/doc_links.ts b/src/plugins/shared_ux/public/services/doc_links.ts new file mode 100644 index 00000000000000..3c6d23bd33ae49 --- /dev/null +++ b/src/plugins/shared_ux/public/services/doc_links.ts @@ -0,0 +1,10 @@ +/* + * 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. + */ +export interface SharedUXDocLinksService { + dataViewsDocsLink: string; +} diff --git a/src/plugins/shared_ux/public/services/editors.ts b/src/plugins/shared_ux/public/services/editors.ts new file mode 100644 index 00000000000000..176b22a6006e16 --- /dev/null +++ b/src/plugins/shared_ux/public/services/editors.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 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. + */ +import { DataView } from '../../../data_views/common'; + +export interface SharedUxDataViewEditorProps { + onSave: (dataView: DataView) => void; +} +export interface SharedUXEditorsService { + openDataViewEditor: (options: SharedUxDataViewEditorProps) => () => void; +} diff --git a/src/plugins/shared_ux/public/services/index.tsx b/src/plugins/shared_ux/public/services/index.tsx index 0677f3ef0ca84d..bdca90c725858c 100644 --- a/src/plugins/shared_ux/public/services/index.tsx +++ b/src/plugins/shared_ux/public/services/index.tsx @@ -9,6 +9,9 @@ import React, { FC, createContext, useContext } from 'react'; import { SharedUXPlatformService } from './platform'; import { servicesFactory } from './stub'; +import { SharedUXUserPermissionsService } from './permissions'; +import { SharedUXEditorsService } from './editors'; +import { SharedUXDocLinksService } from './doc_links'; /** * A collection of services utilized by SharedUX. This serves as a thin @@ -20,6 +23,9 @@ import { servicesFactory } from './stub'; */ export interface SharedUXServices { platform: SharedUXPlatformService; + permissions: SharedUXUserPermissionsService; + editors: SharedUXEditorsService; + docLinks: SharedUXDocLinksService; } // The React Context used to provide the services to the SharedUX components. @@ -48,3 +54,9 @@ export function useServices() { * React hook for accessing the pre-wired `SharedUXPlatformService`. */ export const usePlatformService = () => useServices().platform; + +export const usePermissions = () => useServices().permissions; + +export const useEditors = () => useServices().editors; + +export const useDocLinks = () => useServices().docLinks; diff --git a/src/plugins/shared_ux/public/services/kibana/doc_links.ts b/src/plugins/shared_ux/public/services/kibana/doc_links.ts new file mode 100644 index 00000000000000..eb25114a188a27 --- /dev/null +++ b/src/plugins/shared_ux/public/services/kibana/doc_links.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 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. + */ + +import { KibanaPluginServiceFactory } from '../types'; +import { SharedUXPluginStartDeps } from '../../types'; +import { SharedUXDocLinksService } from '../doc_links'; + +export type DocLinksServiceFactory = KibanaPluginServiceFactory< + SharedUXDocLinksService, + SharedUXPluginStartDeps +>; + +/** + * A factory function for creating a Kibana-based implementation of `SharedUXEditorsService`. + */ +export const docLinksServiceFactory: DocLinksServiceFactory = ({ coreStart }) => ({ + dataViewsDocsLink: coreStart.docLinks.links.indexPatterns?.introduction, +}); diff --git a/src/plugins/shared_ux/public/services/kibana/editors.ts b/src/plugins/shared_ux/public/services/kibana/editors.ts new file mode 100644 index 00000000000000..0f8082d7d00e2a --- /dev/null +++ b/src/plugins/shared_ux/public/services/kibana/editors.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 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. + */ + +import { KibanaPluginServiceFactory } from '../types'; +import { SharedUXEditorsService } from '../editors'; +import { SharedUXPluginStartDeps } from '../../types'; + +export type EditorsServiceFactory = KibanaPluginServiceFactory< + SharedUXEditorsService, + SharedUXPluginStartDeps +>; + +/** + * A factory function for creating a Kibana-based implementation of `SharedUXEditorsService`. + */ +export const editorsServiceFactory: EditorsServiceFactory = ({ startPlugins }) => ({ + openDataViewEditor: startPlugins.dataViewEditor.openEditor, +}); diff --git a/src/plugins/shared_ux/public/services/kibana/index.ts b/src/plugins/shared_ux/public/services/kibana/index.ts index f7c4cd7b2c56d7..506176cffbc458 100644 --- a/src/plugins/shared_ux/public/services/kibana/index.ts +++ b/src/plugins/shared_ux/public/services/kibana/index.ts @@ -10,6 +10,9 @@ import type { SharedUXServices } from '..'; import type { SharedUXPluginStartDeps } from '../../types'; import type { KibanaPluginServiceFactory } from '../types'; import { platformServiceFactory } from './platform'; +import { userPermissionsServiceFactory } from './permissions'; +import { editorsServiceFactory } from './editors'; +import { docLinksServiceFactory } from './doc_links'; /** * A factory function for creating a Kibana-based implementation of `SharedUXServices`. @@ -19,4 +22,7 @@ export const servicesFactory: KibanaPluginServiceFactory< SharedUXPluginStartDeps > = (params) => ({ platform: platformServiceFactory(params), + permissions: userPermissionsServiceFactory(params), + editors: editorsServiceFactory(params), + docLinks: docLinksServiceFactory(params), }); diff --git a/src/plugins/shared_ux/public/services/kibana/permissions.ts b/src/plugins/shared_ux/public/services/kibana/permissions.ts new file mode 100644 index 00000000000000..caaeccdccbccd5 --- /dev/null +++ b/src/plugins/shared_ux/public/services/kibana/permissions.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 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. + */ + +import { KibanaPluginServiceFactory } from '../types'; +import { SharedUXPluginStartDeps } from '../../types'; +import { SharedUXUserPermissionsService } from '../permissions'; + +export type UserPermissionsServiceFactory = KibanaPluginServiceFactory< + SharedUXUserPermissionsService, + SharedUXPluginStartDeps +>; + +/** + * A factory function for creating a Kibana-based implementation of `SharedUXPermissionsService`. + */ +export const userPermissionsServiceFactory: UserPermissionsServiceFactory = ({ startPlugins }) => ({ + canCreateNewDataView: startPlugins.dataViewEditor.userPermissions.editDataView(), +}); diff --git a/src/plugins/shared_ux/public/services/mocks/doc_links.mock.ts b/src/plugins/shared_ux/public/services/mocks/doc_links.mock.ts new file mode 100644 index 00000000000000..28cfa14c50d287 --- /dev/null +++ b/src/plugins/shared_ux/public/services/mocks/doc_links.mock.ts @@ -0,0 +1,19 @@ +/* + * 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. + */ + +import { PluginServiceFactory } from '../types'; +import { SharedUXDocLinksService } from '../doc_links'; + +export type MockDockLinksServiceFactory = PluginServiceFactory; + +/** + * A factory function for creating a Jest-based implementation of `SharedUXDocLinksService`. + */ +export const docLinksServiceFactory: MockDockLinksServiceFactory = () => ({ + dataViewsDocsLink: 'dummy link', +}); diff --git a/src/plugins/shared_ux/public/services/mocks/editors.mock.ts b/src/plugins/shared_ux/public/services/mocks/editors.mock.ts new file mode 100644 index 00000000000000..28a89d5326d629 --- /dev/null +++ b/src/plugins/shared_ux/public/services/mocks/editors.mock.ts @@ -0,0 +1,19 @@ +/* + * 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. + */ + +import { PluginServiceFactory } from '../types'; +import { SharedUXEditorsService } from '../editors'; + +export type MockEditorsServiceFactory = PluginServiceFactory; + +/** + * A factory function for creating a Jest-based implementation of `SharedUXEditorsService`. + */ +export const editorsServiceFactory: MockEditorsServiceFactory = () => ({ + openDataViewEditor: jest.fn(), +}); diff --git a/src/plugins/shared_ux/public/services/mocks/index.ts b/src/plugins/shared_ux/public/services/mocks/index.ts index 14d38a484e53b3..9fce633c52539c 100644 --- a/src/plugins/shared_ux/public/services/mocks/index.ts +++ b/src/plugins/shared_ux/public/services/mocks/index.ts @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import { docLinksServiceFactory } from './doc_links.mock'; export type { MockPlatformServiceFactory } from './platform.mock'; export { platformServiceFactory } from './platform.mock'; @@ -12,10 +13,15 @@ export { platformServiceFactory } from './platform.mock'; import type { SharedUXServices } from '../.'; import { PluginServiceFactory } from '../types'; import { platformServiceFactory } from './platform.mock'; +import { userPermissionsServiceFactory } from './permissions.mock'; +import { editorsServiceFactory } from './editors.mock'; /** * A factory function for creating a Jest-based implementation of `SharedUXServices`. */ export const servicesFactory: PluginServiceFactory = () => ({ platform: platformServiceFactory(), + permissions: userPermissionsServiceFactory(), + editors: editorsServiceFactory(), + docLinks: docLinksServiceFactory(), }); diff --git a/src/plugins/shared_ux/public/services/mocks/permissions.mock.ts b/src/plugins/shared_ux/public/services/mocks/permissions.mock.ts new file mode 100644 index 00000000000000..ff65d2393248ab --- /dev/null +++ b/src/plugins/shared_ux/public/services/mocks/permissions.mock.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 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. + */ + +import { PluginServiceFactory } from '../types'; +import { SharedUXUserPermissionsService } from '../permissions'; + +export type MockUserPermissionsServiceFactory = + PluginServiceFactory; + +/** + * A factory function for creating a Jest-based implementation of `SharedUXUserPermissionsService`. + */ +export const userPermissionsServiceFactory: MockUserPermissionsServiceFactory = () => ({ + canCreateNewDataView: true, +}); diff --git a/src/plugins/shared_ux/public/services/permissions.ts b/src/plugins/shared_ux/public/services/permissions.ts new file mode 100644 index 00000000000000..009f497e357063 --- /dev/null +++ b/src/plugins/shared_ux/public/services/permissions.ts @@ -0,0 +1,11 @@ +/* + * 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. + */ + +export interface SharedUXUserPermissionsService { + canCreateNewDataView: boolean; +} diff --git a/src/plugins/shared_ux/public/services/storybook/doc_links.ts b/src/plugins/shared_ux/public/services/storybook/doc_links.ts new file mode 100644 index 00000000000000..548d5043361083 --- /dev/null +++ b/src/plugins/shared_ux/public/services/storybook/doc_links.ts @@ -0,0 +1,19 @@ +/* + * 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. + */ + +import { PluginServiceFactory } from '../types'; +import { SharedUXDocLinksService } from '../doc_links'; + +export type SharedUXDockLinksServiceFactory = PluginServiceFactory; + +/** + * A factory function for creating a Jest-based implementation of `SharedUXDocLinksService`. + */ +export const docLinksServiceFactory: SharedUXDockLinksServiceFactory = () => ({ + dataViewsDocsLink: 'https://www.elastic.co/guide/en/kibana/master/data-views.html', +}); diff --git a/src/plugins/shared_ux/public/services/storybook/editors.ts b/src/plugins/shared_ux/public/services/storybook/editors.ts new file mode 100644 index 00000000000000..248699d5beb959 --- /dev/null +++ b/src/plugins/shared_ux/public/services/storybook/editors.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 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. + */ + +import { action } from '@storybook/addon-actions'; +import { PluginServiceFactory } from '../types'; +import { SharedUxDataViewEditorProps, SharedUXEditorsService } from '../editors'; + +export type SharedUXEditorsServiceFactory = PluginServiceFactory; + +/** + * A factory function for creating a storybook implementation of `SharedUXEditorsService`. + */ +export const editorsServiceFactory: SharedUXEditorsServiceFactory = () => ({ + openDataViewEditor: action('openEditor') as SharedUXEditorsService['openDataViewEditor'] as ( + options: SharedUxDataViewEditorProps + ) => () => void, +}); diff --git a/src/plugins/shared_ux/public/services/storybook/index.ts b/src/plugins/shared_ux/public/services/storybook/index.ts index 8ea03317e53a28..c915b1a4633652 100644 --- a/src/plugins/shared_ux/public/services/storybook/index.ts +++ b/src/plugins/shared_ux/public/services/storybook/index.ts @@ -9,10 +9,16 @@ import type { SharedUXServices } from '../.'; import { PluginServiceFactory } from '../types'; import { platformServiceFactory } from './platform'; +import { editorsServiceFactory } from './editors'; +import { userPermissionsServiceFactory } from './permissions'; +import { docLinksServiceFactory } from './doc_links'; /** * A factory function for creating a Storybook-based implementation of `SharedUXServices`. */ export const servicesFactory: PluginServiceFactory = (params) => ({ platform: platformServiceFactory(params), + permissions: userPermissionsServiceFactory(), + editors: editorsServiceFactory(), + docLinks: docLinksServiceFactory(), }); diff --git a/src/plugins/shared_ux/public/services/storybook/permissions.ts b/src/plugins/shared_ux/public/services/storybook/permissions.ts new file mode 100644 index 00000000000000..c7110fccf7eeab --- /dev/null +++ b/src/plugins/shared_ux/public/services/storybook/permissions.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 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. + */ + +import { PluginServiceFactory } from '../types'; +import { SharedUXUserPermissionsService } from '../permissions'; + +export type SharedUXUserPermissionsServiceFactory = + PluginServiceFactory; + +/** + * A factory function for creating a storybook implementation of `SharedUXUserPermissionsService`. + */ +export const userPermissionsServiceFactory: SharedUXUserPermissionsServiceFactory = () => ({ + canCreateNewDataView: true, +}); diff --git a/src/plugins/shared_ux/public/services/stub/doc_links.ts b/src/plugins/shared_ux/public/services/stub/doc_links.ts new file mode 100644 index 00000000000000..424a24c5785396 --- /dev/null +++ b/src/plugins/shared_ux/public/services/stub/doc_links.ts @@ -0,0 +1,19 @@ +/* + * 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. + */ + +import { PluginServiceFactory } from '../types'; +import { SharedUXDocLinksService } from '../doc_links'; + +export type DockLinksServiceFactory = PluginServiceFactory; + +/** + * A factory function for creating a Jest-based implementation of `SharedUXDocLinksService`. + */ +export const docLinksServiceFactory: DockLinksServiceFactory = () => ({ + dataViewsDocsLink: 'docs', +}); diff --git a/src/plugins/shared_ux/public/services/stub/editors.ts b/src/plugins/shared_ux/public/services/stub/editors.ts new file mode 100644 index 00000000000000..03fea5e6c98b58 --- /dev/null +++ b/src/plugins/shared_ux/public/services/stub/editors.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 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. + */ + +import { PluginServiceFactory } from '../types'; +import { SharedUXEditorsService } from '../editors'; + +/** + * A factory function for creating a simple stubbed implementation of `SharedUXEditorsService`. + */ +export type EditorsServiceFactory = PluginServiceFactory; + +/** + * A factory function for creating a simple stubbed implementation of `SharedUXEditorsService`. + */ +export const editorsServiceFactory: EditorsServiceFactory = () => ({ + openDataViewEditor: () => () => {}, +}); diff --git a/src/plugins/shared_ux/public/services/stub/index.ts b/src/plugins/shared_ux/public/services/stub/index.ts index 8a5afe8cdcafbe..9e4fa8f03133aa 100644 --- a/src/plugins/shared_ux/public/services/stub/index.ts +++ b/src/plugins/shared_ux/public/services/stub/index.ts @@ -9,10 +9,16 @@ import type { SharedUXServices } from '../.'; import { PluginServiceFactory } from '../types'; import { platformServiceFactory } from './platform'; +import { userPermissionsServiceFactory } from './permissions'; +import { editorsServiceFactory } from './editors'; +import { docLinksServiceFactory } from './doc_links'; /** * A factory function for creating a simple stubbed implemetation of `SharedUXServices`. */ export const servicesFactory: PluginServiceFactory = () => ({ platform: platformServiceFactory(), + permissions: userPermissionsServiceFactory(), + editors: editorsServiceFactory(), + docLinks: docLinksServiceFactory(), }); diff --git a/src/plugins/shared_ux/public/services/stub/permissions.ts b/src/plugins/shared_ux/public/services/stub/permissions.ts new file mode 100644 index 00000000000000..c51abf41e28429 --- /dev/null +++ b/src/plugins/shared_ux/public/services/stub/permissions.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 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. + */ + +import { PluginServiceFactory } from '../types'; +import { SharedUXUserPermissionsService } from '../permissions'; + +/** + * A factory function for creating a simple stubbed implementation of `SharedUXUserPermissionsService`. + */ +export type UserPermissionsServiceFactory = PluginServiceFactory; + +/** + * A factory function for creating a simple stubbed implementation of `SharedUXUserPermissionsService`. + */ +export const userPermissionsServiceFactory: UserPermissionsServiceFactory = () => ({ + canCreateNewDataView: true, +}); diff --git a/src/plugins/shared_ux/public/types/index.ts b/src/plugins/shared_ux/public/types/index.ts index 767dbf1aa10a47..38f91815f2e7b2 100644 --- a/src/plugins/shared_ux/public/types/index.ts +++ b/src/plugins/shared_ux/public/types/index.ts @@ -9,6 +9,7 @@ /* eslint-disable @typescript-eslint/no-empty-interface */ import { FC } from 'react'; +import { DataViewEditorStart } from 'src/plugins/data_view_editor/public'; /** @internal */ export interface SharedUXPluginSetup {} @@ -28,4 +29,6 @@ export interface SharedUXPluginStart { export interface SharedUXPluginSetupDeps {} /** @internal */ -export interface SharedUXPluginStartDeps {} +export interface SharedUXPluginStartDeps { + dataViewEditor: DataViewEditorStart; +} diff --git a/src/plugins/shared_ux/tsconfig.json b/src/plugins/shared_ux/tsconfig.json index 6717616c8f2f31..069fe1d069b4a3 100644 --- a/src/plugins/shared_ux/tsconfig.json +++ b/src/plugins/shared_ux/tsconfig.json @@ -17,5 +17,11 @@ { "path": "../../core/tsconfig.json" }, + { + "path": "../data_view_editor/tsconfig.json" + }, + { + "path": "../data_views/tsconfig.json" + } ] } From c914e9484127193536078cebfe6d668d5e0b2c37 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Thu, 24 Feb 2022 21:41:58 +0100 Subject: [PATCH 07/28] [Status service] reuse plugin status observables (#126334) --- src/core/server/status/plugins_status.ts | 73 +++++++++++++++--------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/src/core/server/status/plugins_status.ts b/src/core/server/status/plugins_status.ts index 719535133e7ab5..c4e8e7e3642482 100644 --- a/src/core/server/status/plugins_status.ts +++ b/src/core/server/status/plugins_status.ts @@ -30,6 +30,13 @@ interface Deps { export class PluginsStatusService { private readonly pluginStatuses = new Map>(); + private readonly derivedStatuses = new Map>(); + private readonly dependenciesStatuses = new Map< + PluginName, + Observable> + >(); + private allPluginsStatuses?: Observable>; + private readonly update$ = new BehaviorSubject(true); private readonly defaultInheritedStatus$: Observable; private newRegistrationsAllowed = true; @@ -59,7 +66,10 @@ export class PluginsStatusService { } public getAll$(): Observable> { - return this.getPluginStatuses$([...this.deps.pluginDependencies.keys()]); + if (!this.allPluginsStatuses) { + this.allPluginsStatuses = this.getPluginStatuses$([...this.deps.pluginDependencies.keys()]); + } + return this.allPluginsStatuses; } public getDependenciesStatus$(plugin: PluginName): Observable> { @@ -67,35 +77,46 @@ export class PluginsStatusService { if (!dependencies) { throw new Error(`Unknown plugin: ${plugin}`); } - - return this.getPluginStatuses$(dependencies).pipe( - // Prevent many emissions at once from dependency status resolution from making this too noisy - debounceTime(25) - ); + if (!this.dependenciesStatuses.has(plugin)) { + this.dependenciesStatuses.set( + plugin, + this.getPluginStatuses$(dependencies).pipe( + // Prevent many emissions at once from dependency status resolution from making this too noisy + debounceTime(25) + ) + ); + } + return this.dependenciesStatuses.get(plugin)!; } public getDerivedStatus$(plugin: PluginName): Observable { - return this.update$.pipe( - debounceTime(25), // Avoid calling the plugin's custom status logic for every plugin that depends on it. - switchMap(() => { - // Only go up the dependency tree if any of this plugin's dependencies have a custom status - // Helps eliminate memory overhead of creating thousands of Observables unnecessarily. - if (this.anyCustomStatuses(plugin)) { - return combineLatest([this.deps.core$, this.getDependenciesStatus$(plugin)]).pipe( - map(([coreStatus, pluginStatuses]) => { - return getSummaryStatus( - [...Object.entries(coreStatus), ...Object.entries(pluginStatuses)], - { - allAvailableSummary: `All dependencies are available`, - } + if (!this.derivedStatuses.has(plugin)) { + this.derivedStatuses.set( + plugin, + this.update$.pipe( + debounceTime(25), // Avoid calling the plugin's custom status logic for every plugin that depends on it. + switchMap(() => { + // Only go up the dependency tree if any of this plugin's dependencies have a custom status + // Helps eliminate memory overhead of creating thousands of Observables unnecessarily. + if (this.anyCustomStatuses(plugin)) { + return combineLatest([this.deps.core$, this.getDependenciesStatus$(plugin)]).pipe( + map(([coreStatus, pluginStatuses]) => { + return getSummaryStatus( + [...Object.entries(coreStatus), ...Object.entries(pluginStatuses)], + { + allAvailableSummary: `All dependencies are available`, + } + ); + }) ); - }) - ); - } else { - return this.defaultInheritedStatus$; - } - }) - ); + } else { + return this.defaultInheritedStatus$; + } + }) + ) + ); + } + return this.derivedStatuses.get(plugin)!; } private getPluginStatuses$(plugins: PluginName[]): Observable> { From 1e16db4d18302c4434acfb8ce2e7fb6a414c5f2f Mon Sep 17 00:00:00 2001 From: John Dorlus Date: Thu, 24 Feb 2022 16:33:11 -0500 Subject: [PATCH 08/28] Added CCS test for data view functionality. (#124586) * Added CCS test for data view functionality. * Changed how we are approaching the test. Just adding a remote to exercise the CCS functionality. * Adjusted test to use correct port for remote cluster. * Added and corrected config for the tests to be able to run. * Changed test title. * Added functional ccs suite to functional test runner script. * Changed config.ts to config.js * Moved es to before clause. * Added await for an async call. * Added await for an async call. * Updated saved queries test to use CCS. One test is failing, all others are passing. * Merge branch 'main' of github.com:elastic/kibana into CCS_Functional_Test_POC * Fixed a reference. * Removed the OSS tests since we created CCS versions of them. * Removed tests from index file. * Restored test files per comment with matthias. * Did some cleanup of comments. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- scripts/functional_tests.js | 1 + .../kbn_archiver/date_nested_ccs.json | 15 ++ .../fixtures/kbn_archiver/discover_ccs.json | 51 ++++ .../apps/discover/_data_view_ccs.ts | 62 +++++ .../apps/discover/_saved_queries_ccs.ts | 221 ++++++++++++++++++ .../apps/discover/ftr_provider_context.d.ts | 13 ++ test/functional_ccs/apps/discover/index.ts | 43 ++++ test/functional_ccs/config.js | 25 ++ 8 files changed, 431 insertions(+) create mode 100644 test/functional/fixtures/kbn_archiver/date_nested_ccs.json create mode 100644 test/functional/fixtures/kbn_archiver/discover_ccs.json create mode 100644 test/functional_ccs/apps/discover/_data_view_ccs.ts create mode 100644 test/functional_ccs/apps/discover/_saved_queries_ccs.ts create mode 100644 test/functional_ccs/apps/discover/ftr_provider_context.d.ts create mode 100644 test/functional_ccs/apps/discover/index.ts create mode 100644 test/functional_ccs/config.js diff --git a/scripts/functional_tests.js b/scripts/functional_tests.js index 6cfd1d57e9c96f..972c682ab0d9f8 100644 --- a/scripts/functional_tests.js +++ b/scripts/functional_tests.js @@ -9,6 +9,7 @@ require('../src/setup_node_env'); require('@kbn/test').runTestsCli([ require.resolve('../test/functional/config.js'), + require.resolve('../test/functional_ccs/config.js'), require.resolve('../test/plugin_functional/config.ts'), require.resolve('../test/ui_capabilities/newsfeed_err/config.ts'), require.resolve('../test/new_visualize_flow/config.ts'), diff --git a/test/functional/fixtures/kbn_archiver/date_nested_ccs.json b/test/functional/fixtures/kbn_archiver/date_nested_ccs.json new file mode 100644 index 00000000000000..933b21d920c00b --- /dev/null +++ b/test/functional/fixtures/kbn_archiver/date_nested_ccs.json @@ -0,0 +1,15 @@ +{ + "attributes": { + "fields": "[]", + "timeFieldName": "nested.timestamp", + "title": "remote:date-nested" + }, + "coreMigrationVersion": "8.2.0", + "id": "remote:date-nested", + "migrationVersion": { + "index-pattern": "8.0.0" + }, + "references": [], + "type": "index-pattern", + "version": "WzIyLDFd" +} diff --git a/test/functional/fixtures/kbn_archiver/discover_ccs.json b/test/functional/fixtures/kbn_archiver/discover_ccs.json new file mode 100644 index 00000000000000..d53aa1bc759afb --- /dev/null +++ b/test/functional/fixtures/kbn_archiver/discover_ccs.json @@ -0,0 +1,51 @@ +{ + "attributes": { + "fieldAttrs": "{\"referer\":{\"customLabel\":\"Referer custom\"}}", + "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"@message\"}}},{\"name\":\"@tags\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"@tags\"}}},{\"name\":\"@timestamp\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"agent\"}}},{\"name\":\"bytes\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"extension\"}}},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"headings\"}}},{\"name\":\"host\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"host\"}}},{\"name\":\"id\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"index\"}}},{\"name\":\"ip\",\"type\":\"ip\",\"esTypes\":[\"ip\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"links\"}}},{\"name\":\"machine.os\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"machine.os\"}}},{\"name\":\"machine.ram\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"esTypes\":[\"double\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"nestedField.child\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"nestedField\"}}},{\"name\":\"phpmemory\",\"type\":\"number\",\"esTypes\":[\"long\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.article:section\"}}},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.article:tag\"}}},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:description\"}}},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:image\"}}},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:image:height\"}}},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:image:width\"}}},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:site_name\"}}},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:title\"}}},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:type\"}}},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.og:url\"}}},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.twitter:card\"}}},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.twitter:description\"}}},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.twitter:image\"}}},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.twitter:site\"}}},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.twitter:title\"}}},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"relatedContent.url\"}}},{\"name\":\"request\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"request\"}}},{\"name\":\"response\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"response\"}}},{\"name\":\"spaces\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"spaces\"}}},{\"name\":\"type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"url\"}}},{\"name\":\"utc_time\",\"type\":\"date\",\"esTypes\":[\"date\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"esTypes\":[\"text\"],\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"xss\"}}}]", + "timeFieldName": "@timestamp", + "title": "remote:logstash-*" + }, + "coreMigrationVersion": "8.0.0", + "id": "remote:logstash-*", + "migrationVersion": { + "index-pattern": "7.11.0" + }, + "references": [], + "type": "index-pattern", + "version": "WzQsMl0=" +} + +{ + "attributes": { + "columns": [ + "_source" + ], + "description": "A Saved Search Description", + "hits": 0, + "kibanaSavedObjectMeta": { + "searchSourceJSON": "{\"highlightAll\":true,\"filter\":[],\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}" + }, + "sort": [ + [ + "@timestamp", + "desc" + ] + ], + "title": "A Saved Search", + "version": 1 + }, + "coreMigrationVersion": "8.0.0", + "id": "ab12e3c0-f231-11e6-9486-733b1ac9221a", + "migrationVersion": { + "search": "7.9.3" + }, + "references": [ + { + "id": "remote:logstash-*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern" + } + ], + "type": "search", + "version": "WzUsMl0=" +} diff --git a/test/functional_ccs/apps/discover/_data_view_ccs.ts b/test/functional_ccs/apps/discover/_data_view_ccs.ts new file mode 100644 index 00000000000000..91d9cb2faf6816 --- /dev/null +++ b/test/functional_ccs/apps/discover/_data_view_ccs.ts @@ -0,0 +1,62 @@ +/* + * 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. + */ + +import { FtrProviderContext } from './ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const retry = getService('retry'); + const testSubjects = getService('testSubjects'); + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + + const security = getService('security'); + const PageObjects = getPageObjects(['common', 'discover', 'header', 'timePicker']); + + const createDataView = async (dataViewName: string) => { + await PageObjects.discover.clickIndexPatternActions(); + await PageObjects.discover.clickCreateNewDataView(); + await testSubjects.setValue('createIndexPatternNameInput', dataViewName, { + clearWithKeyboard: true, + typeCharByChar: true, + }); + await testSubjects.click('saveIndexPatternButton'); + }; + + describe('discover integration with data view editor', function describeIndexTests() { + before(async function () { + await security.testUser.setRoles(['kibana_admin', 'test_logstash_reader']); + await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); + await kibanaServer.savedObjects.clean({ types: ['saved-search', 'index-pattern'] }); + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); + await PageObjects.timePicker.setDefaultAbsoluteRangeViaUiSettings(); + await PageObjects.common.navigateToApp('discover'); + }); + + after(async () => { + await security.testUser.restoreDefaults(); + await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); + await kibanaServer.savedObjects.clean({ types: ['saved-search', 'index-pattern'] }); + }); + + it('use ccs to create a new data view', async function () { + const dataViewToCreate = 'remote:logstash'; + await createDataView(dataViewToCreate); + await PageObjects.header.waitUntilLoadingHasFinished(); + await retry.waitForWithTimeout( + 'data view selector to include a newly created dataview', + 5000, + async () => { + const dataViewTitle = await PageObjects.discover.getCurrentlySelectedDataView(); + // data view editor will add wildcard symbol by default + // so we need to include it in our original title when comparing + return dataViewTitle === `${dataViewToCreate}*`; + } + ); + }); + }); +} diff --git a/test/functional_ccs/apps/discover/_saved_queries_ccs.ts b/test/functional_ccs/apps/discover/_saved_queries_ccs.ts new file mode 100644 index 00000000000000..325f279ff28ab7 --- /dev/null +++ b/test/functional_ccs/apps/discover/_saved_queries_ccs.ts @@ -0,0 +1,221 @@ +/* + * 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. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from './ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const retry = getService('retry'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); + const browser = getService('browser'); + const filterBar = getService('filterBar'); + const queryBar = getService('queryBar'); + const savedQueryManagementComponent = getService('savedQueryManagementComponent'); + const testSubjects = getService('testSubjects'); + const defaultSettings = { + defaultIndex: 'logstash-*', + }; + + const setUpQueriesWithFilters = async () => { + // set up a query with filters and a time filter + log.debug('set up a query with filters to save'); + const from = 'Sep 20, 2015 @ 08:00:00.000'; + const to = 'Sep 21, 2015 @ 08:00:00.000'; + await PageObjects.common.setTime({ from, to }); + await PageObjects.common.navigateToApp('discover'); + await filterBar.addFilter('extension.raw', 'is one of', 'jpg'); + await queryBar.setQuery('response:200'); + }; + + describe('saved queries saved objects', function describeIndexTests() { + before(async function () { + log.debug('load kibana index with default index pattern'); + await kibanaServer.savedObjects.clean({ types: ['search', 'index-pattern'] }); + + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/discover_ccs.json' + ); + await kibanaServer.importExport.load( + 'test/functional/fixtures/kbn_archiver/date_nested_ccs.json' + ); + await esArchiver.load('test/functional/fixtures/es_archiver/date_nested'); + await esArchiver.load('test/functional/fixtures/es_archiver/logstash_functional'); + + await kibanaServer.uiSettings.replace(defaultSettings); + log.debug('discover'); + await PageObjects.common.navigateToApp('discover'); + await PageObjects.timePicker.setDefaultAbsoluteRange(); + }); + + after(async () => { + await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover_ccs'); + await kibanaServer.importExport.unload( + 'test/functional/fixtures/kbn_archiver/date_nested_ccs' + ); + await esArchiver.unload('test/functional/fixtures/es_archiver/date_nested'); + await esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + await PageObjects.common.unsetTime(); + }); + + describe('saved query selection', () => { + before(async () => await setUpQueriesWithFilters()); + + it(`should unselect saved query when navigating to a 'new'`, async function () { + await savedQueryManagementComponent.saveNewQuery( + 'test-unselect-saved-query', + 'mock', + true, + true + ); + + await queryBar.submitQuery(); + + expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); + expect(await queryBar.getQueryString()).to.eql('response:200'); + + await PageObjects.discover.clickNewSearchButton(); + + expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(false); + expect(await queryBar.getQueryString()).to.eql(''); + + await PageObjects.discover.selectIndexPattern('remote:date-nested'); + + expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(false); + expect(await queryBar.getQueryString()).to.eql(''); + + await PageObjects.discover.selectIndexPattern('remote:logstash-*'); + + expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(false); + expect(await queryBar.getQueryString()).to.eql(''); + + // reset state + await savedQueryManagementComponent.deleteSavedQuery('test-unselect-saved-query'); + }); + }); + + describe('saved query management component functionality', function () { + before(async () => await setUpQueriesWithFilters()); + + it('should show the saved query management component when there are no saved queries', async () => { + await savedQueryManagementComponent.openSavedQueryManagementComponent(); + const descriptionText = await testSubjects.getVisibleText('saved-query-management-popover'); + expect(descriptionText).to.eql( + 'Saved Queries\nThere are no saved queries. Save query text and filters that you want to use again.\nSave current query' + ); + }); + + it('should allow a query to be saved via the saved objects management component', async () => { + await savedQueryManagementComponent.saveNewQuery( + 'OkResponse', + '200 responses for .jpg over 24 hours', + true, + true + ); + await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); + await savedQueryManagementComponent.savedQueryTextExist('response:200'); + }); + + it('reinstates filters and the time filter when a saved query has filters and a time filter included', async () => { + await PageObjects.timePicker.setDefaultAbsoluteRange(); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + const timePickerValues = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes(); + expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); + expect(timePickerValues.start).to.not.eql(PageObjects.timePicker.defaultStartTime); + expect(timePickerValues.end).to.not.eql(PageObjects.timePicker.defaultEndTime); + }); + + it('preserves the currently loaded query when the page is reloaded', async () => { + await browser.refresh(); + const timePickerValues = await PageObjects.timePicker.getTimeConfigAsAbsoluteTimes(); + expect(await filterBar.hasFilter('extension.raw', 'jpg')).to.be(true); + expect(timePickerValues.start).to.not.eql(PageObjects.timePicker.defaultStartTime); + expect(timePickerValues.end).to.not.eql(PageObjects.timePicker.defaultEndTime); + await retry.waitFor( + 'the right hit count', + async () => (await PageObjects.discover.getHitCount()) === '2,792' + ); + expect(await savedQueryManagementComponent.getCurrentlyLoadedQueryID()).to.be('OkResponse'); + }); + + it('allows saving changes to a currently loaded query via the saved query management component', async () => { + await queryBar.setQuery('response:404'); + await savedQueryManagementComponent.updateCurrentlyLoadedQuery('OkResponse', false, false); + await savedQueryManagementComponent.savedQueryExistOrFail('OkResponse'); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + expect(await queryBar.getQueryString()).to.eql(''); + await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + expect(await queryBar.getQueryString()).to.eql('response:404'); + }); + + it('allows saving the currently loaded query as a new query', async () => { + await savedQueryManagementComponent.saveCurrentlyLoadedAsNewQuery( + 'OkResponseCopy', + '200 responses', + false, + false + ); + await savedQueryManagementComponent.savedQueryExistOrFail('OkResponseCopy'); + }); + + it('allows deleting the currently loaded saved query in the saved query management component and clears the query', async () => { + await savedQueryManagementComponent.deleteSavedQuery('OkResponseCopy'); + await savedQueryManagementComponent.savedQueryMissingOrFail('OkResponseCopy'); + expect(await queryBar.getQueryString()).to.eql(''); + }); + + it('does not allow saving a query with a non-unique name', async () => { + // this check allows this test to run stand alone, also should fix occacional flakiness + const savedQueryExists = await savedQueryManagementComponent.savedQueryExist('OkResponse'); + if (!savedQueryExists) { + await savedQueryManagementComponent.saveNewQuery( + 'OkResponse', + '200 responses for .jpg over 24 hours', + true, + true + ); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + } + await savedQueryManagementComponent.saveNewQueryWithNameError('OkResponse'); + }); + + it('resets any changes to a loaded query on reloading the same saved query', async () => { + await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + await queryBar.setQuery('response:503'); + await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + expect(await queryBar.getQueryString()).to.eql('response:404'); + }); + + it('allows clearing the currently loaded saved query', async () => { + await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + expect(await queryBar.getQueryString()).to.eql(''); + }); + + it('allows clearing if non default language was remembered in localstorage', async () => { + await queryBar.switchQueryLanguage('lucene'); + await PageObjects.common.navigateToApp('discover'); // makes sure discovered is reloaded without any state in url + await queryBar.expectQueryLanguageOrFail('lucene'); // make sure lucene is remembered after refresh (comes from localstorage) + await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + await queryBar.expectQueryLanguageOrFail('kql'); + await savedQueryManagementComponent.clearCurrentlyLoadedQuery(); + await queryBar.expectQueryLanguageOrFail('lucene'); + }); + + it('changing language removes saved query', async () => { + await savedQueryManagementComponent.loadSavedQuery('OkResponse'); + await queryBar.switchQueryLanguage('lucene'); + expect(await queryBar.getQueryString()).to.eql(''); + }); + }); + }); +} diff --git a/test/functional_ccs/apps/discover/ftr_provider_context.d.ts b/test/functional_ccs/apps/discover/ftr_provider_context.d.ts new file mode 100644 index 00000000000000..ea232d23463e65 --- /dev/null +++ b/test/functional_ccs/apps/discover/ftr_provider_context.d.ts @@ -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. + */ + +import { GenericFtrProviderContext } from '@kbn/test'; +import { services } from '../../../functional/services'; +import { pageObjects } from '../../../functional/page_objects'; + +export type FtrProviderContext = GenericFtrProviderContext; diff --git a/test/functional_ccs/apps/discover/index.ts b/test/functional_ccs/apps/discover/index.ts new file mode 100644 index 00000000000000..629423b1b75aa4 --- /dev/null +++ b/test/functional_ccs/apps/discover/index.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 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. + */ + +import { FtrProviderContext } from './ftr_provider_context'; + +export default function ({ getService, loadTestFile }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const browser = getService('browser'); + const esClient = getService('es'); + + describe('discover app css', function () { + this.tags('ciGroup6'); + + before(async function () { + await esClient.cluster.putSettings({ + persistent: { + cluster: { + remote: { + remote: { + skip_unavailable: 'true', + seeds: ['localhost:9300'], + }, + }, + }, + }, + }); + return browser.setWindowSize(1300, 800); + }); + + after(function unloadMakelogs() { + // Make sure to clean up the cluster setting from the before above. + return esArchiver.unload('test/functional/fixtures/es_archiver/logstash_functional'); + }); + + loadTestFile(require.resolve('./_data_view_ccs')); + loadTestFile(require.resolve('./_saved_queries_ccs')); + }); +} diff --git a/test/functional_ccs/config.js b/test/functional_ccs/config.js new file mode 100644 index 00000000000000..4cd88757983720 --- /dev/null +++ b/test/functional_ccs/config.js @@ -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 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. + */ + +import { services } from '../functional/services'; + +export default async function ({ readConfigFile }) { + const functionalConfig = await readConfigFile(require.resolve('../functional/config')); + + return { + ...functionalConfig.getAll(), + + testFiles: [require.resolve('./apps/discover')], + + services, + + junit: { + reportName: 'Kibana CCS Tests', + }, + }; +} From a16c20b7b4c0ddf867a231b0f41d8a4477b482a0 Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 24 Feb 2022 16:44:37 -0600 Subject: [PATCH 09/28] [kbn/es] add support for --ready-timeout (#126217) --- packages/kbn-es/src/cli_commands/archive.js | 13 +- packages/kbn-es/src/cli_commands/snapshot.js | 11 +- packages/kbn-es/src/cli_commands/source.js | 13 +- packages/kbn-es/src/cluster.js | 144 +++++++++++++----- packages/kbn-es/src/cluster_exec_options.ts | 18 +++ packages/kbn-es/src/utils/index.ts | 1 + packages/kbn-es/src/utils/native_realm.js | 26 +--- .../kbn-es/src/utils/native_realm.test.js | 8 +- .../src/utils/parse_timeout_to_ms.test.ts | 37 +++++ .../kbn-es/src/utils/parse_timeout_to_ms.ts | 43 ++++++ packages/kbn-test/src/es/test_es_cluster.ts | 3 +- 11 files changed, 244 insertions(+), 73 deletions(-) create mode 100644 packages/kbn-es/src/cluster_exec_options.ts create mode 100644 packages/kbn-es/src/utils/parse_timeout_to_ms.test.ts create mode 100644 packages/kbn-es/src/utils/parse_timeout_to_ms.ts diff --git a/packages/kbn-es/src/cli_commands/archive.js b/packages/kbn-es/src/cli_commands/archive.js index c92ed98ce03fc8..96ffc1fec34c20 100644 --- a/packages/kbn-es/src/cli_commands/archive.js +++ b/packages/kbn-es/src/cli_commands/archive.js @@ -10,6 +10,7 @@ const dedent = require('dedent'); const getopts = require('getopts'); const { Cluster } = require('../cluster'); const { createCliError } = require('../errors'); +const { parseTimeoutToMs } = require('../utils'); exports.description = 'Install and run from an Elasticsearch tar'; @@ -27,6 +28,8 @@ exports.help = (defaults = {}) => { --password.[user] Sets password for native realm user [default: ${password}] --ssl Sets up SSL on Elasticsearch -E Additional key=value settings to pass to Elasticsearch + --skip-ready-check Disable the ready check, + --ready-timeout Customize the ready check timeout, in seconds or "Xm" format, defaults to 1m Example: @@ -41,8 +44,13 @@ exports.run = async (defaults = {}) => { basePath: 'base-path', installPath: 'install-path', esArgs: 'E', + skipReadyCheck: 'skip-ready-check', + readyTimeout: 'ready-timeout', }, + string: ['ready-timeout'], + boolean: ['skip-ready-check'], + default: defaults, }); @@ -54,5 +62,8 @@ exports.run = async (defaults = {}) => { } const { installPath } = await cluster.installArchive(path, options); - await cluster.run(installPath, options); + await cluster.run(installPath, { + ...options, + readyTimeout: parseTimeoutToMs(options.readyTimeout), + }); }; diff --git a/packages/kbn-es/src/cli_commands/snapshot.js b/packages/kbn-es/src/cli_commands/snapshot.js index b89f1f82148138..1c902796a0a0c9 100644 --- a/packages/kbn-es/src/cli_commands/snapshot.js +++ b/packages/kbn-es/src/cli_commands/snapshot.js @@ -10,6 +10,7 @@ const dedent = require('dedent'); const getopts = require('getopts'); import { ToolingLog, getTimeReporter } from '@kbn/dev-utils'; const { Cluster } = require('../cluster'); +const { parseTimeoutToMs } = require('../utils'); exports.description = 'Downloads and run from a nightly snapshot'; @@ -30,6 +31,8 @@ exports.help = (defaults = {}) => { --download-only Download the snapshot but don't actually start it --ssl Sets up SSL on Elasticsearch --use-cached Skips cache verification and use cached ES snapshot. + --skip-ready-check Disable the ready check, + --ready-timeout Customize the ready check timeout, in seconds or "Xm" format, defaults to 1m Example: @@ -53,11 +56,12 @@ exports.run = async (defaults = {}) => { dataArchive: 'data-archive', esArgs: 'E', useCached: 'use-cached', + skipReadyCheck: 'skip-ready-check', + readyTimeout: 'ready-timeout', }, - string: ['version'], - - boolean: ['download-only', 'use-cached'], + string: ['version', 'ready-timeout'], + boolean: ['download-only', 'use-cached', 'skip-ready-check'], default: defaults, }); @@ -82,6 +86,7 @@ exports.run = async (defaults = {}) => { reportTime, startTime: runStartTime, ...options, + readyTimeout: parseTimeoutToMs(options.readyTimeout), }); } }; diff --git a/packages/kbn-es/src/cli_commands/source.js b/packages/kbn-es/src/cli_commands/source.js index 5a4192ae7703cb..c16e89e2c7f32a 100644 --- a/packages/kbn-es/src/cli_commands/source.js +++ b/packages/kbn-es/src/cli_commands/source.js @@ -9,6 +9,7 @@ const dedent = require('dedent'); const getopts = require('getopts'); const { Cluster } = require('../cluster'); +const { parseTimeoutToMs } = require('../utils'); exports.description = 'Build and run from source'; @@ -27,6 +28,8 @@ exports.help = (defaults = {}) => { --password.[user] Sets password for native realm user [default: ${password}] --ssl Sets up SSL on Elasticsearch -E Additional key=value settings to pass to Elasticsearch + --skip-ready-check Disable the ready check, + --ready-timeout Customize the ready check timeout, in seconds or "Xm" format, defaults to 1m Example: @@ -42,9 +45,14 @@ exports.run = async (defaults = {}) => { installPath: 'install-path', sourcePath: 'source-path', dataArchive: 'data-archive', + skipReadyCheck: 'skip-ready-check', + readyTimeout: 'ready-timeout', esArgs: 'E', }, + string: ['ready-timeout'], + boolean: ['skip-ready-check'], + default: defaults, }); @@ -55,5 +63,8 @@ exports.run = async (defaults = {}) => { await cluster.extractDataDirectory(installPath, options.dataArchive); } - await cluster.run(installPath, options); + await cluster.run(installPath, { + ...options, + readyTimeout: parseTimeoutToMs(options.readyTimeout), + }); }; diff --git a/packages/kbn-es/src/cluster.js b/packages/kbn-es/src/cluster.js index e8106b52d6aafe..4ad73297523474 100644 --- a/packages/kbn-es/src/cluster.js +++ b/packages/kbn-es/src/cluster.js @@ -6,20 +6,29 @@ * Side Public License, v 1. */ -const fs = require('fs'); -const util = require('util'); +const fsp = require('fs/promises'); const execa = require('execa'); const chalk = require('chalk'); const path = require('path'); +const { Client } = require('@elastic/elasticsearch'); const { downloadSnapshot, installSnapshot, installSource, installArchive } = require('./install'); const { ES_BIN } = require('./paths'); -const { log: defaultLog, parseEsLog, extractConfigFiles, NativeRealm } = require('./utils'); +const { + log: defaultLog, + parseEsLog, + extractConfigFiles, + NativeRealm, + parseTimeoutToMs, +} = require('./utils'); const { createCliError } = require('./errors'); const { promisify } = require('util'); const treeKillAsync = promisify(require('tree-kill')); const { parseSettings, SettingsFilter } = require('./settings'); const { CA_CERT_PATH, ES_NOPASSWORD_P12_PATH, extract } = require('@kbn/dev-utils'); -const readFile = util.promisify(fs.readFile); + +const DEFAULT_READY_TIMEOUT = parseTimeoutToMs('1m'); + +/** @typedef {import('./cluster_exec_options').EsClusterExecOptions} ExecOptions */ // listen to data on stream until map returns anything but undefined const first = (stream, map) => @@ -38,7 +47,6 @@ exports.Cluster = class Cluster { constructor({ log = defaultLog, ssl = false } = {}) { this._log = log.withType('@kbn/es Cluster'); this._ssl = ssl; - this._caCertPromise = ssl ? readFile(CA_CERT_PATH) : undefined; } /** @@ -157,10 +165,8 @@ exports.Cluster = class Cluster { * Starts ES and returns resolved promise once started * * @param {String} installPath - * @param {Object} options - * @property {Array} options.esArgs - * @property {String} options.password - super user password used to bootstrap - * @returns {Promise} + * @param {ExecOptions} options + * @returns {Promise} */ async start(installPath, options = {}) { this._exec(installPath, options); @@ -173,7 +179,7 @@ exports.Cluster = class Cluster { return true; } }), - this._nativeRealmSetup, + this._setupPromise, ]), // await the outcome of the process in case it exits before starting @@ -187,15 +193,14 @@ exports.Cluster = class Cluster { * Starts Elasticsearch and waits for Elasticsearch to exit * * @param {String} installPath - * @param {Object} options - * @property {Array} options.esArgs - * @returns {Promise} + * @param {ExecOptions} options + * @returns {Promise} */ async run(installPath, options = {}) { this._exec(installPath, options); // log native realm setup errors so they aren't uncaught - this._nativeRealmSetup.catch((error) => { + this._setupPromise.catch((error) => { this._log.error(error); this.stop(); }); @@ -233,14 +238,17 @@ exports.Cluster = class Cluster { * * @private * @param {String} installPath - * @param {Object} options - * @property {string|Array} options.esArgs - * @property {string} options.esJavaOpts - * @property {Boolean} options.skipNativeRealmSetup - * @return {undefined} + * @param {ExecOptions} opts */ _exec(installPath, opts = {}) { - const { skipNativeRealmSetup = false, reportTime = () => {}, startTime, ...options } = opts; + const { + skipNativeRealmSetup = false, + reportTime = () => {}, + startTime, + skipReadyCheck, + readyTimeout, + ...options + } = opts; if (this._process || this._outcome) { throw new Error('ES has already been started'); @@ -301,30 +309,49 @@ exports.Cluster = class Cluster { stdio: ['ignore', 'pipe', 'pipe'], }); - // parse log output to find http port - const httpPort = first(this._process.stdout, (data) => { - const match = data.toString('utf8').match(/HttpServer.+publish_address {[0-9.]+:([0-9]+)/); + this._setupPromise = Promise.all([ + // parse log output to find http port + first(this._process.stdout, (data) => { + const match = data.toString('utf8').match(/HttpServer.+publish_address {[0-9.]+:([0-9]+)/); - if (match) { - return match[1]; + if (match) { + return match[1]; + } + }), + + // load the CA cert from disk if necessary + this._ssl ? fsp.readFile(CA_CERT_PATH) : null, + ]).then(async ([port, caCert]) => { + const client = new Client({ + node: `${caCert ? 'https:' : 'http:'}//localhost:${port}`, + auth: { + username: 'elastic', + password: options.password, + }, + tls: caCert + ? { + ca: caCert, + rejectUnauthorized: true, + } + : undefined, + }); + + if (!skipReadyCheck) { + await this._waitForClusterReady(client, readyTimeout); } - }); - // once the http port is available setup the native realm - this._nativeRealmSetup = httpPort.then(async (port) => { - if (skipNativeRealmSetup) { - return; + // once the cluster is ready setup the native realm + if (!skipNativeRealmSetup) { + const nativeRealm = new NativeRealm({ + log: this._log, + elasticPassword: options.password, + client, + }); + + await nativeRealm.setPasswords(options); } - const caCert = await this._caCertPromise; - const nativeRealm = new NativeRealm({ - port, - caCert, - log: this._log, - elasticPassword: options.password, - ssl: this._ssl, - }); - await nativeRealm.setPasswords(options); + this._log.success('kbn/es setup complete'); }); let reportSent = false; @@ -367,4 +394,43 @@ exports.Cluster = class Cluster { } }); } + + async _waitForClusterReady(client, readyTimeout = DEFAULT_READY_TIMEOUT) { + let attempt = 0; + const start = Date.now(); + + this._log.info('waiting for ES cluster to report a yellow or green status'); + + while (true) { + attempt += 1; + + try { + const resp = await client.cluster.health(); + if (resp.status !== 'red') { + return; + } + + throw new Error(`not ready, cluster health is ${resp.status}`); + } catch (error) { + const timeSinceStart = Date.now() - start; + if (timeSinceStart > readyTimeout) { + const sec = readyTimeout / 1000; + throw new Error(`ES cluster failed to come online with the ${sec} second timeout`); + } + + if (error.message.startsWith('not ready,')) { + if (timeSinceStart > 10_000) { + this._log.warning(error.message); + } + } else { + this._log.warning( + `waiting for ES cluster to come online, attempt ${attempt} failed with: ${error.message}` + ); + } + + const waitSec = attempt * 1.5; + await new Promise((resolve) => setTimeout(resolve, waitSec * 1000)); + } + } + } }; diff --git a/packages/kbn-es/src/cluster_exec_options.ts b/packages/kbn-es/src/cluster_exec_options.ts new file mode 100644 index 00000000000000..8ef3b23cd8c51d --- /dev/null +++ b/packages/kbn-es/src/cluster_exec_options.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 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. + */ + +export interface EsClusterExecOptions { + skipNativeRealmSetup?: boolean; + reportTime?: (...args: any[]) => void; + startTime?: number; + esArgs?: string[]; + esJavaOpts?: string; + password?: string; + skipReadyCheck?: boolean; + readyTimeout?: number; +} diff --git a/packages/kbn-es/src/utils/index.ts b/packages/kbn-es/src/utils/index.ts index 4b4ae1bc05259f..4e75d1d81f6fb3 100644 --- a/packages/kbn-es/src/utils/index.ts +++ b/packages/kbn-es/src/utils/index.ts @@ -17,3 +17,4 @@ export { extractConfigFiles } from './extract_config_files'; export { NativeRealm, SYSTEM_INDICES_SUPERUSER } from './native_realm'; export { buildSnapshot } from './build_snapshot'; export { archiveForPlatform } from './build_snapshot'; +export * from './parse_timeout_to_ms'; diff --git a/packages/kbn-es/src/utils/native_realm.js b/packages/kbn-es/src/utils/native_realm.js index 52d6ae807777bf..ae0ce05f4d6b72 100644 --- a/packages/kbn-es/src/utils/native_realm.js +++ b/packages/kbn-es/src/utils/native_realm.js @@ -6,7 +6,6 @@ * Side Public License, v 1. */ -const { Client } = require('@elastic/elasticsearch'); const chalk = require('chalk'); const { log: defaultLog } = require('./log'); @@ -15,14 +14,9 @@ export const SYSTEM_INDICES_SUPERUSER = process.env.TEST_ES_SYSTEM_INDICES_USER || 'system_indices_superuser'; exports.NativeRealm = class NativeRealm { - constructor({ elasticPassword, port, log = defaultLog, ssl = false, caCert }) { - const auth = { username: 'elastic', password: elasticPassword }; - this._client = new Client( - ssl - ? { node: `https://localhost:${port}`, tls: { ca: caCert, rejectUnauthorized: true }, auth } - : { node: `http://localhost:${port}`, auth } - ); + constructor({ elasticPassword, log = defaultLog, client }) { this._elasticPassword = elasticPassword; + this._client = client; this._log = log; } @@ -53,24 +47,14 @@ exports.NativeRealm = class NativeRealm { }); } - async clusterReady() { - return await this._autoRetry({ maxAttempts: 10 }, async () => { - const { status } = await this._client.cluster.health(); - if (status === 'red') { - throw new Error(`not ready, cluster health is ${status}`); - } - }); - } - async setPasswords(options) { - await this.clusterReady(); - if (!(await this.isSecurityEnabled())) { this._log.info('security is not enabled, unable to set native realm passwords'); return; } const reservedUsers = await this.getReservedUsers(); + this._log.info(`Set up ${reservedUsers.length} ES users`); await Promise.all([ ...reservedUsers.map(async (user) => { await this.setPassword(user, options[`password.${user}`]); @@ -108,7 +92,7 @@ exports.NativeRealm = class NativeRealm { } async _autoRetry(opts, fn) { - const { attempt = 1, maxAttempts = 3, sleep = 1000 } = opts; + const { attempt = 1, maxAttempts = 3 } = opts; try { return await fn(attempt); @@ -119,7 +103,7 @@ exports.NativeRealm = class NativeRealm { const sec = 1.5 * attempt; this._log.warning(`assuming ES isn't initialized completely, trying again in ${sec} seconds`); - await new Promise((resolve) => setTimeout(resolve, sleep)); + await new Promise((resolve) => setTimeout(resolve, sec * 1000)); const nextOpts = { ...opts, diff --git a/packages/kbn-es/src/utils/native_realm.test.js b/packages/kbn-es/src/utils/native_realm.test.js index a567c15e743aff..d3eaf6bd97b72d 100644 --- a/packages/kbn-es/src/utils/native_realm.test.js +++ b/packages/kbn-es/src/utils/native_realm.test.js @@ -7,12 +7,7 @@ */ const { NativeRealm } = require('./native_realm'); - -jest.genMockFromModule('@elastic/elasticsearch'); -jest.mock('@elastic/elasticsearch'); - const { ToolingLog } = require('@kbn/dev-utils'); -const { Client } = require('@elastic/elasticsearch'); const mockClient = { xpack: { @@ -28,13 +23,12 @@ const mockClient = { putUser: jest.fn(), }, }; -Client.mockImplementation(() => mockClient); const log = new ToolingLog(); let nativeRealm; beforeEach(() => { - nativeRealm = new NativeRealm({ elasticPassword: 'changeme', port: '9200', log }); + nativeRealm = new NativeRealm({ elasticPassword: 'changeme', client: mockClient, log }); }); afterAll(() => { diff --git a/packages/kbn-es/src/utils/parse_timeout_to_ms.test.ts b/packages/kbn-es/src/utils/parse_timeout_to_ms.test.ts new file mode 100644 index 00000000000000..fba387cad278bf --- /dev/null +++ b/packages/kbn-es/src/utils/parse_timeout_to_ms.test.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 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. + */ + +import { parseTimeoutToMs } from './parse_timeout_to_ms'; + +it('handles empty values', () => { + expect(parseTimeoutToMs(undefined)).toMatchInlineSnapshot(`undefined`); + expect(parseTimeoutToMs('')).toMatchInlineSnapshot(`undefined`); +}); +it('returns numbers', () => { + expect(parseTimeoutToMs(10)).toMatchInlineSnapshot(`10`); +}); +it('parses seconds', () => { + expect(parseTimeoutToMs('10')).toMatchInlineSnapshot(`10000`); +}); +it('parses minutes', () => { + expect(parseTimeoutToMs('10m')).toMatchInlineSnapshot(`600000`); +}); +it('throws for invalid values', () => { + expect(() => parseTimeoutToMs(true)).toThrowErrorMatchingInlineSnapshot( + `"[true] is not a valid timeout value"` + ); + expect(() => parseTimeoutToMs([true])).toThrowErrorMatchingInlineSnapshot( + `"[[ true ]] is not a valid timeout value"` + ); + expect(() => parseTimeoutToMs(['true'])).toThrowErrorMatchingInlineSnapshot( + `"[[ 'true' ]] is not a valid timeout value"` + ); + expect(() => parseTimeoutToMs(NaN)).toThrowErrorMatchingInlineSnapshot( + `"[NaN] is not a valid timeout value"` + ); +}); diff --git a/packages/kbn-es/src/utils/parse_timeout_to_ms.ts b/packages/kbn-es/src/utils/parse_timeout_to_ms.ts new file mode 100644 index 00000000000000..c8272bdfeee512 --- /dev/null +++ b/packages/kbn-es/src/utils/parse_timeout_to_ms.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 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. + */ + +import { inspect } from 'util'; + +function parseInt(n: string) { + const number = Number.parseInt(n, 10); + if (Number.isNaN(number)) { + throw new Error(`invalid number [${n}]`); + } + return number; +} + +/** + * Parse a timeout value to milliseconds. Supports undefined, a number, an + * empty string, a string representing a number of minutes eg 1m, or a string + * representing a number of seconds eg 60. All other values throw an error + */ +export function parseTimeoutToMs(seconds: any): number | undefined { + if (seconds === undefined || seconds === '') { + return undefined; + } + + if (typeof seconds === 'number' && !Number.isNaN(seconds)) { + return seconds; + } + + if (typeof seconds !== 'string') { + throw new Error(`[${inspect(seconds)}] is not a valid timeout value`); + } + + if (seconds.endsWith('m')) { + const m = parseInt(seconds.slice(0, -1)); + return m * 60 * 1000; + } + + return parseInt(seconds) * 1000; +} diff --git a/packages/kbn-test/src/es/test_es_cluster.ts b/packages/kbn-test/src/es/test_es_cluster.ts index 388d578c9af574..6e4fc2fb14628f 100644 --- a/packages/kbn-test/src/es/test_es_cluster.ts +++ b/packages/kbn-test/src/es/test_es_cluster.ts @@ -244,9 +244,10 @@ export function createTestEsCluster< esArgs: assignArgs(esArgs, overriddenArgs), esJavaOpts, // If we have multiple nodes, we shouldn't try setting up the native realm - // right away, or ES will complain as the cluster isn't ready. So we only + // right away or wait for ES to be green, the cluster isn't ready. So we only // set it up after the last node is started. skipNativeRealmSetup: this.nodes.length > 1 && i < this.nodes.length - 1, + skipReadyCheck: this.nodes.length > 1 && i < this.nodes.length - 1, }); }); } From bf89f3d0d36685d90705fb1d9eabec95650cdb1e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 24 Feb 2022 17:57:57 -0500 Subject: [PATCH 10/28] Update dependency core-js to ^3.21.1 (main) (#126299) * Update dependency core-js to ^3.21.1 * dedupe Co-authored-by: Renovate Bot Co-authored-by: Jonathan Budzenski --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 72309e07bde772..b72f87f4243e33 100644 --- a/package.json +++ b/package.json @@ -218,7 +218,7 @@ "constate": "^1.3.2", "content-disposition": "0.5.3", "copy-to-clipboard": "^3.0.8", - "core-js": "^3.21.0", + "core-js": "^3.21.1", "cronstrue": "^1.51.0", "cytoscape": "^3.10.0", "cytoscape-dagre": "^2.2.2", diff --git a/yarn.lock b/yarn.lock index 3a4e8503fc8fa9..ee986fe2f4d321 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11595,10 +11595,10 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.9: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== -core-js@^3.0.4, core-js@^3.21.0, core-js@^3.6.5, core-js@^3.8.2, core-js@^3.8.3: - version "3.21.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.0.tgz#f479dbfc3dffb035a0827602dd056839a774aa71" - integrity sha512-YUdI3fFu4TF/2WykQ2xzSiTQdldLB4KVuL9WeAy5XONZYt5Cun/fpQvctoKbCgvPhmzADeesTk/j2Rdx77AcKQ== +core-js@^3.0.4, core-js@^3.21.1, core-js@^3.6.5, core-js@^3.8.2, core-js@^3.8.3: + version "3.21.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.21.1.tgz#f2e0ddc1fc43da6f904706e8e955bc19d06a0d94" + integrity sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig== core-util-is@1.0.2, core-util-is@^1.0.2, core-util-is@~1.0.0: version "1.0.2" From ef62706a59355a4005920124d0c0faa33bc7798a Mon Sep 17 00:00:00 2001 From: Ersin Erdal <92688503+ersin-erdal@users.noreply.github.com> Date: Fri, 25 Feb 2022 00:36:12 +0100 Subject: [PATCH 11/28] =?UTF-8?q?[Alerting]=20Refactor=20task=5Frunner.=20?= =?UTF-8?q?Move=20Interface=20and=20test=20fixtures=20to=20=E2=80=A6=20(#1?= =?UTF-8?q?25418)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Alerting] Refactor task_runner. Move Interface and test fixtures to their own file --- .../alerting/common/rule_task_instance.ts | 7 + .../alerting/server/task_runner/fixtures.ts | 378 ++ .../server/task_runner/task_runner.test.ts | 4583 +++-------------- .../server/task_runner/task_runner.ts | 93 +- .../alerting/server/task_runner/types.ts | 105 + 5 files changed, 1218 insertions(+), 3948 deletions(-) create mode 100644 x-pack/plugins/alerting/server/task_runner/fixtures.ts create mode 100644 x-pack/plugins/alerting/server/task_runner/types.ts diff --git a/x-pack/plugins/alerting/common/rule_task_instance.ts b/x-pack/plugins/alerting/common/rule_task_instance.ts index 59ed9097f16755..32437338b9c13a 100644 --- a/x-pack/plugins/alerting/common/rule_task_instance.ts +++ b/x-pack/plugins/alerting/common/rule_task_instance.ts @@ -8,6 +8,7 @@ import * as t from 'io-ts'; import { rawAlertInstance } from './alert_instance'; import { DateFromString } from './date_from_string'; +import { IntervalSchedule, RuleMonitoring } from './alert'; const actionSchema = t.partial({ group: t.string, @@ -44,3 +45,9 @@ export const ruleParamsSchema = t.intersection([ }), ]); export type RuleTaskParams = t.TypeOf; + +export interface RuleExecutionRunResult { + state: RuleExecutionState; + monitoring: RuleMonitoring | undefined; + schedule: IntervalSchedule | undefined; +} diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts new file mode 100644 index 00000000000000..07f2487de20fbb --- /dev/null +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -0,0 +1,378 @@ +/* + * 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 { isNil } from 'lodash'; +import { Alert, AlertTypeParams, RecoveredActionGroup } from '../../common'; +import { getDefaultRuleMonitoring } from './task_runner'; +import { UntypedNormalizedRuleType } from '../rule_type_registry'; +import { TaskStatus } from '../../../task_manager/server'; +import { EVENT_LOG_ACTIONS } from '../plugin'; + +interface GeneratorParams { + [key: string]: string | number | boolean | undefined | object[] | boolean[] | object; +} + +export const RULE_NAME = 'rule-name'; +export const RULE_ID = '1'; +export const RULE_TYPE_ID = 'test'; +export const DATE_1969 = '1969-12-31T00:00:00.000Z'; +export const DATE_1970 = '1970-01-01T00:00:00.000Z'; +export const DATE_1970_5_MIN = '1969-12-31T23:55:00.000Z'; +export const MOCK_DURATION = 86400000000000; + +export const SAVED_OBJECT = { + id: '1', + type: 'alert', + attributes: { + apiKey: Buffer.from('123:abc').toString('base64'), + enabled: true, + }, + references: [], +}; + +export const RULE_ACTIONS = [ + { + actionTypeId: 'action', + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + { + actionTypeId: 'action', + group: 'recovered', + id: '2', + params: { + isResolved: true, + }, + }, +]; + +export const SAVED_OBJECT_UPDATE_PARAMS = [ + 'alert', + '1', + { + monitoring: { + execution: { + calculated_metrics: { + success_ratio: 1, + }, + history: [ + { + success: true, + timestamp: 0, + }, + ], + }, + }, + executionStatus: { + error: null, + lastDuration: 0, + lastExecutionDate: '1970-01-01T00:00:00.000Z', + status: 'ok', + }, + }, + { refresh: false, namespace: undefined }, +]; + +export const GENERIC_ERROR_MESSAGE = 'GENERIC ERROR MESSAGE'; + +export const ruleType: jest.Mocked = { + id: RULE_TYPE_ID, + name: 'My test rule', + actionGroups: [{ id: 'default', name: 'Default' }, RecoveredActionGroup], + defaultActionGroupId: 'default', + minimumLicenseRequired: 'basic', + isExportable: true, + recoveryActionGroup: RecoveredActionGroup, + executor: jest.fn(), + producer: 'alerts', +}; + +export const mockRunNowResponse = { + id: 1, +} as jest.ResolvedValue; + +export const mockDate = new Date('2019-02-12T21:01:22.479Z'); + +export const mockedRuleTypeSavedObject: Alert = { + id: '1', + consumer: 'bar', + createdAt: mockDate, + updatedAt: mockDate, + throttle: null, + muteAll: false, + notifyWhen: 'onActiveAlert', + enabled: true, + alertTypeId: ruleType.id, + apiKey: '', + apiKeyOwner: 'elastic', + schedule: { interval: '10s' }, + name: RULE_NAME, + tags: ['rule-', '-tags'], + createdBy: 'rule-creator', + updatedBy: 'rule-updater', + mutedInstanceIds: [], + params: { + bar: true, + }, + actions: [ + { + group: 'default', + id: '1', + actionTypeId: 'action', + params: { + foo: true, + }, + }, + { + group: RecoveredActionGroup.id, + id: '2', + actionTypeId: 'action', + params: { + isResolved: true, + }, + }, + ], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, + monitoring: getDefaultRuleMonitoring(), +}; + +export const mockTaskInstance = () => ({ + id: '', + attempts: 0, + status: TaskStatus.Running, + version: '123', + runAt: new Date(), + schedule: { interval: '10s' }, + scheduledAt: new Date(), + startedAt: new Date(), + retryAt: new Date(Date.now() + 5 * 60 * 1000), + state: {}, + taskType: 'alerting:test', + params: { + alertId: RULE_ID, + }, + ownerId: null, +}); + +export const generateAlertSO = (id: string) => ({ + id, + rel: 'primary', + type: 'alert', + type_id: RULE_TYPE_ID, +}); + +export const generateActionSO = (id: string) => ({ + id, + namespace: undefined, + type: 'action', + type_id: 'action', +}); + +export const generateEventLog = ({ + action, + task, + duration, + start, + end, + outcome, + reason, + instanceId, + actionSubgroup, + actionGroupId, + status, + numberOfTriggeredActions, + savedObjects = [generateAlertSO('1')], +}: GeneratorParams = {}) => ({ + ...(status === 'error' && { + error: { + message: generateErrorMessage(String(reason)), + }, + }), + event: { + action, + ...(!isNil(duration) && { duration }), + ...(start && { start }), + ...(end && { end }), + ...(outcome && { outcome }), + ...(reason && { reason }), + category: ['alerts'], + kind: 'alert', + }, + kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + ...(!isNil(numberOfTriggeredActions) && { + metrics: { + number_of_triggered_actions: numberOfTriggeredActions, + number_of_searches: 3, + es_search_duration_ms: 33, + total_search_duration_ms: 23423, + }, + }), + }, + }, + }, + ...((actionSubgroup || actionGroupId || instanceId || status) && { + alerting: { + ...(actionSubgroup && { action_subgroup: actionSubgroup }), + ...(actionGroupId && { action_group_id: actionGroupId }), + ...(instanceId && { instance_id: instanceId }), + ...(status && { status }), + }, + }), + saved_objects: savedObjects, + ...(task && { + task: { + schedule_delay: 0, + scheduled: '1970-01-01T00:00:00.000Z', + }, + }), + }, + message: generateMessage({ action, instanceId, actionGroupId, actionSubgroup, reason, status }), + rule: { + category: 'test', + id: '1', + license: 'basic', + ...(hasRuleName({ action, status }) && { name: RULE_NAME }), + ruleset: 'alerts', + }, +}); + +const generateMessage = ({ + action, + instanceId, + actionGroupId, + actionSubgroup, + reason, + status, +}: GeneratorParams) => { + if (action === EVENT_LOG_ACTIONS.executeStart) { + return `rule execution start: "${mockTaskInstance().params.alertId}"`; + } + + if (action === EVENT_LOG_ACTIONS.newInstance) { + return `${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}' created new alert: '${instanceId}'`; + } + + if (action === EVENT_LOG_ACTIONS.activeInstance) { + if (actionSubgroup) { + return `${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}' active alert: '${instanceId}' in actionGroup(subgroup): 'default(${actionSubgroup})'`; + } + return `${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}' active alert: '${instanceId}' in actionGroup: '${actionGroupId}'`; + } + + if (action === EVENT_LOG_ACTIONS.recoveredInstance) { + return `${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}' alert '${instanceId}' has recovered`; + } + + if (action === EVENT_LOG_ACTIONS.executeAction) { + if (actionSubgroup) { + return `alert: ${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}' instanceId: '${instanceId}' scheduled actionGroup(subgroup): 'default(${actionSubgroup})' action: action:${instanceId}`; + } + return `alert: ${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}' instanceId: '${instanceId}' scheduled actionGroup: '${actionGroupId}' action: action:${instanceId}`; + } + + if (action === EVENT_LOG_ACTIONS.execute) { + if (status === 'error' && reason === 'execute') { + return `rule execution failure: ${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}'`; + } + if (status === 'error') { + return `${RULE_TYPE_ID}:${RULE_ID}: execution failed`; + } + if (actionGroupId === 'recovered') { + return `rule-name' instanceId: '${instanceId}' scheduled actionGroup: '${actionGroupId}' action: action:${instanceId}`; + } + return `rule executed: ${RULE_TYPE_ID}:${RULE_ID}: '${RULE_NAME}'`; + } +}; + +const generateErrorMessage = (reason: string) => { + if (reason === 'disabled') { + return 'Rule failed to execute because rule ran after it was disabled.'; + } + return GENERIC_ERROR_MESSAGE; +}; + +export const generateRunnerResult = ({ + successRatio = 1, + history = Array(false), + state = false, + interval = '10s', +}: GeneratorParams = {}) => { + return { + monitoring: { + execution: { + calculated_metrics: { + success_ratio: successRatio, + }, + // @ts-ignore + history: history.map((success) => ({ success, timestamp: 0 })), + }, + }, + schedule: { + interval, + }, + state: { + ...(state && { alertInstances: {} }), + ...(state && { alertTypeState: undefined }), + ...(state && { previousStartedAt: new Date('1970-01-01T00:00:00.000Z') }), + }, + }; +}; + +export const generateEnqueueFunctionInput = () => ({ + apiKey: 'MTIzOmFiYw==', + executionId: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + id: '1', + params: { + foo: true, + }, + relatedSavedObjects: [ + { + id: '1', + namespace: undefined, + type: 'alert', + typeId: RULE_TYPE_ID, + }, + ], + source: { + source: { + id: '1', + type: 'alert', + }, + type: 'SAVED_OBJECT', + }, + spaceId: undefined, +}); + +export const generateAlertInstance = ({ id, duration, start }: GeneratorParams = { id: 1 }) => ({ + [String(id)]: { + meta: { + lastScheduledActions: { + date: new Date(DATE_1970), + group: 'default', + subgroup: undefined, + }, + }, + state: { + bar: false, + duration, + start, + }, + }, +}); +const hasRuleName = ({ action, status }: GeneratorParams) => { + return action !== 'execute-start' && status !== 'error'; +}; diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 7496cdf7fd3367..99feefb472df1c 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -19,10 +19,9 @@ import { ConcreteTaskInstance, isUnrecoverableError, RunNowResult, - TaskStatus, } from '../../../task_manager/server'; import { TaskRunnerContext } from './task_runner_factory'; -import { TaskRunner, getDefaultRuleMonitoring } from './task_runner'; +import { TaskRunner } from './task_runner'; import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks'; import { loggingSystemMock, @@ -38,32 +37,42 @@ import { alertsMock, rulesClientMock } from '../mocks'; import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; import { IEventLogger } from '../../../event_log/server'; import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; -import { Alert, RecoveredActionGroup } from '../../common'; import { omit } from 'lodash'; -import { UntypedNormalizedRuleType } from '../rule_type_registry'; import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; import { ExecuteOptions } from '../../../actions/server/create_execute_function'; import moment from 'moment'; +import { + generateActionSO, + generateAlertSO, + generateEventLog, + mockDate, + mockedRuleTypeSavedObject, + mockRunNowResponse, + ruleType, + RULE_NAME, + SAVED_OBJECT, + generateRunnerResult, + RULE_ACTIONS, + generateEnqueueFunctionInput, + SAVED_OBJECT_UPDATE_PARAMS, + mockTaskInstance, + GENERIC_ERROR_MESSAGE, + generateAlertInstance, + MOCK_DURATION, + DATE_1969, + DATE_1970, + DATE_1970_5_MIN, +} from './fixtures'; +import { EVENT_LOG_ACTIONS } from '../plugin'; jest.mock('uuid', () => ({ v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', })); + jest.mock('../lib/wrap_scoped_cluster_client', () => ({ createWrappedScopedClusterClientFactory: jest.fn(), })); -const ruleType: jest.Mocked = { - id: 'test', - name: 'My test rule', - actionGroups: [{ id: 'default', name: 'Default' }, RecoveredActionGroup], - defaultActionGroupId: 'default', - minimumLicenseRequired: 'basic', - isExportable: true, - recoveryActionGroup: RecoveredActionGroup, - executor: jest.fn(), - producer: 'alerts', -}; - let fakeTimer: sinon.SinonFakeTimers; const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract(); @@ -74,23 +83,7 @@ describe('Task Runner', () => { beforeAll(() => { fakeTimer = sinon.useFakeTimers(); - mockedTaskInstance = { - id: '', - attempts: 0, - status: TaskStatus.Running, - version: '123', - runAt: new Date(), - schedule: { interval: '10s' }, - scheduledAt: new Date(), - startedAt: new Date(), - retryAt: new Date(Date.now() + 5 * 60 * 1000), - state: {}, - taskType: 'alerting:test', - params: { - alertId: '1', - }, - ownerId: null, - }; + mockedTaskInstance = mockTaskInstance(); }); afterAll(() => fakeTimer.restore()); @@ -131,56 +124,6 @@ describe('Task Runner', () => { usageCounter: mockUsageCounter, }; - const mockDate = new Date('2019-02-12T21:01:22.479Z'); - const mockedRuleTypeSavedObject: Alert = { - id: '1', - consumer: 'bar', - createdAt: mockDate, - updatedAt: mockDate, - throttle: null, - muteAll: false, - notifyWhen: 'onActiveAlert', - enabled: true, - alertTypeId: ruleType.id, - apiKey: '', - apiKeyOwner: 'elastic', - schedule: { interval: '10s' }, - name: 'rule-name', - tags: ['rule-', '-tags'], - createdBy: 'rule-creator', - updatedBy: 'rule-updater', - mutedInstanceIds: [], - params: { - bar: true, - }, - actions: [ - { - group: 'default', - id: '1', - actionTypeId: 'action', - params: { - foo: true, - }, - }, - { - group: RecoveredActionGroup.id, - id: '2', - actionTypeId: 'action', - params: { - isResolved: true, - }, - }, - ], - executionStatus: { - status: 'unknown', - lastExecutionDate: new Date('2020-08-20T19:23:38Z'), - }, - monitoring: getDefaultRuleMonitoring(), - }; - const mockRunNowResponse = { - id: 1, - } as jest.ResolvedValue; - const ephemeralTestParams: Array< [ nameExtension: string, @@ -241,65 +184,25 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 1, - }, - "history": Array [ - Object { - "success": true, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "10s", - }, - "state": Object { - "alertInstances": Object {}, - "alertTypeState": undefined, - "previousStartedAt": 1970-01-01T00:00:00.000Z, - }, - } - `); + expect(runnerResult).toEqual(generateRunnerResult({ state: true, history: [true] })); expect(ruleType.executor).toHaveBeenCalledTimes(1); const call = ruleType.executor.mock.calls[0][0]; - expect(call.params).toMatchInlineSnapshot(` - Object { - "bar": true, - } - `); - expect(call.startedAt).toMatchInlineSnapshot(`1970-01-01T00:00:00.000Z`); - expect(call.previousStartedAt).toMatchInlineSnapshot(`1969-12-31T23:55:00.000Z`); - expect(call.state).toMatchInlineSnapshot(`Object {}`); - expect(call.name).toBe('rule-name'); + expect(call.params).toEqual({ bar: true }); + expect(call.startedAt).toStrictEqual(new Date(DATE_1970)); + expect(call.previousStartedAt).toStrictEqual(new Date(DATE_1970_5_MIN)); + expect(call.state).toEqual({}); + expect(call.name).toBe(RULE_NAME); expect(call.tags).toEqual(['rule-', '-tags']); expect(call.createdBy).toBe('rule-creator'); expect(call.updatedBy).toBe('rule-updater'); expect(call.rule).not.toBe(null); - expect(call.rule.name).toBe('rule-name'); + expect(call.rule.name).toBe(RULE_NAME); expect(call.rule.tags).toEqual(['rule-', '-tags']); expect(call.rule.consumer).toBe('bar'); expect(call.rule.enabled).toBe(true); - expect(call.rule.schedule).toMatchInlineSnapshot(` - Object { - "interval": "10s", - } - `); + expect(call.rule.schedule).toEqual({ interval: '10s' }); expect(call.rule.createdBy).toBe('rule-creator'); expect(call.rule.updatedBy).toBe('rule-updater'); expect(call.rule.createdAt).toBe(mockDate); @@ -309,26 +212,7 @@ describe('Task Runner', () => { expect(call.rule.producer).toBe('alerts'); expect(call.rule.ruleTypeId).toBe('test'); expect(call.rule.ruleTypeName).toBe('My test rule'); - expect(call.rule.actions).toMatchInlineSnapshot(` - Array [ - Object { - "actionTypeId": "action", - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - Object { - "actionTypeId": "action", - "group": "recovered", - "id": "2", - "params": Object { - "isResolved": true, - }, - }, - ] - `); + expect(call.rule.actions).toEqual(RULE_ACTIONS); expect(call.services.alertFactory.create).toBeTruthy(); expect(call.services.scopedClusterClient).toBeTruthy(); expect(call.services).toBeTruthy(); @@ -344,75 +228,16 @@ describe('Task Runner', () => { const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls[0][0]).toMatchInlineSnapshot(` - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - } - `); + expect(eventLogger.logEvent).toHaveBeenCalledWith( + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); expect( taskRunnerFactoryInitializerParams.internalSavedObjectsRepository.update - ).toHaveBeenCalledWith( - 'alert', - '1', - { - monitoring: { - execution: { - calculated_metrics: { - success_ratio: 1, - }, - history: [ - { - success: true, - timestamp: 0, - }, - ], - }, - }, - executionStatus: { - error: null, - lastDuration: 0, - lastExecutionDate: '1970-01-01T00:00:00.000Z', - status: 'ok', - }, - }, - { refresh: false, namespace: undefined } - ); + ).toHaveBeenCalledWith(...SAVED_OBJECT_UPDATE_PARAMS); expect(taskRunnerFactoryInitializerParams.executionContext.withContext).toBeCalledTimes(1); expect(taskRunnerFactoryInitializerParams.executionContext.withContext).toHaveBeenCalledWith( @@ -461,262 +286,74 @@ describe('Task Runner', () => { customTaskRunnerFactoryInitializerParams ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); expect(enqueueFunction).toHaveBeenCalledTimes(1); - expect((enqueueFunction as jest.Mock).mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", - "executionId": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - "id": "1", - "params": Object { - "foo": true, - }, - "relatedSavedObjects": Array [ - Object { - "id": "1", - "namespace": undefined, - "type": "alert", - "typeId": "test", - }, - ], - "source": Object { - "source": Object { - "id": "1", - "type": "alert", - }, - "type": "SAVED_OBJECT", - }, - "spaceId": undefined, - }, - ] - `); + expect(enqueueFunction).toHaveBeenCalledWith(generateEnqueueFunctionInput()); const logger = customTaskRunnerFactoryInitializerParams.logger; expect(logger.debug).toHaveBeenCalledTimes(4); expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z'); expect(logger.debug).nthCalledWith( 2, - `rule test:1: 'rule-name' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` + `rule test:1: '${RULE_NAME}' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` ); expect(logger.debug).nthCalledWith( 3, 'ruleExecutionStatus for test:1: {"metrics":{"numSearches":3,"esSearchDurationMs":33,"totalSearchDurationMs":23423},"numberOfTriggeredActions":1,"lastExecutionDate":"1970-01-01T00:00:00.000Z","status":"active"}' ); - // ruleExecutionStatus for test:1: {\"lastExecutionDate\":\"1970-01-01T00:00:00.000Z\",\"status\":\"error\",\"error\":{\"reason\":\"unknown\",\"message\":\"Cannot read property 'catch' of undefined\"}} const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(5); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, { - event: { - action: 'execute-start', - category: ['alerts'], - kind: 'alert', - }, - kibana: { - alert: { - rule: { - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - }, - }, - task: { - schedule_delay: 0, - scheduled: '1970-01-01T00:00:00.000Z', - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - }, - message: `rule execution start: "1"`, - rule: { - category: 'test', - id: '1', - license: 'basic', - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(2, { - event: { - action: 'new-instance', - category: ['alerts'], - kind: 'alert', + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ duration: 0, - start: '1970-01-01T00:00:00.000Z', - }, - kibana: { - alert: { - rule: { - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - }, - }, - alerting: { - action_group_id: 'default', - action_subgroup: 'subDefault', - instance_id: '1', - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - }, - message: "test:1: 'rule-name' created new alert: '1'", - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - namespace: undefined, - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(3, { - event: { - action: 'active-instance', - category: ['alerts'], + start: DATE_1970, + action: EVENT_LOG_ACTIONS.newInstance, + actionSubgroup: 'subDefault', + actionGroupId: 'default', + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 3, + generateEventLog({ duration: 0, - kind: 'alert', - start: '1970-01-01T00:00:00.000Z', - }, - kibana: { - alert: { - rule: { - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - }, - }, - alerting: { - action_group_id: 'default', - action_subgroup: 'subDefault', - instance_id: '1', - }, - saved_objects: [ - { id: '1', namespace: undefined, rel: 'primary', type: 'alert', type_id: 'test' }, - ], - }, - message: - "test:1: 'rule-name' active alert: '1' in actionGroup(subgroup): 'default(subDefault)'", - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - namespace: undefined, - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(4, { - event: { - action: 'execute-action', - category: ['alerts'], - kind: 'alert', - }, - kibana: { - alert: { - rule: { - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - }, - }, - alerting: { - instance_id: '1', - action_group_id: 'default', - action_subgroup: 'subDefault', - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - { - id: '1', - namespace: undefined, - type: 'action', - type_id: 'action', - }, - ], - }, - message: - "alert: test:1: 'rule-name' instanceId: '1' scheduled actionGroup(subgroup): 'default(subDefault)' action: action:1", - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - namespace: undefined, - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(5, { - event: { action: 'execute', category: ['alerts'], kind: 'alert', outcome: 'success' }, - kibana: { - alert: { - rule: { - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - metrics: { - number_of_searches: 3, - number_of_triggered_actions: 1, - es_search_duration_ms: 33, - total_search_duration_ms: 23423, - }, - }, - }, - }, - task: { - schedule_delay: 0, - scheduled: '1970-01-01T00:00:00.000Z', - }, - alerting: { - status: 'active', - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - }, - message: "rule executed: test:1: 'rule-name'", - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - ruleset: 'alerts', - }, - }); + start: DATE_1970, + action: EVENT_LOG_ACTIONS.activeInstance, + actionGroupId: 'default', + actionSubgroup: 'subDefault', + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 4, + generateEventLog({ + action: EVENT_LOG_ACTIONS.executeAction, + actionGroupId: 'default', + instanceId: '1', + actionSubgroup: 'subDefault', + savedObjects: [generateAlertSO('1'), generateActionSO('1')], + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 5, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'success', + status: 'active', + numberOfTriggeredActions: 1, + task: true, + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); } ); @@ -746,15 +383,7 @@ describe('Task Runner', () => { ...mockedRuleTypeSavedObject, muteAll: true, }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); expect(actionsClient.ephemeralEnqueuedExecution).toHaveBeenCalledTimes(0); @@ -763,11 +392,11 @@ describe('Task Runner', () => { expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z'); expect(logger.debug).nthCalledWith( 2, - `rule test:1: 'rule-name' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` + `rule test:1: '${RULE_NAME}' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` ); expect(logger.debug).nthCalledWith( 3, - `no scheduling of actions for rule test:1: 'rule-name': rule is muted.` + `no scheduling of actions for rule test:1: '${RULE_NAME}': rule is muted.` ); expect(logger.debug).nthCalledWith( 4, @@ -777,169 +406,43 @@ describe('Task Runner', () => { const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(1, { - event: { - action: 'execute-start', - category: ['alerts'], - kind: 'alert', - }, - kibana: { - task: { - schedule_delay: 0, - scheduled: '1970-01-01T00:00:00.000Z', - }, - alert: { - rule: { - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - }, - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - }, - message: `rule execution start: \"1\"`, - rule: { - category: 'test', - id: '1', - license: 'basic', - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(2, { - event: { - action: 'new-instance', - category: ['alerts'], - kind: 'alert', + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ duration: 0, - start: '1970-01-01T00:00:00.000Z', - }, - kibana: { - alert: { - rule: { - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - }, - }, - alerting: { - action_group_id: 'default', - instance_id: '1', - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - }, - message: "test:1: 'rule-name' created new alert: '1'", - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - namespace: undefined, - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(3, { - event: { - action: 'active-instance', - category: ['alerts'], - kind: 'alert', + start: DATE_1970, + action: EVENT_LOG_ACTIONS.newInstance, + actionGroupId: 'default', + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 3, + generateEventLog({ duration: 0, - start: '1970-01-01T00:00:00.000Z', - }, - kibana: { - alert: { - rule: { - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - }, - }, - alerting: { - instance_id: '1', - action_group_id: 'default', - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - }, - message: "test:1: 'rule-name' active alert: '1' in actionGroup: 'default'", - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - namespace: undefined, - ruleset: 'alerts', - }, - }); - expect(eventLogger.logEvent).toHaveBeenNthCalledWith(4, { - event: { - action: 'execute', - category: ['alerts'], - kind: 'alert', + start: DATE_1970, + action: EVENT_LOG_ACTIONS.activeInstance, + actionGroupId: 'default', + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 4, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, outcome: 'success', - }, - kibana: { - alert: { - rule: { - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - metrics: { - number_of_searches: 3, - number_of_triggered_actions: 0, - es_search_duration_ms: 33, - total_search_duration_ms: 23423, - }, - }, - }, - }, - alerting: { - status: 'active', - }, - task: { - schedule_delay: 0, - scheduled: '1970-01-01T00:00:00.000Z', - }, - saved_objects: [ - { - id: '1', - namespace: undefined, - rel: 'primary', - type: 'alert', - type_id: 'test', - }, - ], - }, - message: "rule executed: test:1: 'rule-name'", - rule: { - category: 'test', - id: '1', - license: 'basic', - name: 'rule-name', - ruleset: 'alerts', - }, - }); + status: 'active', + numberOfTriggeredActions: 0, + task: true, + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -976,15 +479,7 @@ describe('Task Runner', () => { ...mockedRuleTypeSavedObject, mutedInstanceIds: ['2'], }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); expect(enqueueFunction).toHaveBeenCalledTimes(1); @@ -993,11 +488,11 @@ describe('Task Runner', () => { expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z'); expect(logger.debug).nthCalledWith( 2, - `rule test:1: 'rule-name' has 2 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"},{\"instanceId\":\"2\",\"actionGroup\":\"default\"}]` + `rule test:1: '${RULE_NAME}' has 2 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"},{\"instanceId\":\"2\",\"actionGroup\":\"default\"}]` ); expect(logger.debug).nthCalledWith( 3, - `skipping scheduling of actions for '2' in rule test:1: 'rule-name': rule is muted` + `skipping scheduling of actions for '2' in rule test:1: '${RULE_NAME}': rule is muted` ); expect(logger.debug).nthCalledWith( 4, @@ -1044,8 +539,8 @@ describe('Task Runner', () => { }, state: { bar: false, - start: '1969-12-31T00:00:00.000Z', - duration: 86400000000000, + start: DATE_1969, + duration: MOCK_DURATION, }, }, }, @@ -1057,15 +552,7 @@ describe('Task Runner', () => { ...mockedRuleTypeSavedObject, throttle: '1d', }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); // expect(enqueueFunction).toHaveBeenCalledTimes(1); @@ -1073,7 +560,7 @@ describe('Task Runner', () => { // expect(logger.debug).toHaveBeenCalledTimes(5); expect(logger.debug).nthCalledWith( 3, - `skipping scheduling of actions for '2' in rule test:1: 'rule-name': rule is throttled` + `skipping scheduling of actions for '2' in rule test:1: '${RULE_NAME}': rule is throttled` ); } ); @@ -1108,22 +595,14 @@ describe('Task Runner', () => { mutedInstanceIds: ['2'], notifyWhen: 'onActionGroupChange', }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); expect(enqueueFunction).toHaveBeenCalledTimes(1); const logger = customTaskRunnerFactoryInitializerParams.logger; expect(logger.debug).toHaveBeenCalledTimes(5); expect(logger.debug).nthCalledWith( 3, - `skipping scheduling of actions for '2' in rule test:1: 'rule-name': rule is muted` + `skipping scheduling of actions for '2' in rule test:1: '${RULE_NAME}': rule is muted` ); } ); @@ -1153,12 +632,12 @@ describe('Task Runner', () => { alertInstances: { '1': { meta: { - lastScheduledActions: { date: '1970-01-01T00:00:00.000Z', group: 'default' }, + lastScheduledActions: { date: DATE_1970, group: 'default' }, }, state: { bar: false, - start: '1969-12-31T00:00:00.000Z', - duration: 86400000000000, + start: DATE_1969, + duration: MOCK_DURATION, }, }, }, @@ -1170,159 +649,40 @@ describe('Task Runner', () => { ...mockedRuleTypeSavedObject, notifyWhen: 'onActionGroupChange', }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); expect(actionsClient.ephemeralEnqueuedExecution).toHaveBeenCalledTimes(0); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(3); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "active-instance", - "category": Array [ - "alerts", - ], - "duration": 86400000000000, - "kind": "alert", - "start": "1969-12-31T00:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' active alert: '1' in actionGroup: 'default'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "success", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "metrics": Object { - "es_search_duration_ms": 33, - "number_of_searches": 3, - "number_of_triggered_actions": 0, - "total_search_duration_ms": 23423, - }, - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "active", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule executed: test:1: 'rule-name'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - ] - `); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + duration: MOCK_DURATION, + start: DATE_1969, + action: EVENT_LOG_ACTIONS.activeInstance, + actionGroupId: 'default', + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 3, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'success', + status: 'active', + numberOfTriggeredActions: 0, + task: true, + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -1370,35 +730,19 @@ describe('Task Runner', () => { ...mockedRuleTypeSavedObject, notifyWhen: 'onActionGroupChange', }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; await taskRunner.run(); - expect(eventLogger.logEvent.mock.calls[3][0]).toEqual( - expect.objectContaining({ - kibana: expect.objectContaining({ - alert: expect.objectContaining({ - rule: expect.objectContaining({ - execution: expect.objectContaining({ - metrics: expect.objectContaining({ - number_of_searches: 3, - number_of_triggered_actions: 1, - es_search_duration_ms: 33, - total_search_duration_ms: 23423, - }), - }), - }), - }), - }), + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 4, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'success', + status: 'active', + numberOfTriggeredActions: 1, + task: true, }) ); expect(enqueueFunction).toHaveBeenCalledTimes(1); @@ -1457,37 +801,22 @@ describe('Task Runner', () => { ...mockedRuleTypeSavedObject, notifyWhen: 'onActionGroupChange', }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; - expect(eventLogger.logEvent.mock.calls[3][0]).toEqual( - expect.objectContaining({ - kibana: expect.objectContaining({ - alert: expect.objectContaining({ - rule: expect.objectContaining({ - execution: expect.objectContaining({ - metrics: expect.objectContaining({ - number_of_searches: 3, - number_of_triggered_actions: 1, - es_search_duration_ms: 33, - total_search_duration_ms: 23423, - }), - }), - }), - }), - }), + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 4, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'success', + status: 'active', + numberOfTriggeredActions: 1, + task: true, }) ); + expect(enqueueFunction).toHaveBeenCalledTimes(1); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); } @@ -1522,15 +851,7 @@ describe('Task Runner', () => { customTaskRunnerFactoryInitializerParams ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); await taskRunner.run(); expect( customTaskRunnerFactoryInitializerParams.actionsPlugin.getActionsClientWithRequest @@ -1553,266 +874,58 @@ describe('Task Runner', () => { ); expect(enqueueFunction).toHaveBeenCalledTimes(1); - expect((enqueueFunction as jest.Mock).mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", - "executionId": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - "id": "1", - "params": Object { - "foo": true, - }, - "relatedSavedObjects": Array [ - Object { - "id": "1", - "namespace": undefined, - "type": "alert", - "typeId": "test", - }, - ], - "source": Object { - "source": Object { - "id": "1", - "type": "alert", - }, - "type": "SAVED_OBJECT", - }, - "spaceId": undefined, - }, - ] - `); + expect(enqueueFunction).toHaveBeenCalledWith(generateEnqueueFunctionInput()); const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(5); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "new-instance", - "category": Array [ - "alerts", - ], - "duration": 0, - "kind": "alert", - "start": "1970-01-01T00:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' created new alert: '1'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "active-instance", - "category": Array [ - "alerts", - ], - "duration": 0, - "kind": "alert", - "start": "1970-01-01T00:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' active alert: '1' in actionGroup: 'default'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "execute-action", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - Object { - "id": "1", - "namespace": undefined, - "type": "action", - "type_id": "action", - }, - ], - }, - "message": "alert: test:1: 'rule-name' instanceId: '1' scheduled actionGroup: 'default' action: action:1", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "success", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "metrics": Object { - "es_search_duration_ms": 33, - "number_of_searches": 3, - "number_of_triggered_actions": 1, - "total_search_duration_ms": 23423, - }, - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "active", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule executed: test:1: 'rule-name'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - ] - `); + + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + duration: 0, + start: DATE_1970, + action: EVENT_LOG_ACTIONS.newInstance, + actionGroupId: 'default', + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 3, + generateEventLog({ + duration: 0, + start: DATE_1970, + action: EVENT_LOG_ACTIONS.activeInstance, + actionGroupId: 'default', + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 4, + generateEventLog({ + action: EVENT_LOG_ACTIONS.executeAction, + actionGroupId: 'default', + instanceId: '1', + savedObjects: [generateAlertSO('1'), generateActionSO('1')], + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 5, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'success', + status: 'active', + numberOfTriggeredActions: 1, + task: true, + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); } ); @@ -1852,7 +965,7 @@ describe('Task Runner', () => { meta: {}, state: { bar: false, - start: '1969-12-31T00:00:00.000Z', + start: DATE_1969, duration: 80000000000, }, }, @@ -1870,45 +983,22 @@ describe('Task Runner', () => { customTaskRunnerFactoryInitializerParams ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult.state.alertInstances).toMatchInlineSnapshot(` - Object { - "1": Object { - "meta": Object { - "lastScheduledActions": Object { - "date": 1970-01-01T00:00:00.000Z, - "group": "default", - "subgroup": undefined, - }, - }, - "state": Object { - "bar": false, - "duration": 86400000000000, - "start": "1969-12-31T00:00:00.000Z", - }, - }, - } - `); + expect(runnerResult.state.alertInstances).toEqual( + generateAlertInstance({ id: 1, duration: MOCK_DURATION, start: DATE_1969 }) + ); const logger = customTaskRunnerFactoryInitializerParams.logger; expect(logger.debug).toHaveBeenCalledTimes(5); expect(logger.debug).nthCalledWith(1, 'executing rule test:1 at 1970-01-01T00:00:00.000Z'); expect(logger.debug).nthCalledWith( 2, - `rule test:1: 'rule-name' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` + `rule test:1: '${RULE_NAME}' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` ); expect(logger.debug).nthCalledWith( 3, - `rule test:1: 'rule-name' has 1 recovered alerts: [\"2\"]` + `rule test:1: '${RULE_NAME}' has 1 recovered alerts: [\"2\"]` ); expect(logger.debug).nthCalledWith( 4, @@ -1918,311 +1008,66 @@ describe('Task Runner', () => { const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(6); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "recovered-instance", - "category": Array [ - "alerts", - ], - "duration": 64800000000000, - "end": "1970-01-01T00:00:00.000Z", - "kind": "alert", - "start": "1969-12-31T06:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "instance_id": "2", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' alert '2' has recovered", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "active-instance", - "category": Array [ - "alerts", - ], - "duration": 86400000000000, - "kind": "alert", - "start": "1969-12-31T00:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' active alert: '1' in actionGroup: 'default'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "execute-action", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "recovered", - "instance_id": "2", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - Object { - "id": "2", - "namespace": undefined, - "type": "action", - "type_id": "action", - }, - ], - }, - "message": "alert: test:1: 'rule-name' instanceId: '2' scheduled actionGroup: 'recovered' action: action:2", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "execute-action", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - Object { - "id": "1", - "namespace": undefined, - "type": "action", - "type_id": "action", - }, - ], - }, - "message": "alert: test:1: 'rule-name' instanceId: '1' scheduled actionGroup: 'default' action: action:1", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "success", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "metrics": Object { - "es_search_duration_ms": 33, - "number_of_searches": 3, - "number_of_triggered_actions": 2, - "total_search_duration_ms": 23423, - }, - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "active", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule executed: test:1: 'rule-name'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - ] - `); + + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + action: EVENT_LOG_ACTIONS.recoveredInstance, + duration: 64800000000000, + instanceId: '2', + start: '1969-12-31T06:00:00.000Z', + end: DATE_1970, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 3, + generateEventLog({ + action: EVENT_LOG_ACTIONS.activeInstance, + actionGroupId: 'default', + duration: MOCK_DURATION, + start: DATE_1969, + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 4, + generateEventLog({ + action: EVENT_LOG_ACTIONS.executeAction, + savedObjects: [generateAlertSO('1'), generateActionSO('2')], + actionGroupId: 'recovered', + instanceId: '2', + }) + ); + + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 5, + generateEventLog({ + action: EVENT_LOG_ACTIONS.executeAction, + savedObjects: [generateAlertSO('1'), generateActionSO('1')], + actionGroupId: 'default', + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 6, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'success', + status: 'active', + numberOfTriggeredActions: 2, + task: true, + }) + ); expect(enqueueFunction).toHaveBeenCalledTimes(2); - expect((enqueueFunction as jest.Mock).mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", - "executionId": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - "id": "2", - "params": Object { - "isResolved": true, - }, - "relatedSavedObjects": Array [ - Object { - "id": "1", - "namespace": undefined, - "type": "alert", - "typeId": "test", - }, - ], - "source": Object { - "source": Object { - "id": "1", - "type": "alert", - }, - "type": "SAVED_OBJECT", - }, - "spaceId": undefined, - }, - ] - `); + expect(enqueueFunction).toHaveBeenCalledWith(generateEnqueueFunctionInput()); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); } ); @@ -2273,41 +1118,18 @@ describe('Task Runner', () => { customTaskRunnerFactoryInitializerParams ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: alertId, - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult.state.alertInstances).toMatchInlineSnapshot(` - Object { - "1": Object { - "meta": Object { - "lastScheduledActions": Object { - "date": 1970-01-01T00:00:00.000Z, - "group": "default", - "subgroup": undefined, - }, - }, - "state": Object { - "bar": false, - }, - }, - } - `); + expect(runnerResult.state.alertInstances).toEqual(generateAlertInstance()); const logger = customTaskRunnerFactoryInitializerParams.logger; expect(logger.debug).toHaveBeenCalledWith( - `rule test:${alertId}: 'rule-name' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` + `rule test:${alertId}: '${RULE_NAME}' has 1 active alerts: [{\"instanceId\":\"1\",\"actionGroup\":\"default\"}]` ); expect(logger.debug).nthCalledWith( 3, - `rule test:${alertId}: 'rule-name' has 1 recovered alerts: [\"2\"]` + `rule test:${alertId}: '${RULE_NAME}' has 1 recovered alerts: [\"2\"]` ); expect(logger.debug).nthCalledWith( 4, @@ -2393,64 +1215,14 @@ describe('Task Runner', () => { }, ], }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult.state.alertInstances).toMatchInlineSnapshot(` - Object { - "1": Object { - "meta": Object { - "lastScheduledActions": Object { - "date": 1970-01-01T00:00:00.000Z, - "group": "default", - "subgroup": undefined, - }, - }, - "state": Object { - "bar": false, - }, - }, - } - `); + expect(runnerResult.state.alertInstances).toEqual(generateAlertInstance()); const eventLogger = customTaskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(6); expect(enqueueFunction).toHaveBeenCalledTimes(2); - expect((enqueueFunction as jest.Mock).mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "apiKey": "MTIzOmFiYw==", - "executionId": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - "id": "2", - "params": Object { - "isResolved": true, - }, - "relatedSavedObjects": Array [ - Object { - "id": "1", - "namespace": undefined, - "type": "alert", - "typeId": "test", - }, - ], - "source": Object { - "source": Object { - "id": "1", - "type": "alert", - }, - "type": "SAVED_OBJECT", - }, - "spaceId": undefined, - }, - ] - `); + expect(enqueueFunction).toHaveBeenCalledWith(generateEnqueueFunctionInput()); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); } ); @@ -2481,7 +1253,7 @@ describe('Task Runner', () => { meta: { lastScheduledActions: { group: 'default', date } }, state: { bar: false, - start: '1969-12-31T00:00:00.000Z', + start: DATE_1969, duration: 80000000000, }, }, @@ -2499,222 +1271,56 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult.state.alertInstances).toMatchInlineSnapshot(` - Object { - "1": Object { - "meta": Object { - "lastScheduledActions": Object { - "date": 1970-01-01T00:00:00.000Z, - "group": "default", - "subgroup": undefined, - }, - }, - "state": Object { - "bar": false, - "duration": 86400000000000, - "start": "1969-12-31T00:00:00.000Z", - }, - }, - } - `); + expect(runnerResult.state.alertInstances).toEqual( + generateAlertInstance({ id: 1, duration: MOCK_DURATION, start: DATE_1969 }) + ); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "recovered-instance", - "category": Array [ - "alerts", - ], - "duration": 64800000000000, - "end": "1970-01-01T00:00:00.000Z", - "kind": "alert", - "start": "1969-12-31T06:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "2", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' alert '2' has recovered", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "active-instance", - "category": Array [ - "alerts", - ], - "duration": 86400000000000, - "kind": "alert", - "start": "1969-12-31T00:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' active alert: '1' in actionGroup: 'default'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "success", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "metrics": Object { - "es_search_duration_ms": 33, - "number_of_searches": 3, - "number_of_triggered_actions": 0, - "total_search_duration_ms": 23423, - }, - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "active", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule executed: test:1: 'rule-name'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - ] - `); - expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); - }); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + action: EVENT_LOG_ACTIONS.recoveredInstance, + actionGroupId: 'default', + duration: 64800000000000, + instanceId: '2', + start: '1969-12-31T06:00:00.000Z', + end: DATE_1970, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 3, + generateEventLog({ + action: EVENT_LOG_ACTIONS.activeInstance, + actionGroupId: 'default', + duration: MOCK_DURATION, + start: DATE_1969, + instanceId: '1', + }) + ); + + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 4, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'success', + status: 'active', + numberOfTriggeredActions: 0, + task: true, + }) + ); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); + }); test('validates params before executing the alert type', async () => { const taskRunner = new TaskRunner( @@ -2736,37 +1342,9 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 0, - }, - "history": Array [ - Object { - "success": false, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "10s", - }, - "state": Object {}, - } - `); + expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); expect(taskRunnerFactoryInitializerParams.logger.error).toHaveBeenCalledWith( `Executing Rule foo:test:1 has resulted in Error: params invalid: [param1]: expected value of type [string] but got [undefined]` ); @@ -2780,15 +1358,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); await taskRunner.run(); expect(taskRunnerFactoryInitializerParams.getRulesClientWithRequest).toHaveBeenCalledWith( @@ -2816,12 +1386,8 @@ describe('Task Runner', () => { ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - enabled: true, - }, - references: [], + ...SAVED_OBJECT, + attributes: { enabled: true }, }); await taskRunner.run(); @@ -2853,42 +1419,12 @@ describe('Task Runner', () => { ...mockedRuleTypeSavedObject, schedule: { interval: '30s' }, }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 1, - }, - "history": Array [ - Object { - "success": true, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "30s", - }, - "state": Object { - "alertInstances": Object {}, - "alertTypeState": undefined, - "previousStartedAt": 1970-01-01T00:00:00.000Z, - }, - } - `); + expect(runnerResult).toEqual( + generateRunnerResult({ state: true, interval: '30s', history: [true] }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -2903,7 +1439,7 @@ describe('Task Runner', () => { AlertInstanceContext, string >) => { - throw new Error('OMG'); + throw new Error(GENERIC_ERROR_MESSAGE); } ); @@ -2914,141 +1450,37 @@ describe('Task Runner', () => { ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 0, - }, - "history": Array [ - Object { - "success": false, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "10s", - }, - "state": Object {}, - } - `); - + expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "error": Object { - "message": "OMG", - }, - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "failure", - "reason": "execute", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "error", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution failure: test:1: 'rule-name'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - ] - `); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'failure', + reason: 'execute', + task: true, + status: 'error', + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); test('recovers gracefully when the Alert Task Runner throws an exception when fetching the encrypted attributes', async () => { encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockImplementation(() => { - throw new Error('OMG'); + throw new Error(GENERIC_ERROR_MESSAGE); }); const taskRunner = new TaskRunner( @@ -3061,129 +1493,34 @@ describe('Task Runner', () => { const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 0, - }, - "history": Array [ - Object { - "success": false, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "10s", - }, - "state": Object {}, - } - `); + expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "error": Object { - "message": "OMG", - }, - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "failure", - "reason": "decrypt", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "error", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "test:1: execution failed", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - ] - `); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'failure', + task: true, + reason: 'decrypt', + status: 'error', + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); test('recovers gracefully when the Alert Task Runner throws an exception when license is higher than supported', async () => { ruleTypeRegistry.ensureRuleTypeEnabled.mockImplementation(() => { - throw new Error('OMG'); + throw new Error(GENERIC_ERROR_MESSAGE); }); const taskRunner = new TaskRunner( @@ -3193,141 +1530,38 @@ describe('Task Runner', () => { ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 0, - }, - "history": Array [ - Object { - "success": false, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "10s", - }, - "state": Object {}, - } - `); + expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "error": Object { - "message": "OMG", - }, - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "failure", - "reason": "license", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "error", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "test:1: execution failed", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - ] - `); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'failure', + task: true, + reason: 'license', + status: 'error', + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); test('recovers gracefully when the Alert Task Runner throws an exception when getting internal Services', async () => { taskRunnerFactoryInitializerParams.getRulesClientWithRequest.mockImplementation(() => { - throw new Error('OMG'); + throw new Error(GENERIC_ERROR_MESSAGE); }); const taskRunner = new TaskRunner( @@ -3337,141 +1571,31 @@ describe('Task Runner', () => { ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 0, - }, - "history": Array [ - Object { - "success": false, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "10s", - }, - "state": Object {}, - } - `); + expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "error": Object { - "message": "OMG", - }, - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "failure", - "reason": "unknown", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "error", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "test:1: execution failed", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - ] - `); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'failure', + task: true, + reason: 'unknown', + status: 'error', + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); test('recovers gracefully when the Alert Task Runner throws an exception when fetching attributes', async () => { rulesClient.get.mockImplementation(() => { - throw new Error('OMG'); + throw new Error(GENERIC_ERROR_MESSAGE); }); const taskRunner = new TaskRunner( @@ -3480,141 +1604,31 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 0, - }, - "history": Array [ - Object { - "success": false, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "10s", - }, - "state": Object {}, - } - `); + expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0 })); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "error": Object { - "message": "OMG", - }, - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "failure", - "reason": "read", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "error", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "test:1: execution failed", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - ] - `); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'failure', + task: true, + reason: 'read', + status: 'error', + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); test('recovers gracefully when the Runner of a legacy Alert task which has no schedule throws an exception when fetching attributes', async () => { rulesClient.get.mockImplementation(() => { - throw new Error('OMG'); + throw new Error(GENERIC_ERROR_MESSAGE); }); // legacy alerts used to run by returning a new `runAt` instead of using a schedule @@ -3627,45 +1641,17 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 0, - }, - "history": Array [ - Object { - "success": false, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "5m", - }, - "state": Object {}, - } - `); + expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0, interval: '5m' })); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); test(`doesn't change previousStartedAt when it fails to run`, async () => { const originalAlertSate = { - previousStartedAt: '1970-01-05T00:00:00.000Z', + previousStartedAt: DATE_1970, }; ruleType.executor.mockImplementation( @@ -3678,7 +1664,7 @@ describe('Task Runner', () => { AlertInstanceContext, string >) => { - throw new Error('OMG'); + throw new Error(GENERIC_ERROR_MESSAGE); } ); @@ -3692,15 +1678,7 @@ describe('Task Runner', () => { ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); @@ -3727,19 +1705,11 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const logger = taskRunnerFactoryInitializerParams.logger; return taskRunner.run().catch((ex) => { - expect(ex).toMatchInlineSnapshot(`[Error: Saved object [alert/1] not found]`); + expect(ex.toString()).toEqual(`Error: Saved object [alert/1] not found`); expect(logger.debug).toHaveBeenCalledWith( `Executing Rule foo:test:1 has resulted in Error: Saved object [alert/1] not found` ); @@ -3764,15 +1734,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); expect(runnerResult.schedule!.interval).toEqual(mockedTaskInstance.schedule!.interval); @@ -3794,15 +1756,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); @@ -3826,19 +1780,11 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const logger = taskRunnerFactoryInitializerParams.logger; return taskRunner.run().catch((ex) => { - expect(ex).toMatchInlineSnapshot(`[Error: Saved object [alert/1] not found]`); + expect(ex.toString()).toEqual(`Error: Saved object [alert/1] not found`); expect(logger.debug).toHaveBeenCalledWith( `Executing Rule test space:test:1 has resulted in Error: Saved object [alert/1] not found` ); @@ -3884,287 +1830,70 @@ describe('Task Runner', () => { notifyWhen: 'onActionGroupChange', actions: [], }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(6); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "new-instance", - "category": Array [ - "alerts", - ], - "duration": 0, - "kind": "alert", - "start": "1970-01-01T00:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' created new alert: '1'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "new-instance", - "category": Array [ - "alerts", - ], - "duration": 0, - "kind": "alert", - "start": "1970-01-01T00:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "2", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' created new alert: '2'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "active-instance", - "category": Array [ - "alerts", - ], - "duration": 0, - "kind": "alert", - "start": "1970-01-01T00:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' active alert: '1' in actionGroup: 'default'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "active-instance", - "category": Array [ - "alerts", - ], - "duration": 0, - "kind": "alert", - "start": "1970-01-01T00:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "2", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' active alert: '2' in actionGroup: 'default'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "success", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "metrics": Object { - "es_search_duration_ms": 33, - "number_of_searches": 3, - "number_of_triggered_actions": 0, - "total_search_duration_ms": 23423, - }, - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "active", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule executed: test:1: 'rule-name'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - ] - `); + + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + duration: 0, + start: DATE_1970, + action: EVENT_LOG_ACTIONS.newInstance, + actionGroupId: 'default', + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 3, + generateEventLog({ + duration: 0, + start: DATE_1970, + action: EVENT_LOG_ACTIONS.newInstance, + actionGroupId: 'default', + instanceId: '2', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 4, + generateEventLog({ + duration: 0, + start: DATE_1970, + action: EVENT_LOG_ACTIONS.activeInstance, + actionGroupId: 'default', + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 5, + generateEventLog({ + duration: 0, + start: DATE_1970, + action: EVENT_LOG_ACTIONS.activeInstance, + actionGroupId: 'default', + instanceId: '2', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 6, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'success', + status: 'active', + numberOfTriggeredActions: 0, + task: true, + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -4196,7 +1925,7 @@ describe('Task Runner', () => { meta: {}, state: { bar: false, - start: '1969-12-31T00:00:00.000Z', + start: DATE_1969, duration: 80000000000, }, }, @@ -4218,201 +1947,51 @@ describe('Task Runner', () => { notifyWhen: 'onActionGroupChange', actions: [], }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "active-instance", - "category": Array [ - "alerts", - ], - "duration": 86400000000000, - "kind": "alert", - "start": "1969-12-31T00:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' active alert: '1' in actionGroup: 'default'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "active-instance", - "category": Array [ - "alerts", - ], - "duration": 64800000000000, - "kind": "alert", - "start": "1969-12-31T06:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "2", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' active alert: '2' in actionGroup: 'default'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "success", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "metrics": Object { - "es_search_duration_ms": 33, - "number_of_searches": 3, - "number_of_triggered_actions": 0, - "total_search_duration_ms": 23423, - }, - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "active", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule executed: test:1: 'rule-name'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - ] - `); + + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + action: EVENT_LOG_ACTIONS.activeInstance, + actionGroupId: 'default', + duration: MOCK_DURATION, + start: DATE_1969, + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 3, + generateEventLog({ + action: EVENT_LOG_ACTIONS.activeInstance, + actionGroupId: 'default', + duration: 64800000000000, + start: '1969-12-31T06:00:00.000Z', + instanceId: '2', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 4, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'success', + status: 'active', + numberOfTriggeredActions: 0, + task: true, + }) + ); + expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -4458,197 +2037,45 @@ describe('Task Runner', () => { notifyWhen: 'onActionGroupChange', actions: [], }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "active-instance", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' active alert: '1' in actionGroup: 'default'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "active-instance", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "action_group_id": "default", - "instance_id": "2", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' active alert: '2' in actionGroup: 'default'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "success", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "metrics": Object { - "es_search_duration_ms": 33, - "number_of_searches": 3, - "number_of_triggered_actions": 0, - "total_search_duration_ms": 23423, - }, - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "active", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule executed: test:1: 'rule-name'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - ] - `); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + action: EVENT_LOG_ACTIONS.activeInstance, + actionGroupId: 'default', + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 3, + generateEventLog({ + action: EVENT_LOG_ACTIONS.activeInstance, + actionGroupId: 'default', + instanceId: '2', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 4, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'success', + status: 'active', + numberOfTriggeredActions: 0, + task: true, + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -4667,7 +2094,7 @@ describe('Task Runner', () => { meta: {}, state: { bar: false, - start: '1969-12-31T00:00:00.000Z', + start: DATE_1969, duration: 80000000000, }, }, @@ -4689,201 +2116,50 @@ describe('Task Runner', () => { notifyWhen: 'onActionGroupChange', actions: [], }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "recovered-instance", - "category": Array [ - "alerts", - ], - "duration": 86400000000000, - "end": "1970-01-01T00:00:00.000Z", - "kind": "alert", - "start": "1969-12-31T00:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' alert '1' has recovered", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "recovered-instance", - "category": Array [ - "alerts", - ], - "duration": 64800000000000, - "end": "1970-01-01T00:00:00.000Z", - "kind": "alert", - "start": "1969-12-31T06:00:00.000Z", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "instance_id": "2", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' alert '2' has recovered", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "success", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "metrics": Object { - "es_search_duration_ms": 33, - "number_of_searches": 3, - "number_of_triggered_actions": 0, - "total_search_duration_ms": 23423, - }, - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "ok", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule executed: test:1: 'rule-name'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - ] - `); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + action: EVENT_LOG_ACTIONS.recoveredInstance, + duration: MOCK_DURATION, + start: DATE_1969, + end: DATE_1970, + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 3, + generateEventLog({ + action: EVENT_LOG_ACTIONS.recoveredInstance, + duration: 64800000000000, + start: '1969-12-31T06:00:00.000Z', + end: DATE_1970, + instanceId: '2', + }) + ); + + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 4, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'success', + status: 'ok', + numberOfTriggeredActions: 0, + task: true, + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -4926,195 +2202,44 @@ describe('Task Runner', () => { notifyWhen: 'onActionGroupChange', actions: [], }); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(4); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "recovered-instance", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "instance_id": "1", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' alert '1' has recovered", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "recovered-instance", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "instance_id": "2", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - }, - "message": "test:1: 'rule-name' alert '2' has recovered", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - Array [ - Object { - "event": Object { - "action": "execute", - "category": Array [ - "alerts", - ], - "kind": "alert", - "outcome": "success", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "metrics": Object { - "es_search_duration_ms": 33, - "number_of_searches": 3, - "number_of_triggered_actions": 0, - "total_search_duration_ms": 23423, - }, - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "alerting": Object { - "status": "ok", - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule executed: test:1: 'rule-name'", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "name": "rule-name", - "ruleset": "alerts", - }, - }, - ], - ] - `); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + action: EVENT_LOG_ACTIONS.recoveredInstance, + instanceId: '1', + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 3, + generateEventLog({ + action: EVENT_LOG_ACTIONS.recoveredInstance, + instanceId: '2', + }) + ); + + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 4, + generateEventLog({ + action: EVENT_LOG_ACTIONS.execute, + outcome: 'success', + status: 'ok', + numberOfTriggeredActions: 0, + task: true, + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -5134,65 +2259,25 @@ describe('Task Runner', () => { } ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 1, - }, - "history": Array [ - Object { - "success": true, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "10s", - }, - "state": Object { - "alertInstances": Object {}, - "alertTypeState": undefined, - "previousStartedAt": 1970-01-01T00:00:00.000Z, - }, - } - `); + expect(runnerResult).toEqual(generateRunnerResult({ state: true, history: [true] })); expect(ruleType.executor).toHaveBeenCalledTimes(1); const call = ruleType.executor.mock.calls[0][0]; - expect(call.params).toMatchInlineSnapshot(` - Object { - "bar": true, - } - `); - expect(call.startedAt).toMatchInlineSnapshot(`1970-01-01T00:00:00.000Z`); - expect(call.previousStartedAt).toMatchInlineSnapshot(`1969-12-31T23:55:00.000Z`); - expect(call.state).toMatchInlineSnapshot(`Object {}`); - expect(call.name).toBe('rule-name'); + expect(call.params).toEqual({ bar: true }); + expect(call.startedAt).toEqual(new Date(DATE_1970)); + expect(call.previousStartedAt).toEqual(new Date(DATE_1970_5_MIN)); + expect(call.state).toEqual({}); + expect(call.name).toBe(RULE_NAME); expect(call.tags).toEqual(['rule-', '-tags']); expect(call.createdBy).toBe('rule-creator'); expect(call.updatedBy).toBe('rule-updater'); expect(call.rule).not.toBe(null); - expect(call.rule.name).toBe('rule-name'); + expect(call.rule.name).toBe(RULE_NAME); expect(call.rule.tags).toEqual(['rule-', '-tags']); expect(call.rule.consumer).toBe('bar'); expect(call.rule.enabled).toBe(true); - expect(call.rule.schedule).toMatchInlineSnapshot(` - Object { - "interval": "10s", - } - `); + expect(call.rule.schedule).toEqual({ interval: '10s' }); expect(call.rule.createdBy).toBe('rule-creator'); expect(call.rule.updatedBy).toBe('rule-updater'); expect(call.rule.createdAt).toBe(mockDate); @@ -5202,26 +2287,7 @@ describe('Task Runner', () => { expect(call.rule.producer).toBe('alerts'); expect(call.rule.ruleTypeId).toBe('test'); expect(call.rule.ruleTypeName).toBe('My test rule'); - expect(call.rule.actions).toMatchInlineSnapshot(` - Array [ - Object { - "actionTypeId": "action", - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - Object { - "actionTypeId": "action", - "group": "recovered", - "id": "2", - "params": Object { - "isResolved": true, - }, - }, - ] - `); + expect(call.rule.actions).toEqual(RULE_ACTIONS); expect(call.services.alertFactory.create).toBeTruthy(); expect(call.services.scopedClusterClient).toBeTruthy(); expect(call.services).toBeTruthy(); @@ -5237,75 +2303,16 @@ describe('Task Runner', () => { const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); expect(eventLogger.startTiming).toHaveBeenCalledTimes(1); - expect(eventLogger.logEvent.mock.calls[0][0]).toMatchInlineSnapshot(` - Object { - "event": Object { - "action": "execute-start", - "category": Array [ - "alerts", - ], - "kind": "alert", - }, - "kibana": Object { - "alert": Object { - "rule": Object { - "execution": Object { - "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", - }, - }, - }, - "saved_objects": Array [ - Object { - "id": "1", - "namespace": undefined, - "rel": "primary", - "type": "alert", - "type_id": "test", - }, - ], - "task": Object { - "schedule_delay": 0, - "scheduled": "1970-01-01T00:00:00.000Z", - }, - }, - "message": "rule execution start: \\"1\\"", - "rule": Object { - "category": "test", - "id": "1", - "license": "basic", - "ruleset": "alerts", - }, - } - `); - + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); expect( taskRunnerFactoryInitializerParams.internalSavedObjectsRepository.update - ).toHaveBeenCalledWith( - 'alert', - '1', - { - monitoring: { - execution: { - calculated_metrics: { - success_ratio: 1, - }, - history: [ - { - success: true, - timestamp: 0, - }, - ], - }, - }, - executionStatus: { - error: null, - lastDuration: 0, - lastExecutionDate: '1970-01-01T00:00:00.000Z', - status: 'ok', - }, - }, - { refresh: false, namespace: undefined } - ); + ).toHaveBeenCalledWith(...SAVED_OBJECT_UPDATE_PARAMS); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -5324,13 +2331,8 @@ describe('Task Runner', () => { ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: false, - }, - references: [], + ...SAVED_OBJECT, + attributes: { ...SAVED_OBJECT.attributes, enabled: false }, }); const runnerResult = await taskRunner.run(); expect(runnerResult.state.previousStartedAt?.toISOString()).toBe(state.previousStartedAt); @@ -5338,69 +2340,24 @@ describe('Task Runner', () => { const eventLogger = taskRunnerFactoryInitializerParams.eventLogger; expect(eventLogger.logEvent).toHaveBeenCalledTimes(2); - expect(eventLogger.logEvent.mock.calls[0][0]).toStrictEqual({ - event: { - action: 'execute-start', - kind: 'alert', - category: ['alerts'], - }, - kibana: { - alert: { - rule: { - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - }, - }, - saved_objects: [ - { rel: 'primary', type: 'alert', id: '1', namespace: undefined, type_id: 'test' }, - ], - task: { scheduled: '1970-01-01T00:00:00.000Z', schedule_delay: 0 }, - }, - rule: { - id: '1', - license: 'basic', - category: 'test', - ruleset: 'alerts', - }, - message: 'rule execution start: "1"', - }); - expect(eventLogger.logEvent.mock.calls[1][0]).toStrictEqual({ - event: { - action: 'execute', - kind: 'alert', - category: ['alerts'], - reason: 'disabled', + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 1, + generateEventLog({ + task: true, + action: EVENT_LOG_ACTIONS.executeStart, + }) + ); + expect(eventLogger.logEvent).toHaveBeenNthCalledWith( + 2, + generateEventLog({ + errorMessage: 'Rule failed to execute because rule ran after it was disabled.', + action: EVENT_LOG_ACTIONS.execute, outcome: 'failure', - }, - kibana: { - alert: { - rule: { - execution: { - uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', - }, - }, - }, - saved_objects: [ - { rel: 'primary', type: 'alert', id: '1', namespace: undefined, type_id: 'test' }, - ], - task: { - scheduled: '1970-01-01T00:00:00.000Z', - schedule_delay: 0, - }, - alerting: { status: 'error' }, - }, - rule: { - id: '1', - license: 'basic', - category: 'test', - ruleset: 'alerts', - }, - error: { - message: 'Rule failed to execute because rule ran after it was disabled.', - }, - message: 'test:1: execution failed', - }); + task: true, + reason: 'disabled', + status: 'error', + }) + ); expect(mockUsageCounter.incrementCounter).not.toHaveBeenCalled(); }); @@ -5412,41 +2369,9 @@ describe('Task Runner', () => { ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 1, - }, - "history": Array [ - Object { - "success": true, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "10s", - }, - "state": Object { - "alertInstances": Object {}, - "alertTypeState": undefined, - "previousStartedAt": 1970-01-01T00:00:00.000Z, - }, - } - `); + expect(runnerResult).toEqual(generateRunnerResult({ state: true, history: [true] })); }); test('successfully stores failure runs', async () => { @@ -5456,15 +2381,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce(SAVED_OBJECT); ruleType.executor.mockImplementation( async ({ services: executorServices, @@ -5475,31 +2392,11 @@ describe('Task Runner', () => { AlertInstanceContext, string >) => { - throw new Error('OMG'); + throw new Error(GENERIC_ERROR_MESSAGE); } ); const runnerResult = await taskRunner.run(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 0, - }, - "history": Array [ - Object { - "success": false, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "10s", - }, - "state": Object {}, - } - `); + expect(runnerResult).toEqual(generateRunnerResult({ successRatio: 0, success: false })); }); test('successfully stores the success ratio', async () => { @@ -5509,15 +2406,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); await taskRunner.run(); await taskRunner.run(); await taskRunner.run(); @@ -5532,44 +2421,14 @@ describe('Task Runner', () => { AlertInstanceContext, string >) => { - throw new Error('OMG'); + throw new Error(GENERIC_ERROR_MESSAGE); } ); const runnerResult = await taskRunner.run(); ruleType.executor.mockClear(); - expect(runnerResult).toMatchInlineSnapshot(` - Object { - "monitoring": Object { - "execution": Object { - "calculated_metrics": Object { - "success_ratio": 0.75, - }, - "history": Array [ - Object { - "success": true, - "timestamp": 0, - }, - Object { - "success": true, - "timestamp": 0, - }, - Object { - "success": true, - "timestamp": 0, - }, - Object { - "success": false, - "timestamp": 0, - }, - ], - }, - }, - "schedule": Object { - "interval": "10s", - }, - "state": Object {}, - } - `); + expect(runnerResult).toEqual( + generateRunnerResult({ successRatio: 0.75, history: [true, true, true, false] }) + ); }); test('caps monitoring history at 200', async () => { @@ -5579,15 +2438,7 @@ describe('Task Runner', () => { taskRunnerFactoryInitializerParams ); rulesClient.get.mockResolvedValue(mockedRuleTypeSavedObject); - encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue({ - id: '1', - type: 'alert', - attributes: { - apiKey: Buffer.from('123:abc').toString('base64'), - enabled: true, - }, - references: [], - }); + encryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValue(SAVED_OBJECT); for (let i = 0; i < 300; i++) { await taskRunner.run(); diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index c5651dcf4f57b0..dbc7749a0fbdf6 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -5,7 +5,7 @@ * 2.0. */ import apm from 'elastic-apm-node'; -import { Dictionary, pickBy, mapValues, without, cloneDeep, concat, set, omit } from 'lodash'; +import { pickBy, mapValues, without, cloneDeep, concat, set, omit } from 'lodash'; import type { Request } from '@hapi/hapi'; import { UsageCounter } from 'src/plugins/usage_collection/server'; import uuid from 'uuid'; @@ -38,16 +38,16 @@ import { RawRuleExecutionStatus, AlertAction, RuleExecutionState, + RuleExecutionRunResult, } from '../types'; import { promiseResult, map, Resultable, asOk, asErr, resolveErr } from '../lib/result_type'; import { getExecutionSuccessRatio, getExecutionDurationPercentiles } from '../lib/monitoring'; import { taskInstanceToAlertTaskInstance } from './alert_task_instance'; import { EVENT_LOG_ACTIONS } from '../plugin'; -import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; +import { IEvent, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { isAlertSavedObjectNotFoundError, isEsUnavailableError } from '../lib/is_alerting_error'; import { partiallyUpdateAlert } from '../saved_objects'; import { - ActionGroup, AlertTypeParams, AlertTypeState, AlertInstanceState, @@ -65,6 +65,14 @@ import { import { createAbortableEsClientFactory } from '../lib/create_abortable_es_client_factory'; import { createWrappedScopedClusterClientFactory } from '../lib'; import { getRecoveredAlerts } from '../lib'; +import { + GenerateNewAndRecoveredAlertEventsParams, + LogActiveAndRecoveredAlertsParams, + RuleTaskInstance, + RuleTaskRunResult, + ScheduleActionsForRecoveredAlertsParams, + TrackAlertDurationsParams, +} from './types'; const FALLBACK_RETRY_INTERVAL = '5m'; const CONNECTIVITY_RETRY_INTERVAL = '5m'; @@ -81,22 +89,6 @@ export const getDefaultRuleMonitoring = (): RuleMonitoring => ({ }, }); -interface RuleExecutionRunResult { - state: RuleExecutionState; - monitoring: RuleMonitoring | undefined; - schedule: IntervalSchedule | undefined; -} - -interface RuleTaskRunResult { - state: RuleTaskState; - monitoring: RuleMonitoring | undefined; - schedule: IntervalSchedule | undefined; -} - -interface RuleTaskInstance extends ConcreteTaskInstance { - state: RuleTaskState; -} - export class TaskRunner< Params extends AlertTypeParams, ExtractedParams extends AlertTypeParams, @@ -940,15 +932,6 @@ export class TaskRunner< } } -interface TrackAlertDurationsParams< - InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext -> { - originalAlerts: Dictionary>; - currentAlerts: Dictionary>; - recoveredAlerts: Dictionary>; -} - function trackAlertDurations< InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext @@ -995,34 +978,6 @@ function trackAlertDurations< } } -interface GenerateNewAndRecoveredAlertEventsParams< - InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext -> { - eventLogger: IEventLogger; - executionId: string; - originalAlerts: Dictionary>; - currentAlerts: Dictionary>; - recoveredAlerts: Dictionary>; - ruleId: string; - ruleLabel: string; - namespace: string | undefined; - ruleType: NormalizedRuleType< - AlertTypeParams, - AlertTypeParams, - AlertTypeState, - { - [x: string]: unknown; - }, - { - [x: string]: unknown; - }, - string, - string - >; - rule: SanitizedAlert; -} - function generateNewAndRecoveredAlertEvents< InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext @@ -1144,19 +1099,6 @@ function generateNewAndRecoveredAlertEvents< } } -interface ScheduleActionsForRecoveredAlertsParams< - InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext, - RecoveryActionGroupId extends string -> { - logger: Logger; - recoveryActionGroup: ActionGroup; - recoveredAlerts: Dictionary>; - executionHandler: ExecutionHandler; - mutedAlertIdsSet: Set; - ruleLabel: string; -} - async function scheduleActionsForRecoveredAlerts< InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext, @@ -1200,19 +1142,6 @@ async function scheduleActionsForRecoveredAlerts< return triggeredActions; } -interface LogActiveAndRecoveredAlertsParams< - InstanceState extends AlertInstanceState, - InstanceContext extends AlertInstanceContext, - ActionGroupIds extends string, - RecoveryActionGroupId extends string -> { - logger: Logger; - activeAlerts: Dictionary>; - recoveredAlerts: Dictionary>; - ruleLabel: string; - canSetRecoveryContext: boolean; -} - function logActiveAndRecoveredAlerts< InstanceState extends AlertInstanceState, InstanceContext extends AlertInstanceContext, diff --git a/x-pack/plugins/alerting/server/task_runner/types.ts b/x-pack/plugins/alerting/server/task_runner/types.ts new file mode 100644 index 00000000000000..c14ccfbef32202 --- /dev/null +++ b/x-pack/plugins/alerting/server/task_runner/types.ts @@ -0,0 +1,105 @@ +/* + * 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 { Dictionary } from 'lodash'; +import { Logger } from 'kibana/server'; +import { + ActionGroup, + AlertInstanceContext, + AlertInstanceState, + AlertTypeParams, + AlertTypeState, + IntervalSchedule, + RuleExecutionState, + RuleMonitoring, + RuleTaskState, + SanitizedAlert, +} from '../../common'; +import { ConcreteTaskInstance } from '../../../task_manager/server'; +import { Alert as CreatedAlert } from '../alert'; +import { IEventLogger } from '../../../event_log/server'; +import { NormalizedRuleType } from '../rule_type_registry'; +import { ExecutionHandler } from './create_execution_handler'; + +export interface RuleTaskRunResultWithActions { + state: RuleExecutionState; + monitoring: RuleMonitoring | undefined; + schedule: IntervalSchedule | undefined; +} + +export interface RuleTaskRunResult { + state: RuleTaskState; + monitoring: RuleMonitoring | undefined; + schedule: IntervalSchedule | undefined; +} + +export interface RuleTaskInstance extends ConcreteTaskInstance { + state: RuleTaskState; +} + +export interface TrackAlertDurationsParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { + originalAlerts: Dictionary>; + currentAlerts: Dictionary>; + recoveredAlerts: Dictionary>; +} + +export interface GenerateNewAndRecoveredAlertEventsParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext +> { + eventLogger: IEventLogger; + executionId: string; + originalAlerts: Dictionary>; + currentAlerts: Dictionary>; + recoveredAlerts: Dictionary>; + ruleId: string; + ruleLabel: string; + namespace: string | undefined; + ruleType: NormalizedRuleType< + AlertTypeParams, + AlertTypeParams, + AlertTypeState, + { + [x: string]: unknown; + }, + { + [x: string]: unknown; + }, + string, + string + >; + rule: SanitizedAlert; +} + +export interface ScheduleActionsForRecoveredAlertsParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext, + RecoveryActionGroupId extends string +> { + logger: Logger; + recoveryActionGroup: ActionGroup; + recoveredAlerts: Dictionary>; + executionHandler: ExecutionHandler; + mutedAlertIdsSet: Set; + ruleLabel: string; +} + +export interface LogActiveAndRecoveredAlertsParams< + InstanceState extends AlertInstanceState, + InstanceContext extends AlertInstanceContext, + ActionGroupIds extends string, + RecoveryActionGroupId extends string +> { + logger: Logger; + activeAlerts: Dictionary>; + recoveredAlerts: Dictionary>; + ruleLabel: string; + canSetRecoveryContext: boolean; +} From 0393f3f261b4f53bc2460ac41ba542a78ec5eb4e Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Fri, 25 Feb 2022 00:45:18 +0100 Subject: [PATCH 12/28] [main] update ES client version (#126129) * update renovate bot settings * update es client to 8.1-canary3 * specify type explicitly when pass a serialized object * attempt 1 in fleet.specify type explicitly when pass a serialized object * fix the first batch of ts errors * fix more violations * fix unit test * fix more violations 2 * fix more violations 3 * fix more violations 4 * review comments Co-authored-by: pgayvallet Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 2 +- renovate.json | 6 ++-- .../create_alert_history_index_template.ts | 1 - .../canvas/server/lib/essql_strategy.ts | 2 +- .../server/es/cluster_client_adapter.test.ts | 4 ++- .../server/es/cluster_client_adapter.ts | 3 +- .../epm/elasticsearch/ml_model/install.ts | 21 +++++++++----- .../epm/elasticsearch/transform/remove.ts | 1 - .../elasticsearch/transform/transform.test.ts | 2 ++ .../server/lib/fetch_indices.ts | 2 -- .../license_management/server/lib/license.ts | 1 + .../chart_loader.ts | 14 ++++++---- .../revert_model_snapshot_flyout.tsx | 4 +-- .../hooks/use_index_data.ts | 5 +++- .../new_job/common/job_creator/job_creator.ts | 3 +- .../data_frame_analytics/models_provider.ts | 1 + .../models/data_recognizer/data_recognizer.ts | 5 ++-- .../ml/server/models/job_service/jobs.ts | 3 +- .../memory_overview_service.ts | 2 +- .../server/utils/create_or_update_index.ts | 2 +- .../painless_lab/server/routes/api/execute.ts | 15 +++++++--- .../check_ilm_migration_status.ts | 1 + .../routes/deprecations/deprecations.ts | 6 ++-- .../routes/api/jobs/register_create_route.ts | 1 + .../resource_installer.ts | 3 +- .../server/routes/api/snapshots.ts | 3 +- .../transform_health_service.ts | 6 +++- .../transform/server/routes/api/transforms.ts | 7 ++--- .../lib/reindexing/index_settings.test.ts | 7 +++-- .../server/routes/cloud_backup_status.ts | 1 - .../aggregated_scripted_job.ts | 4 --- .../ml/stack_management_jobs/export_jobs.ts | 3 -- yarn.lock | 28 +++++++++---------- 33 files changed, 94 insertions(+), 75 deletions(-) diff --git a/package.json b/package.json index b72f87f4243e33..5626837a4e48b6 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "@elastic/apm-synthtrace": "link:bazel-bin/packages/elastic-apm-synthtrace", "@elastic/charts": "43.1.1", "@elastic/datemath": "link:bazel-bin/packages/elastic-datemath", - "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.1.0-canary.2", + "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.1.0-canary.3", "@elastic/ems-client": "8.0.0", "@elastic/eui": "48.1.1", "@elastic/filesaver": "1.1.2", diff --git a/renovate.json b/renovate.json index 95358af48a2cc6..9b673a5a9ccf65 100644 --- a/renovate.json +++ b/renovate.json @@ -35,14 +35,14 @@ "matchPackageNames": ["@elastic/elasticsearch"], "reviewers": ["team:kibana-operations", "team:kibana-core"], "matchBaseBranches": ["main"], - "labels": ["release_note:skip", "backport:skip", "Team:Operations", "Team:Core", "v8.1.0"], + "labels": ["release_note:skip", "backport:skip", "Team:Operations", "Team:Core"], "enabled": true }, { "groupName": "@elastic/elasticsearch", "matchPackageNames": ["@elastic/elasticsearch"], "reviewers": ["team:kibana-operations", "team:kibana-core"], - "matchBaseBranches": ["7.16"], + "matchBaseBranches": ["8.1"], "labels": ["release_note:skip", "Team:Operations", "Team:Core", "backport:skip"], "enabled": true }, @@ -50,7 +50,7 @@ "groupName": "@elastic/elasticsearch", "matchPackageNames": ["@elastic/elasticsearch"], "reviewers": ["team:kibana-operations", "team:kibana-core"], - "matchBaseBranches": ["7.15"], + "matchBaseBranches": ["7.17"], "labels": ["release_note:skip", "Team:Operations", "Team:Core", "backport:skip"], "enabled": true }, diff --git a/x-pack/plugins/actions/server/preconfigured_connectors/alert_history_es_index/create_alert_history_index_template.ts b/x-pack/plugins/actions/server/preconfigured_connectors/alert_history_es_index/create_alert_history_index_template.ts index bf3c4ff18e5700..0a1c5037e7f8a1 100644 --- a/x-pack/plugins/actions/server/preconfigured_connectors/alert_history_es_index/create_alert_history_index_template.ts +++ b/x-pack/plugins/actions/server/preconfigured_connectors/alert_history_es_index/create_alert_history_index_template.ts @@ -53,7 +53,6 @@ async function createIndexTemplate({ await client.indices.putIndexTemplate({ name: templateName, body: template, - // @ts-expect-error doesn't exist in @elastic/elasticsearch create: true, }); } catch (err) { diff --git a/x-pack/plugins/canvas/server/lib/essql_strategy.ts b/x-pack/plugins/canvas/server/lib/essql_strategy.ts index 0cc5c8a21121b4..4e69f4831d375c 100644 --- a/x-pack/plugins/canvas/server/lib/essql_strategy.ts +++ b/x-pack/plugins/canvas/server/lib/essql_strategy.ts @@ -32,11 +32,11 @@ export const essqlSearchStrategyProvider = (): ISearchStrategy< format: 'json', body: { query, - // @ts-expect-error `params` missing from `QuerySqlRequest` type params, field_multi_value_leniency: true, time_zone: timezone, fetch_size: count, + // @ts-expect-error `client_id` missing from `QuerySqlRequest` type client_id: 'canvas', filter: { bool: { diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts index 5ff3b6c481d746..22898ac54db5ac 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts @@ -393,7 +393,9 @@ describe('setIndexToHidden', () => { expect(clusterClient.indices.putSettings).toHaveBeenCalledWith({ index: 'foo-bar-000001', body: { - 'index.hidden': true, + index: { + hidden: true, + }, }, }); }); 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 010d162c62ea13..bb958c3ce2b54f 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 @@ -178,7 +178,6 @@ export class ClusterClientAdapter - esClient.ml.putTrainedModel({ - model_id: mlModel.installationName, - defer_definition_decompression: true, - timeout: '45s', - // @ts-expect-error expects an object not a string - body: mlModel.content, - }), + esClient.ml.putTrainedModel( + { + model_id: mlModel.installationName, + defer_definition_decompression: true, + timeout: '45s', + // @ts-expect-error expects an object not a string + body: mlModel.content, + }, + { + headers: { + 'content-type': 'application/json', + }, + } + ), { logger } ); } catch (err) { diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/remove.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/remove.ts index 6a2284e0df742a..07748c1635b3a6 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/remove.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/remove.ts @@ -46,7 +46,6 @@ export const deleteTransforms = async (esClient: ElasticsearchClient, transformI await esClient.transport.request( { method: 'DELETE', - // @ts-expect-error @elastic/elasticsearch Transform is empty interface path: `/${transform?.dest?.index}`, }, { diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform.test.ts index 879c7614fedbf4..4a1909cc813e7a 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/transform/transform.test.ts @@ -106,6 +106,7 @@ describe('test transform install', () => { esClient.transform.getTransform.mockResponseOnce({ count: 1, transforms: [ + // @ts-expect-error incomplete data { dest: { index: 'index', @@ -394,6 +395,7 @@ describe('test transform install', () => { esClient.transform.getTransform.mockResponseOnce({ count: 1, transforms: [ + // @ts-expect-error incomplete data { dest: { index: 'index', diff --git a/x-pack/plugins/index_management/server/lib/fetch_indices.ts b/x-pack/plugins/index_management/server/lib/fetch_indices.ts index 9e8a8b23a7d9d0..cec763a247ed7e 100644 --- a/x-pack/plugins/index_management/server/lib/fetch_indices.ts +++ b/x-pack/plugins/index_management/server/lib/fetch_indices.ts @@ -51,9 +51,7 @@ async function fetchIndicesCall( const indexStats = indicesStats[indexName]; const aliases = Object.keys(indexData.aliases!); return { - // @ts-expect-error new property https://github.com/elastic/elasticsearch-specification/issues/1253 health: indexStats?.health, - // @ts-expect-error new property https://github.com/elastic/elasticsearch-specification/issues/1253 status: indexStats?.status, name: indexName, uuid: indexStats?.uuid, diff --git a/x-pack/plugins/license_management/server/lib/license.ts b/x-pack/plugins/license_management/server/lib/license.ts index 12f831f3d780a3..915d3a8b50a3d9 100644 --- a/x-pack/plugins/license_management/server/lib/license.ts +++ b/x-pack/plugins/license_management/server/lib/license.ts @@ -18,6 +18,7 @@ interface PutLicenseArg { export async function putLicense({ acknowledge, client, licensing, license }: PutLicenseArg) { try { const response = await client.asCurrentUser.license.post({ + // @ts-expect-error license is not typed in LM code body: license, acknowledge, }); diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/chart_loader.ts b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/chart_loader.ts index ad790b75f0454d..790b69f7ebdd0e 100644 --- a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/chart_loader.ts +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/chart_loader.ts @@ -19,7 +19,8 @@ export function chartLoaderProvider(mlResultsService: MlResultsService) { ): Promise { const intervalMs = Math.max( Math.floor( - (job.data_counts.latest_record_timestamp - job.data_counts.earliest_record_timestamp) / bars + (job.data_counts.latest_record_timestamp! - job.data_counts.earliest_record_timestamp!) / + bars ), bucketSpanMs ); @@ -27,8 +28,8 @@ export function chartLoaderProvider(mlResultsService: MlResultsService) { job.datafeed_config.indices.join(), job.datafeed_config.query, job.data_description.time_field!, - job.data_counts.earliest_record_timestamp, - job.data_counts.latest_record_timestamp, + job.data_counts.earliest_record_timestamp!, + job.data_counts.latest_record_timestamp!, intervalMs, job.datafeed_config.runtime_mappings, job.datafeed_config.indices_options @@ -60,15 +61,16 @@ export function chartLoaderProvider(mlResultsService: MlResultsService) { ) { const intervalMs = Math.max( Math.floor( - (job.data_counts.latest_record_timestamp - job.data_counts.earliest_record_timestamp) / bars + (job.data_counts.latest_record_timestamp! - job.data_counts.earliest_record_timestamp!) / + bars ), bucketSpanMs ); const resp = await mlResultsService.getScoresByBucket( [job.job_id], - job.data_counts.earliest_record_timestamp, - job.data_counts.latest_record_timestamp, + job.data_counts.earliest_record_timestamp!, + job.data_counts.latest_record_timestamp!, intervalMs, 1 ); diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx index b6b03f879bb57a..498a27834d050f 100644 --- a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx @@ -231,7 +231,7 @@ export const RevertModelSnapshotFlyout: FC = ({ overlayRanges={[ { start: currentSnapshot.latest_record_time_stamp, - end: job.data_counts.latest_record_timestamp, + end: job.data_counts.latest_record_timestamp!, color: '#ff0000', }, ]} @@ -334,7 +334,7 @@ export const RevertModelSnapshotFlyout: FC = ({ calendarEvents={calendarEvents} setCalendarEvents={setCalendarEvents} minSelectableTimeStamp={snapshot.latest_record_time_stamp} - maxSelectableTimeStamp={job.data_counts.latest_record_timestamp} + maxSelectableTimeStamp={job.data_counts.latest_record_timestamp!} eventRateData={eventRateData} anomalies={anomalies} chartReady={chartReady} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts index 018fb326ba398b..b8b8db4c916aee 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts @@ -44,7 +44,10 @@ interface MLEuiDataGridColumn extends EuiDataGridColumn { function getRuntimeFieldColumns(runtimeMappings: RuntimeMappings) { return Object.keys(runtimeMappings).map((id) => { - const field = runtimeMappings[id]; + let field = runtimeMappings[id]; + if (Array.isArray(field)) { + field = field[0]; + } const schema = getDataGridSchemaFromESFieldType( field.type as estypes.MappingRuntimeField['type'] ); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts index 804a368174c76b..8c5a45137a8a12 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts @@ -262,6 +262,7 @@ export class JobCreator { this._initModelPlotConfig(); this._job_config.model_plot_config!.enabled = enable; } + public get modelPlot() { return ( this._job_config.model_plot_config !== undefined && @@ -737,7 +738,7 @@ export class JobCreator { ({ id, name: id, - type: runtimeField.type, + type: Array.isArray(runtimeField) ? runtimeField[0].type : runtimeField.type, aggregatable: true, aggs: [], runtimeField, diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts index aba12ae93fdec9..c12f611c011f6d 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/models_provider.ts @@ -34,6 +34,7 @@ const NODE_FIELDS = ['attributes', 'name', 'roles', 'version'] as const; export type RequiredNodeFields = Pick; +// @ts-expect-error TrainedModelDeploymentStatsResponse missing properties from MlTrainedModelDeploymentStats interface TrainedModelStatsResponse extends MlTrainedModelStats { deployment_stats?: Omit; model_size_stats?: TrainedModelModelSizeStats; diff --git a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts index dad1ecb7bc4baa..3df5016f560c07 100644 --- a/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts +++ b/x-pack/plugins/ml/server/models/data_recognizer/data_recognizer.ts @@ -603,8 +603,8 @@ export class DataRecognizer { } as JobStat; if (job.data_counts) { - jobStat.earliestTimestampMs = job.data_counts.earliest_record_timestamp; - jobStat.latestTimestampMs = job.data_counts.latest_record_timestamp; + jobStat.earliestTimestampMs = job.data_counts.earliest_record_timestamp!; + jobStat.latestTimestampMs = job.data_counts.latest_record_timestamp!; jobStat.latestResultsTimestampMs = getLatestDataOrBucketTimestamp( jobStat.latestTimestampMs, latestBucketTimestampsByJob[job.job_id] as number @@ -781,6 +781,7 @@ export class DataRecognizer { } private async _saveJob(job: ModuleJob) { + // @ts-expect-error type mismatch on MlPutJobRequest.body return this._mlClient.putJob({ job_id: job.id, body: job.config }); } diff --git a/x-pack/plugins/ml/server/models/job_service/jobs.ts b/x-pack/plugins/ml/server/models/job_service/jobs.ts index 81e1b1f6934493..2f8e17ce142a7a 100644 --- a/x-pack/plugins/ml/server/models/job_service/jobs.ts +++ b/x-pack/plugins/ml/server/models/job_service/jobs.ts @@ -590,7 +590,7 @@ export function jobsProvider( if (body.jobs.length) { const statsForJob = body.jobs[0]; - const time = statsForJob.data_counts.latest_record_timestamp; + const time = statsForJob.data_counts.latest_record_timestamp!; const progress = (time - start) / (end - start); const isJobClosed = statsForJob.state === JOB_STATE.CLOSED; return { @@ -631,6 +631,7 @@ export function jobsProvider( results[job.job_id] = { job: { success: false }, datafeed: { success: false } }; try { + // @ts-expect-error type mismatch on MlPutJobRequest.body await mlClient.putJob({ job_id: job.job_id, body: job }); results[job.job_id].job = { success: true }; } catch (error) { diff --git a/x-pack/plugins/ml/server/models/memory_overview/memory_overview_service.ts b/x-pack/plugins/ml/server/models/memory_overview/memory_overview_service.ts index e7bbc95ded7426..d7f6eb584f7fe0 100644 --- a/x-pack/plugins/ml/server/models/memory_overview/memory_overview_service.ts +++ b/x-pack/plugins/ml/server/models/memory_overview/memory_overview_service.ts @@ -74,7 +74,7 @@ export function memoryOverviewServiceProvider(mlClient: MlClient) { .filter((v) => v.state === 'opened') .map((jobStats) => { return { - node_id: jobStats.node.id, + node_id: jobStats.node!.id, // @ts-expect-error model_bytes can be string | number, cannot sum it with AD_PROCESS_MEMORY_OVERHEAD model_size: jobStats.model_size_stats.model_bytes + AD_PROCESS_MEMORY_OVERHEAD, job_id: jobStats.job_id, diff --git a/x-pack/plugins/observability/server/utils/create_or_update_index.ts b/x-pack/plugins/observability/server/utils/create_or_update_index.ts index a9d583bbf86afd..af7dc670b0be80 100644 --- a/x-pack/plugins/observability/server/utils/create_or_update_index.ts +++ b/x-pack/plugins/observability/server/utils/create_or_update_index.ts @@ -77,7 +77,7 @@ function createNewIndex({ index, body: { // auto_expand_replicas: Allows cluster to not have replicas for this index - settings: { 'index.auto_expand_replicas': '0-1' }, + settings: { index: { auto_expand_replicas: '0-1' } }, mappings, }, }); diff --git a/x-pack/plugins/painless_lab/server/routes/api/execute.ts b/x-pack/plugins/painless_lab/server/routes/api/execute.ts index bc850f9e8043d5..58cb9f4328d297 100644 --- a/x-pack/plugins/painless_lab/server/routes/api/execute.ts +++ b/x-pack/plugins/painless_lab/server/routes/api/execute.ts @@ -26,10 +26,17 @@ export function registerExecuteRoute({ router, license }: RouteDependencies) { try { const client = ctx.core.elasticsearch.client.asCurrentUser; - const response = await client.scriptsPainlessExecute({ - // @ts-expect-error `ExecutePainlessScriptRequest.body` does not allow `string` - body, - }); + const response = await client.scriptsPainlessExecute( + { + // @ts-expect-error `ExecutePainlessScriptRequest.body` does not allow `string` + body, + }, + { + headers: { + 'content-type': 'application/json', + }, + } + ); return res.ok({ body: response, diff --git a/x-pack/plugins/reporting/server/lib/deprecations/check_ilm_migration_status.ts b/x-pack/plugins/reporting/server/lib/deprecations/check_ilm_migration_status.ts index a7b9ecc7dc437c..c3aea64171444c 100644 --- a/x-pack/plugins/reporting/server/lib/deprecations/check_ilm_migration_status.ts +++ b/x-pack/plugins/reporting/server/lib/deprecations/check_ilm_migration_status.ts @@ -29,6 +29,7 @@ export const checkIlmMigrationStatus = async ({ const hasUnmanagedIndices = Object.values(reportingIndicesSettings).some((settings) => { return ( settings?.settings?.index?.lifecycle?.name !== ILM_POLICY_NAME && + // @ts-expect-error index.lifecycle not present on type def settings?.settings?.['index.lifecycle']?.name !== ILM_POLICY_NAME ); }); diff --git a/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts b/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts index b369a5758fcb5d..4c368337cd4822 100644 --- a/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts +++ b/x-pack/plugins/reporting/server/routes/deprecations/deprecations.ts @@ -126,8 +126,10 @@ export const registerDeprecationsRoutes = (reporting: ReportingCore, logger: Log await client.indices.putSettings({ index: indexPattern, body: { - 'index.lifecycle': { - name: ILM_POLICY_NAME, + index: { + lifecycle: { + name: ILM_POLICY_NAME, + }, }, }, }); diff --git a/x-pack/plugins/rollup/server/routes/api/jobs/register_create_route.ts b/x-pack/plugins/rollup/server/routes/api/jobs/register_create_route.ts index a13bf4936a77f5..ca067d08339811 100644 --- a/x-pack/plugins/rollup/server/routes/api/jobs/register_create_route.ts +++ b/x-pack/plugins/rollup/server/routes/api/jobs/register_create_route.ts @@ -35,6 +35,7 @@ export const registerCreateRoute = ({ // Create job. await clusterClient.asCurrentUser.rollup.putJob({ id, + // @ts-expect-error type mismatch on RollupPutJobRequest.body body: rest, }); // Then request the newly created job. diff --git a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts index 2bda23ca3c46f8..8e7d13b0dc210d 100644 --- a/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts +++ b/x-pack/plugins/rule_registry/server/rule_data_plugin_service/resource_installer.ts @@ -309,10 +309,9 @@ export class ResourceInstaller { template: { settings: { hidden: true, + // @ts-expect-error type only defines nested structure 'index.lifecycle': { name: ilmPolicyName, - // TODO: fix the types in the ES package, they don't include rollover_alias??? - // @ts-expect-error rollover_alias: primaryNamespacedAlias, }, 'index.mapping.total_fields.limit': 1700, diff --git a/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.ts b/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.ts index f85c9c33756b71..7425ad0c272c63 100644 --- a/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.ts +++ b/x-pack/plugins/snapshot_restore/server/routes/api/snapshots.ts @@ -127,8 +127,6 @@ export function registerSnapshotsRoutes({ operator: searchOperator, }) : '_all', - // @ts-expect-error @elastic/elasticsearch new API params - // https://github.com/elastic/elasticsearch-specification/issues/845 slm_policy_filter: searchField === 'policyName' ? getSnapshotSearchWildcard({ @@ -139,6 +137,7 @@ export function registerSnapshotsRoutes({ }) : '*,_none', order: sortDirection, + // @ts-expect-error sortField: string is not compatible with SnapshotSnapshotSort type sort: sortField, size: pageSize, offset: pageIndex * pageSize, diff --git a/x-pack/plugins/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.ts b/x-pack/plugins/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.ts index e2512bfac2c554..7aebf83b27cca1 100644 --- a/x-pack/plugins/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.ts +++ b/x-pack/plugins/transform/server/lib/alerting/transform_health_rule_type/transform_health_service.ts @@ -29,7 +29,11 @@ interface TestResult { context: TransformHealthAlertContext; } -type Transform = estypes.Transform & { id: string; description?: string; sync: object }; +type Transform = estypes.TransformGetTransformTransformSummary & { + id: string; + description?: string; + sync: object; +}; type TransformWithAlertingRules = Transform & { alerting_rules: TransformHealthAlertRule[] }; diff --git a/x-pack/plugins/transform/server/routes/api/transforms.ts b/x-pack/plugins/transform/server/routes/api/transforms.ts index 09fab2b45909e9..2f82b9a70389b9 100644 --- a/x-pack/plugins/transform/server/routes/api/transforms.ts +++ b/x-pack/plugins/transform/server/routes/api/transforms.ts @@ -508,12 +508,9 @@ async function deleteTransforms( transform_id: transformId, }); const transformConfig = body.transforms[0]; - // @ts-expect-error @elastic/elasticsearch doesn't provide typings for Transform destinationIndex = Array.isArray(transformConfig.dest.index) - ? // @ts-expect-error @elastic/elasticsearch doesn't provide typings for Transform - transformConfig.dest.index[0] - : // @ts-expect-error @elastic/elasticsearch doesn't provide typings for Transform - transformConfig.dest.index; + ? transformConfig.dest.index[0] + : transformConfig.dest.index; } catch (getTransformConfigError) { transformDeleted.error = getTransformConfigError.meta.body.error; results[transformId] = { diff --git a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts index 0c31a5b8d2fe51..69584dd75de7d7 100644 --- a/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts +++ b/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.test.ts @@ -36,11 +36,11 @@ describe('transformFlatSettings', () => { transformFlatSettings({ settings: { // Settings that should get preserved + // @ts-expect-error @elastic/elasticsearch doesn't declare it 'index.number_of_replicas': '1', 'index.number_of_shards': '5', // Blacklisted settings - // @ts-expect-error @elastic/elasticsearch doesn't declare it 'index.allocation.existing_shards_allocator': 'gateway_allocator', 'index.blocks.write': 'true', 'index.creation_date': '1547052614626', @@ -87,11 +87,11 @@ describe('transformFlatSettings', () => { transformFlatSettings({ settings: { // Settings that should get preserved + // @ts-expect-error @elastic/elasticsearch doesn't declare it 'index.number_of_replicas': '1', 'index.number_of_shards': '5', // Deprecated settings - // @ts-expect-error @elastic/elasticsearch doesn't declare it 'index.soft_deletes.enabled': 'true', 'index.translog.retention.size': '5b', }, @@ -111,11 +111,11 @@ describe('transformFlatSettings', () => { transformFlatSettings({ settings: { // Settings that should get preserved + // @ts-expect-error @elastic/elasticsearch doesn't declare it 'index.number_of_replicas': '1', 'index.number_of_shards': '5', // Deprecated settings - // @ts-expect-error @elastic/elasticsearch doesn't declare it 'index.soft_deletes.enabled': 'true', 'index.translog.retention.age': '5d', }, @@ -245,6 +245,7 @@ describe('transformFlatSettings', () => { expect( getReindexWarnings({ settings: { + // @ts-expect-error @elastic/elasticsearch doesn't declare it 'index.number_of_replicas': '1', }, mappings: {}, diff --git a/x-pack/plugins/upgrade_assistant/server/routes/cloud_backup_status.ts b/x-pack/plugins/upgrade_assistant/server/routes/cloud_backup_status.ts index b757602c6ab530..6dffead8ec91f6 100644 --- a/x-pack/plugins/upgrade_assistant/server/routes/cloud_backup_status.ts +++ b/x-pack/plugins/upgrade_assistant/server/routes/cloud_backup_status.ts @@ -24,7 +24,6 @@ export function registerCloudBackupStatusRoutes({ repository: CLOUD_SNAPSHOT_REPOSITORY, snapshot: '_all', ignore_unavailable: true, // Allow request to succeed even if some snapshots are unavailable. - // @ts-expect-error @elastic/elasticsearch "desc" is a new param order: 'desc', sort: 'start_time', size: 1, diff --git a/x-pack/test/functional/apps/ml/anomaly_detection/aggregated_scripted_job.ts b/x-pack/test/functional/apps/ml/anomaly_detection/aggregated_scripted_job.ts index 4edf87ab8d1fbe..e5b2fb30d4e456 100644 --- a/x-pack/test/functional/apps/ml/anomaly_detection/aggregated_scripted_job.ts +++ b/x-pack/test/functional/apps/ml/anomaly_detection/aggregated_scripted_job.ts @@ -16,7 +16,6 @@ export default function ({ getService }: FtrProviderContext) { const supportedTestSuites = [ { suiteTitle: 'supported job with aggregation field', - // @ts-expect-error not convertable to Job type jobConfig: { job_id: `fq_supported_aggs_${ts}`, job_type: 'anomaly_detector', @@ -103,7 +102,6 @@ export default function ({ getService }: FtrProviderContext) { }, { suiteTitle: 'supported job with scripted field', - // @ts-expect-error not convertable to Job type jobConfig: { job_id: `fq_supported_script_${ts}`, job_type: 'anomaly_detector', @@ -178,7 +176,6 @@ export default function ({ getService }: FtrProviderContext) { const unsupportedTestSuites = [ { suiteTitle: 'unsupported job with bucket_script aggregation field', - // @ts-expect-error not convertable to Job type jobConfig: { job_id: `fq_unsupported_aggs_${ts}`, job_type: 'anomaly_detector', @@ -283,7 +280,6 @@ export default function ({ getService }: FtrProviderContext) { }, { suiteTitle: 'unsupported job with partition by of a scripted field', - // @ts-expect-error not convertable to Job type jobConfig: { job_id: `fq_unsupported_script_${ts}`, job_type: 'anomaly_detector', diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/export_jobs.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/export_jobs.ts index a31b9faa169f98..c3d0ee718cecd7 100644 --- a/x-pack/test/functional/apps/ml/stack_management_jobs/export_jobs.ts +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/export_jobs.ts @@ -11,7 +11,6 @@ import type { DataFrameAnalyticsConfig } from '../../../../../plugins/ml/public/ const testADJobs: Array<{ job: Job; datafeed: Datafeed }> = [ { - // @ts-expect-error not full interface job: { job_id: 'fq_single_1_smv', groups: ['farequote', 'automated', 'single-metric'], @@ -64,7 +63,6 @@ const testADJobs: Array<{ job: Job; datafeed: Datafeed }> = [ }, }, { - // @ts-expect-error not full interface job: { job_id: 'fq_single_2_smv', groups: ['farequote', 'automated', 'single-metric'], @@ -117,7 +115,6 @@ const testADJobs: Array<{ job: Job; datafeed: Datafeed }> = [ }, }, { - // @ts-expect-error not full interface job: { job_id: 'fq_single_3_smv', groups: ['farequote', 'automated', 'single-metric'], diff --git a/yarn.lock b/yarn.lock index ee986fe2f4d321..e68fcf7c1e3c47 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2371,12 +2371,12 @@ dependencies: "@elastic/ecs-helpers" "^1.1.0" -"@elastic/elasticsearch@npm:@elastic/elasticsearch-canary@8.1.0-canary.2": - version "8.1.0-canary.2" - resolved "https://registry.yarnpkg.com/@elastic/elasticsearch-canary/-/elasticsearch-canary-8.1.0-canary.2.tgz#7676b3bdad79a37be4b4ada38f97751314a33a52" - integrity sha512-nmr7yZbvlTqA5SHu/IJZFsU6v14+Y2nx0btMKB9Hjd0vardaibCAdovO9Bp1RPxda2g6XayEkKEzwq5s79xR1g== +"@elastic/elasticsearch@npm:@elastic/elasticsearch-canary@8.1.0-canary.3": + version "8.1.0-canary.3" + resolved "https://registry.yarnpkg.com/@elastic/elasticsearch-canary/-/elasticsearch-canary-8.1.0-canary.3.tgz#a84669ad45ea465e533d860bf99aa55aed781cb3" + integrity sha512-rpsMiJX5sAAlPjfWzZhijQgpu7ZlPwjcJQHCT3wNz03DTDnokLCqkhc8gsU+uqesbQ/GqYUlSL9erCk4GqjOLg== dependencies: - "@elastic/transport" "^8.1.0-beta.1" + "@elastic/transport" "^8.0.2" tslib "^2.3.0" "@elastic/ems-client@8.0.0": @@ -2559,17 +2559,17 @@ ts-node "^10.2.1" typescript "^4.3.5" -"@elastic/transport@^8.1.0-beta.1": - version "8.1.0-beta.1" - resolved "https://registry.yarnpkg.com/@elastic/transport/-/transport-8.1.0-beta.1.tgz#37fde777cf83226f1ea46bf0a22e51a3e43efb85" - integrity sha512-aqncMX86d3r6tNGlve6HEy+NF8XZXetMxDXpplrOAcShL20mHXkMFTJyUyML01tgfkbbgwXnN714YEjin1u1Xg== +"@elastic/transport@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@elastic/transport/-/transport-8.0.2.tgz#715f06c7739516867508108df30c33973ca8e81c" + integrity sha512-OlDz3WO3pKE9vSxW4wV/mn7rYCtBmSsDwxr64h/S1Uc/zrIBXb0iUsRMSkiybXugXhjwyjqG2n1Wc7jjFxrskQ== dependencies: debug "^4.3.2" hpagent "^0.1.2" ms "^2.1.3" secure-json-parse "^2.4.0" tslib "^2.3.0" - undici "^4.7.0" + undici "^4.14.1" "@emotion/babel-plugin-jsx-pragmatic@^0.1.5": version "0.1.5" @@ -29147,10 +29147,10 @@ undertaker@^1.2.1: object.reduce "^1.0.0" undertaker-registry "^1.0.0" -undici@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/undici/-/undici-4.7.0.tgz#3bda286d67bf45d0ab1b94ca6c84e546dcb3b0d4" - integrity sha512-O1q+/EIs4g0HnVMH8colei3qODGiYBLpavWYv3kI+JazBBsBIndnZfUqZ2MEfPJ12H9d56yVdwZG1/nV/xcoSQ== +undici@^4.14.1: + version "4.14.1" + resolved "https://registry.yarnpkg.com/undici/-/undici-4.14.1.tgz#7633b143a8a10d6d63335e00511d071e8d52a1d9" + integrity sha512-WJ+g+XqiZcATcBaUeluCajqy4pEDcQfK1vy+Fo+bC4/mqXI9IIQD/XWHLS70fkGUT6P52Drm7IFslO651OdLPQ== unfetch@^4.2.0: version "4.2.0" From 4155f166f3f1017b5a9fa8c2e2a8679e204ab400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Fri, 25 Feb 2022 01:01:20 +0100 Subject: [PATCH 13/28] Bump backport to 7.3.0 (#126149) --- .github/workflows/backport-next.yml | 27 +++++++++++++++++ package.json | 2 +- yarn.lock | 46 ++++++++++++++++++++++++----- 3 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/backport-next.yml diff --git a/.github/workflows/backport-next.yml b/.github/workflows/backport-next.yml new file mode 100644 index 00000000000000..6779bb42472418 --- /dev/null +++ b/.github/workflows/backport-next.yml @@ -0,0 +1,27 @@ +on: + pull_request_target: + branches: + - main + types: + - labeled + - closed + +jobs: + backport: + name: Backport PR + runs-on: ubuntu-latest + if: | + github.event.pull_request.merged == true + && contains(github.event.pull_request.labels.*.name, 'auto-backport-next') + && ( + (github.event.action == 'labeled' && github.event.label.name == 'auto-backport-next') + || (github.event.action == 'closed') + ) + steps: + - name: Backport Action + uses: sqren/backport-github-action@v7.3.1 + with: + github_token: ${{secrets.KIBANAMACHINE_TOKEN}} + + - name: Backport log + run: cat /home/runner/.backport/backport.log diff --git a/package.json b/package.json index 5626837a4e48b6..c87ce15f455cac 100644 --- a/package.json +++ b/package.json @@ -733,7 +733,7 @@ "babel-plugin-require-context-hook": "^1.0.0", "babel-plugin-styled-components": "^2.0.2", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", - "backport": "7.0.1", + "backport": "^7.3.1", "callsites": "^3.1.0", "chai": "3.5.0", "chance": "1.0.18", diff --git a/yarn.lock b/yarn.lock index e68fcf7c1e3c47..68816f9b172a92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2142,6 +2142,11 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" @@ -9587,10 +9592,10 @@ bach@^1.0.0: async-settle "^1.0.0" now-and-later "^2.0.0" -backport@7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/backport/-/backport-7.0.1.tgz#021f70db76b89699b2c7b826cb3040e9c1d991c9" - integrity sha512-f/7+NDzLFd307c85Tz60cfBzoRd4HlFlNOm3MYFynQwI4igMmKd4J9bFxLgc3KdToaVWDmZ37Gx9nRkYgMfUkA== +backport@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/backport/-/backport-7.3.1.tgz#c5c57e03c87f5883f769e30efc0e7193ce7670f2" + integrity sha512-F1gjJx/pxn9zI74Np6FlTD8ovqeUbzzgzGyBpoYypAdDTJG8Vxt1jcrcwZtRIaSd7McfXCSoQsinlFzO4qlPcA== dependencies: "@octokit/rest" "^18.12.0" axios "^0.25.0" @@ -9608,7 +9613,7 @@ backport@7.0.1: strip-json-comments "^3.1.1" terminal-link "^2.1.1" utility-types "^3.10.0" - winston "^3.5.1" + winston "^3.6.0" yargs "^17.3.1" yargs-parser "^21.0.0" @@ -20101,6 +20106,17 @@ logform@^2.3.2: safe-stable-stringify "^1.1.0" triple-beam "^1.3.0" +logform@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe" + integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw== + dependencies: + "@colors/colors" "1.5.0" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + loglevel@^1.6.8: version "1.6.8" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" @@ -30808,7 +30824,7 @@ windows-release@^3.1.0: dependencies: execa "^1.0.0" -winston-transport@^4.4.2: +winston-transport@^4.4.2, winston-transport@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== @@ -30817,7 +30833,7 @@ winston-transport@^4.4.2: readable-stream "^3.6.0" triple-beam "^1.3.0" -winston@^3.0.0, winston@^3.3.3, winston@^3.5.1: +winston@^3.0.0, winston@^3.3.3: version "3.5.1" resolved "https://registry.yarnpkg.com/winston/-/winston-3.5.1.tgz#b25cc899d015836dbf8c583dec8c4c4483a0da2e" integrity sha512-tbRtVy+vsSSCLcZq/8nXZaOie/S2tPXPFt4be/Q3vI/WtYwm7rrwidxVw2GRa38FIXcJ1kUM6MOZ9Jmnk3F3UA== @@ -30833,6 +30849,22 @@ winston@^3.0.0, winston@^3.3.3, winston@^3.5.1: triple-beam "^1.3.0" winston-transport "^4.4.2" +winston@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.6.0.tgz#be32587a099a292b88c49fac6fa529d478d93fb6" + integrity sha512-9j8T75p+bcN6D00sF/zjFVmPp+t8KMPB1MzbbzYjeN9VWxdsYnTB40TkbNUEXAmILEfChMvAMgidlX64OG3p6w== + dependencies: + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.4.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.5.0" + wkt-parser@^1.2.4: version "1.3.2" resolved "https://registry.yarnpkg.com/wkt-parser/-/wkt-parser-1.3.2.tgz#deeff04a21edc5b170a60da418e9ed1d1ab0e219" From 45a003fa061d6bea1185b484d2259f7fa1c80590 Mon Sep 17 00:00:00 2001 From: Muhammad Ibragimov <53621505+mibragimov@users.noreply.github.com> Date: Fri, 25 Feb 2022 10:03:53 +0500 Subject: [PATCH 14/28] [Console] unskip flaky tests (#124783) * Unskip flaky console tests Co-authored-by: Muhammad Ibragimov Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- test/functional/apps/console/_autocomplete.ts | 62 +++++++++++++++++++ test/functional/apps/console/_console.ts | 34 +--------- test/functional/apps/console/index.js | 4 +- test/functional/page_objects/console_page.ts | 28 ++++++--- 4 files changed, 83 insertions(+), 45 deletions(-) create mode 100644 test/functional/apps/console/_autocomplete.ts diff --git a/test/functional/apps/console/_autocomplete.ts b/test/functional/apps/console/_autocomplete.ts new file mode 100644 index 00000000000000..423440ecdf3f85 --- /dev/null +++ b/test/functional/apps/console/_autocomplete.ts @@ -0,0 +1,62 @@ +/* + * 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. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getService, getPageObjects }: FtrProviderContext) { + const log = getService('log'); + const PageObjects = getPageObjects(['common', 'console']); + + describe('console autocomplete feature', function describeIndexTests() { + this.tags('includeFirefox'); + before(async () => { + log.debug('navigateTo console'); + await PageObjects.common.navigateToApp('console'); + // Ensure that the text area can be interacted with + await PageObjects.console.dismissTutorial(); + }); + + it('should provide basic auto-complete functionality', async () => { + await PageObjects.console.enterRequest(); + await PageObjects.console.promptAutocomplete(); + expect(PageObjects.console.isAutocompleteVisible()).to.be.eql(true); + }); + + describe('with a missing comma in query', () => { + const LINE_NUMBER = 4; + beforeEach(async () => { + await PageObjects.console.clearTextArea(); + await PageObjects.console.enterRequest(); + }); + it('should add a comma after previous non empty line', async () => { + await PageObjects.console.enterText(`{\n\t"query": {\n\t\t"match": {}`); + await PageObjects.console.pressEnter(); + await PageObjects.console.pressEnter(); + await PageObjects.console.pressEnter(); + await PageObjects.console.promptAutocomplete(); + await PageObjects.console.pressEnter(); + + const text = await PageObjects.console.getVisibleTextAt(LINE_NUMBER); + const lastChar = text.charAt(text.length - 1); + expect(lastChar).to.be.eql(','); + }); + + it('should add a comma after the triple quoted strings', async () => { + await PageObjects.console.enterText(`{\n\t"query": {\n\t\t"term": """some data"""`); + await PageObjects.console.pressEnter(); + await PageObjects.console.promptAutocomplete(); + await PageObjects.console.pressEnter(); + + const text = await PageObjects.console.getVisibleTextAt(LINE_NUMBER); + const lastChar = text.charAt(text.length - 1); + expect(lastChar).to.be.eql(','); + }); + }); + }); +} diff --git a/test/functional/apps/console/_console.ts b/test/functional/apps/console/_console.ts index 12d3663ebecbb3..1913a38d7573e1 100644 --- a/test/functional/apps/console/_console.ts +++ b/test/functional/apps/console/_console.ts @@ -27,8 +27,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects(['common', 'console']); const toasts = getService('toasts'); - // FLAKY: https://github.com/elastic/kibana/issues/124104 - describe.skip('console app', function describeIndexTests() { + describe('console app', function describeIndexTests() { this.tags('includeFirefox'); before(async () => { log.debug('navigateTo console'); @@ -82,37 +81,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(initialSize.width).to.be.greaterThan(afterSize.width); }); - it('should provide basic auto-complete functionality', async () => { - // Ensure that the text area can be interacted with - await PageObjects.console.dismissTutorial(); - expect(await PageObjects.console.hasAutocompleter()).to.be(false); - await PageObjects.console.enterRequest(); - await PageObjects.console.promptAutocomplete(); - await retry.waitFor('autocomplete to be visible', () => - PageObjects.console.hasAutocompleter() - ); - }); - - it('should add comma after previous non empty line on autocomplete', async () => { - const LINE_NUMBER = 4; - - await PageObjects.console.dismissTutorial(); - await PageObjects.console.clearTextArea(); - await PageObjects.console.enterRequest(); - - await PageObjects.console.enterText(`{\n\t"query": {\n\t\t"match": {}`); - await PageObjects.console.pressEnter(); - await PageObjects.console.pressEnter(); - await PageObjects.console.pressEnter(); - await PageObjects.console.promptAutocomplete(); - await PageObjects.console.pressEnter(); - - const textOfPreviousNonEmptyLine = await PageObjects.console.getVisibleTextAt(LINE_NUMBER); - log.debug(textOfPreviousNonEmptyLine); - const lastChar = textOfPreviousNonEmptyLine.charAt(textOfPreviousNonEmptyLine.length - 1); - expect(lastChar).to.be.equal(','); - }); - describe('with a data URI in the load_from query', () => { it('loads the data from the URI', async () => { await PageObjects.common.navigateToApp('console', { diff --git a/test/functional/apps/console/index.js b/test/functional/apps/console/index.js index 55f9dffdedb063..7a1fb578b4e4a1 100644 --- a/test/functional/apps/console/index.js +++ b/test/functional/apps/console/index.js @@ -9,8 +9,7 @@ export default function ({ getService, loadTestFile }) { const browser = getService('browser'); - // FLAKY: https://github.com/elastic/kibana/issues/123556 - describe.skip('console app', function () { + describe('console app', function () { this.tags('ciGroup1'); before(async function () { @@ -18,5 +17,6 @@ export default function ({ getService, loadTestFile }) { }); loadTestFile(require.resolve('./_console')); + loadTestFile(require.resolve('./_autocomplete')); }); } diff --git a/test/functional/page_objects/console_page.ts b/test/functional/page_objects/console_page.ts index 4fdc47756e7108..e6450480bbb023 100644 --- a/test/functional/page_objects/console_page.ts +++ b/test/functional/page_objects/console_page.ts @@ -87,14 +87,15 @@ export class ConsolePageObject extends FtrService { const textArea = await this.testSubjects.find('console-textarea'); // There should be autocomplete for this on all license levels await textArea.pressKeys([Key.CONTROL, Key.SPACE]); + await this.retry.waitFor('autocomplete to be visible', () => this.isAutocompleteVisible()); } - public async hasAutocompleter(): Promise { - try { - return Boolean(await this.find.byCssSelector('.ace_autocomplete')); - } catch (e) { - return false; - } + public async isAutocompleteVisible() { + const element = await this.find.byCssSelector('.ace_autocomplete'); + if (!element) return false; + + const attribute = await element.getAttribute('style'); + return !attribute.includes('display: none;'); } public async enterRequest(request: string = '\nGET _search') { @@ -104,8 +105,8 @@ export class ConsolePageObject extends FtrService { } public async enterText(text: string) { - const textArea = await this.getEditorTextArea(); - await textArea.pressKeys(text); + const textArea = await this.testSubjects.find('console-textarea'); + await textArea.type(text); } private async getEditorTextArea() { @@ -138,9 +139,16 @@ export class ConsolePageObject extends FtrService { } public async clearTextArea() { - const textArea = await this.getEditorTextArea(); - await this.retry.try(async () => { + await this.retry.waitForWithTimeout('text area is cleared', 20000, async () => { + const textArea = await this.testSubjects.find('console-textarea'); + await textArea.clickMouseButton(); await textArea.clearValueWithKeyboard(); + + const editor = await this.getEditor(); + const lines = await editor.findAllByClassName('ace_line_group'); + // there should be only one empty line after clearing the textarea + const text = await lines[lines.length - 1].getVisibleText(); + return lines.length === 1 && text.trim() === ''; }); } } From 61fc407e905d5d06196b21398f1c731f1e60ff08 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Fri, 25 Feb 2022 09:56:33 +0200 Subject: [PATCH 15/28] [Cases] Add telemetry to the cases APIs (#125928) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/cases/kibana.json | 3 +- .../cases/server/client/attachments/get.ts | 10 +- x-pack/plugins/cases/server/plugin.ts | 15 +- .../routes/api/cases/alerts/get_cases.ts | 59 ++-- .../server/routes/api/cases/delete_cases.ts | 49 ++- .../server/routes/api/cases/find_cases.ts | 45 ++- .../cases/server/routes/api/cases/get_case.ts | 146 +++++---- .../server/routes/api/cases/patch_cases.ts | 46 ++- .../server/routes/api/cases/post_case.ts | 46 ++- .../server/routes/api/cases/push_case.ts | 61 ++-- .../api/cases/reporters/get_reporters.ts | 42 ++- .../server/routes/api/cases/tags/get_tags.ts | 42 ++- .../api/comments/delete_all_comments.ts | 51 ++-- .../routes/api/comments/delete_comment.ts | 55 ++-- .../routes/api/comments/find_comments.ts | 64 ++-- .../server/routes/api/comments/get_alerts.ts | 51 ++-- .../routes/api/comments/get_all_comment.ts | 70 +++-- .../server/routes/api/comments/get_comment.ts | 57 ++-- .../routes/api/comments/patch_comment.ts | 64 ++-- .../routes/api/comments/post_comment.ts | 58 ++-- .../routes/api/configure/get_configure.ts | 42 ++- .../routes/api/configure/get_connectors.ts | 35 +-- .../routes/api/configure/patch_configure.ts | 51 ++-- .../routes/api/configure/post_configure.ts | 48 ++- .../server/routes/api/create_cases_route.ts | 10 + .../server/routes/api/get_external_routes.ts | 61 ++++ .../plugins/cases/server/routes/api/index.ts | 69 +---- .../routes/api/metrics/get_case_metrics.ts | 58 ++-- .../server/routes/api/register_routes.test.ts | 280 ++++++++++++++++++ .../server/routes/api/register_routes.ts | 88 ++++++ .../server/routes/api/stats/get_status.ts | 56 ++-- .../plugins/cases/server/routes/api/types.ts | 33 ++- .../api/user_actions/get_all_user_actions.ts | 72 +++-- .../plugins/cases/server/routes/api/utils.ts | 4 +- .../cases/server/services/cases/index.ts | 6 +- 35 files changed, 1119 insertions(+), 828 deletions(-) create mode 100644 x-pack/plugins/cases/server/routes/api/create_cases_route.ts create mode 100644 x-pack/plugins/cases/server/routes/api/get_external_routes.ts create mode 100644 x-pack/plugins/cases/server/routes/api/register_routes.test.ts create mode 100644 x-pack/plugins/cases/server/routes/api/register_routes.ts diff --git a/x-pack/plugins/cases/kibana.json b/x-pack/plugins/cases/kibana.json index bf3cc0ee320bd6..170ac2a96aaa8d 100644 --- a/x-pack/plugins/cases/kibana.json +++ b/x-pack/plugins/cases/kibana.json @@ -11,7 +11,8 @@ "kibanaVersion":"kibana", "optionalPlugins":[ "security", - "spaces" + "spaces", + "usageCollection" ], "owner":{ "githubTeam":"response-ops", diff --git a/x-pack/plugins/cases/server/client/attachments/get.ts b/x-pack/plugins/cases/server/client/attachments/get.ts index 4cd620ac5a772f..6e9e924fbee305 100644 --- a/x-pack/plugins/cases/server/client/attachments/get.ts +++ b/x-pack/plugins/cases/server/client/attachments/get.ts @@ -25,7 +25,7 @@ import { getIDsAndIndicesAsArrays, } from '../../common/utils'; import { createCaseError } from '../../common/error'; -import { defaultPage, defaultPerPage } from '../../routes/api'; +import { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '../../routes/api'; import { CasesClientArgs } from '../types'; import { combineFilters, stringToKueryNode } from '../utils'; import { Operations } from '../../authorization'; @@ -170,8 +170,8 @@ export async function find( // We need this because the default behavior of getAllCaseComments is to return all the comments // unless the page and/or perPage is specified. Since we're spreading the query after the request can // still override this behavior. - page: defaultPage, - perPage: defaultPerPage, + page: DEFAULT_PAGE, + perPage: DEFAULT_PER_PAGE, sortField: 'created_at', filter: combinedFilter, ...queryWithoutFilter, @@ -183,8 +183,8 @@ export async function find( unsecuredSavedObjectsClient, id, options: { - page: defaultPage, - perPage: defaultPerPage, + page: DEFAULT_PAGE, + perPage: DEFAULT_PER_PAGE, sortField: 'created_at', filter: combinedFilter, }, diff --git a/x-pack/plugins/cases/server/plugin.ts b/x-pack/plugins/cases/server/plugin.ts index 5881b7b7633be5..e6c4faac939389 100644 --- a/x-pack/plugins/cases/server/plugin.ts +++ b/x-pack/plugins/cases/server/plugin.ts @@ -8,6 +8,7 @@ import { IContextProvider, KibanaRequest, Logger, PluginInitializerContext } from 'kibana/server'; import { CoreSetup, CoreStart } from 'src/core/server'; +import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server'; import { SecurityPluginSetup, SecurityPluginStart } from '../../security/server'; import { PluginSetupContract as ActionsPluginSetup, @@ -15,7 +16,6 @@ import { } from '../../actions/server'; import { APP_ID } from '../common/constants'; -import { initCaseApi } from './routes/api'; import { createCaseCommentSavedObjectType, caseConfigureSavedObjectType, @@ -30,11 +30,14 @@ import { CasesClientFactory } from './client/factory'; import { SpacesPluginStart } from '../../spaces/server'; import { PluginStartContract as FeaturesPluginStart } from '../../features/server'; import { LensServerPluginSetup } from '../../lens/server'; +import { registerRoutes } from './routes/api/register_routes'; +import { getExternalRoutes } from './routes/api/get_external_routes'; export interface PluginsSetup { - security?: SecurityPluginSetup; actions: ActionsPluginSetup; lens: LensServerPluginSetup; + usageCollection?: UsageCollectionSetup; + security?: SecurityPluginSetup; } export interface PluginsStart { @@ -100,10 +103,14 @@ export class CasePlugin { ); const router = core.http.createRouter(); - initCaseApi({ - logger: this.log, + const telemetryUsageCounter = plugins.usageCollection?.createUsageCounter(APP_ID); + + registerRoutes({ router, + routes: getExternalRoutes(), + logger: this.log, kibanaVersion: this.kibanaVersion, + telemetryUsageCounter, }); } diff --git a/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts index 8a490e2f68bd0d..00a368e834a0a2 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/alerts/get_cases.ts @@ -6,42 +6,35 @@ */ import { schema } from '@kbn/config-schema'; -import Boom from '@hapi/boom'; -import { RouteDeps } from '../../types'; -import { escapeHatch, wrapError } from '../../utils'; import { CasesByAlertIDRequest } from '../../../../../common/api'; import { CASE_ALERTS_URL } from '../../../../../common/constants'; +import { createCaseError } from '../../../../common/error'; +import { createCasesRoute } from '../../create_cases_route'; -export function initGetCasesByAlertIdApi({ router, logger }: RouteDeps) { - router.get( - { - path: CASE_ALERTS_URL, - validate: { - params: schema.object({ - alert_id: schema.string(), - }), - query: escapeHatch, - }, - }, - async (context, request, response) => { - try { - const alertID = request.params.alert_id; - if (alertID == null || alertID === '') { - throw Boom.badRequest('The `alertId` is not valid'); - } - const casesClient = await context.cases.getCasesClient(); - const options = request.query as CasesByAlertIDRequest; +export const getCasesByAlertIdRoute = createCasesRoute({ + method: 'get', + path: CASE_ALERTS_URL, + params: { + params: schema.object({ + alert_id: schema.string({ minLength: 1 }), + }), + }, + handler: async ({ context, request, response }) => { + try { + const alertID = request.params.alert_id; - return response.ok({ - body: await casesClient.cases.getCasesByAlertID({ alertID, options }), - }); - } catch (error) { - logger.error( - `Failed to retrieve case ids for this alert id: ${request.params.alert_id}: ${error}` - ); - return response.customError(wrapError(error)); - } + const casesClient = await context.cases.getCasesClient(); + const options = request.query as CasesByAlertIDRequest; + + return response.ok({ + body: await casesClient.cases.getCasesByAlertID({ alertID, options }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to retrieve case ids for this alert id: ${request.params.alert_id}: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts index 1784a434292cc3..a63d07037de01b 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/delete_cases.ts @@ -7,32 +7,31 @@ import { schema } from '@kbn/config-schema'; -import { RouteDeps } from '../types'; -import { wrapError } from '../utils'; import { CASES_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initDeleteCasesApi({ router, logger }: RouteDeps) { - router.delete( - { - path: CASES_URL, - validate: { - query: schema.object({ - ids: schema.arrayOf(schema.string()), - }), - }, - }, - async (context, request, response) => { - try { - const client = await context.cases.getCasesClient(); - await client.cases.delete(request.query.ids); +export const deleteCaseRoute = createCasesRoute({ + method: 'delete', + path: CASES_URL, + params: { + query: schema.object({ + ids: schema.arrayOf(schema.string()), + }), + }, + handler: async ({ context, request, response }) => { + try { + const client = await context.cases.getCasesClient(); + await client.cases.delete(request.query.ids); - return response.noContent(); - } catch (error) { - logger.error( - `Failed to delete cases in route ids: ${JSON.stringify(request.query.ids)}: ${error}` - ); - return response.customError(wrapError(error)); - } + return response.noContent(); + } catch (error) { + throw createCaseError({ + message: `Failed to delete cases in route ids: ${JSON.stringify( + request.query.ids + )}: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts index 8474d781a202a1..711c6909df46c0 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/find_cases.ts @@ -7,32 +7,25 @@ import { CasesFindRequest } from '../../../../common/api'; import { CASES_URL } from '../../../../common/constants'; -import { wrapError, escapeHatch } from '../utils'; -import { RouteDeps } from '../types'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initFindCasesApi({ router, logger }: RouteDeps) { - router.get( - { - path: `${CASES_URL}/_find`, - validate: { - query: escapeHatch, - }, - }, - async (context, request, response) => { - try { - if (!context.cases) { - return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' }); - } - const casesClient = await context.cases.getCasesClient(); - const options = request.query as CasesFindRequest; +export const findCaseRoute = createCasesRoute({ + method: 'get', + path: `${CASES_URL}/_find`, + handler: async ({ context, request, response }) => { + try { + const casesClient = await context.cases.getCasesClient(); + const options = request.query as CasesFindRequest; - return response.ok({ - body: await casesClient.cases.find({ ...options }), - }); - } catch (error) { - logger.error(`Failed to find cases in route: ${error}`); - return response.customError(wrapError(error)); - } + return response.ok({ + body: await casesClient.cases.find({ ...options }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to find cases in route: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/get_case.ts b/x-pack/plugins/cases/server/routes/api/cases/get_case.ts index c8558d09e5c5fd..f0e53e82f14940 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/get_case.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/get_case.ts @@ -7,91 +7,83 @@ import { schema } from '@kbn/config-schema'; -import { RouteDeps } from '../types'; -import { getWarningHeader, logDeprecatedEndpoint, wrapError } from '../utils'; +import { getWarningHeader, logDeprecatedEndpoint } from '../utils'; import { CASE_DETAILS_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initGetCaseApi({ router, logger, kibanaVersion }: RouteDeps) { - router.get( - { - path: CASE_DETAILS_URL, - validate: { - params: schema.object({ - case_id: schema.string(), - }), - query: schema.object({ - /** - * @deprecated since version 8.1.0 - */ - includeComments: schema.boolean({ defaultValue: true }), - }), - }, - }, - async (context, request, response) => { - try { - const isIncludeCommentsParamProvidedByTheUser = - request.url.searchParams.has('includeComments'); - - if (isIncludeCommentsParamProvidedByTheUser) { - logDeprecatedEndpoint( - logger, - request.headers, - `The query parameter 'includeComments' of the get case API '${CASE_DETAILS_URL}' is deprecated` - ); - } +const params = { + params: schema.object({ + case_id: schema.string(), + }), + query: schema.object({ + /** + * @deprecated since version 8.1.0 + */ + includeComments: schema.boolean({ defaultValue: true }), + }), +}; - const casesClient = await context.cases.getCasesClient(); - const id = request.params.case_id; +export const getCaseRoute = createCasesRoute({ + method: 'get', + path: CASE_DETAILS_URL, + params, + handler: async ({ context, request, response, logger, kibanaVersion }) => { + try { + const isIncludeCommentsParamProvidedByTheUser = + request.url.searchParams.has('includeComments'); - return response.ok({ - ...(isIncludeCommentsParamProvidedByTheUser && { - headers: { - ...getWarningHeader(kibanaVersion, 'Deprecated query parameter includeComments'), - }, - }), - body: await casesClient.cases.get({ - id, - includeComments: request.query.includeComments, - }), - }); - } catch (error) { - logger.error( - `Failed to retrieve case in route case id: ${request.params.case_id} \ninclude comments: ${request.query.includeComments}: ${error}` + if (isIncludeCommentsParamProvidedByTheUser) { + logDeprecatedEndpoint( + logger, + request.headers, + `The query parameter 'includeComments' of the get case API '${CASE_DETAILS_URL}' is deprecated` ); - return response.customError(wrapError(error)); } - } - ); - router.get( - { - path: `${CASE_DETAILS_URL}/resolve`, - validate: { - params: schema.object({ - case_id: schema.string(), + const casesClient = await context.cases.getCasesClient(); + const id = request.params.case_id; + + return response.ok({ + ...(isIncludeCommentsParamProvidedByTheUser && { + headers: { + ...getWarningHeader(kibanaVersion, 'Deprecated query parameter includeComments'), + }, }), - query: schema.object({ - includeComments: schema.boolean({ defaultValue: true }), + body: await casesClient.cases.get({ + id, + includeComments: request.query.includeComments, }), - }, - }, - async (context, request, response) => { - try { - const casesClient = await context.cases.getCasesClient(); - const id = request.params.case_id; + }); + } catch (error) { + throw createCaseError({ + message: `Failed to retrieve case in route case id: ${request.params.case_id} \ninclude comments: ${request.query.includeComments}: ${error}`, + error, + }); + } + }, +}); - return response.ok({ - body: await casesClient.cases.resolve({ - id, - includeComments: request.query.includeComments, - }), - }); - } catch (error) { - logger.error( - `Failed to retrieve case in resolve route case id: ${request.params.case_id} \ninclude comments: ${request.query.includeComments}: ${error}` - ); - return response.customError(wrapError(error)); - } +export const resolveCaseRoute = createCasesRoute({ + method: 'get', + path: `${CASE_DETAILS_URL}/resolve`, + params, + handler: async ({ context, request, response }) => { + try { + const casesClient = await context.cases.getCasesClient(); + const id = request.params.case_id; + + return response.ok({ + body: await casesClient.cases.resolve({ + id, + includeComments: request.query.includeComments, + }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to retrieve case in resolve route case id: ${request.params.case_id} \ninclude comments: ${request.query.includeComments}: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts b/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts index 5cde28bcb01f94..c148a45220a74d 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/patch_cases.ts @@ -5,35 +5,27 @@ * 2.0. */ -import { escapeHatch, wrapError } from '../utils'; -import { RouteDeps } from '../types'; import { CasesPatchRequest } from '../../../../common/api'; import { CASES_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initPatchCasesApi({ router, logger }: RouteDeps) { - router.patch( - { - path: CASES_URL, - validate: { - body: escapeHatch, - }, - }, - async (context, request, response) => { - try { - if (!context.cases) { - return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' }); - } +export const patchCaseRoute = createCasesRoute({ + method: 'patch', + path: CASES_URL, + handler: async ({ context, request, response }) => { + try { + const casesClient = await context.cases.getCasesClient(); + const cases = request.body as CasesPatchRequest; - const casesClient = await context.cases.getCasesClient(); - const cases = request.body as CasesPatchRequest; - - return response.ok({ - body: await casesClient.cases.update(cases), - }); - } catch (error) { - logger.error(`Failed to patch cases in route: ${error}`); - return response.customError(wrapError(error)); - } + return response.ok({ + body: await casesClient.cases.update(cases), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to patch cases in route: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/post_case.ts b/x-pack/plugins/cases/server/routes/api/cases/post_case.ts index df994f18c5bbdc..226d0308a3152b 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/post_case.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/post_case.ts @@ -5,35 +5,27 @@ * 2.0. */ -import { wrapError, escapeHatch } from '../utils'; - -import { RouteDeps } from '../types'; import { CasePostRequest } from '../../../../common/api'; import { CASES_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initPostCaseApi({ router, logger }: RouteDeps) { - router.post( - { - path: CASES_URL, - validate: { - body: escapeHatch, - }, - }, - async (context, request, response) => { - try { - if (!context.cases) { - return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' }); - } - const casesClient = await context.cases.getCasesClient(); - const theCase = request.body as CasePostRequest; +export const postCaseRoute = createCasesRoute({ + method: 'post', + path: CASES_URL, + handler: async ({ context, request, response }) => { + try { + const casesClient = await context.cases.getCasesClient(); + const theCase = request.body as CasePostRequest; - return response.ok({ - body: await casesClient.cases.create({ ...theCase }), - }); - } catch (error) { - logger.error(`Failed to post case in route: ${error}`); - return response.customError(wrapError(error)); - } + return response.ok({ + body: await casesClient.cases.create({ ...theCase }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to post case in route: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/push_case.ts b/x-pack/plugins/cases/server/routes/api/cases/push_case.ts index 2b3e7954febfee..175838a9d313c0 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/push_case.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/push_case.ts @@ -10,44 +10,35 @@ import { pipe } from 'fp-ts/lib/pipeable'; import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; -import { wrapError, escapeHatch } from '../utils'; - import { throwErrors, CasePushRequestParamsRt } from '../../../../common/api'; import { CASE_PUSH_URL } from '../../../../common/constants'; -import { RouteDeps } from '../types'; - -export function initPushCaseApi({ router, logger }: RouteDeps) { - router.post( - { - path: CASE_PUSH_URL, - validate: { - params: escapeHatch, - body: escapeHatch, - }, - }, - async (context, request, response) => { - try { - if (!context.cases) { - return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' }); - } +import { CaseRoute } from '../types'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; - const casesClient = await context.cases.getCasesClient(); +export const pushCaseRoute: CaseRoute = createCasesRoute({ + method: 'post', + path: CASE_PUSH_URL, + handler: async ({ context, request, response }) => { + try { + const casesClient = await context.cases.getCasesClient(); - const params = pipe( - CasePushRequestParamsRt.decode(request.params), - fold(throwErrors(Boom.badRequest), identity) - ); + const params = pipe( + CasePushRequestParamsRt.decode(request.params), + fold(throwErrors(Boom.badRequest), identity) + ); - return response.ok({ - body: await casesClient.cases.push({ - caseId: params.case_id, - connectorId: params.connector_id, - }), - }); - } catch (error) { - logger.error(`Failed to push case in route: ${error}`); - return response.customError(wrapError(error)); - } + return response.ok({ + body: await casesClient.cases.push({ + caseId: params.case_id, + connectorId: params.connector_id, + }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to push case in route: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts b/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts index 8e0d0640263ec9..ee413d73565ee1 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/reporters/get_reporters.ts @@ -5,33 +5,25 @@ * 2.0. */ -import { RouteDeps } from '../../types'; -import { wrapError, escapeHatch } from '../../utils'; import { AllReportersFindRequest } from '../../../../../common/api'; import { CASE_REPORTERS_URL } from '../../../../../common/constants'; +import { createCaseError } from '../../../../common/error'; +import { createCasesRoute } from '../../create_cases_route'; -export function initGetReportersApi({ router, logger }: RouteDeps) { - router.get( - { - path: CASE_REPORTERS_URL, - validate: { - query: escapeHatch, - }, - }, - async (context, request, response) => { - try { - if (!context.cases) { - return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' }); - } +export const getReportersRoute = createCasesRoute({ + method: 'get', + path: CASE_REPORTERS_URL, + handler: async ({ context, request, response }) => { + try { + const client = await context.cases.getCasesClient(); + const options = request.query as AllReportersFindRequest; - const client = await context.cases.getCasesClient(); - const options = request.query as AllReportersFindRequest; - - return response.ok({ body: await client.cases.getReporters({ ...options }) }); - } catch (error) { - logger.error(`Failed to get reporters in route: ${error}`); - return response.customError(wrapError(error)); - } + return response.ok({ body: await client.cases.getReporters({ ...options }) }); + } catch (error) { + throw createCaseError({ + message: `Failed to find cases in route: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts b/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts index 2afa96be95bc10..7dfa948aa623cd 100644 --- a/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts +++ b/x-pack/plugins/cases/server/routes/api/cases/tags/get_tags.ts @@ -5,33 +5,25 @@ * 2.0. */ -import { RouteDeps } from '../../types'; -import { wrapError, escapeHatch } from '../../utils'; import { AllTagsFindRequest } from '../../../../../common/api'; import { CASE_TAGS_URL } from '../../../../../common/constants'; +import { createCaseError } from '../../../../common/error'; +import { createCasesRoute } from '../../create_cases_route'; -export function initGetTagsApi({ router, logger }: RouteDeps) { - router.get( - { - path: CASE_TAGS_URL, - validate: { - query: escapeHatch, - }, - }, - async (context, request, response) => { - try { - if (!context.cases) { - return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' }); - } +export const getTagsRoute = createCasesRoute({ + method: 'get', + path: CASE_TAGS_URL, + handler: async ({ context, request, response }) => { + try { + const client = await context.cases.getCasesClient(); + const options = request.query as AllTagsFindRequest; - const client = await context.cases.getCasesClient(); - const options = request.query as AllTagsFindRequest; - - return response.ok({ body: await client.cases.getTags({ ...options }) }); - } catch (error) { - logger.error(`Failed to retrieve tags in route: ${error}`); - return response.customError(wrapError(error)); - } + return response.ok({ body: await client.cases.getTags({ ...options }) }); + } catch (error) { + throw createCaseError({ + message: `Failed to retrieve tags in route: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/comments/delete_all_comments.ts b/x-pack/plugins/cases/server/routes/api/comments/delete_all_comments.ts index d79f90ac43935e..0a1ebd3b66a74a 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/delete_all_comments.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/delete_all_comments.ts @@ -6,35 +6,32 @@ */ import { schema } from '@kbn/config-schema'; -import { RouteDeps } from '../types'; -import { wrapError } from '../utils'; import { CASE_COMMENTS_URL } from '../../../../common/constants'; +import { createCasesRoute } from '../create_cases_route'; +import { createCaseError } from '../../../common/error'; -export function initDeleteAllCommentsApi({ router, logger }: RouteDeps) { - router.delete( - { - path: CASE_COMMENTS_URL, - validate: { - params: schema.object({ - case_id: schema.string(), - }), - }, - }, - async (context, request, response) => { - try { - const client = await context.cases.getCasesClient(); +export const deleteAllCommentsRoute = createCasesRoute({ + method: 'delete', + path: CASE_COMMENTS_URL, + params: { + params: schema.object({ + case_id: schema.string(), + }), + }, + handler: async ({ context, request, response }) => { + try { + const client = await context.cases.getCasesClient(); - await client.attachments.deleteAll({ - caseID: request.params.case_id, - }); + await client.attachments.deleteAll({ + caseID: request.params.case_id, + }); - return response.noContent(); - } catch (error) { - logger.error( - `Failed to delete all comments in route case id: ${request.params.case_id}: ${error}` - ); - return response.customError(wrapError(error)); - } + return response.noContent(); + } catch (error) { + throw createCaseError({ + message: `Failed to delete all comments in route case id: ${request.params.case_id}: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/comments/delete_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/delete_comment.ts index b27be46d7220d1..220fbffc76cc03 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/delete_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/delete_comment.ts @@ -7,36 +7,33 @@ import { schema } from '@kbn/config-schema'; -import { RouteDeps } from '../types'; -import { wrapError } from '../utils'; import { CASE_COMMENT_DETAILS_URL } from '../../../../common/constants'; +import { createCasesRoute } from '../create_cases_route'; +import { createCaseError } from '../../../common/error'; -export function initDeleteCommentApi({ router, logger }: RouteDeps) { - router.delete( - { - path: CASE_COMMENT_DETAILS_URL, - validate: { - params: schema.object({ - case_id: schema.string(), - comment_id: schema.string(), - }), - }, - }, - async (context, request, response) => { - try { - const client = await context.cases.getCasesClient(); - await client.attachments.delete({ - attachmentID: request.params.comment_id, - caseID: request.params.case_id, - }); +export const deleteCommentRoute = createCasesRoute({ + method: 'delete', + path: CASE_COMMENT_DETAILS_URL, + params: { + params: schema.object({ + case_id: schema.string(), + comment_id: schema.string(), + }), + }, + handler: async ({ context, request, response }) => { + try { + const client = await context.cases.getCasesClient(); + await client.attachments.delete({ + attachmentID: request.params.comment_id, + caseID: request.params.case_id, + }); - return response.noContent(); - } catch (error) { - logger.error( - `Failed to delete comment in route case id: ${request.params.case_id} comment id: ${request.params.comment_id}: ${error}` - ); - return response.customError(wrapError(error)); - } + return response.noContent(); + } catch (error) { + throw createCaseError({ + message: `Failed to delete comment in route case id: ${request.params.case_id} comment id: ${request.params.comment_id}: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts b/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts index d4c65e6306a636..14c6090d62ea17 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/find_comments.ts @@ -14,40 +14,36 @@ import { identity } from 'fp-ts/lib/function'; import { FindQueryParamsRt, throwErrors, excess } from '../../../../common/api'; import { CASE_COMMENTS_URL } from '../../../../common/constants'; -import { RouteDeps } from '../types'; -import { escapeHatch, wrapError } from '../utils'; +import { createCasesRoute } from '../create_cases_route'; +import { createCaseError } from '../../../common/error'; -export function initFindCaseCommentsApi({ router, logger }: RouteDeps) { - router.get( - { - path: `${CASE_COMMENTS_URL}/_find`, - validate: { - params: schema.object({ - case_id: schema.string(), - }), - query: escapeHatch, - }, - }, - async (context, request, response) => { - try { - const query = pipe( - excess(FindQueryParamsRt).decode(request.query), - fold(throwErrors(Boom.badRequest), identity) - ); +export const findCommentsRoute = createCasesRoute({ + method: 'get', + path: `${CASE_COMMENTS_URL}/_find`, + params: { + params: schema.object({ + case_id: schema.string(), + }), + }, + handler: async ({ context, request, response }) => { + try { + const query = pipe( + excess(FindQueryParamsRt).decode(request.query), + fold(throwErrors(Boom.badRequest), identity) + ); - const client = await context.cases.getCasesClient(); - return response.ok({ - body: await client.attachments.find({ - caseID: request.params.case_id, - queryParams: query, - }), - }); - } catch (error) { - logger.error( - `Failed to find comments in route case id: ${request.params.case_id}: ${error}` - ); - return response.customError(wrapError(error)); - } + const client = await context.cases.getCasesClient(); + return response.ok({ + body: await client.attachments.find({ + caseID: request.params.case_id, + queryParams: query, + }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to find comments in route case id: ${request.params.case_id}: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts b/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts index 9c0bfac4d9c6e7..4fa793059ed630 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/get_alerts.ts @@ -7,35 +7,32 @@ import { schema } from '@kbn/config-schema'; -import { RouteDeps } from '../types'; -import { wrapError } from '../utils'; import { CASE_DETAILS_ALERTS_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initGetAllAlertsAttachToCaseApi({ router, logger }: RouteDeps) { - router.get( - { - path: CASE_DETAILS_ALERTS_URL, - validate: { - params: schema.object({ - case_id: schema.string({ minLength: 1 }), - }), - }, - }, - async (context, request, response) => { - try { - const caseId = request.params.case_id; +export const getAllAlertsAttachedToCaseRoute = createCasesRoute({ + method: 'get', + path: CASE_DETAILS_ALERTS_URL, + params: { + params: schema.object({ + case_id: schema.string({ minLength: 1 }), + }), + }, + handler: async ({ context, request, response }) => { + try { + const caseId = request.params.case_id; - const casesClient = await context.cases.getCasesClient(); + const casesClient = await context.cases.getCasesClient(); - return response.ok({ - body: await casesClient.attachments.getAllAlertsAttachToCase({ caseId }), - }); - } catch (error) { - logger.error( - `Failed to retrieve alert ids for this case id: ${request.params.case_id}: ${error}` - ); - return response.customError(wrapError(error)); - } + return response.ok({ + body: await casesClient.attachments.getAllAlertsAttachToCase({ caseId }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to retrieve alert ids for this case id: ${request.params.case_id}: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts index e94b19cdd9a1c1..d1e47276af1ab6 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts @@ -7,47 +7,45 @@ import { schema } from '@kbn/config-schema'; -import { RouteDeps } from '../types'; -import { wrapError, getWarningHeader, logDeprecatedEndpoint } from '../utils'; +import { getWarningHeader, logDeprecatedEndpoint } from '../utils'; import { CASE_COMMENTS_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; /** * @deprecated since version 8.1.0 */ -export function initGetAllCommentsApi({ router, logger, kibanaVersion }: RouteDeps) { - router.get( - { - path: CASE_COMMENTS_URL, - validate: { - params: schema.object({ - case_id: schema.string(), - }), - }, - }, - async (context, request, response) => { - try { - logDeprecatedEndpoint( - logger, - request.headers, - `The get all cases comments API '${CASE_COMMENTS_URL}' is deprecated.` - ); +export const getAllCommentsRoute = createCasesRoute({ + method: 'get', + path: CASE_COMMENTS_URL, + params: { + params: schema.object({ + case_id: schema.string(), + }), + }, + handler: async ({ context, request, response, logger, kibanaVersion }) => { + try { + logDeprecatedEndpoint( + logger, + request.headers, + `The get all cases comments API '${CASE_COMMENTS_URL}' is deprecated.` + ); - const client = await context.cases.getCasesClient(); + const client = await context.cases.getCasesClient(); - return response.ok({ - headers: { - ...getWarningHeader(kibanaVersion), - }, - body: await client.attachments.getAll({ - caseID: request.params.case_id, - }), - }); - } catch (error) { - logger.error( - `Failed to get all comments in route case id: ${request.params.case_id}: ${error}` - ); - return response.customError(wrapError(error)); - } + return response.ok({ + headers: { + ...getWarningHeader(kibanaVersion), + }, + body: await client.attachments.getAll({ + caseID: request.params.case_id, + }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to get all comments in route case id: ${request.params.case_id}: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts index 09805c00cb10a0..91adf832f1ea69 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/get_comment.ts @@ -7,37 +7,34 @@ import { schema } from '@kbn/config-schema'; -import { RouteDeps } from '../types'; -import { wrapError } from '../utils'; import { CASE_COMMENT_DETAILS_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initGetCommentApi({ router, logger }: RouteDeps) { - router.get( - { - path: CASE_COMMENT_DETAILS_URL, - validate: { - params: schema.object({ - case_id: schema.string(), - comment_id: schema.string(), - }), - }, - }, - async (context, request, response) => { - try { - const client = await context.cases.getCasesClient(); +export const getCommentRoute = createCasesRoute({ + method: 'get', + path: CASE_COMMENT_DETAILS_URL, + params: { + params: schema.object({ + case_id: schema.string(), + comment_id: schema.string(), + }), + }, + handler: async ({ context, request, response }) => { + try { + const client = await context.cases.getCasesClient(); - return response.ok({ - body: await client.attachments.get({ - attachmentID: request.params.comment_id, - caseID: request.params.case_id, - }), - }); - } catch (error) { - logger.error( - `Failed to get comment in route case id: ${request.params.case_id} comment id: ${request.params.comment_id}: ${error}` - ); - return response.customError(wrapError(error)); - } + return response.ok({ + body: await client.attachments.get({ + attachmentID: request.params.comment_id, + caseID: request.params.case_id, + }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to get comment in route case id: ${request.params.case_id} comment id: ${request.params.comment_id}: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts index 5f9d885178404c..ebc17daa256110 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/patch_comment.ts @@ -11,43 +11,39 @@ import { identity } from 'fp-ts/lib/function'; import { schema } from '@kbn/config-schema'; import Boom from '@hapi/boom'; -import { RouteDeps } from '../types'; -import { escapeHatch, wrapError } from '../utils'; import { CommentPatchRequestRt, throwErrors } from '../../../../common/api'; import { CASE_COMMENTS_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initPatchCommentApi({ router, logger }: RouteDeps) { - router.patch( - { - path: CASE_COMMENTS_URL, - validate: { - params: schema.object({ - case_id: schema.string(), - }), - body: escapeHatch, - }, - }, - async (context, request, response) => { - try { - const query = pipe( - CommentPatchRequestRt.decode(request.body), - fold(throwErrors(Boom.badRequest), identity) - ); +export const patchCommentRoute = createCasesRoute({ + method: 'patch', + path: CASE_COMMENTS_URL, + params: { + params: schema.object({ + case_id: schema.string(), + }), + }, + handler: async ({ context, request, response }) => { + try { + const query = pipe( + CommentPatchRequestRt.decode(request.body), + fold(throwErrors(Boom.badRequest), identity) + ); - const client = await context.cases.getCasesClient(); + const client = await context.cases.getCasesClient(); - return response.ok({ - body: await client.attachments.update({ - caseID: request.params.case_id, - updateRequest: query, - }), - }); - } catch (error) { - logger.error( - `Failed to patch comment in route case id: ${request.params.case_id}: ${error}` - ); - return response.customError(wrapError(error)); - } + return response.ok({ + body: await client.attachments.update({ + caseID: request.params.case_id, + updateRequest: query, + }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to patch comment in route case id: ${request.params.case_id}: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts index ed9c9170084172..1ececb3653741d 100644 --- a/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts +++ b/x-pack/plugins/cases/server/routes/api/comments/post_comment.ts @@ -6,41 +6,33 @@ */ import { schema } from '@kbn/config-schema'; -import { escapeHatch, wrapError } from '../utils'; -import { RouteDeps } from '../types'; import { CASE_COMMENTS_URL } from '../../../../common/constants'; import { CommentRequest } from '../../../../common/api'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initPostCommentApi({ router, logger }: RouteDeps) { - router.post( - { - path: CASE_COMMENTS_URL, - validate: { - params: schema.object({ - case_id: schema.string(), - }), - body: escapeHatch, - }, - }, - async (context, request, response) => { - try { - if (!context.cases) { - return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' }); - } +export const postCommentRoute = createCasesRoute({ + method: 'post', + path: CASE_COMMENTS_URL, + params: { + params: schema.object({ + case_id: schema.string(), + }), + }, + handler: async ({ context, request, response }) => { + try { + const casesClient = await context.cases.getCasesClient(); + const caseId = request.params.case_id; + const comment = request.body as CommentRequest; - const casesClient = await context.cases.getCasesClient(); - const caseId = request.params.case_id; - const comment = request.body as CommentRequest; - - return response.ok({ - body: await casesClient.attachments.add({ caseId, comment }), - }); - } catch (error) { - logger.error( - `Failed to post comment in route case id: ${request.params.case_id}: ${error}` - ); - return response.customError(wrapError(error)); - } + return response.ok({ + body: await casesClient.attachments.add({ caseId, comment }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to post comment in route case id: ${request.params.case_id}: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/configure/get_configure.ts b/x-pack/plugins/cases/server/routes/api/configure/get_configure.ts index 8222ac8fe56909..8dabf7862fc88c 100644 --- a/x-pack/plugins/cases/server/routes/api/configure/get_configure.ts +++ b/x-pack/plugins/cases/server/routes/api/configure/get_configure.ts @@ -5,31 +5,27 @@ * 2.0. */ -import { RouteDeps } from '../types'; -import { escapeHatch, wrapError } from '../utils'; import { CASE_CONFIGURE_URL } from '../../../../common/constants'; import { GetConfigureFindRequest } from '../../../../common/api'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initGetCaseConfigure({ router, logger }: RouteDeps) { - router.get( - { - path: CASE_CONFIGURE_URL, - validate: { - query: escapeHatch, - }, - }, - async (context, request, response) => { - try { - const client = await context.cases.getCasesClient(); - const options = request.query as GetConfigureFindRequest; +export const getCaseConfigureRoute = createCasesRoute({ + method: 'get', + path: CASE_CONFIGURE_URL, + handler: async ({ context, request, response }) => { + try { + const client = await context.cases.getCasesClient(); + const options = request.query as GetConfigureFindRequest; - return response.ok({ - body: await client.configure.get({ ...options }), - }); - } catch (error) { - logger.error(`Failed to get case configure in route: ${error}`); - return response.customError(wrapError(error)); - } + return response.ok({ + body: await client.configure.get({ ...options }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to get case configure in route: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts b/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts index 46c110bbb8ba52..da99cd19065d6a 100644 --- a/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts +++ b/x-pack/plugins/cases/server/routes/api/configure/get_connectors.ts @@ -5,29 +5,26 @@ * 2.0. */ -import { RouteDeps } from '../types'; -import { wrapError } from '../utils'; - import { CASE_CONFIGURE_CONNECTORS_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; /* * Be aware that this api will only return 20 connectors */ -export function initCaseConfigureGetActionConnector({ router, logger }: RouteDeps) { - router.get( - { - path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`, - validate: false, - }, - async (context, request, response) => { - try { - const client = await context.cases.getCasesClient(); +export const getConnectorsRoute = createCasesRoute({ + method: 'get', + path: `${CASE_CONFIGURE_CONNECTORS_URL}/_find`, + handler: async ({ context, response }) => { + try { + const client = await context.cases.getCasesClient(); - return response.ok({ body: await client.configure.getConnectors() }); - } catch (error) { - logger.error(`Failed to get connectors in route: ${error}`); - return response.customError(wrapError(error)); - } + return response.ok({ body: await client.configure.getConnectors() }); + } catch (error) { + throw createCaseError({ + message: `Failed to get connectors in route: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/configure/patch_configure.ts b/x-pack/plugins/cases/server/routes/api/configure/patch_configure.ts index e856a568f387a5..40b0d5123f4294 100644 --- a/x-pack/plugins/cases/server/routes/api/configure/patch_configure.ts +++ b/x-pack/plugins/cases/server/routes/api/configure/patch_configure.ts @@ -17,35 +17,30 @@ import { excess, } from '../../../../common/api'; import { CASE_CONFIGURE_DETAILS_URL } from '../../../../common/constants'; -import { RouteDeps } from '../types'; -import { wrapError, escapeHatch } from '../utils'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initPatchCaseConfigure({ router, logger }: RouteDeps) { - router.patch( - { - path: CASE_CONFIGURE_DETAILS_URL, - validate: { - params: escapeHatch, - body: escapeHatch, - }, - }, - async (context, request, response) => { - try { - const params = pipe( - excess(CaseConfigureRequestParamsRt).decode(request.params), - fold(throwErrors(Boom.badRequest), identity) - ); +export const patchCaseConfigureRoute = createCasesRoute({ + method: 'patch', + path: CASE_CONFIGURE_DETAILS_URL, + handler: async ({ context, request, response }) => { + try { + const params = pipe( + excess(CaseConfigureRequestParamsRt).decode(request.params), + fold(throwErrors(Boom.badRequest), identity) + ); - const client = await context.cases.getCasesClient(); - const configuration = request.body as CasesConfigurePatch; + const client = await context.cases.getCasesClient(); + const configuration = request.body as CasesConfigurePatch; - return response.ok({ - body: await client.configure.update(params.configuration_id, configuration), - }); - } catch (error) { - logger.error(`Failed to get patch configure in route: ${error}`); - return response.customError(wrapError(error)); - } + return response.ok({ + body: await client.configure.update(params.configuration_id, configuration), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to patch configure in route: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/configure/post_configure.ts b/x-pack/plugins/cases/server/routes/api/configure/post_configure.ts index ed4c3529f2ca08..bb64175fb52adf 100644 --- a/x-pack/plugins/cases/server/routes/api/configure/post_configure.ts +++ b/x-pack/plugins/cases/server/routes/api/configure/post_configure.ts @@ -12,33 +12,29 @@ import { identity } from 'fp-ts/lib/function'; import { CasesConfigureRequestRt, throwErrors } from '../../../../common/api'; import { CASE_CONFIGURE_URL } from '../../../../common/constants'; -import { RouteDeps } from '../types'; -import { wrapError, escapeHatch } from '../utils'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initPostCaseConfigure({ router, logger }: RouteDeps) { - router.post( - { - path: CASE_CONFIGURE_URL, - validate: { - body: escapeHatch, - }, - }, - async (context, request, response) => { - try { - const query = pipe( - CasesConfigureRequestRt.decode(request.body), - fold(throwErrors(Boom.badRequest), identity) - ); +export const postCaseConfigureRoute = createCasesRoute({ + method: 'post', + path: CASE_CONFIGURE_URL, + handler: async ({ context, request, response }) => { + try { + const query = pipe( + CasesConfigureRequestRt.decode(request.body), + fold(throwErrors(Boom.badRequest), identity) + ); - const client = await context.cases.getCasesClient(); + const client = await context.cases.getCasesClient(); - return response.ok({ - body: await client.configure.create(query), - }); - } catch (error) { - logger.error(`Failed to post case configure in route: ${error}`); - return response.customError(wrapError(error)); - } + return response.ok({ + body: await client.configure.create(query), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to post case configure in route: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/create_cases_route.ts b/x-pack/plugins/cases/server/routes/api/create_cases_route.ts new file mode 100644 index 00000000000000..eb6a1079440a0c --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/create_cases_route.ts @@ -0,0 +1,10 @@ +/* + * 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 { CaseRoute } from './types'; + +export const createCasesRoute = (route: CaseRoute) => route; diff --git a/x-pack/plugins/cases/server/routes/api/get_external_routes.ts b/x-pack/plugins/cases/server/routes/api/get_external_routes.ts new file mode 100644 index 00000000000000..7908e4eb84359f --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/get_external_routes.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 { getCasesByAlertIdRoute } from './cases/alerts/get_cases'; +import { deleteCaseRoute } from './cases/delete_cases'; +import { findCaseRoute } from './cases/find_cases'; +import { getCaseRoute, resolveCaseRoute } from './cases/get_case'; +import { patchCaseRoute } from './cases/patch_cases'; +import { postCaseRoute } from './cases/post_case'; +import { pushCaseRoute } from './cases/push_case'; +import { getReportersRoute } from './cases/reporters/get_reporters'; +import { getStatusRoute } from './stats/get_status'; +import { getUserActionsRoute } from './user_actions/get_all_user_actions'; +import { CaseRoute } from './types'; +import { getTagsRoute } from './cases/tags/get_tags'; +import { deleteAllCommentsRoute } from './comments/delete_all_comments'; +import { deleteCommentRoute } from './comments/delete_comment'; +import { findCommentsRoute } from './comments/find_comments'; +import { getCommentRoute } from './comments/get_comment'; +import { getAllCommentsRoute } from './comments/get_all_comment'; +import { patchCommentRoute } from './comments/patch_comment'; +import { postCommentRoute } from './comments/post_comment'; +import { getCaseConfigureRoute } from './configure/get_configure'; +import { getConnectorsRoute } from './configure/get_connectors'; +import { patchCaseConfigureRoute } from './configure/patch_configure'; +import { postCaseConfigureRoute } from './configure/post_configure'; +import { getAllAlertsAttachedToCaseRoute } from './comments/get_alerts'; +import { getCaseMetricRoute } from './metrics/get_case_metrics'; + +export const getExternalRoutes = () => + [ + deleteCaseRoute, + findCaseRoute, + getCaseRoute, + resolveCaseRoute, + patchCaseRoute, + postCaseRoute, + pushCaseRoute, + getUserActionsRoute, + getStatusRoute, + getCasesByAlertIdRoute, + getReportersRoute, + getTagsRoute, + deleteCommentRoute, + deleteAllCommentsRoute, + findCommentsRoute, + getCommentRoute, + getAllCommentsRoute, + patchCommentRoute, + postCommentRoute, + getCaseConfigureRoute, + getConnectorsRoute, + patchCaseConfigureRoute, + postCaseConfigureRoute, + getAllAlertsAttachedToCaseRoute, + getCaseMetricRoute, + ] as CaseRoute[]; diff --git a/x-pack/plugins/cases/server/routes/api/index.ts b/x-pack/plugins/cases/server/routes/api/index.ts index 8298f7469f2369..31eafe0f29d28e 100644 --- a/x-pack/plugins/cases/server/routes/api/index.ts +++ b/x-pack/plugins/cases/server/routes/api/index.ts @@ -5,76 +5,11 @@ * 2.0. */ -import { initDeleteCasesApi } from './cases/delete_cases'; -import { initFindCasesApi } from '././cases/find_cases'; -import { initGetCaseApi } from './cases/get_case'; -import { initPatchCasesApi } from './cases/patch_cases'; -import { initPostCaseApi } from './cases/post_case'; -import { initPushCaseApi } from './cases/push_case'; -import { initGetReportersApi } from './cases/reporters/get_reporters'; -import { initGetCasesStatusApi } from './stats/get_status'; -import { initGetTagsApi } from './cases/tags/get_tags'; -import { initGetAllCaseUserActionsApi } from './user_actions/get_all_user_actions'; - -import { initDeleteCommentApi } from './comments/delete_comment'; -import { initDeleteAllCommentsApi } from './comments/delete_all_comments'; -import { initFindCaseCommentsApi } from './comments/find_comments'; -import { initGetAllCommentsApi } from './comments/get_all_comment'; -import { initGetCommentApi } from './comments/get_comment'; -import { initPatchCommentApi } from './comments/patch_comment'; -import { initPostCommentApi } from './comments/post_comment'; - -import { initCaseConfigureGetActionConnector } from './configure/get_connectors'; -import { initGetCaseConfigure } from './configure/get_configure'; -import { initPatchCaseConfigure } from './configure/patch_configure'; -import { initPostCaseConfigure } from './configure/post_configure'; - -import { RouteDeps } from './types'; -import { initGetCasesByAlertIdApi } from './cases/alerts/get_cases'; -import { initGetAllAlertsAttachToCaseApi } from './comments/get_alerts'; -import { initGetCaseMetricsApi } from './metrics/get_case_metrics'; - /** * Default page number when interacting with the saved objects API. */ -export const defaultPage = 1; +export const DEFAULT_PAGE = 1; /** * Default number of results when interacting with the saved objects API. */ -export const defaultPerPage = 20; - -export function initCaseApi(deps: RouteDeps) { - // Cases - initDeleteCasesApi(deps); - initFindCasesApi(deps); - initGetCaseApi(deps); - initPatchCasesApi(deps); - initPostCaseApi(deps); - initPushCaseApi(deps); - initGetAllCaseUserActionsApi(deps); - - // Comments - initDeleteCommentApi(deps); - initDeleteAllCommentsApi(deps); - initFindCaseCommentsApi(deps); - initGetCommentApi(deps); - initGetAllCommentsApi(deps); - initPatchCommentApi(deps); - initPostCommentApi(deps); - // Cases Configure - initCaseConfigureGetActionConnector(deps); - initGetCaseConfigure(deps); - initPatchCaseConfigure(deps); - initPostCaseConfigure(deps); - // Reporters - initGetReportersApi(deps); - // Status - initGetCasesStatusApi(deps); - // Tags - initGetTagsApi(deps); - // Alerts - initGetCasesByAlertIdApi(deps); - initGetAllAlertsAttachToCaseApi(deps); - // Metrics - initGetCaseMetricsApi(deps); -} +export const DEFAULT_PER_PAGE = 20; diff --git a/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts b/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts index 0cfad10b28316e..b86b84410abe62 100644 --- a/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts +++ b/x-pack/plugins/cases/server/routes/api/metrics/get_case_metrics.ts @@ -7,37 +7,35 @@ import { schema } from '@kbn/config-schema'; -import { RouteDeps } from '../types'; -import { wrapError } from '../utils'; - import { CASE_METRICS_DETAILS_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; -export function initGetCaseMetricsApi({ router, logger }: RouteDeps) { - router.get( - { - path: CASE_METRICS_DETAILS_URL, - validate: { - params: schema.object({ - case_id: schema.string({ minLength: 1 }), - }), - query: schema.object({ - features: schema.arrayOf(schema.string({ minLength: 1 })), +export const getCaseMetricRoute = createCasesRoute({ + method: 'get', + path: CASE_METRICS_DETAILS_URL, + params: { + params: schema.object({ + case_id: schema.string({ minLength: 1 }), + }), + query: schema.object({ + features: schema.arrayOf(schema.string({ minLength: 1 })), + }), + }, + handler: async ({ context, request, response }) => { + try { + const client = await context.cases.getCasesClient(); + return response.ok({ + body: await client.metrics.getCaseMetrics({ + caseId: request.params.case_id, + features: request.query.features, }), - }, - }, - async (context, request, response) => { - try { - const client = await context.cases.getCasesClient(); - return response.ok({ - body: await client.metrics.getCaseMetrics({ - caseId: request.params.case_id, - features: request.query.features, - }), - }); - } catch (error) { - logger.error(`Failed to get case metrics in route: ${error}`); - return response.customError(wrapError(error)); - } + }); + } catch (error) { + throw createCaseError({ + message: `Failed to get case metrics in route: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/register_routes.test.ts b/x-pack/plugins/cases/server/routes/api/register_routes.test.ts new file mode 100644 index 00000000000000..71ec6f2bce5cd6 --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/register_routes.test.ts @@ -0,0 +1,280 @@ +/* + * 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 { schema } from '@kbn/config-schema'; + +import { + httpServerMock, + httpServiceMock, + loggingSystemMock, +} from '../../../../../../src/core/server/mocks'; + +import { usageCollectionPluginMock } from '../../../../../../src/plugins/usage_collection/server/mocks'; + +import { CasesRouter } from '../../types'; +import { createCasesRoute } from './create_cases_route'; +import { registerRoutes } from './register_routes'; +import { CaseRoute } from './types'; + +describe('registerRoutes', () => { + let router: jest.Mocked; + const logger = loggingSystemMock.createLogger(); + const response = httpServerMock.createResponseFactory(); + const telemetryUsageCounter = usageCollectionPluginMock + .createSetupContract() + .createUsageCounter('test'); + + const handler = jest.fn(); + const customError = jest.fn(); + const badRequest = jest.fn(); + + const routes = [ + createCasesRoute({ + method: 'get', + path: '/foo/{case_id}', + params: { + params: schema.object({ + case_id: schema.string(), + }), + query: schema.object({ + includeComments: schema.boolean(), + }), + }, + handler, + }), + + createCasesRoute({ + method: 'post', + path: '/bar', + params: { + body: schema.object({ + title: schema.string(), + }), + }, + handler: async () => response.ok(), + }), + createCasesRoute({ + method: 'put', + path: '/baz', + handler: async () => response.ok(), + }), + createCasesRoute({ + method: 'patch', + path: '/qux', + handler: async () => response.ok(), + }), + createCasesRoute({ + method: 'delete', + path: '/quux', + handler: async () => response.ok(), + }), + ] as CaseRoute[]; + + const initApi = (casesRoutes: CaseRoute[]) => { + registerRoutes({ + router, + logger, + routes: casesRoutes, + kibanaVersion: '8.2.0', + telemetryUsageCounter, + }); + + const simulateRequest = async ({ + method, + path, + context = { cases: {} }, + headers = {}, + }: { + method: keyof Pick; + path: string; + context?: Record; + headers?: Record; + }) => { + const [, registeredRouteHandler] = + // @ts-ignore + router[method].mock.calls.find((call) => { + return call[0].path === path; + }) ?? []; + + const result = await registeredRouteHandler( + context, + { headers }, + { customError, badRequest } + ); + return result; + }; + + return { + simulateRequest, + }; + }; + + const initAndSimulateError = async () => { + const { simulateRequest } = initApi([ + ...routes, + createCasesRoute({ + method: 'get', + path: '/error', + handler: async () => { + throw new Error('API error'); + }, + }), + ]); + + await simulateRequest({ + method: 'get', + path: '/error', + }); + }; + + beforeEach(() => { + jest.clearAllMocks(); + router = httpServiceMock.createRouter(); + }); + + describe('api registration', () => { + const endpoints: Array<[CaseRoute['method'], string]> = [ + ['get', '/foo/{case_id}'], + ['post', '/bar'], + ['put', '/baz'], + ['patch', '/qux'], + ['delete', '/quux'], + ]; + + it('registers the endpoints correctly', () => { + initApi(routes); + + for (const endpoint of endpoints) { + const [method, path] = endpoint; + + expect(router[method]).toHaveBeenCalledTimes(1); + expect(router[method]).toBeCalledWith( + { path, validate: expect.anything() }, + expect.anything() + ); + } + }); + }); + + describe('api validation', () => { + const validation: Array< + ['params' | 'query' | 'body', keyof CasesRouter, Record] + > = [ + ['params', 'get', { case_id: '123' }], + ['query', 'get', { includeComments: false }], + ['body', 'post', { title: 'test' }], + ]; + + describe.each(validation)('%s', (type, method, value) => { + it(`validates ${type} correctly`, () => { + initApi(routes); + // @ts-ignore + const params = router[method].mock.calls[0][0].validate[type]; + expect(() => params.validate(value)).not.toThrow(); + }); + + it(`throws if ${type} is wrong`, () => { + initApi(routes); + // @ts-ignore + const params = router[method].mock.calls[0][0].validate[type]; + expect(() => params.validate({})).toThrow(); + }); + + it(`skips path parameter validation if ${type} is not provided`, () => { + initApi(routes); + // @ts-ignore + const params = router.put.mock.calls[0][0].validate[type]; + expect(() => params.validate({})).not.toThrow(); + }); + }); + }); + + describe('handler execution', () => { + it('calls the handler correctly', async () => { + const { simulateRequest } = initApi(routes); + await simulateRequest({ method: 'get', path: '/foo/{case_id}' }); + expect(handler).toHaveBeenCalled(); + }); + }); + + describe('telemetry', () => { + it('increases the counters correctly on a successful kibana request', async () => { + const { simulateRequest } = initApi(routes); + await simulateRequest({ + method: 'get', + path: '/foo/{case_id}', + headers: { 'kbn-version': '8.2.0', referer: 'https://example.com' }, + }); + expect(telemetryUsageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: 'GET /foo/{case_id}', + counterType: 'success', + }); + + expect(telemetryUsageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: 'GET /foo/{case_id}', + counterType: 'kibanaRequest.yes', + }); + }); + + it('increases the counters correctly on a successful non kibana request', async () => { + const { simulateRequest } = initApi(routes); + await simulateRequest({ + method: 'get', + path: '/foo/{case_id}', + }); + expect(telemetryUsageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: 'GET /foo/{case_id}', + counterType: 'success', + }); + + expect(telemetryUsageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: 'GET /foo/{case_id}', + counterType: 'kibanaRequest.no', + }); + }); + + it('increases the counters correctly on an error', async () => { + await initAndSimulateError(); + + expect(telemetryUsageCounter.incrementCounter).toHaveBeenCalledWith({ + counterName: 'GET /error', + counterType: 'error', + }); + }); + }); + + describe('errors', () => { + it('logs the error', async () => { + await initAndSimulateError(); + + expect(logger.error).toBeCalledWith('API error'); + }); + + it('returns an error response', async () => { + await initAndSimulateError(); + + expect(customError).toBeCalledWith({ + body: expect.anything(), + headers: {}, + statusCode: 500, + }); + }); + + it('returns an error response when the case context is not registered', async () => { + const { simulateRequest } = initApi(routes); + await simulateRequest({ + method: 'get', + path: '/foo/{case_id}', + context: {}, + }); + + expect(badRequest).toBeCalledWith({ + body: 'RouteHandlerContext is not registered for cases', + }); + }); + }); +}); diff --git a/x-pack/plugins/cases/server/routes/api/register_routes.ts b/x-pack/plugins/cases/server/routes/api/register_routes.ts new file mode 100644 index 00000000000000..843009f3b22c5a --- /dev/null +++ b/x-pack/plugins/cases/server/routes/api/register_routes.ts @@ -0,0 +1,88 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { RouteRegistrar } from 'kibana/server'; +import { CasesRequestHandlerContext } from '../../types'; +import { CaseRoute, RegisterRoutesDeps } from './types'; +import { escapeHatch, getIsKibanaRequest, wrapError } from './utils'; + +const increaseTelemetryCounters = ({ + telemetryUsageCounter, + method, + path, + isKibanaRequest, + isError = false, +}: { + telemetryUsageCounter: Exclude; + method: string; + path: string; + isKibanaRequest: boolean; + isError?: boolean; +}) => { + const counterName = `${method.toUpperCase()} ${path}`; + + telemetryUsageCounter.incrementCounter({ + counterName, + counterType: isError ? 'error' : 'success', + }); + + telemetryUsageCounter.incrementCounter({ + counterName, + counterType: `kibanaRequest.${isKibanaRequest ? 'yes' : 'no'}`, + }); +}; + +export const registerRoutes = (deps: RegisterRoutesDeps) => { + const { router, routes, logger, kibanaVersion, telemetryUsageCounter } = deps; + + routes.forEach((route) => { + const { method, path, params, handler } = route as CaseRoute; + + (router[method] as RouteRegistrar)( + { + path, + validate: { + params: params?.params ?? escapeHatch, + query: params?.query ?? escapeHatch, + body: params?.body ?? schema.nullable(escapeHatch), + }, + }, + async (context, request, response) => { + const isKibanaRequest = getIsKibanaRequest(request.headers); + + if (!context.cases) { + return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' }); + } + + try { + const res = await handler({ logger, context, request, response, kibanaVersion }); + + if (telemetryUsageCounter) { + increaseTelemetryCounters({ telemetryUsageCounter, method, path, isKibanaRequest }); + } + + return res; + } catch (error) { + logger.error(error.message); + + if (telemetryUsageCounter) { + increaseTelemetryCounters({ + telemetryUsageCounter, + method, + path, + isError: true, + isKibanaRequest, + }); + } + + return response.customError(wrapError(error)); + } + } + ); + }); +}; diff --git a/x-pack/plugins/cases/server/routes/api/stats/get_status.ts b/x-pack/plugins/cases/server/routes/api/stats/get_status.ts index 90044c9516b3a8..4cd5bd7eebd0a6 100644 --- a/x-pack/plugins/cases/server/routes/api/stats/get_status.ts +++ b/x-pack/plugins/cases/server/routes/api/stats/get_status.ts @@ -5,40 +5,40 @@ * 2.0. */ -import { RouteDeps } from '../types'; -import { escapeHatch, wrapError, getWarningHeader, logDeprecatedEndpoint } from '../utils'; +import { CaseRoute } from '../types'; +import { getWarningHeader, logDeprecatedEndpoint } from '../utils'; import { CasesStatusRequest } from '../../../../common/api'; import { CASE_STATUS_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; /** * @deprecated since version 8.1.0 */ -export function initGetCasesStatusApi({ router, logger, kibanaVersion }: RouteDeps) { - router.get( - { - path: CASE_STATUS_URL, - validate: { query: escapeHatch }, - }, - async (context, request, response) => { - try { - logDeprecatedEndpoint( - logger, - request.headers, - `The get cases status API '${CASE_STATUS_URL}' is deprecated.` - ); +export const getStatusRoute: CaseRoute = createCasesRoute({ + method: 'get', + path: CASE_STATUS_URL, + handler: async ({ context, request, response, logger, kibanaVersion }) => { + try { + logDeprecatedEndpoint( + logger, + request.headers, + `The get cases status API '${CASE_STATUS_URL}' is deprecated.` + ); - const client = await context.cases.getCasesClient(); - return response.ok({ - headers: { - ...getWarningHeader(kibanaVersion), - }, - body: await client.metrics.getStatusTotalsByType(request.query as CasesStatusRequest), - }); - } catch (error) { - logger.error(`Failed to get status stats in route: ${error}`); - return response.customError(wrapError(error)); - } + const client = await context.cases.getCasesClient(); + return response.ok({ + headers: { + ...getWarningHeader(kibanaVersion), + }, + body: await client.metrics.getStatusTotalsByType(request.query as CasesStatusRequest), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to get status stats in route: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/types.ts b/x-pack/plugins/cases/server/routes/api/types.ts index e3aa6e0e970fa2..2b1893ebb75c7e 100644 --- a/x-pack/plugins/cases/server/routes/api/types.ts +++ b/x-pack/plugins/cases/server/routes/api/types.ts @@ -5,17 +5,44 @@ * 2.0. */ -import type { Logger, PluginInitializerContext } from 'kibana/server'; +import type { + Logger, + PluginInitializerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, + RouteValidatorConfig, +} from 'kibana/server'; -import type { CasesRouter } from '../../types'; +import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/server'; +import type { CasesRequestHandlerContext, CasesRouter } from '../../types'; -export interface RouteDeps { +type TelemetryUsageCounter = ReturnType; + +export interface RegisterRoutesDeps { router: CasesRouter; + routes: CaseRoute[]; logger: Logger; kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; + telemetryUsageCounter?: TelemetryUsageCounter; } export interface TotalCommentByCase { caseId: string; totalComments: number; } + +interface CaseRouteHandlerArguments { + request: KibanaRequest; + context: CasesRequestHandlerContext; + response: KibanaResponseFactory; + logger: Logger; + kibanaVersion: PluginInitializerContext['env']['packageInfo']['version']; +} + +export interface CaseRoute

{ + method: 'get' | 'post' | 'put' | 'delete' | 'patch'; + path: string; + params?: RouteValidatorConfig; + handler: (args: CaseRouteHandlerArguments) => Promise; +} diff --git a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts index 2e38ac8b4ebc79..b9b6ce43a9fdfd 100644 --- a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts +++ b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts @@ -7,50 +7,44 @@ import { schema } from '@kbn/config-schema'; -import { RouteDeps } from '../types'; -import { getWarningHeader, logDeprecatedEndpoint, wrapError } from '../utils'; +import { getWarningHeader, logDeprecatedEndpoint } from '../utils'; import { CASE_USER_ACTIONS_URL } from '../../../../common/constants'; +import { createCaseError } from '../../../common/error'; +import { createCasesRoute } from '../create_cases_route'; /** * @deprecated since version 8.1.0 */ -export function initGetAllCaseUserActionsApi({ router, logger, kibanaVersion }: RouteDeps) { - router.get( - { - path: CASE_USER_ACTIONS_URL, - validate: { - params: schema.object({ - case_id: schema.string(), - }), - }, - }, - async (context, request, response) => { - try { - if (!context.cases) { - return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' }); - } +export const getUserActionsRoute = createCasesRoute({ + method: 'get', + path: CASE_USER_ACTIONS_URL, + params: { + params: schema.object({ + case_id: schema.string(), + }), + }, + handler: async ({ context, request, response, logger, kibanaVersion }) => { + try { + logDeprecatedEndpoint( + logger, + request.headers, + `The get all cases user actions API '${CASE_USER_ACTIONS_URL}' is deprecated.` + ); - logDeprecatedEndpoint( - logger, - request.headers, - `The get all cases user actions API '${CASE_USER_ACTIONS_URL}' is deprecated.` - ); + const casesClient = await context.cases.getCasesClient(); + const caseId = request.params.case_id; - const casesClient = await context.cases.getCasesClient(); - const caseId = request.params.case_id; - - return response.ok({ - headers: { - ...getWarningHeader(kibanaVersion), - }, - body: await casesClient.userActions.getAll({ caseId }), - }); - } catch (error) { - logger.error( - `Failed to retrieve case user actions in route case id: ${request.params.case_id}: ${error}` - ); - return response.customError(wrapError(error)); - } + return response.ok({ + headers: { + ...getWarningHeader(kibanaVersion), + }, + body: await casesClient.userActions.getAll({ caseId }), + }); + } catch (error) { + throw createCaseError({ + message: `Failed to retrieve case user actions in route case id: ${request.params.case_id}: ${error}`, + error, + }); } - ); -} + }, +}); diff --git a/x-pack/plugins/cases/server/routes/api/utils.ts b/x-pack/plugins/cases/server/routes/api/utils.ts index 532a316e9a7b84..3536e4db346679 100644 --- a/x-pack/plugins/cases/server/routes/api/utils.ts +++ b/x-pack/plugins/cases/server/routes/api/utils.ts @@ -52,10 +52,10 @@ export const getWarningHeader = ( * https://github.com/elastic/kibana/blob/ec30f2aeeb10fb64b507935e558832d3ef5abfaa/x-pack/plugins/spaces/server/usage_stats/usage_stats_client.ts#L113-L118 */ -const getIsKibanaRequest = (headers?: Headers) => { +export const getIsKibanaRequest = (headers?: Headers): boolean => { // The presence of these two request headers gives us a good indication that this is a first-party request from the Kibana client. // We can't be 100% certain, but this is a reasonable attempt. - return headers && headers['kbn-version'] && headers.referer; + return !!(headers && headers['kbn-version'] && headers.referer); }; export const logDeprecatedEndpoint = (logger: Logger, headers: Headers, msg: string) => { diff --git a/x-pack/plugins/cases/server/services/cases/index.ts b/x-pack/plugins/cases/server/services/cases/index.ts index 832d12071b4662..684edcc077f8e2 100644 --- a/x-pack/plugins/cases/server/services/cases/index.ts +++ b/x-pack/plugins/cases/server/services/cases/index.ts @@ -39,7 +39,7 @@ import { } from '../../../common/api'; import { SavedObjectFindOptionsKueryNode } from '../../common/types'; import { defaultSortField, flattenCaseSavedObject } from '../../common/utils'; -import { defaultPage, defaultPerPage } from '../../routes/api'; +import { DEFAULT_PAGE, DEFAULT_PER_PAGE } from '../../routes/api'; import { combineFilters } from '../../client/utils'; import { includeFieldsRequiredForAuthentication } from '../../authorization/utils'; import { @@ -420,8 +420,8 @@ export class CasesService { return { saved_objects: [], total: 0, - per_page: options?.perPage ?? defaultPerPage, - page: options?.page ?? defaultPage, + per_page: options?.perPage ?? DEFAULT_PER_PAGE, + page: options?.page ?? DEFAULT_PAGE, }; } From 5d1ef0e7e58868b32eeb6130b6a94a65cc5ec912 Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Fri, 25 Feb 2022 14:41:58 -0500 Subject: [PATCH 16/28] [Response Ops] Alert search strategy (#124430) * Initial code for search strategy in rule registry for use in triggers actions ui * WIP * More * Bump this up * Add a couple basic tests * More separation * Some api tests * Fix types * fix type * Remove tests * add this back in, not sure why this happened * Remove test code * PR feedback * Fix typing * Fix unit tests * Skip this test due to errors * Add more tests * Use fields api * Add issue link * PR feedback * Fix types and test * Use nested key TS definition Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../plugins/rule_registry/common/constants.ts | 1 + x-pack/plugins/rule_registry/common/index.ts | 4 +- .../common/search_strategy/index.ts | 58 +++++ x-pack/plugins/rule_registry/kibana.json | 2 +- .../server/alert_data_client/alerts_client.ts | 18 +- .../server/lib/get_authz_filter.test.ts | 20 ++ .../server/lib/get_authz_filter.ts | 33 +++ .../server/lib/get_spaces_filter.test.ts | 20 ++ .../server/lib/get_spaces_filter.ts | 11 + .../plugins/rule_registry/server/lib/index.ts | 8 + x-pack/plugins/rule_registry/server/plugin.ts | 25 +++ .../server/search_strategy/index.ts | 8 + .../search_strategy/search_strategy.test.ts | 202 ++++++++++++++++++ .../server/search_strategy/search_strategy.ts | 149 +++++++++++++ x-pack/plugins/rule_registry/tsconfig.json | 1 - .../plugins/triggers_actions_ui/tsconfig.json | 1 + .../security_and_spaces/tests/basic/index.ts | 15 +- .../tests/basic/search_strategy.ts | 138 ++++++++++++ 18 files changed, 691 insertions(+), 23 deletions(-) create mode 100644 x-pack/plugins/rule_registry/common/search_strategy/index.ts create mode 100644 x-pack/plugins/rule_registry/server/lib/get_authz_filter.test.ts create mode 100644 x-pack/plugins/rule_registry/server/lib/get_authz_filter.ts create mode 100644 x-pack/plugins/rule_registry/server/lib/get_spaces_filter.test.ts create mode 100644 x-pack/plugins/rule_registry/server/lib/get_spaces_filter.ts create mode 100644 x-pack/plugins/rule_registry/server/lib/index.ts create mode 100644 x-pack/plugins/rule_registry/server/search_strategy/index.ts create mode 100644 x-pack/plugins/rule_registry/server/search_strategy/search_strategy.test.ts create mode 100644 x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts create mode 100644 x-pack/test/rule_registry/security_and_spaces/tests/basic/search_strategy.ts diff --git a/x-pack/plugins/rule_registry/common/constants.ts b/x-pack/plugins/rule_registry/common/constants.ts index 72793b1087e7b3..1c5fad0e2215fd 100644 --- a/x-pack/plugins/rule_registry/common/constants.ts +++ b/x-pack/plugins/rule_registry/common/constants.ts @@ -6,3 +6,4 @@ */ export const BASE_RAC_ALERTS_API_PATH = '/internal/rac/alerts'; +export const MAX_ALERT_SEARCH_SIZE = 1000; diff --git a/x-pack/plugins/rule_registry/common/index.ts b/x-pack/plugins/rule_registry/common/index.ts index 5d36cd8cad7be2..2dd7f6bbc456e9 100644 --- a/x-pack/plugins/rule_registry/common/index.ts +++ b/x-pack/plugins/rule_registry/common/index.ts @@ -4,4 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -export { parseTechnicalFields } from './parse_technical_fields'; +export { parseTechnicalFields, type ParsedTechnicalFields } from './parse_technical_fields'; +export type { RuleRegistrySearchRequest, RuleRegistrySearchResponse } from './search_strategy'; +export { BASE_RAC_ALERTS_API_PATH } from './constants'; diff --git a/x-pack/plugins/rule_registry/common/search_strategy/index.ts b/x-pack/plugins/rule_registry/common/search_strategy/index.ts new file mode 100644 index 00000000000000..efb8a3478263ea --- /dev/null +++ b/x-pack/plugins/rule_registry/common/search_strategy/index.ts @@ -0,0 +1,58 @@ +/* + * 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 { ValidFeatureId } from '@kbn/rule-data-utils'; +import { Ecs } from 'kibana/server'; +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { IEsSearchRequest, IEsSearchResponse } from 'src/plugins/data/common'; + +export type RuleRegistrySearchRequest = IEsSearchRequest & { + featureIds: ValidFeatureId[]; + query?: { bool: estypes.QueryDslBoolQuery }; +}; + +type Prev = [ + never, + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + ...Array<0> +]; + +type Join = K extends string | number + ? P extends string | number + ? `${K}${'' extends P ? '' : '.'}${P}` + : never + : never; + +type DotNestedKeys = [D] extends [never] + ? never + : T extends object + ? { [K in keyof T]-?: Join> }[keyof T] + : ''; + +type EcsFieldsResponse = { + [Property in DotNestedKeys]: string[]; +}; +export type RuleRegistrySearchResponse = IEsSearchResponse; diff --git a/x-pack/plugins/rule_registry/kibana.json b/x-pack/plugins/rule_registry/kibana.json index 75e0c2c8c0bac4..9603cb0a2640bd 100644 --- a/x-pack/plugins/rule_registry/kibana.json +++ b/x-pack/plugins/rule_registry/kibana.json @@ -8,6 +8,6 @@ "kibanaVersion": "kibana", "configPath": ["xpack", "ruleRegistry"], "requiredPlugins": ["alerting", "data", "triggersActionsUi"], - "optionalPlugins": ["security"], + "optionalPlugins": ["security", "spaces"], "server": true } diff --git a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts index 1f8cfb4b78c858..a97b43332e0a9e 100644 --- a/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts +++ b/x-pack/plugins/rule_registry/server/alert_data_client/alerts_client.ts @@ -21,7 +21,7 @@ import { InlineScript, QueryDslQueryContainer, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { AlertTypeParams, AlertingAuthorizationFilterType } from '../../../alerting/server'; +import { AlertTypeParams } from '../../../alerting/server'; import { ReadOperations, AlertingAuthorization, @@ -39,6 +39,7 @@ import { } from '../../common/technical_rule_data_field_names'; import { ParsedTechnicalFields } from '../../common/parse_technical_fields'; import { Dataset, IRuleDataService } from '../rule_data_plugin_service'; +import { getAuthzFilter, getSpacesFilter } from '../lib'; // TODO: Fix typings https://github.com/elastic/kibana/issues/101776 type NonNullableProps = Omit & { @@ -369,14 +370,8 @@ export class AlertsClient { config: EsQueryConfig ) { try { - const { filter: authzFilter } = await this.authorization.getAuthorizationFilter( - AlertingAuthorizationEntity.Alert, - { - type: AlertingAuthorizationFilterType.ESDSL, - fieldNames: { consumer: ALERT_RULE_CONSUMER, ruleTypeId: ALERT_RULE_TYPE_ID }, - }, - operation - ); + const authzFilter = (await getAuthzFilter(this.authorization, operation)) as Filter; + const spacesFilter = getSpacesFilter(alertSpaceId) as unknown as Filter; let esQuery; if (id != null) { esQuery = { query: `_id:${id}`, language: 'kuery' }; @@ -388,10 +383,7 @@ export class AlertsClient { const builtQuery = buildEsQuery( undefined, esQuery == null ? { query: ``, language: 'kuery' } : esQuery, - [ - authzFilter as unknown as Filter, - { query: { term: { [SPACE_IDS]: alertSpaceId } } } as unknown as Filter, - ], + [authzFilter, spacesFilter], config ); if (query != null && typeof query === 'object') { diff --git a/x-pack/plugins/rule_registry/server/lib/get_authz_filter.test.ts b/x-pack/plugins/rule_registry/server/lib/get_authz_filter.test.ts new file mode 100644 index 00000000000000..3b79c7a5bad8a7 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/lib/get_authz_filter.test.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 { alertingAuthorizationMock } from '../../../alerting/server/authorization/alerting_authorization.mock'; +import { ReadOperations } from '../../../alerting/server'; +import { getAuthzFilter } from './get_authz_filter'; + +describe('getAuthzFilter()', () => { + it('should call `getAuthorizationFilter`', async () => { + const authorization = alertingAuthorizationMock.create(); + authorization.getAuthorizationFilter.mockImplementationOnce(async () => { + return { filter: { test: true }, ensureRuleTypeIsAuthorized: () => {} }; + }); + const filter = await getAuthzFilter(authorization, ReadOperations.Find); + expect(filter).toStrictEqual({ test: true }); + }); +}); diff --git a/x-pack/plugins/rule_registry/server/lib/get_authz_filter.ts b/x-pack/plugins/rule_registry/server/lib/get_authz_filter.ts new file mode 100644 index 00000000000000..88b8feb2ca97cf --- /dev/null +++ b/x-pack/plugins/rule_registry/server/lib/get_authz_filter.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 { PublicMethodsOf } from '@kbn/utility-types'; +import { + ReadOperations, + WriteOperations, + AlertingAuthorization, + AlertingAuthorizationEntity, + AlertingAuthorizationFilterType, +} from '../../../alerting/server'; +import { + ALERT_RULE_CONSUMER, + ALERT_RULE_TYPE_ID, +} from '../../common/technical_rule_data_field_names'; + +export async function getAuthzFilter( + authorization: PublicMethodsOf, + operation: WriteOperations.Update | ReadOperations.Get | ReadOperations.Find +) { + const { filter } = await authorization.getAuthorizationFilter( + AlertingAuthorizationEntity.Alert, + { + type: AlertingAuthorizationFilterType.ESDSL, + fieldNames: { consumer: ALERT_RULE_CONSUMER, ruleTypeId: ALERT_RULE_TYPE_ID }, + }, + operation + ); + return filter; +} diff --git a/x-pack/plugins/rule_registry/server/lib/get_spaces_filter.test.ts b/x-pack/plugins/rule_registry/server/lib/get_spaces_filter.test.ts new file mode 100644 index 00000000000000..7fd5f00fd99b19 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/lib/get_spaces_filter.test.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 { getSpacesFilter } from '.'; +describe('getSpacesFilter()', () => { + it('should return a spaces filter', () => { + expect(getSpacesFilter('1')).toStrictEqual({ + term: { + 'kibana.space_ids': '1', + }, + }); + }); + + it('should return undefined if no space id is provided', () => { + expect(getSpacesFilter()).toBeUndefined(); + }); +}); diff --git a/x-pack/plugins/rule_registry/server/lib/get_spaces_filter.ts b/x-pack/plugins/rule_registry/server/lib/get_spaces_filter.ts new file mode 100644 index 00000000000000..2756b3d600f18b --- /dev/null +++ b/x-pack/plugins/rule_registry/server/lib/get_spaces_filter.ts @@ -0,0 +1,11 @@ +/* + * 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 { SPACE_IDS } from '../../common/technical_rule_data_field_names'; + +export function getSpacesFilter(spaceId?: string) { + return spaceId ? { term: { [SPACE_IDS]: spaceId } } : undefined; +} diff --git a/x-pack/plugins/rule_registry/server/lib/index.ts b/x-pack/plugins/rule_registry/server/lib/index.ts new file mode 100644 index 00000000000000..c9ed157e7c18ab --- /dev/null +++ b/x-pack/plugins/rule_registry/server/lib/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 { getAuthzFilter } from './get_authz_filter'; +export { getSpacesFilter } from './get_spaces_filter'; diff --git a/x-pack/plugins/rule_registry/server/plugin.ts b/x-pack/plugins/rule_registry/server/plugin.ts index 713e7862207b87..292e987879d58f 100644 --- a/x-pack/plugins/rule_registry/server/plugin.ts +++ b/x-pack/plugins/rule_registry/server/plugin.ts @@ -17,6 +17,11 @@ import { import { PluginStartContract as AlertingStart } from '../../alerting/server'; import { SecurityPluginSetup } from '../../security/server'; +import { SpacesPluginStart } from '../../spaces/server'; +import { + PluginStart as DataPluginStart, + PluginSetup as DataPluginSetup, +} from '../../../../src/plugins/data/server'; import { RuleRegistryPluginConfig } from './config'; import { IRuleDataService, RuleDataService } from './rule_data_plugin_service'; @@ -24,13 +29,17 @@ import { AlertsClientFactory } from './alert_data_client/alerts_client_factory'; import { AlertsClient } from './alert_data_client/alerts_client'; import { RacApiRequestHandlerContext, RacRequestHandlerContext } from './types'; import { defineRoutes } from './routes'; +import { ruleRegistrySearchStrategyProvider } from './search_strategy'; export interface RuleRegistryPluginSetupDependencies { security?: SecurityPluginSetup; + data: DataPluginSetup; } export interface RuleRegistryPluginStartDependencies { alerting: AlertingStart; + data: DataPluginStart; + spaces?: SpacesPluginStart; } export interface RuleRegistryPluginSetupContract { @@ -95,6 +104,22 @@ export class RuleRegistryPlugin this.ruleDataService.initializeService(); + core.getStartServices().then(([_, depsStart]) => { + const ruleRegistrySearchStrategy = ruleRegistrySearchStrategyProvider( + depsStart.data, + this.ruleDataService!, + depsStart.alerting, + logger, + plugins.security, + depsStart.spaces + ); + + plugins.data.search.registerSearchStrategy( + 'ruleRegistryAlertsSearchStrategy', + ruleRegistrySearchStrategy + ); + }); + // ALERTS ROUTES const router = core.http.createRouter(); core.http.registerRouteHandlerContext( diff --git a/x-pack/plugins/rule_registry/server/search_strategy/index.ts b/x-pack/plugins/rule_registry/server/search_strategy/index.ts new file mode 100644 index 00000000000000..63f39430a55224 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/search_strategy/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 { ruleRegistrySearchStrategyProvider } from './search_strategy'; diff --git a/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.test.ts b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.test.ts new file mode 100644 index 00000000000000..9f83930dadc69b --- /dev/null +++ b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.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 { of } from 'rxjs'; +import { merge } from 'lodash'; +import { loggerMock } from '@kbn/logging-mocks'; +import { AlertConsumers } from '@kbn/rule-data-utils'; +import { ruleRegistrySearchStrategyProvider, EMPTY_RESPONSE } from './search_strategy'; +import { ruleDataServiceMock } from '../rule_data_plugin_service/rule_data_plugin_service.mock'; +import { dataPluginMock } from '../../../../../src/plugins/data/server/mocks'; +import { SearchStrategyDependencies } from '../../../../../src/plugins/data/server'; +import { alertsMock } from '../../../alerting/server/mocks'; +import { securityMock } from '../../../security/server/mocks'; +import { spacesMock } from '../../../spaces/server/mocks'; +import { RuleRegistrySearchRequest } from '../../common/search_strategy'; +import { IndexInfo } from '../rule_data_plugin_service/index_info'; +import * as getAuthzFilterImport from '../lib/get_authz_filter'; + +const getBasicResponse = (overwrites = {}) => { + return merge( + { + isPartial: false, + isRunning: false, + total: 0, + loaded: 0, + rawResponse: { + took: 1, + timed_out: false, + _shards: { + failed: 0, + successful: 1, + total: 1, + }, + hits: { + max_score: 0, + hits: [], + total: 0, + }, + }, + }, + overwrites + ); +}; + +describe('ruleRegistrySearchStrategyProvider()', () => { + const data = dataPluginMock.createStartContract(); + const ruleDataService = ruleDataServiceMock.create(); + const alerting = alertsMock.createStart(); + const security = securityMock.createSetup(); + const spaces = spacesMock.createStart(); + const logger = loggerMock.create(); + + const response = getBasicResponse({ + rawResponse: { + hits: { + hits: [ + { + _source: { + foo: 1, + }, + }, + ], + }, + }, + }); + + let getAuthzFilterSpy: jest.SpyInstance; + + beforeEach(() => { + ruleDataService.findIndicesByFeature.mockImplementation(() => { + return [ + { + baseName: 'test', + } as IndexInfo, + ]; + }); + + data.search.getSearchStrategy.mockImplementation(() => { + return { + search: () => of(response), + }; + }); + + getAuthzFilterSpy = jest + .spyOn(getAuthzFilterImport, 'getAuthzFilter') + .mockImplementation(async () => { + return {}; + }); + }); + + afterEach(() => { + ruleDataService.findIndicesByFeature.mockClear(); + data.search.getSearchStrategy.mockClear(); + getAuthzFilterSpy.mockClear(); + }); + + it('should handle a basic search request', async () => { + const request: RuleRegistrySearchRequest = { + featureIds: [AlertConsumers.LOGS], + }; + const options = {}; + const deps = { + request: {}, + }; + + const strategy = ruleRegistrySearchStrategyProvider( + data, + ruleDataService, + alerting, + logger, + security, + spaces + ); + + const result = await strategy + .search(request, options, deps as unknown as SearchStrategyDependencies) + .toPromise(); + expect(result).toBe(response); + }); + + it('should use the active space in siem queries', async () => { + const request: RuleRegistrySearchRequest = { + featureIds: [AlertConsumers.SIEM], + }; + const options = {}; + const deps = { + request: {}, + }; + + spaces.spacesService.getActiveSpace.mockImplementation(async () => { + return { + id: 'testSpace', + name: 'Test Space', + disabledFeatures: [], + }; + }); + + ruleDataService.findIndicesByFeature.mockImplementation(() => { + return [ + { + baseName: 'myTestIndex', + } as unknown as IndexInfo, + ]; + }); + + let searchRequest: RuleRegistrySearchRequest = {} as unknown as RuleRegistrySearchRequest; + data.search.getSearchStrategy.mockImplementation(() => { + return { + search: (_request) => { + searchRequest = _request as unknown as RuleRegistrySearchRequest; + return of(response); + }, + }; + }); + + const strategy = ruleRegistrySearchStrategyProvider( + data, + ruleDataService, + alerting, + logger, + security, + spaces + ); + + await strategy + .search(request, options, deps as unknown as SearchStrategyDependencies) + .toPromise(); + spaces.spacesService.getActiveSpace.mockClear(); + expect(searchRequest?.params?.index).toStrictEqual(['myTestIndex-testSpace*']); + }); + + it('should return an empty response if no valid indices are found', async () => { + const request: RuleRegistrySearchRequest = { + featureIds: [AlertConsumers.LOGS], + }; + const options = {}; + const deps = { + request: {}, + }; + + ruleDataService.findIndicesByFeature.mockImplementationOnce(() => { + return []; + }); + + const strategy = ruleRegistrySearchStrategyProvider( + data, + ruleDataService, + alerting, + logger, + security, + spaces + ); + + const result = await strategy + .search(request, options, deps as unknown as SearchStrategyDependencies) + .toPromise(); + expect(result).toBe(EMPTY_RESPONSE); + }); +}); diff --git a/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts new file mode 100644 index 00000000000000..dd7f392b0a2684 --- /dev/null +++ b/x-pack/plugins/rule_registry/server/search_strategy/search_strategy.ts @@ -0,0 +1,149 @@ +/* + * 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 { map, mergeMap, catchError } from 'rxjs/operators'; +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { Logger } from 'src/core/server'; +import { from, of } from 'rxjs'; +import { isValidFeatureId } from '@kbn/rule-data-utils'; +import { ENHANCED_ES_SEARCH_STRATEGY } from '../../../../../src/plugins/data/common'; +import { ISearchStrategy, PluginStart } from '../../../../../src/plugins/data/server'; +import { + RuleRegistrySearchRequest, + RuleRegistrySearchResponse, +} from '../../common/search_strategy'; +import { ReadOperations, PluginStartContract as AlertingStart } from '../../../alerting/server'; +import { SecurityPluginSetup } from '../../../security/server'; +import { SpacesPluginStart } from '../../../spaces/server'; +import { IRuleDataService } from '..'; +import { Dataset } from '../rule_data_plugin_service/index_options'; +import { MAX_ALERT_SEARCH_SIZE } from '../../common/constants'; +import { AlertAuditAction, alertAuditEvent } from '../'; +import { getSpacesFilter, getAuthzFilter } from '../lib'; + +export const EMPTY_RESPONSE: RuleRegistrySearchResponse = { + rawResponse: {} as RuleRegistrySearchResponse['rawResponse'], +}; + +export const ruleRegistrySearchStrategyProvider = ( + data: PluginStart, + ruleDataService: IRuleDataService, + alerting: AlertingStart, + logger: Logger, + security?: SecurityPluginSetup, + spaces?: SpacesPluginStart +): ISearchStrategy => { + const es = data.search.getSearchStrategy(ENHANCED_ES_SEARCH_STRATEGY); + + return { + search: (request, options, deps) => { + const securityAuditLogger = security?.audit.asScoped(deps.request); + const getActiveSpace = async () => spaces?.spacesService.getActiveSpace(deps.request); + const getAsync = async () => { + const [space, authorization] = await Promise.all([ + getActiveSpace(), + alerting.getAlertingAuthorizationWithRequest(deps.request), + ]); + const authzFilter = (await getAuthzFilter( + authorization, + ReadOperations.Find + )) as estypes.QueryDslQueryContainer; + return { space, authzFilter }; + }; + return from(getAsync()).pipe( + mergeMap(({ space, authzFilter }) => { + const indices: string[] = request.featureIds.reduce((accum: string[], featureId) => { + if (!isValidFeatureId(featureId)) { + logger.warn( + `Found invalid feature '${featureId}' while using rule registry search strategy. No alert data from this feature will be searched.` + ); + return accum; + } + + return [ + ...accum, + ...ruleDataService + .findIndicesByFeature(featureId, Dataset.alerts) + .map((indexInfo) => { + return featureId === 'siem' + ? `${indexInfo.baseName}-${space?.id ?? ''}*` + : `${indexInfo.baseName}*`; + }), + ]; + }, []); + + if (indices.length === 0) { + return of(EMPTY_RESPONSE); + } + + const filter = request.query?.bool?.filter + ? Array.isArray(request.query?.bool?.filter) + ? request.query?.bool?.filter + : [request.query?.bool?.filter] + : []; + if (authzFilter) { + filter.push(authzFilter); + } + if (space?.id) { + filter.push(getSpacesFilter(space.id) as estypes.QueryDslQueryContainer); + } + + const query = { + bool: { + ...request.query?.bool, + filter, + }, + }; + const params = { + index: indices, + body: { + _source: false, + fields: ['*'], + size: MAX_ALERT_SEARCH_SIZE, + query, + }, + }; + return es.search({ ...request, params }, options, deps); + }), + map((response) => { + // Do we have to loop over each hit? Yes. + // ecs auditLogger requires that we log each alert independently + if (securityAuditLogger != null) { + response.rawResponse.hits?.hits?.forEach((hit) => { + securityAuditLogger.log( + alertAuditEvent({ + action: AlertAuditAction.FIND, + id: hit._id, + outcome: 'success', + }) + ); + }); + } + return response; + }), + catchError((err) => { + // check if auth error, if yes, write to ecs logger + if (securityAuditLogger != null && err?.output?.statusCode === 403) { + securityAuditLogger.log( + alertAuditEvent({ + action: AlertAuditAction.FIND, + outcome: 'failure', + error: err, + }) + ); + } + + throw err; + }) + ); + }, + cancel: async (id, options, deps) => { + if (es.cancel) { + return es.cancel(id, options, deps); + } + }, + }; +}; diff --git a/x-pack/plugins/rule_registry/tsconfig.json b/x-pack/plugins/rule_registry/tsconfig.json index 384ffa0ee34281..810524a7a8122e 100644 --- a/x-pack/plugins/rule_registry/tsconfig.json +++ b/x-pack/plugins/rule_registry/tsconfig.json @@ -19,6 +19,5 @@ { "path": "../../../src/plugins/data/tsconfig.json" }, { "path": "../alerting/tsconfig.json" }, { "path": "../security/tsconfig.json" }, - { "path": "../triggers_actions_ui/tsconfig.json" } ] } diff --git a/x-pack/plugins/triggers_actions_ui/tsconfig.json b/x-pack/plugins/triggers_actions_ui/tsconfig.json index ac36780f10c018..38d3fa9ad59960 100644 --- a/x-pack/plugins/triggers_actions_ui/tsconfig.json +++ b/x-pack/plugins/triggers_actions_ui/tsconfig.json @@ -17,6 +17,7 @@ { "path": "../../../src/core/tsconfig.json" }, { "path": "../alerting/tsconfig.json" }, { "path": "../features/tsconfig.json" }, + { "path": "../rule_registry/tsconfig.json" }, { "path": "../../../src/plugins/data/tsconfig.json" }, { "path": "../../../src/plugins/saved_objects/tsconfig.json" }, { "path": "../../../src/plugins/home/tsconfig.json" }, diff --git a/x-pack/test/rule_registry/security_and_spaces/tests/basic/index.ts b/x-pack/test/rule_registry/security_and_spaces/tests/basic/index.ts index e4512798db7d35..229f31375200af 100644 --- a/x-pack/test/rule_registry/security_and_spaces/tests/basic/index.ts +++ b/x-pack/test/rule_registry/security_and_spaces/tests/basic/index.ts @@ -10,8 +10,7 @@ import { createSpacesAndUsers, deleteSpacesAndUsers } from '../../../common/lib/ // eslint-disable-next-line import/no-default-export export default ({ loadTestFile, getService }: FtrProviderContext): void => { - // FAILING: https://github.com/elastic/kibana/issues/110153 - describe.skip('rules security and spaces enabled: basic', function () { + describe('rules security and spaces enabled: basic', function () { // Fastest ciGroup for the moment. this.tags('ciGroup5'); @@ -24,10 +23,12 @@ export default ({ loadTestFile, getService }: FtrProviderContext): void => { }); // Basic - loadTestFile(require.resolve('./get_alert_by_id')); - loadTestFile(require.resolve('./update_alert')); - loadTestFile(require.resolve('./bulk_update_alerts')); - loadTestFile(require.resolve('./find_alerts')); - loadTestFile(require.resolve('./get_alerts_index')); + // FAILING: https://github.com/elastic/kibana/issues/110153 + // loadTestFile(require.resolve('./get_alert_by_id')); + // loadTestFile(require.resolve('./update_alert')); + // loadTestFile(require.resolve('./bulk_update_alerts')); + // loadTestFile(require.resolve('./find_alerts')); + // loadTestFile(require.resolve('./get_alerts_index')); + loadTestFile(require.resolve('./search_strategy')); }); }; diff --git a/x-pack/test/rule_registry/security_and_spaces/tests/basic/search_strategy.ts b/x-pack/test/rule_registry/security_and_spaces/tests/basic/search_strategy.ts new file mode 100644 index 00000000000000..2124cb8a1d04ba --- /dev/null +++ b/x-pack/test/rule_registry/security_and_spaces/tests/basic/search_strategy.ts @@ -0,0 +1,138 @@ +/* + * 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 { AlertConsumers } from '@kbn/rule-data-utils'; + +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import { RuleRegistrySearchResponse } from '../../../../../plugins/rule_registry/common/search_strategy'; +import { + deleteSignalsIndex, + createSignalsIndex, + deleteAllAlerts, + getRuleForSignalTesting, + createRule, + waitForSignalsToBePresent, + waitForRuleSuccessOrStatus, +} from '../../../../detection_engine_api_integration/utils'; +import { ID } from '../../../../detection_engine_api_integration/security_and_spaces/tests/generating_signals'; +import { QueryCreateSchema } from '../../../../../plugins/security_solution/common/detection_engine/schemas/request'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + const bsearch = getService('bsearch'); + const log = getService('log'); + + const SPACE1 = 'space1'; + + describe('ruleRegistryAlertsSearchStrategy', () => { + describe('logs', () => { + beforeEach(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); + }); + afterEach(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); + }); + it('should return alerts from log rules', async () => { + const result = await bsearch.send({ + supertest, + options: { + featureIds: [AlertConsumers.LOGS], + }, + strategy: 'ruleRegistryAlertsSearchStrategy', + }); + expect(result.rawResponse.hits.total).to.eql(5); + const consumers = result.rawResponse.hits.hits.map((hit) => { + return hit.fields?.['kibana.alert.rule.consumer']; + }); + expect(consumers.every((consumer) => consumer === AlertConsumers.LOGS)); + }); + }); + + describe('siem', () => { + beforeEach(async () => { + await deleteSignalsIndex(supertest, log); + await createSignalsIndex(supertest, log); + }); + + afterEach(async () => { + await deleteSignalsIndex(supertest, log); + await deleteAllAlerts(supertest, log); + }); + + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); + }); + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts'); + }); + + it('should return alerts from siem rules', async () => { + const rule: QueryCreateSchema = { + ...getRuleForSignalTesting(['auditbeat-*']), + query: `_id:${ID}`, + }; + const { id: createdId } = await createRule(supertest, log, rule); + await waitForRuleSuccessOrStatus(supertest, log, createdId); + await waitForSignalsToBePresent(supertest, log, 1, [createdId]); + + const result = await bsearch.send({ + supertest, + options: { + featureIds: [AlertConsumers.SIEM], + }, + strategy: 'ruleRegistryAlertsSearchStrategy', + }); + expect(result.rawResponse.hits.total).to.eql(1); + const consumers = result.rawResponse.hits.hits.map( + (hit) => hit.fields?.['kibana.alert.rule.consumer'] + ); + expect(consumers.every((consumer) => consumer === AlertConsumers.SIEM)); + }); + }); + + describe('apm', () => { + beforeEach(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/rule_registry/alerts'); + }); + afterEach(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/rule_registry/alerts'); + }); + + it('should return alerts from apm rules', async () => { + const result = await bsearch.send({ + supertest, + options: { + featureIds: [AlertConsumers.APM], + }, + strategy: 'ruleRegistryAlertsSearchStrategy', + space: SPACE1, + }); + expect(result.rawResponse.hits.total).to.eql(2); + const consumers = result.rawResponse.hits.hits.map( + (hit) => hit.fields?.['kibana.alert.rule.consumer'] + ); + expect(consumers.every((consumer) => consumer === AlertConsumers.APM)); + }); + }); + + describe('empty response', () => { + it('should return an empty response', async () => { + const result = await bsearch.send({ + supertest, + options: { + featureIds: [], + }, + strategy: 'ruleRegistryAlertsSearchStrategy', + space: SPACE1, + }); + expect(result.rawResponse).to.eql({}); + }); + }); + }); +}; From d499ed533a5d744cabea76a3347b0a99d72de7a4 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Sun, 27 Feb 2022 00:54:58 -0500 Subject: [PATCH 17/28] skip failing test suite (#126421) --- test/functional/apps/console/_autocomplete.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/console/_autocomplete.ts b/test/functional/apps/console/_autocomplete.ts index 423440ecdf3f85..580847351be9c0 100644 --- a/test/functional/apps/console/_autocomplete.ts +++ b/test/functional/apps/console/_autocomplete.ts @@ -13,7 +13,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const PageObjects = getPageObjects(['common', 'console']); - describe('console autocomplete feature', function describeIndexTests() { + // Failing: See https://github.com/elastic/kibana/issues/126421 + describe.skip('console autocomplete feature', function describeIndexTests() { this.tags('includeFirefox'); before(async () => { log.debug('navigateTo console'); From e7e9be9fa69e6d96b54aeaf1b2457247001972f0 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Mon, 28 Feb 2022 09:49:55 +0100 Subject: [PATCH 18/28] [Discover] Fix ""range filter on version" permissions for cloud functional testing (#126377) --- test/functional/config.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/functional/config.js b/test/functional/config.js index 09eccc863a0e53..389f432641acf9 100644 --- a/test/functional/config.js +++ b/test/functional/config.js @@ -201,6 +201,21 @@ export default async function ({ readConfigFile }) { kibana: [], }, + version_test: { + elasticsearch: { + cluster: [], + indices: [ + { + names: ['version-test'], + privileges: ['read', 'view_index_metadata', 'manage', 'create_index', 'index'], + field_security: { grant: ['*'], except: [] }, + }, + ], + run_as: [], + }, + kibana: [], + }, + kibana_sample_read: { elasticsearch: { cluster: [], From 18af3deb7c6dc3d1f737a912fb3046090f916c45 Mon Sep 17 00:00:00 2001 From: Miriam <31922082+MiriamAparicio@users.noreply.github.com> Date: Mon, 28 Feb 2022 09:49:03 +0000 Subject: [PATCH 19/28] Add exception type column to errors table (#126140) * [APM] Add exception type column to errors table in service overview * add e2e test for type column * replace legacy linking to client linking * fix test --- .../cypress/fixtures/synthtrace/opbeans.ts | 6 ++- .../service_overview/errors_table.spec.ts | 15 +++++++ .../error_group_list.stories.tsx | 6 ++- .../error_group_list/index.tsx | 17 ++++---- .../get_columns.tsx | 34 ++++++++++++++++ .../service_overview_errors_table/index.tsx | 18 +++++---- .../shared/links/apm/error_overview_link.tsx | 40 ++++++++----------- 7 files changed, 93 insertions(+), 43 deletions(-) diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/fixtures/synthtrace/opbeans.ts b/x-pack/plugins/apm/ftr_e2e/cypress/fixtures/synthtrace/opbeans.ts index a6d2454de99fd3..b51874f951c0ef 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/fixtures/synthtrace/opbeans.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/fixtures/synthtrace/opbeans.ts @@ -34,7 +34,11 @@ export function opbeans({ from, to }: { from: number; to: number }) { .timestamp(timestamp) .duration(1000) .success() - .errors(opbeansJava.error('[MockError] Foo').timestamp(timestamp)) + .errors( + opbeansJava + .error('[MockError] Foo', `Exception`) + .timestamp(timestamp) + ) .children( opbeansJava .span('SELECT * FROM product', 'db', 'postgresql') diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/errors_table.spec.ts b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/errors_table.spec.ts index 9ea6ef028b8055..efeeec72941a28 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/errors_table.spec.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/integration/read_only_user/service_overview/errors_table.spec.ts @@ -48,6 +48,21 @@ describe('Errors table', () => { cy.url().should('include', '/opbeans-java/errors'); }); + it('clicking on type adds a filter in the kuerybar and navigates to errors page', () => { + cy.visit(serviceOverviewHref); + cy.get('[data-test-subj="headerFilterKuerybar"]') + .invoke('val') + .should('be.empty'); + // `force: true` because Cypress says the element is 0x0 + cy.contains('Exception').click({ + force: true, + }); + cy.get('[data-test-subj="headerFilterKuerybar"]') + .its('length') + .should('be.gt', 0); + cy.get('table').find('td:contains("Exception")').should('have.length', 1); + }); + it('navigates to error detail page', () => { cy.visit(serviceOverviewHref); cy.contains('a', '[MockError] Foo').click(); diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/error_group_list.stories.tsx b/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/error_group_list.stories.tsx index 3d6a9af7079554..c61a37fa86e5e8 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/error_group_list.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/error_group_list.stories.tsx @@ -21,7 +21,11 @@ const stories: Meta = { decorators: [ (StoryComponent) => { return ( - + diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx index cd991b0f128c5a..7a54a633e7f154 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_overview/error_group_list/index.tsx @@ -13,15 +13,14 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; +import { useApmParams } from '../../../../hooks/use_apm_params'; import { asInteger } from '../../../../../common/utils/formatters'; import { euiStyled } from '../../../../../../../../src/plugins/kibana_react/common'; import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; -import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params'; import { APIReturnType } from '../../../../services/rest/create_call_apm_api'; import { truncate, unit } from '../../../../utils/style'; import { ErrorDetailLink } from '../../../shared/links/apm/error_detail_link'; import { ErrorOverviewLink } from '../../../shared/links/apm/error_overview_link'; -import { APMQueryParams } from '../../../shared/links/url_helpers'; import { ITableColumn, ManagedTable } from '../../../shared/managed_table'; import { TimestampTooltip } from '../../../shared/timestamp_tooltip'; import { SparkPlot } from '../../../shared/charts/spark_plot'; @@ -70,7 +69,7 @@ function ErrorGroupList({ detailedStatistics, comparisonEnabled, }: Props) { - const { urlParams } = useLegacyUrlParams(); + const { query } = useApmParams('/services/{serviceName}/errors'); const columns = useMemo(() => { return [ @@ -119,12 +118,10 @@ function ErrorGroupList({ {type} @@ -232,7 +229,7 @@ function ErrorGroupList({ }, }, ] as Array>; - }, [serviceName, urlParams, detailedStatistics, comparisonEnabled]); + }, [serviceName, query, detailedStatistics, comparisonEnabled]); return ( ; @@ -28,12 +37,37 @@ export function getColumns({ serviceName, errorGroupDetailedStatistics, comparisonEnabled, + query, }: { serviceName: string; errorGroupDetailedStatistics: ErrorGroupDetailedStatistics; comparisonEnabled?: boolean; + query: TypeOf['query']; }): Array> { return [ + { + name: i18n.translate('xpack.apm.errorsTable.typeColumnLabel', { + defaultMessage: 'Type', + }), + field: 'type', + sortable: false, + render: (_, { type }) => { + return ( + ['query'] + } + > + {type} + + ); + }, + }, { field: 'name', name: i18n.translate('xpack.apm.serviceOverview.errorsTableColumnName', { diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx index 0b7d3c32957e2e..8eb5158f304c5b 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_errors_table/index.tsx @@ -15,7 +15,6 @@ import { i18n } from '@kbn/i18n'; import { orderBy } from 'lodash'; import React, { useState } from 'react'; import uuid from 'uuid'; -import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params'; import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; import { APIReturnType } from '../../../../services/rest/create_call_apm_api'; import { ErrorOverviewLink } from '../../../shared/links/apm/error_overview_link'; @@ -58,9 +57,6 @@ const INITIAL_STATE_DETAILED_STATISTICS: ErrorGroupDetailedStatistics = { }; export function ServiceOverviewErrorsTable({ serviceName }: Props) { - const { - urlParams: { comparisonType, comparisonEnabled }, - } = useLegacyUrlParams(); const [tableOptions, setTableOptions] = useState<{ pageIndex: number; sort: { @@ -72,9 +68,16 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { sort: DEFAULT_SORT, }); + const { query } = useApmParams('/services/{serviceName}/overview'); + const { - query: { environment, kuery, rangeFrom, rangeTo }, - } = useApmParams('/services/{serviceName}/overview'); + environment, + kuery, + rangeFrom, + rangeTo, + comparisonType, + comparisonEnabled, + } = query; const { start, end } = useTimeRange({ rangeFrom, rangeTo }); @@ -177,6 +180,7 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { serviceName, errorGroupDetailedStatistics, comparisonEnabled, + query, }); return ( @@ -197,7 +201,7 @@ export function ServiceOverviewErrorsTable({ serviceName }: Props) { - + {i18n.translate('xpack.apm.serviceOverview.errorsTableLinkText', { defaultMessage: 'View errors', })} diff --git a/x-pack/plugins/apm/public/components/shared/links/apm/error_overview_link.tsx b/x-pack/plugins/apm/public/components/shared/links/apm/error_overview_link.tsx index b517a39c1004df..bfa2067e9e7ee2 100644 --- a/x-pack/plugins/apm/public/components/shared/links/apm/error_overview_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/apm/error_overview_link.tsx @@ -6,34 +6,26 @@ */ import React from 'react'; -import { pickKeys } from '../../../../../common/utils/pick_keys'; -import { useLegacyUrlParams } from '../../../../context/url_params_context/use_url_params'; -import { APMQueryParams } from '../url_helpers'; -import { APMLink, APMLinkExtendProps } from './apm_link'; +import { EuiLink } from '@elastic/eui'; +import { TypeOf } from '@kbn/typed-react-router-config'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; +import { ApmRoutes } from '../../../routing/apm_route_config'; -const persistedFilters: Array = [ - 'host', - 'containerId', - 'podName', - 'serviceVersion', -]; - -interface Props extends APMLinkExtendProps { +interface Props { + children: React.ReactNode; + title?: string; serviceName: string; - query?: APMQueryParams; + query: TypeOf['query']; } export function ErrorOverviewLink({ serviceName, query, ...rest }: Props) { - const { urlParams } = useLegacyUrlParams(); + const router = useApmRouter(); + const errorOverviewLink = router.link('/services/{serviceName}/errors', { + path: { + serviceName, + }, + query, + }); - return ( - - ); + return ; } From 5ec3206bf83eb97256992c051d30bbe51b59e052 Mon Sep 17 00:00:00 2001 From: Mat Schaffer Date: Mon, 28 Feb 2022 19:07:03 +0900 Subject: [PATCH 20/28] [Stack Monitoring] api_integration - Ignore `logs` section of index detail api (#126427) --- .../apis/monitoring/elasticsearch/index_detail.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/test/api_integration/apis/monitoring/elasticsearch/index_detail.js b/x-pack/test/api_integration/apis/monitoring/elasticsearch/index_detail.js index e243c3a372f7b3..87371187c83e84 100644 --- a/x-pack/test/api_integration/apis/monitoring/elasticsearch/index_detail.js +++ b/x-pack/test/api_integration/apis/monitoring/elasticsearch/index_detail.js @@ -41,6 +41,9 @@ export default function ({ getService }) { }) .expect(200); + // Work around ESTF failure outlined in https://github.com/elastic/kibana/issues/124594 + indexDetailFixture.logs = body.logs; + expect(body).to.eql(indexDetailFixture); }); From b00831cedd9c08b0c7fe823cebd617bd32f33371 Mon Sep 17 00:00:00 2001 From: Josh Dover <1813008+joshdover@users.noreply.github.com> Date: Mon, 28 Feb 2022 11:37:15 +0100 Subject: [PATCH 21/28] Add back Fleet QA labeling automation (#126327) * Revert "Remove Fleet QA labeling automation (#126244)" This reverts commit 324245c94c6808089b06966ae60bf9035ab965db. * Use pull_request_target event instead Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .github/workflows/label-qa-fixed-in.yml | 87 +++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 .github/workflows/label-qa-fixed-in.yml diff --git a/.github/workflows/label-qa-fixed-in.yml b/.github/workflows/label-qa-fixed-in.yml new file mode 100644 index 00000000000000..92c1f95f317595 --- /dev/null +++ b/.github/workflows/label-qa-fixed-in.yml @@ -0,0 +1,87 @@ +name: Add QA labels to Fleet issues +on: + # pull_request_target allows running actions on PRs from forks with a read/write GITHUB_TOKEN, but it will not allow + # running workflows defined in the PRs itself, only workflows already merged into the target branch. This avoids + # potential vulnerabilities that could allow someone to open a PR and retrieve secrets. + # It's important that this workflow never runs any checkout actions which could be used to circumvent this protection. + # See these links for more information: + # - https://github.blog/2020-08-03-github-actions-improvements-for-fork-and-pull-request-workflows/ + # - https://nathandavison.com/blog/github-actions-and-the-threat-of-malicious-pull-requests + pull_request_target: + types: + - closed + +jobs: + fetch_issues_to_label: + runs-on: ubuntu-latest + # Only run on PRs that were merged for the Fleet team + if: | + github.event.pull_request.merged_at && + contains(github.event.pull_request.labels.*.name, 'Team:Fleet') + outputs: + matrix: ${{ steps.issues_to_label.outputs.value }} + label_ids: ${{ steps.label_ids.outputs.value }} + steps: + - uses: octokit/graphql-action@v2.x + id: closing_issues + with: + query: | + query closingIssueNumbersQuery($prnumber: Int!) { + repository(owner: "elastic", name: "kibana") { + pullRequest(number: $prnumber) { + closingIssuesReferences(first: 10) { + nodes { + id + labels(first: 20) { + nodes { + id + name + } + } + } + } + } + } + } + prnumber: ${{ github.event.number }} + env: + GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }} + - uses: sergeysova/jq-action@v2 + id: issues_to_label + with: + # Map to the issues' node id + cmd: echo $CLOSING_ISSUES | jq -c '.repository.pullRequest.closingIssuesReferences.nodes | map(.id)' + multiline: true + env: + CLOSING_ISSUES: ${{ steps.closing_issues.outputs.data }} + - uses: sergeysova/jq-action@v2 + id: label_ids + with: + # Get list of version labels on pull request and map to label's node id, append 'QA:Ready For Testing' id ("MDU6TGFiZWwyNTQ1NjcwOTI4") + cmd: echo $PR_LABELS | jq -c 'map(select(.name | test("v[0-9]+\\.[0-9]+\\.[0-9]+")) | .node_id) + ["MDU6TGFiZWwyNTQ1NjcwOTI4"]' + multiline: true + env: + PR_LABELS: ${{ toJSON(github.event.pull_request.labels) }} + + label_issues: + needs: fetch_issues_to_label + runs-on: ubuntu-latest + # For each issue closed by the PR run this job + strategy: + matrix: + issueNodeId: ${{ fromJSON(needs.fetch_issues_to_label.outputs.matrix) }} + name: Label issue ${{ matrix.issueNodeId }} + steps: + - uses: octokit/graphql-action@v2.x + id: add_labels_to_closed_issue + with: + query: | + mutation add_label($issueid:String!, $labelids:[String!]!) { + addLabelsToLabelable(input: {labelableId: $issueid, labelIds: $labelids}) { + clientMutationId + } + } + issueid: ${{ matrix.issueNodeId }} + labelids: ${{ needs.fetch_issues_to_label.outputs.label_ids }} + env: + GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }} From 2597dfc2dfd4568a647b675466568670ca06828c Mon Sep 17 00:00:00 2001 From: Miriam <31922082+MiriamAparicio@users.noreply.github.com> Date: Mon, 28 Feb 2022 11:15:03 +0000 Subject: [PATCH 22/28] [APM] remove legacy data toast (#126352) * [APM] remove legacy data toast * remove useUpgradeAssistantHref --- .../app/service_inventory/index.tsx | 47 +------------------ .../public/components/shared/links/kibana.ts | 6 --- .../translations/translations/ja-JP.json | 3 -- .../translations/translations/zh-CN.json | 3 -- 4 files changed, 2 insertions(+), 57 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx index dc7b80ded50ad3..1e736409a96041 100644 --- a/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_inventory/index.tsx @@ -5,24 +5,16 @@ * 2.0. */ -import { - EuiFlexGroup, - EuiFlexItem, - EuiLink, - EuiEmptyPrompt, -} from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiEmptyPrompt } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useEffect } from 'react'; +import React from 'react'; import uuid from 'uuid'; -import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public'; import { useAnomalyDetectionJobsContext } from '../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context'; -import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { useLegacyUrlParams } from '../../../context/url_params_context/use_url_params'; import { useLocalStorage } from '../../../hooks/use_local_storage'; import { useAnyOfApmParams } from '../../../hooks/use_apm_params'; import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { useTimeRange } from '../../../hooks/use_time_range'; -import { useUpgradeAssistantHref } from '../../shared/links/kibana'; import { SearchBar } from '../../shared/search_bar'; import { getTimeRangeComparison } from '../../shared/time_comparison/get_time_range_comparison'; import { ServiceList } from './service_list'; @@ -37,8 +29,6 @@ const initialData = { }, }; -let hasDisplayedToast = false; - function useServicesFetcher() { const { urlParams: { comparisonEnabled, comparisonType }, @@ -50,9 +40,6 @@ function useServicesFetcher() { const { start, end } = useTimeRange({ rangeFrom, rangeTo }); - const { core } = useApmPluginContext(); - const upgradeAssistantHref = useUpgradeAssistantHref(); - const { offset } = getTimeRangeComparison({ start, end, @@ -113,36 +100,6 @@ function useServicesFetcher() { { preservePreviousData: false } ); - useEffect(() => { - if (!hasDisplayedToast) { - hasDisplayedToast = true; - - core.notifications.toasts.addWarning({ - title: i18n.translate('xpack.apm.serviceInventory.toastTitle', { - defaultMessage: - 'Legacy data was detected within the selected time range', - }), - text: toMountPoint( -

- {i18n.translate('xpack.apm.serviceInventory.toastText', { - defaultMessage: - "You're running Elastic Stack 7.0+ and we've detected incompatible data from a previous 6.x version. If you want to view this data in APM, you should migrate it. See more in ", - })} - - - {i18n.translate( - 'xpack.apm.serviceInventory.upgradeAssistantLinkText', - { - defaultMessage: 'the upgrade assistant', - } - )} - -

- ), - }); - } - }, [upgradeAssistantHref, core.notifications.toasts]); - return { mainStatisticsData, mainStatisticsStatus, diff --git a/x-pack/plugins/apm/public/components/shared/links/kibana.ts b/x-pack/plugins/apm/public/components/shared/links/kibana.ts index c0bdf3a98aa31e..e05ccf4e6fcff9 100644 --- a/x-pack/plugins/apm/public/components/shared/links/kibana.ts +++ b/x-pack/plugins/apm/public/components/shared/links/kibana.ts @@ -12,12 +12,6 @@ export function getUpgradeAssistantHref(basePath: IBasePath) { return basePath.prepend('/app/management/stack/upgrade_assistant'); } -export function useUpgradeAssistantHref() { - const { core } = useApmPluginContext(); - - return getUpgradeAssistantHref(core.http.basePath); -} - export function useFleetCloudAgentPolicyHref() { const { core: { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index bf120f49965c95..c10f30068bfb38 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7315,9 +7315,6 @@ "xpack.apm.serviceIcons.serviceDetails.service.frameworkLabel": "フレームワーク名", "xpack.apm.serviceIcons.serviceDetails.service.runtimeLabel": "ランタイム名・バージョン", "xpack.apm.serviceIcons.serviceDetails.service.versionLabel": "サービスバージョン", - "xpack.apm.serviceInventory.toastText": "現在 Elastic Stack 7.0+ を実行中で、以前のバージョン 6.x からの互換性のないデータを検知しました。このデータを APM で表示するには、移行が必要です。詳細 ", - "xpack.apm.serviceInventory.toastTitle": "選択された時間範囲内にレガシーデータが検知されました。", - "xpack.apm.serviceInventory.upgradeAssistantLinkText": "アップグレードアシスタント", "xpack.apm.serviceLogs.noInfrastructureMessage": "表示するログメッセージがありません。", "xpack.apm.serviceMap.anomalyDetectionPopoverDisabled": "APM 設定で異常検知を有効にすると、サービス正常性インジケーターが表示されます。", "xpack.apm.serviceMap.anomalyDetectionPopoverLink": "異常を表示", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index afdf5b7561bc47..6cd78eb4cac681 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7332,9 +7332,6 @@ "xpack.apm.serviceIcons.serviceDetails.service.frameworkLabel": "框架名称", "xpack.apm.serviceIcons.serviceDetails.service.runtimeLabel": "运行时名称和版本", "xpack.apm.serviceIcons.serviceDetails.service.versionLabel": "服务版本", - "xpack.apm.serviceInventory.toastText": "您正在运行 Elastic Stack 7.0+,我们检测到来自以前 6.x 版本的数据不兼容。如果想在 APM 中查看此数据,您应迁移数据。在以下位置查看更多内容: ", - "xpack.apm.serviceInventory.toastTitle": "在选定时间范围中检测到旧数据", - "xpack.apm.serviceInventory.upgradeAssistantLinkText": "升级助手", "xpack.apm.serviceLogs.noInfrastructureMessage": "没有可显示的日志消息。", "xpack.apm.serviceMap.anomalyDetectionPopoverDisabled": "通过在 APM 设置中启用异常检测来显示服务运行状况指标。", "xpack.apm.serviceMap.anomalyDetectionPopoverLink": "查看异常", From e2dd796bdf0ba08dd05f10e5290bde73bd3bd9a9 Mon Sep 17 00:00:00 2001 From: Josh Dover <1813008+joshdover@users.noreply.github.com> Date: Mon, 28 Feb 2022 12:25:15 +0100 Subject: [PATCH 23/28] [Fleet] Fix github token fetching in QA label action (#126445) --- .github/workflows/label-qa-fixed-in.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/label-qa-fixed-in.yml b/.github/workflows/label-qa-fixed-in.yml index 92c1f95f317595..65efc6fd694f89 100644 --- a/.github/workflows/label-qa-fixed-in.yml +++ b/.github/workflows/label-qa-fixed-in.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest # Only run on PRs that were merged for the Fleet team if: | - github.event.pull_request.merged_at && + github.event.pull_request_target.merged == true && contains(github.event.pull_request.labels.*.name, 'Team:Fleet') outputs: matrix: ${{ steps.issues_to_label.outputs.value }} @@ -45,7 +45,7 @@ jobs: } prnumber: ${{ github.event.number }} env: - GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: sergeysova/jq-action@v2 id: issues_to_label with: @@ -84,4 +84,4 @@ jobs: issueid: ${{ matrix.issueNodeId }} labelids: ${{ needs.fetch_issues_to_label.outputs.label_ids }} env: - GITHUB_TOKEN: ${{ env.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 9ddb3e8756e9294c857e58b3ab0deedfb9375cbe Mon Sep 17 00:00:00 2001 From: Josh Dover <1813008+joshdover@users.noreply.github.com> Date: Mon, 28 Feb 2022 12:30:41 +0100 Subject: [PATCH 24/28] [Fleet] Fix event condition in QA label action (#126447) --- .github/workflows/label-qa-fixed-in.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/label-qa-fixed-in.yml b/.github/workflows/label-qa-fixed-in.yml index 65efc6fd694f89..5bbdd5b71ef750 100644 --- a/.github/workflows/label-qa-fixed-in.yml +++ b/.github/workflows/label-qa-fixed-in.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest # Only run on PRs that were merged for the Fleet team if: | - github.event.pull_request_target.merged == true && + github.event.pull_request.merged_at && contains(github.event.pull_request.labels.*.name, 'Team:Fleet') outputs: matrix: ${{ steps.issues_to_label.outputs.value }} From 58aba97c20bdb93f6710453e88f5584d088f3c3d Mon Sep 17 00:00:00 2001 From: Uladzislau Lasitsa Date: Mon, 28 Feb 2022 14:42:29 +0300 Subject: [PATCH 25/28] [Expression] Problem with formatting when we use vis_dimension type as arg (#126016) * Use format from datatable if user doesn't specify format in expression * Fix shapshots * Fix shapshot * Update comment Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../expression_functions/vis_dimension.ts | 21 ++++++++++++------- .../snapshots/baseline/combined_test3.json | 2 +- .../snapshots/baseline/final_output_test.json | 2 +- .../snapshots/baseline/metric_all_data.json | 2 +- .../snapshots/baseline/metric_empty_data.json | 2 +- .../baseline/metric_multi_metric_data.json | 2 +- .../baseline/metric_percentage_mode.json | 2 +- .../baseline/metric_single_metric_data.json | 2 +- .../snapshots/baseline/partial_test_1.json | 2 +- .../snapshots/baseline/partial_test_2.json | 2 +- .../snapshots/baseline/step_output_test3.json | 2 +- .../snapshots/baseline/tagcloud_all_data.json | 2 +- .../baseline/tagcloud_empty_data.json | 2 +- .../snapshots/baseline/tagcloud_fontsize.json | 2 +- .../baseline/tagcloud_metric_data.json | 2 +- .../snapshots/baseline/tagcloud_options.json | 2 +- 16 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/plugins/visualizations/common/expression_functions/vis_dimension.ts b/src/plugins/visualizations/common/expression_functions/vis_dimension.ts index 254f3ac5b68e84..bd5694cb69a031 100644 --- a/src/plugins/visualizations/common/expression_functions/vis_dimension.ts +++ b/src/plugins/visualizations/common/expression_functions/vis_dimension.ts @@ -27,7 +27,7 @@ export type ExpressionValueVisDimension = ExpressionValueBoxed< accessor: number | DatatableColumn; format: { id?: string; - params: Record; + params?: Record; }; } >; @@ -54,14 +54,12 @@ export const visDimension = (): ExpressionFunctionDefinition< }, format: { types: ['string'], - default: 'string', help: i18n.translate('visualizations.function.visDimension.format.help', { defaultMessage: 'Format', }), }, formatParams: { types: ['string'], - default: '"{}"', help: i18n.translate('visualizations.function.visDimension.formatParams.help', { defaultMessage: 'Format params', }), @@ -69,13 +67,22 @@ export const visDimension = (): ExpressionFunctionDefinition< }, fn: (input, args) => { const accessor = findAccessorOrFail(args.accessor, input.columns); + const column = typeof accessor === 'number' ? input.columns[accessor] : accessor; + const columnFormat = column.meta.params; + // if a user hasn't specified the format of the column and its format is not specified at the table columns, + // then the default format id ('string') should be used + const format = + args.format || args.formatParams || !columnFormat + ? { + id: args.format || 'string', + params: JSON.parse(args.formatParams || '{}'), + } + : columnFormat; + return { type: 'vis_dimension', accessor, - format: { - id: args.format, - params: JSON.parse(args.formatParams!), - }, + format, }; }, }); diff --git a/test/interpreter_functional/snapshots/baseline/combined_test3.json b/test/interpreter_functional/snapshots/baseline/combined_test3.json index aefe875ff248c8..dc39ecfc535940 100644 --- a/test/interpreter_functional/snapshots/baseline/combined_test3.json +++ b/test/interpreter_functional/snapshots/baseline/combined_test3.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/final_output_test.json b/test/interpreter_functional/snapshots/baseline/final_output_test.json index aefe875ff248c8..dc39ecfc535940 100644 --- a/test/interpreter_functional/snapshots/baseline/final_output_test.json +++ b/test/interpreter_functional/snapshots/baseline/final_output_test.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_all_data.json b/test/interpreter_functional/snapshots/baseline/metric_all_data.json index c146b8ca6e39da..a26b85daee9324 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_all_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_all_data.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"bytes","params":null},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_empty_data.json b/test/interpreter_functional/snapshots/baseline/metric_empty_data.json index 84ea37086d00cb..0917ecc8f9b2ed 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_empty_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_empty_data.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json index de55a313fde430..44bc7717db04f3 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json index aacb5a1d38eafe..99604aa3774753 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json +++ b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":{"colors":["rgb(0,0,0,0)","rgb(100, 100, 100)"],"continuity":"none","gradient":false,"range":"number","rangeMax":10000,"rangeMin":0,"stops":[0,10000]},"percentageMode":true,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":{"colors":["rgb(0,0,0,0)","rgb(100, 100, 100)"],"continuity":"none","gradient":false,"range":"number","rangeMax":10000,"rangeMin":0,"stops":[0,10000]},"percentageMode":true,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json index 91d78e9942f1aa..63c91a3cc749df 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":null},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/partial_test_1.json b/test/interpreter_functional/snapshots/baseline/partial_test_1.json index 9e6888a319c38d..e8a847b43de3b4 100644 --- a/test/interpreter_functional/snapshots/baseline/partial_test_1.json +++ b/test/interpreter_functional/snapshots/baseline/partial_test_1.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/partial_test_2.json b/test/interpreter_functional/snapshots/baseline/partial_test_2.json index aefe875ff248c8..dc39ecfc535940 100644 --- a/test/interpreter_functional/snapshots/baseline/partial_test_2.json +++ b/test/interpreter_functional/snapshots/baseline/partial_test_2.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/step_output_test3.json b/test/interpreter_functional/snapshots/baseline/step_output_test3.json index aefe875ff248c8..dc39ecfc535940 100644 --- a/test/interpreter_functional/snapshots/baseline/step_output_test3.json +++ b/test/interpreter_functional/snapshots/baseline/step_output_test3.json @@ -1 +1 @@ -{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metricVis","type":"render","value":{"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"autoScale":null,"colorFullBackground":false,"labels":{"position":"bottom","show":true,"style":{"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:24px;line-height:1","spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"24px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}},"metricColorMode":"None","palette":null,"percentageMode":false,"style":{"bgColor":false,"css":"font-family:'Open Sans', Helvetica, Arial, sans-serif;font-weight:normal;font-style:normal;text-decoration:none;text-align:center;font-size:60px;line-height:1","labelColor":false,"spec":{"fontFamily":"'Open Sans', Helvetica, Arial, sans-serif","fontSize":"60px","fontStyle":"normal","fontWeight":"normal","lineHeight":"1","textAlign":"center","textDecoration":"none"},"type":"style"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json b/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json index 775839764b410b..518eb529e70f40 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_empty_data.json b/test/interpreter_functional/snapshots/baseline/tagcloud_empty_data.json index 70c7ea6d7827b1..7417545550cd82 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_empty_data.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_empty_data.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[],"type":"datatable"},"visParams":{"ariaLabel":null,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[],"type":"datatable"},"visParams":{"ariaLabel":null,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json b/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json index dc251faaee8271..986e6d19e91f33 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json b/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json index 89df8d2f4146bc..cf0aa1162c23f4 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_options.json b/test/interpreter_functional/snapshots/baseline/tagcloud_options.json index 7bd4ff7dedfa09..357ac0fc76784c 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_options.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_options.json @@ -1 +1 @@ -{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagcloud","type":"render","value":{"syncColors":false,"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"appliedTimeRange":null,"enabled":true,"hasPrecisionError":false,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"ariaLabel":null,"bucket":{"accessor":1,"format":{"id":"number"},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other"}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","palette":{"name":"custom","params":{"colors":["#882E72","#B178A6","#D6C1DE","#1965B0","#5289C7","#7BAFDE","#4EB265","#90C987","#CAE0AB","#F7EE55","#F6C141","#F1932D","#E8601C","#DC050C"],"continuity":"above","gradient":false,"range":"percent","rangeMax":null,"rangeMin":0,"stops":[]},"type":"palette"},"scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file From f2a56adc36404a3abb8be7b9c1b2dc3fc3429a7a Mon Sep 17 00:00:00 2001 From: Julia Bardi <90178898+juliaElastic@users.noreply.github.com> Date: Mon, 28 Feb 2022 13:43:08 +0100 Subject: [PATCH 26/28] hide enroll command when clicking on create agent policy (#126431) --- .../agent_enrollment_flyout/agent_policy_select_create.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx index 655875d0448938..53e8140086c2c4 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/agent_policy_select_create.tsx @@ -73,6 +73,9 @@ export const SelectCreateAgentPolicy: React.FC = ({ const onClickCreatePolicy = () => { setCreateState({ status: CREATE_STATUS.INITIAL }); setShowCreatePolicy(true); + if (withKeySelection && onKeyChange) { + onKeyChange(undefined); + } }; return ( From 865c351ecf179b68d99115d909040036f2d9af73 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 28 Feb 2022 13:14:03 +0000 Subject: [PATCH 27/28] skip flaky suite (#92567) --- .../security_solution_endpoint/apps/endpoint/policy_details.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts index 3f057d198a25c1..4e4a1fc61d7076 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts @@ -337,7 +337,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - describe('and the save button is clicked', () => { + // FLAKY: https://github.com/elastic/kibana/issues/92567 + describe.skip('and the save button is clicked', () => { let policyInfo: PolicyTestResourceInfo; beforeEach(async () => { From 1ded153692d222b7f126767037d3bd7a5a55c917 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 28 Feb 2022 13:21:36 +0000 Subject: [PATCH 28/28] skip flaky suite (#126414) --- test/functional/apps/console/_autocomplete.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/console/_autocomplete.ts b/test/functional/apps/console/_autocomplete.ts index 580847351be9c0..1ba4bcaa76b365 100644 --- a/test/functional/apps/console/_autocomplete.ts +++ b/test/functional/apps/console/_autocomplete.ts @@ -29,7 +29,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(PageObjects.console.isAutocompleteVisible()).to.be.eql(true); }); - describe('with a missing comma in query', () => { + // FLAKY: https://github.com/elastic/kibana/issues/126414 + describe.skip('with a missing comma in query', () => { const LINE_NUMBER = 4; beforeEach(async () => { await PageObjects.console.clearTextArea();