From 6e2691358fdb59af717a9aaa2b32887b9cdf2d2a Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Thu, 30 Apr 2020 10:28:14 +0200 Subject: [PATCH 01/47] add generic typings for SavedObjectMigrationFn (#63943) * add generic typings for SavedObjectMigrationFn * change default attributes type to unknown * update generated doc * adapt new calls * update generated doc * update migration example * fix merge conflicts --- ...ugin-core-server.savedobjectmigrationfn.md | 32 ++++++++--- ...gin-core-server.savedobjectsanitizeddoc.md | 2 +- ...n-core-server.savedobjectunsanitizeddoc.md | 2 +- src/core/MIGRATION_EXAMPLES.md | 2 +- .../saved_objects/migrations/core/index.ts | 2 +- .../server/saved_objects/migrations/mocks.ts | 43 +++++++++++++++ .../server/saved_objects/migrations/types.ts | 34 ++++++++---- .../saved_objects_service.mock.ts | 2 + .../saved_objects/serialization/types.ts | 8 +-- src/core/server/server.api.md | 6 +- .../dashboard_migrations.test.ts | 31 ++++++----- .../saved_objects/dashboard_migrations.ts | 10 ++-- .../saved_objects/migrate_match_all_query.ts | 2 +- .../saved_objects/migrations_730.test.ts | 14 ++--- .../saved_objects/index_pattern_migrations.ts | 4 +- .../server/saved_objects/search_migrations.ts | 14 ++--- .../server/saved_objects/tsvb_telemetry.ts | 2 +- .../saved_objects/visualization_migrations.ts | 55 ++++++++++--------- .../graph/server/saved_objects/migrations.ts | 2 +- x-pack/plugins/lens/server/migrations.ts | 9 ++- .../saved_objects/migrations/migrate_6x.ts | 2 +- 21 files changed, 177 insertions(+), 101 deletions(-) create mode 100644 src/core/server/saved_objects/migrations/mocks.ts diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationfn.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationfn.md index a502c40db0cd8c..a3294fb0a087ae 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationfn.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectmigrationfn.md @@ -9,22 +9,36 @@ A migration function for a [saved object type](./kibana-plugin-core-server.saved Signature: ```typescript -export declare type SavedObjectMigrationFn = (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => SavedObjectUnsanitizedDoc; +export declare type SavedObjectMigrationFn = (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => SavedObjectUnsanitizedDoc; ``` ## Example ```typescript -const migrateProperty: SavedObjectMigrationFn = (doc, { log }) => { - if(doc.attributes.someProp === null) { - log.warn('Skipping migration'); - } else { - doc.attributes.someProp = migrateProperty(doc.attributes.someProp); - } - - return doc; +interface TypeV1Attributes { + someKey: string; + obsoleteProperty: number; } +interface TypeV2Attributes { + someKey: string; + newProperty: string; +} + +const migrateToV2: SavedObjectMigrationFn = (doc, { log }) => { + const { obsoleteProperty, ...otherAttributes } = doc.attributes; + // instead of mutating `doc` we make a shallow copy so that we can use separate types for the input + // and output attributes. We don't need to make a deep copy, we just need to ensure that obsolete + // attributes are not present on the returned doc. + return { + ...doc, + attributes: { + ...otherAttributes, + newProperty: migrate(obsoleteProperty), + }, + }; +}; + ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsanitizeddoc.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsanitizeddoc.md index 6d4e252fe7532e..3f4090619edbfb 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsanitizeddoc.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsanitizeddoc.md @@ -9,5 +9,5 @@ Describes Saved Object documents that have passed through the migration framewor Signature: ```typescript -export declare type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; +export declare type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; ``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectunsanitizeddoc.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectunsanitizeddoc.md index be51400addbbc5..8e2395ee6310d8 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectunsanitizeddoc.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectunsanitizeddoc.md @@ -9,5 +9,5 @@ Describes Saved Object documents from Kibana < 7.0.0 which don't have a `refe Signature: ```typescript -export declare type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; +export declare type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; ``` diff --git a/src/core/MIGRATION_EXAMPLES.md b/src/core/MIGRATION_EXAMPLES.md index 8c5fe4875aaea2..c91c00bc1aa021 100644 --- a/src/core/MIGRATION_EXAMPLES.md +++ b/src/core/MIGRATION_EXAMPLES.md @@ -957,7 +957,7 @@ const migration = (doc, log) => {...} Would be converted to: ```typescript -const migration: SavedObjectMigrationFn = (doc, { log }) => {...} +const migration: SavedObjectMigrationFn = (doc, { log }) => {...} ``` ### Remarks diff --git a/src/core/server/saved_objects/migrations/core/index.ts b/src/core/server/saved_objects/migrations/core/index.ts index 466d399f653cd6..f7274740ea5fe3 100644 --- a/src/core/server/saved_objects/migrations/core/index.ts +++ b/src/core/server/saved_objects/migrations/core/index.ts @@ -21,5 +21,5 @@ export { DocumentMigrator } from './document_migrator'; export { IndexMigrator } from './index_migrator'; export { buildActiveMappings } from './build_active_mappings'; export { CallCluster } from './call_cluster'; -export { LogFn } from './migration_logger'; +export { LogFn, SavedObjectsMigrationLogger } from './migration_logger'; export { MigrationResult, MigrationStatus } from './migration_coordinator'; diff --git a/src/core/server/saved_objects/migrations/mocks.ts b/src/core/server/saved_objects/migrations/mocks.ts new file mode 100644 index 00000000000000..76a890d26bfa0c --- /dev/null +++ b/src/core/server/saved_objects/migrations/mocks.ts @@ -0,0 +1,43 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { SavedObjectMigrationContext } from './types'; +import { SavedObjectsMigrationLogger } from './core'; + +const createLoggerMock = (): jest.Mocked => { + const mock = { + debug: jest.fn(), + info: jest.fn(), + warning: jest.fn(), + warn: jest.fn(), + }; + + return mock; +}; + +const createContextMock = (): jest.Mocked => { + const mock = { + log: createLoggerMock(), + }; + return mock; +}; + +export const migrationMocks = { + createContext: createContextMock, +}; diff --git a/src/core/server/saved_objects/migrations/types.ts b/src/core/server/saved_objects/migrations/types.ts index 6bc085dde872e3..85f15b4c18b66a 100644 --- a/src/core/server/saved_objects/migrations/types.ts +++ b/src/core/server/saved_objects/migrations/types.ts @@ -26,23 +26,37 @@ import { SavedObjectsMigrationLogger } from './core/migration_logger'; * * @example * ```typescript - * const migrateProperty: SavedObjectMigrationFn = (doc, { log }) => { - * if(doc.attributes.someProp === null) { - * log.warn('Skipping migration'); - * } else { - * doc.attributes.someProp = migrateProperty(doc.attributes.someProp); - * } + * interface TypeV1Attributes { + * someKey: string; + * obsoleteProperty: number; + * } * - * return doc; + * interface TypeV2Attributes { + * someKey: string; + * newProperty: string; * } + * + * const migrateToV2: SavedObjectMigrationFn = (doc, { log }) => { + * const { obsoleteProperty, ...otherAttributes } = doc.attributes; + * // instead of mutating `doc` we make a shallow copy so that we can use separate types for the input + * // and output attributes. We don't need to make a deep copy, we just need to ensure that obsolete + * // attributes are not present on the returned doc. + * return { + * ...doc, + * attributes: { + * ...otherAttributes, + * newProperty: migrate(obsoleteProperty), + * }, + * }; + * }; * ``` * * @public */ -export type SavedObjectMigrationFn = ( - doc: SavedObjectUnsanitizedDoc, +export type SavedObjectMigrationFn = ( + doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext -) => SavedObjectUnsanitizedDoc; +) => SavedObjectUnsanitizedDoc; /** * Migration context provided when invoking a {@link SavedObjectMigrationFn | migration handler} diff --git a/src/core/server/saved_objects/saved_objects_service.mock.ts b/src/core/server/saved_objects/saved_objects_service.mock.ts index 7ba4613c857d7b..4e1f5981d6a410 100644 --- a/src/core/server/saved_objects/saved_objects_service.mock.ts +++ b/src/core/server/saved_objects/saved_objects_service.mock.ts @@ -31,6 +31,7 @@ import { savedObjectsClientProviderMock } from './service/lib/scoped_client_prov import { savedObjectsRepositoryMock } from './service/lib/repository.mock'; import { savedObjectsClientMock } from './service/saved_objects_client.mock'; import { typeRegistryMock } from './saved_objects_type_registry.mock'; +import { migrationMocks } from './migrations/mocks'; import { ServiceStatusLevels } from '../status'; type SavedObjectsServiceContract = PublicMethodsOf; @@ -105,4 +106,5 @@ export const savedObjectsServiceMock = { createSetupContract: createSetupContractMock, createInternalStartContract: createInternalStartContractMock, createStartContract: createStartContractMock, + createMigrationContext: migrationMocks.createContext, }; diff --git a/src/core/server/saved_objects/serialization/types.ts b/src/core/server/saved_objects/serialization/types.ts index a33e16895078ed..acd2c7b5284aa2 100644 --- a/src/core/server/saved_objects/serialization/types.ts +++ b/src/core/server/saved_objects/serialization/types.ts @@ -47,8 +47,8 @@ export interface SavedObjectsRawDocSource { /** * Saved Object base document */ -interface SavedObjectDoc { - attributes: any; +interface SavedObjectDoc { + attributes: T; id?: string; // NOTE: SavedObjectDoc is used for uncreated objects where `id` is optional type: string; namespace?: string; @@ -69,7 +69,7 @@ interface Referencable { * * @public */ -export type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; +export type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; /** * Describes Saved Object documents that have passed through the migration @@ -77,4 +77,4 @@ export type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; * * @public */ -export type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; +export type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index dc1c9d379d508a..e8b77a8570291e 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1680,7 +1680,7 @@ export interface SavedObjectMigrationContext { } // @public -export type SavedObjectMigrationFn = (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => SavedObjectUnsanitizedDoc; +export type SavedObjectMigrationFn = (doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext) => SavedObjectUnsanitizedDoc; // @public export interface SavedObjectMigrationMap { @@ -1708,7 +1708,7 @@ export interface SavedObjectsAddToNamespacesOptions extends SavedObjectsBaseOpti // Warning: (ae-forgotten-export) The symbol "Referencable" needs to be exported by the entry point index.d.ts // // @public -export type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; +export type SavedObjectSanitizedDoc = SavedObjectDoc & Referencable; // @public (undocumented) export interface SavedObjectsBaseOptions { @@ -2311,7 +2311,7 @@ export class SavedObjectTypeRegistry { } // @public -export type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; +export type SavedObjectUnsanitizedDoc = SavedObjectDoc & Partial; // @public export type ScopeableRequest = KibanaRequest | LegacyRequest | FakeRequest; diff --git a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts index 9829498118cc0a..22ed18f75c652a 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts +++ b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.test.ts @@ -18,14 +18,17 @@ */ import { SavedObjectUnsanitizedDoc } from 'kibana/server'; +import { savedObjectsServiceMock } from '../../../../core/server/mocks'; import { dashboardSavedObjectTypeMigrations as migrations } from './dashboard_migrations'; +const contextMock = savedObjectsServiceMock.createMigrationContext(); + describe('dashboard', () => { describe('7.0.0', () => { const migration = migrations['7.0.0']; test('skips error on empty object', () => { - expect(migration({} as SavedObjectUnsanitizedDoc)).toMatchInlineSnapshot(` + expect(migration({} as SavedObjectUnsanitizedDoc, contextMock)).toMatchInlineSnapshot(` Object { "references": Array [], } @@ -44,7 +47,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - const migratedDoc = migration(doc); + const migratedDoc = migration(doc, contextMock); expect(migratedDoc).toMatchInlineSnapshot(` Object { "attributes": Object { @@ -83,7 +86,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - const migratedDoc = migration(doc); + const migratedDoc = migration(doc, contextMock); expect(migratedDoc).toMatchInlineSnapshot(` Object { "attributes": Object { @@ -122,7 +125,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "kibanaSavedObjectMeta": Object { @@ -160,7 +163,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "kibanaSavedObjectMeta": Object { @@ -198,7 +201,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - const migratedDoc = migration(doc); + const migratedDoc = migration(doc, contextMock); expect(migratedDoc).toMatchInlineSnapshot(` Object { "attributes": Object { @@ -237,7 +240,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - const migratedDoc = migration(doc); + const migratedDoc = migration(doc, contextMock); expect(migratedDoc).toMatchInlineSnapshot(` Object { "attributes": Object { @@ -291,7 +294,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, }; - const migratedDoc = migration(doc); + const migratedDoc = migration(doc, contextMock); expect(migratedDoc).toMatchInlineSnapshot(` Object { @@ -331,7 +334,7 @@ Object { panelsJSON: 123, }, } as SavedObjectUnsanitizedDoc; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "panelsJSON": 123, @@ -349,7 +352,7 @@ Object { panelsJSON: '{123abc}', }, } as SavedObjectUnsanitizedDoc; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "panelsJSON": "{123abc}", @@ -367,7 +370,7 @@ Object { panelsJSON: '{}', }, } as SavedObjectUnsanitizedDoc; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "panelsJSON": "{}", @@ -385,7 +388,7 @@ Object { panelsJSON: '[{"id":"123"}]', }, } as SavedObjectUnsanitizedDoc; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "panelsJSON": "[{\\"id\\":\\"123\\"}]", @@ -403,7 +406,7 @@ Object { panelsJSON: '[{"type":"visualization"}]', }, } as SavedObjectUnsanitizedDoc; - expect(migration(doc)).toMatchInlineSnapshot(` + expect(migration(doc, contextMock)).toMatchInlineSnapshot(` Object { "attributes": Object { "panelsJSON": "[{\\"type\\":\\"visualization\\"}]", @@ -422,7 +425,7 @@ Object { '[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]', }, } as SavedObjectUnsanitizedDoc; - const migratedDoc = migration(doc); + const migratedDoc = migration(doc, contextMock); expect(migratedDoc).toMatchInlineSnapshot(` Object { "attributes": Object { diff --git a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts index 7c1d0568cd3d71..4f7945d6dd6017 100644 --- a/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts +++ b/src/plugins/dashboard/server/saved_objects/dashboard_migrations.ts @@ -19,7 +19,7 @@ import { get, flow } from 'lodash'; -import { SavedObjectMigrationFn, SavedObjectUnsanitizedDoc } from 'kibana/server'; +import { SavedObjectMigrationFn } from 'kibana/server'; import { migrations730 } from './migrations_730'; import { migrateMatchAllQuery } from './migrate_match_all_query'; import { DashboardDoc700To720 } from '../../common'; @@ -62,7 +62,7 @@ function migrateIndexPattern(doc: DashboardDoc700To720) { doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource); } -const migrations700: SavedObjectMigrationFn = (doc): DashboardDoc700To720 => { +const migrations700: SavedObjectMigrationFn = (doc): DashboardDoc700To720 => { // Set new "references" attribute doc.references = doc.references || []; @@ -111,7 +111,7 @@ export const dashboardSavedObjectTypeMigrations = { * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7 * only contained the 6.7.2 migration and not the 7.0.1 migration. */ - '6.7.2': flow(migrateMatchAllQuery), - '7.0.0': flow<(doc: SavedObjectUnsanitizedDoc) => DashboardDoc700To720>(migrations700), - '7.3.0': flow(migrations730), + '6.7.2': flow>(migrateMatchAllQuery), + '7.0.0': flow>(migrations700), + '7.3.0': flow>(migrations730), }; diff --git a/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts b/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts index 5b8582bf821ef5..db2fbeb2788023 100644 --- a/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts +++ b/src/plugins/dashboard/server/saved_objects/migrate_match_all_query.ts @@ -21,7 +21,7 @@ import { SavedObjectMigrationFn } from 'kibana/server'; import { get } from 'lodash'; import { DEFAULT_QUERY_LANGUAGE } from '../../../data/common'; -export const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { +export const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); if (searchSourceJSON) { diff --git a/src/plugins/dashboard/server/saved_objects/migrations_730.test.ts b/src/plugins/dashboard/server/saved_objects/migrations_730.test.ts index aa744324428a41..a58df547fa5224 100644 --- a/src/plugins/dashboard/server/saved_objects/migrations_730.test.ts +++ b/src/plugins/dashboard/server/saved_objects/migrations_730.test.ts @@ -17,19 +17,13 @@ * under the License. */ +import { savedObjectsServiceMock } from '../../../../core/server/mocks'; import { dashboardSavedObjectTypeMigrations as migrations } from './dashboard_migrations'; import { migrations730 } from './migrations_730'; import { DashboardDoc700To720, DashboardDoc730ToLatest, DashboardDocPre700 } from '../../common'; import { RawSavedDashboardPanel730ToLatest } from '../../common'; -const mockContext = { - log: { - warning: () => {}, - warn: () => {}, - debug: () => {}, - info: () => {}, - }, -}; +const mockContext = savedObjectsServiceMock.createMigrationContext(); test('dashboard migration 7.3.0 migrates filters to query on search source', () => { const doc: DashboardDoc700To720 = { @@ -95,7 +89,7 @@ test('dashboard migration 7.3.0 migrates filters to query on search source when }, }; - const doc700: DashboardDoc700To720 = migrations['7.0.0'](doc); + const doc700 = migrations['7.0.0'](doc, mockContext); const newDoc = migrations['7.3.0'](doc700, mockContext); const parsedSearchSource = JSON.parse(newDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON); @@ -127,7 +121,7 @@ test('dashboard migration works when panelsJSON is missing panelIndex', () => { }, }; - const doc700: DashboardDoc700To720 = migrations['7.0.0'](doc); + const doc700 = migrations['7.0.0'](doc, mockContext); const newDoc = migrations['7.3.0'](doc700, mockContext); const parsedSearchSource = JSON.parse(newDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON); diff --git a/src/plugins/data/server/saved_objects/index_pattern_migrations.ts b/src/plugins/data/server/saved_objects/index_pattern_migrations.ts index 7a16386ea484c8..c64f7361a8cf4f 100644 --- a/src/plugins/data/server/saved_objects/index_pattern_migrations.ts +++ b/src/plugins/data/server/saved_objects/index_pattern_migrations.ts @@ -20,7 +20,7 @@ import { flow, omit } from 'lodash'; import { SavedObjectMigrationFn } from 'kibana/server'; -const migrateAttributeTypeAndAttributeTypeMeta: SavedObjectMigrationFn = doc => ({ +const migrateAttributeTypeAndAttributeTypeMeta: SavedObjectMigrationFn = doc => ({ ...doc, attributes: { ...doc.attributes, @@ -29,7 +29,7 @@ const migrateAttributeTypeAndAttributeTypeMeta: SavedObjectMigrationFn = doc => }, }); -const migrateSubTypeAndParentFieldProperties: SavedObjectMigrationFn = doc => { +const migrateSubTypeAndParentFieldProperties: SavedObjectMigrationFn = doc => { if (!doc.attributes.fields) return doc; const fieldsString = doc.attributes.fields; diff --git a/src/plugins/data/server/saved_objects/search_migrations.ts b/src/plugins/data/server/saved_objects/search_migrations.ts index 45fa5e11e2a3d7..c8ded51193c92b 100644 --- a/src/plugins/data/server/saved_objects/search_migrations.ts +++ b/src/plugins/data/server/saved_objects/search_migrations.ts @@ -21,7 +21,7 @@ import { flow, get } from 'lodash'; import { SavedObjectMigrationFn } from 'kibana/server'; import { DEFAULT_QUERY_LANGUAGE } from '../../common'; -const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { +const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); if (searchSourceJSON) { @@ -55,7 +55,7 @@ const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { return doc; }; -const migrateIndexPattern: SavedObjectMigrationFn = doc => { +const migrateIndexPattern: SavedObjectMigrationFn = doc => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); if (typeof searchSourceJSON !== 'string') { return doc; @@ -97,13 +97,13 @@ const migrateIndexPattern: SavedObjectMigrationFn = doc => { return doc; }; -const setNewReferences: SavedObjectMigrationFn = (doc, context) => { +const setNewReferences: SavedObjectMigrationFn = (doc, context) => { doc.references = doc.references || []; // Migrate index pattern return migrateIndexPattern(doc, context); }; -const migrateSearchSortToNestedArray: SavedObjectMigrationFn = doc => { +const migrateSearchSortToNestedArray: SavedObjectMigrationFn = doc => { const sort = get(doc, 'attributes.sort'); if (!sort) return doc; @@ -122,7 +122,7 @@ const migrateSearchSortToNestedArray: SavedObjectMigrationFn = doc => { }; export const searchSavedObjectTypeMigrations = { - '6.7.2': flow(migrateMatchAllQuery), - '7.0.0': flow(setNewReferences), - '7.4.0': flow(migrateSearchSortToNestedArray), + '6.7.2': flow>(migrateMatchAllQuery), + '7.0.0': flow>(setNewReferences), + '7.4.0': flow>(migrateSearchSortToNestedArray), }; diff --git a/src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts b/src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts index 34922976f22ff5..1e5508b44ee0ec 100644 --- a/src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts +++ b/src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts @@ -20,7 +20,7 @@ import { flow } from 'lodash'; import { SavedObjectMigrationFn, SavedObjectsType } from 'kibana/server'; -const resetCount: SavedObjectMigrationFn = doc => ({ +const resetCount: SavedObjectMigrationFn = doc => ({ ...doc, attributes: { ...doc.attributes, diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts index 94473e35a942d5..f6455d0c1e43f4 100644 --- a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts @@ -21,7 +21,7 @@ import { SavedObjectMigrationFn } from 'kibana/server'; import { cloneDeep, get, omit, has, flow } from 'lodash'; import { DEFAULT_QUERY_LANGUAGE } from '../../../data/common'; -const migrateIndexPattern: SavedObjectMigrationFn = doc => { +const migrateIndexPattern: SavedObjectMigrationFn = doc => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); if (typeof searchSourceJSON !== 'string') { return doc; @@ -64,7 +64,7 @@ const migrateIndexPattern: SavedObjectMigrationFn = doc => { }; // [TSVB] Migrate percentile-rank aggregation (value -> values) -const migratePercentileRankAggregation: SavedObjectMigrationFn = doc => { +const migratePercentileRankAggregation: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); let visState; @@ -100,7 +100,7 @@ const migratePercentileRankAggregation: SavedObjectMigrationFn = doc => { }; // [TSVB] Remove stale opperator key -const migrateOperatorKeyTypo: SavedObjectMigrationFn = doc => { +const migrateOperatorKeyTypo: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); let visState; @@ -132,7 +132,7 @@ const migrateOperatorKeyTypo: SavedObjectMigrationFn = doc => { }; // Migrate date histogram aggregation (remove customInterval) -const migrateDateHistogramAggregation: SavedObjectMigrationFn = doc => { +const migrateDateHistogramAggregation: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); let visState; @@ -174,7 +174,7 @@ const migrateDateHistogramAggregation: SavedObjectMigrationFn = doc => { return doc; }; -const removeDateHistogramTimeZones: SavedObjectMigrationFn = doc => { +const removeDateHistogramTimeZones: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); if (visStateJSON) { let visState; @@ -206,7 +206,7 @@ const removeDateHistogramTimeZones: SavedObjectMigrationFn = doc => { // migrate gauge verticalSplit to alignment // https://github.com/elastic/kibana/issues/34636 -const migrateGaugeVerticalSplitToAlignment: SavedObjectMigrationFn = (doc, logger) => { +const migrateGaugeVerticalSplitToAlignment: SavedObjectMigrationFn = (doc, logger) => { const visStateJSON = get(doc, 'attributes.visState'); if (visStateJSON) { @@ -241,7 +241,7 @@ const migrateGaugeVerticalSplitToAlignment: SavedObjectMigrationFn = (doc, logge Path to the series array is thus: attributes.visState. */ -const transformFilterStringToQueryObject: SavedObjectMigrationFn = (doc, logger) => { +const transformFilterStringToQueryObject: SavedObjectMigrationFn = (doc, logger) => { // Migrate filters // If any filters exist and they are a string, we assume it to be lucene and transform the filter into an object accordingly const newDoc = cloneDeep(doc); @@ -325,7 +325,7 @@ const transformFilterStringToQueryObject: SavedObjectMigrationFn = (doc, logger) return newDoc; }; -const transformSplitFiltersStringToQueryObject: SavedObjectMigrationFn = doc => { +const transformSplitFiltersStringToQueryObject: SavedObjectMigrationFn = doc => { // Migrate split_filters in TSVB objects that weren't migrated in 7.3 // If any filters exist and they are a string, we assume them to be lucene syntax and transform the filter into an object accordingly const newDoc = cloneDeep(doc); @@ -370,7 +370,7 @@ const transformSplitFiltersStringToQueryObject: SavedObjectMigrationFn = doc => return newDoc; }; -const migrateFiltersAggQuery: SavedObjectMigrationFn = doc => { +const migrateFiltersAggQuery: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); if (visStateJSON) { @@ -402,7 +402,7 @@ const migrateFiltersAggQuery: SavedObjectMigrationFn = doc => { return doc; }; -const replaceMovAvgToMovFn: SavedObjectMigrationFn = (doc, logger) => { +const replaceMovAvgToMovFn: SavedObjectMigrationFn = (doc, logger) => { const visStateJSON = get(doc, 'attributes.visState'); let visState; @@ -450,7 +450,7 @@ const replaceMovAvgToMovFn: SavedObjectMigrationFn = (doc, logger) => { return doc; }; -const migrateFiltersAggQueryStringQueries: SavedObjectMigrationFn = (doc, logger) => { +const migrateFiltersAggQueryStringQueries: SavedObjectMigrationFn = (doc, logger) => { const visStateJSON = get(doc, 'attributes.visState'); if (visStateJSON) { @@ -483,12 +483,12 @@ const migrateFiltersAggQueryStringQueries: SavedObjectMigrationFn = (doc, logger return doc; }; -const addDocReferences: SavedObjectMigrationFn = doc => ({ +const addDocReferences: SavedObjectMigrationFn = doc => ({ ...doc, references: doc.references || [], }); -const migrateSavedSearch: SavedObjectMigrationFn = doc => { +const migrateSavedSearch: SavedObjectMigrationFn = doc => { const savedSearchId = get(doc, 'attributes.savedSearchId'); if (savedSearchId && doc.references) { @@ -505,7 +505,7 @@ const migrateSavedSearch: SavedObjectMigrationFn = doc => { return doc; }; -const migrateControls: SavedObjectMigrationFn = doc => { +const migrateControls: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); if (visStateJSON) { @@ -536,7 +536,7 @@ const migrateControls: SavedObjectMigrationFn = doc => { return doc; }; -const migrateTableSplits: SavedObjectMigrationFn = doc => { +const migrateTableSplits: SavedObjectMigrationFn = doc => { try { const visState = JSON.parse(doc.attributes.visState); if (get(visState, 'type') !== 'table') { @@ -572,7 +572,7 @@ const migrateTableSplits: SavedObjectMigrationFn = doc => { } }; -const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { +const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); if (searchSourceJSON) { @@ -606,7 +606,7 @@ const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { }; // [TSVB] Default color palette is changing, keep the default for older viz -const migrateTsvbDefaultColorPalettes: SavedObjectMigrationFn = doc => { +const migrateTsvbDefaultColorPalettes: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); let visState; @@ -649,27 +649,30 @@ export const visualizationSavedObjectTypeMigrations = { * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7 * only contained the 6.7.2 migration and not the 7.0.1 migration. */ - '6.7.2': flow(migrateMatchAllQuery, removeDateHistogramTimeZones), - '7.0.0': flow( + '6.7.2': flow>( + migrateMatchAllQuery, + removeDateHistogramTimeZones + ), + '7.0.0': flow>( addDocReferences, migrateIndexPattern, migrateSavedSearch, migrateControls, migrateTableSplits ), - '7.0.1': flow(removeDateHistogramTimeZones), - '7.2.0': flow( + '7.0.1': flow>(removeDateHistogramTimeZones), + '7.2.0': flow>( migratePercentileRankAggregation, migrateDateHistogramAggregation ), - '7.3.0': flow( + '7.3.0': flow>( migrateGaugeVerticalSplitToAlignment, transformFilterStringToQueryObject, migrateFiltersAggQuery, replaceMovAvgToMovFn ), - '7.3.1': flow(migrateFiltersAggQueryStringQueries), - '7.4.2': flow(transformSplitFiltersStringToQueryObject), - '7.7.0': flow(migrateOperatorKeyTypo), - '7.8.0': flow(migrateTsvbDefaultColorPalettes), + '7.3.1': flow>(migrateFiltersAggQueryStringQueries), + '7.4.2': flow>(transformSplitFiltersStringToQueryObject), + '7.7.0': flow>(migrateOperatorKeyTypo), + '7.8.0': flow>(migrateTsvbDefaultColorPalettes), }; diff --git a/x-pack/plugins/graph/server/saved_objects/migrations.ts b/x-pack/plugins/graph/server/saved_objects/migrations.ts index e77d2ea0fb7c97..beb31d548c6702 100644 --- a/x-pack/plugins/graph/server/saved_objects/migrations.ts +++ b/x-pack/plugins/graph/server/saved_objects/migrations.ts @@ -8,7 +8,7 @@ import { get } from 'lodash'; import { SavedObjectUnsanitizedDoc } from 'kibana/server'; export const graphMigrations = { - '7.0.0': (doc: SavedObjectUnsanitizedDoc) => { + '7.0.0': (doc: SavedObjectUnsanitizedDoc) => { // Set new "references" attribute doc.references = doc.references || []; // Migrate index pattern diff --git a/x-pack/plugins/lens/server/migrations.ts b/x-pack/plugins/lens/server/migrations.ts index 51fcd3b6198c3b..583fba1a4a9992 100644 --- a/x-pack/plugins/lens/server/migrations.ts +++ b/x-pack/plugins/lens/server/migrations.ts @@ -19,7 +19,8 @@ interface XYLayerPre77 { * Removes the `lens_auto_date` subexpression from a stored expression * string. For example: aggConfigs={lens_auto_date aggConfigs="JSON string"} */ -const removeLensAutoDate: SavedObjectMigrationFn = (doc, context) => { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const removeLensAutoDate: SavedObjectMigrationFn = (doc, context) => { const expression: string = doc.attributes?.expression; try { const ast = fromExpression(expression); @@ -73,7 +74,8 @@ const removeLensAutoDate: SavedObjectMigrationFn = (doc, context) => { /** * Adds missing timeField arguments to esaggs in the Lens expression */ -const addTimeFieldToEsaggs: SavedObjectMigrationFn = (doc, context) => { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const addTimeFieldToEsaggs: SavedObjectMigrationFn = (doc, context) => { const expression: string = doc.attributes?.expression; try { @@ -131,7 +133,8 @@ const addTimeFieldToEsaggs: SavedObjectMigrationFn = (doc, context) => { } }; -export const migrations: Record = { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const migrations: Record> = { '7.7.0': doc => { const newDoc = cloneDeep(doc); if (newDoc.attributes?.visualizationType === 'lnsXY') { diff --git a/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts b/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts index b063404f68e4fb..65a810ff94a1f5 100644 --- a/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts +++ b/x-pack/plugins/spaces/server/saved_objects/migrations/migrate_6x.ts @@ -6,7 +6,7 @@ import { SavedObjectMigrationFn } from 'src/core/server'; -export const migrateToKibana660: SavedObjectMigrationFn = doc => { +export const migrateToKibana660: SavedObjectMigrationFn = doc => { if (!doc.attributes.hasOwnProperty('disabledFeatures')) { doc.attributes.disabledFeatures = []; } From dcc4081bd81d080776f019e98232fe743bd226d2 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 30 Apr 2020 10:34:32 +0200 Subject: [PATCH 02/47] Dashboard url generator to preserve saved filters from destination dashboard (#64767) --- src/plugins/dashboard/public/plugin.tsx | 12 +- .../dashboard/public/url_generator.test.ts | 207 +++++++++++++++++- src/plugins/dashboard/public/url_generator.ts | 39 +++- 3 files changed, 244 insertions(+), 14 deletions(-) diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index 5f6b67ee6ad202..7de054f2eaa9c8 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -141,10 +141,14 @@ export class DashboardPlugin if (share) { share.urlGenerators.registerUrlGenerator( - createDirectAccessDashboardLinkGenerator(async () => ({ - appBasePath: (await startServices)[0].application.getUrlForApp('dashboard'), - useHashedUrl: (await startServices)[0].uiSettings.get('state:storeInSessionStorage'), - })) + createDirectAccessDashboardLinkGenerator(async () => { + const [coreStart, , selfStart] = await startServices; + return { + appBasePath: coreStart.application.getUrlForApp('dashboard'), + useHashedUrl: coreStart.uiSettings.get('state:storeInSessionStorage'), + savedDashboardLoader: selfStart.getSavedDashboardLoader(), + }; + }) ); } diff --git a/src/plugins/dashboard/public/url_generator.test.ts b/src/plugins/dashboard/public/url_generator.test.ts index d48aacc1d8c1e4..248a3f991d6cbf 100644 --- a/src/plugins/dashboard/public/url_generator.test.ts +++ b/src/plugins/dashboard/public/url_generator.test.ts @@ -21,10 +21,33 @@ import { createDirectAccessDashboardLinkGenerator } from './url_generator'; import { hashedItemStore } from '../../kibana_utils/public'; // eslint-disable-next-line import { mockStorage } from '../../kibana_utils/public/storage/hashed_item_store/mock'; -import { esFilters } from '../../data/public'; +import { esFilters, Filter } from '../../data/public'; +import { SavedObjectLoader } from '../../saved_objects/public'; const APP_BASE_PATH: string = 'xyz/app/kibana'; +const createMockDashboardLoader = ( + dashboardToFilters: { + [dashboardId: string]: () => Filter[]; + } = {} +) => { + return { + get: async (dashboardId: string) => { + return { + searchSource: { + getField: (field: string) => { + if (field === 'filter') + return dashboardToFilters[dashboardId] ? dashboardToFilters[dashboardId]() : []; + throw new Error( + `createMockDashboardLoader > searchSource > getField > ${field} is not mocked` + ); + }, + }, + }; + }, + } as SavedObjectLoader; +}; + describe('dashboard url generator', () => { beforeEach(() => { // @ts-ignore @@ -33,7 +56,11 @@ describe('dashboard url generator', () => { test('creates a link to a saved dashboard', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false }) + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader(), + }) ); const url = await generator.createUrl!({}); expect(url).toMatchInlineSnapshot(`"xyz/app/kibana#/dashboard?_a=()&_g=()"`); @@ -41,7 +68,11 @@ describe('dashboard url generator', () => { test('creates a link with global time range set up', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false }) + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader(), + }) ); const url = await generator.createUrl!({ timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, @@ -53,7 +84,11 @@ describe('dashboard url generator', () => { test('creates a link with filters, time range, refresh interval and query to a saved object', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false }) + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader(), + }) ); const url = await generator.createUrl!({ timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, @@ -89,7 +124,11 @@ describe('dashboard url generator', () => { test('if no useHash setting is given, uses the one was start services', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: true }) + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: true, + savedDashboardLoader: createMockDashboardLoader(), + }) ); const url = await generator.createUrl!({ timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, @@ -99,7 +138,11 @@ describe('dashboard url generator', () => { test('can override a false useHash ui setting', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: false }) + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader(), + }) ); const url = await generator.createUrl!({ timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, @@ -110,7 +153,11 @@ describe('dashboard url generator', () => { test('can override a true useHash ui setting', async () => { const generator = createDirectAccessDashboardLinkGenerator(() => - Promise.resolve({ appBasePath: APP_BASE_PATH, useHashedUrl: true }) + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: true, + savedDashboardLoader: createMockDashboardLoader(), + }) ); const url = await generator.createUrl!({ timeRange: { to: 'now', from: 'now-15m', mode: 'relative' }, @@ -118,4 +165,150 @@ describe('dashboard url generator', () => { }); expect(url.indexOf('relative')).toBeGreaterThan(1); }); + + describe('preserving saved filters', () => { + const savedFilter1 = { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'savedfilter1' }, + }; + + const savedFilter2 = { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'savedfilter2' }, + }; + + const appliedFilter = { + meta: { + alias: null, + disabled: false, + negate: false, + }, + query: { query: 'appliedfilter' }, + }; + + test('attaches filters from destination dashboard', async () => { + const generator = createDirectAccessDashboardLinkGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader({ + ['dashboard1']: () => [savedFilter1], + ['dashboard2']: () => [savedFilter2], + }), + }) + ); + + const urlToDashboard1 = await generator.createUrl!({ + dashboardId: 'dashboard1', + filters: [appliedFilter], + }); + + expect(urlToDashboard1).toEqual(expect.stringContaining('query:savedfilter1')); + expect(urlToDashboard1).toEqual(expect.stringContaining('query:appliedfilter')); + + const urlToDashboard2 = await generator.createUrl!({ + dashboardId: 'dashboard2', + filters: [appliedFilter], + }); + + expect(urlToDashboard2).toEqual(expect.stringContaining('query:savedfilter2')); + expect(urlToDashboard2).toEqual(expect.stringContaining('query:appliedfilter')); + }); + + test("doesn't fail if can't retrieve filters from destination dashboard", async () => { + const generator = createDirectAccessDashboardLinkGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader({ + ['dashboard1']: () => { + throw new Error('Not found'); + }, + }), + }) + ); + + const url = await generator.createUrl!({ + dashboardId: 'dashboard1', + filters: [appliedFilter], + }); + + expect(url).not.toEqual(expect.stringContaining('query:savedfilter1')); + expect(url).toEqual(expect.stringContaining('query:appliedfilter')); + }); + + test('can enforce empty filters', async () => { + const generator = createDirectAccessDashboardLinkGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader({ + ['dashboard1']: () => [savedFilter1], + }), + }) + ); + + const url = await generator.createUrl!({ + dashboardId: 'dashboard1', + filters: [], + preserveSavedFilters: false, + }); + + expect(url).not.toEqual(expect.stringContaining('query:savedfilter1')); + expect(url).not.toEqual(expect.stringContaining('query:appliedfilter')); + expect(url).toMatchInlineSnapshot( + `"xyz/app/kibana#/dashboard/dashboard1?_a=(filters:!())&_g=(filters:!())"` + ); + }); + + test('no filters in result url if no filters applied', async () => { + const generator = createDirectAccessDashboardLinkGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader({ + ['dashboard1']: () => [savedFilter1], + }), + }) + ); + + const url = await generator.createUrl!({ + dashboardId: 'dashboard1', + }); + expect(url).not.toEqual(expect.stringContaining('filters')); + expect(url).toMatchInlineSnapshot(`"xyz/app/kibana#/dashboard/dashboard1?_a=()&_g=()"`); + }); + + test('can turn off preserving filters', async () => { + const generator = createDirectAccessDashboardLinkGenerator(() => + Promise.resolve({ + appBasePath: APP_BASE_PATH, + useHashedUrl: false, + savedDashboardLoader: createMockDashboardLoader({ + ['dashboard1']: () => [savedFilter1], + }), + }) + ); + const urlWithPreservedFiltersTurnedOff = await generator.createUrl!({ + dashboardId: 'dashboard1', + filters: [appliedFilter], + preserveSavedFilters: false, + }); + + expect(urlWithPreservedFiltersTurnedOff).not.toEqual( + expect.stringContaining('query:savedfilter1') + ); + expect(urlWithPreservedFiltersTurnedOff).toEqual( + expect.stringContaining('query:appliedfilter') + ); + }); + }); }); diff --git a/src/plugins/dashboard/public/url_generator.ts b/src/plugins/dashboard/public/url_generator.ts index 0fdf395e75bcaa..6f121ceb2d3731 100644 --- a/src/plugins/dashboard/public/url_generator.ts +++ b/src/plugins/dashboard/public/url_generator.ts @@ -27,6 +27,7 @@ import { } from '../../data/public'; import { setStateToKbnUrl } from '../../kibana_utils/public'; import { UrlGeneratorsDefinition, UrlGeneratorState } from '../../share/public'; +import { SavedObjectLoader } from '../../saved_objects/public'; export const STATE_STORAGE_KEY = '_a'; export const GLOBAL_STATE_STORAGE_KEY = '_g'; @@ -64,10 +65,22 @@ export type DashboardAppLinkGeneratorState = UrlGeneratorState<{ * whether to hash the data in the url to avoid url length issues. */ useHash?: boolean; + + /** + * When `true` filters from saved filters from destination dashboard as merged with applied filters + * When `false` applied filters take precedence and override saved filters + * + * true is default + */ + preserveSavedFilters?: boolean; }>; export const createDirectAccessDashboardLinkGenerator = ( - getStartServices: () => Promise<{ appBasePath: string; useHashedUrl: boolean }> + getStartServices: () => Promise<{ + appBasePath: string; + useHashedUrl: boolean; + savedDashboardLoader: SavedObjectLoader; + }> ): UrlGeneratorsDefinition => ({ id: DASHBOARD_APP_URL_GENERATOR, createUrl: async state => { @@ -76,6 +89,19 @@ export const createDirectAccessDashboardLinkGenerator = ( const appBasePath = startServices.appBasePath; const hash = state.dashboardId ? `dashboard/${state.dashboardId}` : `dashboard`; + const getSavedFiltersFromDestinationDashboardIfNeeded = async (): Promise => { + if (state.preserveSavedFilters === false) return []; + if (!state.dashboardId) return []; + try { + const dashboard = await startServices.savedDashboardLoader.get(state.dashboardId); + return dashboard?.searchSource?.getField('filter') ?? []; + } catch (e) { + // in case dashboard is missing, built the url without those filters + // dashboard app will handle redirect to landing page with toast message + return []; + } + }; + const cleanEmptyKeys = (stateObj: Record) => { Object.keys(stateObj).forEach(key => { if (stateObj[key] === undefined) { @@ -85,11 +111,18 @@ export const createDirectAccessDashboardLinkGenerator = ( return stateObj; }; + // leave filters `undefined` if no filters was applied + // in this case dashboard will restore saved filters on its own + const filters = state.filters && [ + ...(await getSavedFiltersFromDestinationDashboardIfNeeded()), + ...state.filters, + ]; + const appStateUrl = setStateToKbnUrl( STATE_STORAGE_KEY, cleanEmptyKeys({ query: state.query, - filters: state.filters?.filter(f => !esFilters.isFilterPinned(f)), + filters: filters?.filter(f => !esFilters.isFilterPinned(f)), }), { useHash }, `${appBasePath}#/${hash}` @@ -99,7 +132,7 @@ export const createDirectAccessDashboardLinkGenerator = ( GLOBAL_STATE_STORAGE_KEY, cleanEmptyKeys({ time: state.timeRange, - filters: state.filters?.filter(f => esFilters.isFilterPinned(f)), + filters: filters?.filter(f => esFilters.isFilterPinned(f)), refreshInterval: state.refreshInterval, }), { useHash }, From 21bc46f5af1ede7dd209e5a58a224edc37d4b986 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 30 Apr 2020 11:07:14 +0100 Subject: [PATCH 03/47] [ML] Disable data frame anaylics clone button based on permission (#64830) --- .../components/analytics_list/action_clone.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx index 8c65af1d92959f..cc75ddbe08cfb9 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/action_clone.tsx @@ -18,6 +18,7 @@ import { } from '../../hooks/use_create_analytics_form'; import { State } from '../../hooks/use_create_analytics_form/state'; import { DataFrameAnalyticsListRow } from './common'; +import { checkPermission } from '../../../../../capabilities/check_capabilities'; interface PropDefinition { /** @@ -322,6 +323,8 @@ interface CloneActionProps { * to support EuiContext with a valid DOM structure without nested buttons. */ export const CloneAction: FC = ({ createAnalyticsForm, item }) => { + const canCreateDataFrameAnalytics: boolean = checkPermission('canCreateDataFrameAnalytics'); + const buttonText = i18n.translate('xpack.ml.dataframe.analyticsList.cloneJobButtonLabel', { defaultMessage: 'Clone job', }); @@ -338,6 +341,7 @@ export const CloneAction: FC = ({ createAnalyticsForm, item }) iconType="copy" onClick={onClick} aria-label={buttonText} + disabled={canCreateDataFrameAnalytics === false} > {buttonText} From 3c56a8e296b4b08c9f73a4924b189b67518a3441 Mon Sep 17 00:00:00 2001 From: Sonja Krause-Harder Date: Thu, 30 Apr 2020 12:43:34 +0200 Subject: [PATCH 04/47] [EPM] Handle constant_keyword type in KB index patterns and ES index templates (#64876) * Unit-test constant_keyword mapping generation * Treat constant_keyword as string in kibana index patterns --- .../elasticsearch/template/template.test.ts | 18 ++++++++++++++++++ .../epm/kibana/index_pattern/install.test.ts | 2 ++ .../epm/kibana/index_pattern/install.ts | 1 + 3 files changed, 21 insertions(+) diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts index 3679c577ee5713..25180244b02147 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts @@ -309,3 +309,21 @@ test('tests processing object field with property, reverse order', () => { const mappings = generateMappings(processedFields); expect(JSON.stringify(mappings)).toEqual(JSON.stringify(objectFieldWithPropertyReversedMapping)); }); + +test('tests constant_keyword field type handling', () => { + const constantKeywordLiteralYaml = ` +- name: constantKeyword + type: constant_keyword + `; + const constantKeywordMapping = { + properties: { + constantKeyword: { + type: 'constant_keyword', + }, + }, + }; + const fields: Field[] = safeLoad(constantKeywordLiteralYaml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(JSON.stringify(mappings)).toEqual(JSON.stringify(constantKeywordMapping)); +}); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.test.ts index bc1694348b4c2f..f1660fbc015913 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.test.ts @@ -150,6 +150,7 @@ describe('creating index patterns from yaml fields', () => { { fields: [{ name: 'testField', type: 'text' }], expect: 'string' }, { fields: [{ name: 'testField', type: 'date' }], expect: 'date' }, { fields: [{ name: 'testField', type: 'geo_point' }], expect: 'geo_point' }, + { fields: [{ name: 'testField', type: 'constant_keyword' }], expect: 'string' }, ]; tests.forEach(test => { @@ -191,6 +192,7 @@ describe('creating index patterns from yaml fields', () => { attr: 'aggregatable', }, { fields: [{ name, type: 'keyword' }], expect: true, attr: 'aggregatable' }, + { fields: [{ name, type: 'constant_keyword' }], expect: true, attr: 'aggregatable' }, { fields: [{ name, type: 'text', aggregatable: true }], expect: false, attr: 'aggregatable' }, { fields: [{ name, type: 'text' }], expect: false, attr: 'aggregatable' }, { diff --git a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts index 05e64c6565dc67..ec657820a22251 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts @@ -47,6 +47,7 @@ const typeMap: TypeMap = { date: 'date', ip: 'ip', boolean: 'boolean', + constant_keyword: 'string', }; export interface IndexPatternField { From b140ad7194338610b791750001730279a4806ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Thu, 30 Apr 2020 08:56:15 -0400 Subject: [PATCH 05/47] Remove edit alert button from alerts list (#64643) * Remove edit alert button from alerts list * Remove unused code * Cleanup translation files Co-authored-by: Elastic Machine --- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../sections/alert_form/alert_form.test.tsx | 20 ++ .../components/alerts_list.test.tsx | 8 - .../alerts_list/components/alerts_list.tsx | 38 +--- .../apps/triggers_actions_ui/alerts.ts | 187 ------------------ 6 files changed, 21 insertions(+), 234 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 81dc44f3a4cb41..a3011ab5bdfa9e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -15899,7 +15899,6 @@ "xpack.triggersActionsUI.sections.alertsList.actionTypeFilterLabel": "アクションタイプ", "xpack.triggersActionsUI.sections.alertsList.addActionButtonLabel": "アラートの作成", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.alertTypeTitle": "タイプ", - "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.editLinkTitle": "編集", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.intervalTitle": "次の間隔で実行", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.nameTitle": "名前", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.tagsText": "タグ", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index e06edb45de8fa9..e373d05a7d8516 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -15904,7 +15904,6 @@ "xpack.triggersActionsUI.sections.alertsList.actionTypeFilterLabel": "操作类型", "xpack.triggersActionsUI.sections.alertsList.addActionButtonLabel": "创建告警", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.alertTypeTitle": "类型", - "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.editLinkTitle": "编辑", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.intervalTitle": "运行间隔", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.nameTitle": "名称", "xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.tagsText": "标记", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx index 72c22f46f217e7..8406987e4ed9df 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.test.tsx @@ -190,5 +190,25 @@ describe('alert_form', () => { const alertTypeSelectOptions = wrapper.find('[data-test-subj="selectedAlertTypeTitle"]'); expect(alertTypeSelectOptions.exists()).toBeTruthy(); }); + + it('should update throttle value', async () => { + const newThrottle = 17; + await setup(); + const throttleField = wrapper.find('[data-test-subj="throttleInput"]'); + expect(throttleField.exists()).toBeTruthy(); + throttleField.at(1).simulate('change', { target: { value: newThrottle.toString() } }); + const throttleFieldAfterUpdate = wrapper.find('[data-test-subj="throttleInput"]'); + expect(throttleFieldAfterUpdate.at(1).prop('value')).toEqual(newThrottle); + }); + + it('should unset throttle value', async () => { + const newThrottle = ''; + await setup(); + const throttleField = wrapper.find('[data-test-subj="throttleInput"]'); + expect(throttleField.exists()).toBeTruthy(); + throttleField.at(1).simulate('change', { target: { value: newThrottle } }); + const throttleFieldAfterUpdate = wrapper.find('[data-test-subj="throttleInput"]'); + expect(throttleFieldAfterUpdate.at(1).prop('value')).toEqual(newThrottle); + }); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx index 66aa02e1930a3e..a51ebc31267853 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx @@ -245,10 +245,6 @@ describe('alerts_list component with items', () => { expect(wrapper.find('EuiBasicTable')).toHaveLength(1); expect(wrapper.find('EuiTableRow')).toHaveLength(2); }); - it('renders edit button for registered alert types', async () => { - await setup(); - expect(wrapper.find('[data-test-subj="alertsTableCell-editLink"]').length).toBeGreaterThan(0); - }); }); describe('alerts_list component empty with show only capability', () => { @@ -442,8 +438,4 @@ describe('alerts_list with show only capability', () => { expect(wrapper.find('EuiTableRow')).toHaveLength(2); // TODO: check delete button }); - it('not renders edit button for non registered alert types', async () => { - await setup(); - expect(wrapper.find('[data-test-subj="alertsTableCell-editLink"]').length).toBe(0); - }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index 1103d7c3921a7e..2d9cfcdbda89f8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -24,7 +24,7 @@ import { isEmpty } from 'lodash'; import { AlertsContextProvider } from '../../../context/alerts_context'; import { useAppDependencies } from '../../../app_context'; import { ActionType, Alert, AlertTableItem, AlertTypeIndex, Pagination } from '../../../../types'; -import { AlertAdd, AlertEdit } from '../../alert_form'; +import { AlertAdd } from '../../alert_form'; import { BulkOperationPopover } from '../../common/components/bulk_operation_popover'; import { AlertQuickEditButtonsWithApi as AlertQuickEditButtons } from '../../common/components/alert_quick_edit_buttons'; import { CollapsedItemActionsWithApi as CollapsedItemActions } from './collapsed_item_actions'; @@ -85,8 +85,6 @@ export const AlertsList: React.FunctionComponent = () => { data: [], totalItemCount: 0, }); - const [editedAlertItem, setEditedAlertItem] = useState(undefined); - const [editFlyoutVisible, setEditFlyoutVisibility] = useState(false); const [alertsToDelete, setAlertsToDelete] = useState([]); useEffect(() => { @@ -162,11 +160,6 @@ export const AlertsList: React.FunctionComponent = () => { } } - async function editItem(alertTableItem: AlertTableItem) { - setEditedAlertItem(alertTableItem); - setEditFlyoutVisibility(true); - } - const alertsTableColumns = [ { field: 'name', @@ -219,27 +212,6 @@ export const AlertsList: React.FunctionComponent = () => { truncateText: false, 'data-test-subj': 'alertsTableCell-interval', }, - { - name: '', - width: '50px', - render(item: AlertTableItem) { - if (!canSave || !alertTypeRegistry.has(item.alertTypeId)) { - return; - } - return ( - editItem(item)} - > - - - ); - }, - }, { name: '', width: '40px', @@ -453,14 +425,6 @@ export const AlertsList: React.FunctionComponent = () => { addFlyoutVisible={alertFlyoutVisible} setAddFlyoutVisibility={setAlertFlyoutVisibility} /> - {editFlyoutVisible && editedAlertItem ? ( - - ) : null} ); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts index 597f1ad9119b09..9e99c60b4dcb7e 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts @@ -126,193 +126,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ]); }); - it('should edit an alert', async () => { - const createdAlert = await createAlert({ - alertTypeId: '.index-threshold', - name: generateUniqueKey(), - params: { - aggType: 'count', - termSize: 5, - thresholdComparator: '>', - timeWindowSize: 5, - timeWindowUnit: 'm', - groupBy: 'all', - threshold: [1000, 5000], - index: ['.kibana_1'], - timeField: 'alert', - }, - }); - await pageObjects.common.navigateToApp('triggersActions'); - await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - - const searchResults = await pageObjects.triggersActionsUI.getAlertsList(); - expect(searchResults).to.eql([ - { - name: createdAlert.name, - tagsText: 'foo, bar', - alertType: 'Index threshold', - interval: '1m', - }, - ]); - const editLink = await testSubjects.findAll('alertsTableCell-editLink'); - await editLink[0].click(); - - const updatedAlertName = `Changed Alert Name ${generateUniqueKey()}`; - await testSubjects.setValue('alertNameInput', updatedAlertName, { clearWithKeyboard: true }); - - await find.clickByCssSelector('[data-test-subj="saveEditedAlertButton"]:not(disabled)'); - - const toastTitle = await pageObjects.common.closeToast(); - expect(toastTitle).to.eql(`Updated '${updatedAlertName}'`); - await pageObjects.common.navigateToApp('triggersActions'); - await pageObjects.triggersActionsUI.searchAlerts(updatedAlertName); - - const searchResultsAfterEdit = await pageObjects.triggersActionsUI.getAlertsList(); - expect(searchResultsAfterEdit).to.eql([ - { - name: updatedAlertName, - tagsText: 'foo, bar', - alertType: 'Index threshold', - interval: '1m', - }, - ]); - }); - - it('should set an alert throttle', async () => { - const alertName = `edit throttle ${generateUniqueKey()}`; - const createdAlert = await createAlert({ - alertTypeId: '.index-threshold', - name: alertName, - params: { - aggType: 'count', - termSize: 5, - thresholdComparator: '>', - timeWindowSize: 5, - timeWindowUnit: 'm', - groupBy: 'all', - threshold: [1000, 5000], - index: ['.kibana_1'], - timeField: 'alert', - }, - }); - await pageObjects.common.navigateToApp('triggersActions'); - await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - - const searchResults = await pageObjects.triggersActionsUI.getAlertsList(); - expect(searchResults).to.eql([ - { - name: createdAlert.name, - tagsText: 'foo, bar', - alertType: 'Index threshold', - interval: '1m', - }, - ]); - - const editLink = await testSubjects.findAll('alertsTableCell-editLink'); - await editLink[0].click(); - - await testSubjects.setValue('throttleInput', '1', { clearWithKeyboard: true }); - - await find.clickByCssSelector('[data-test-subj="saveEditedAlertButton"]:not(disabled)'); - - expect(await pageObjects.common.closeToast()).to.eql(`Updated '${createdAlert.name}'`); - - await pageObjects.common.navigateToApp('triggersActions'); - await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - await (await testSubjects.findAll('alertsTableCell-editLink'))[0].click(); - const throttleInput = await testSubjects.find('throttleInput'); - expect(await throttleInput.getAttribute('value')).to.eql('1'); - }); - - it('should unset an alert throttle', async () => { - const alertName = `edit throttle ${generateUniqueKey()}`; - const createdAlert = await createAlert({ - alertTypeId: '.index-threshold', - name: alertName, - throttle: '10m', - params: { - aggType: 'count', - termSize: 5, - thresholdComparator: '>', - timeWindowSize: 5, - timeWindowUnit: 'm', - groupBy: 'all', - threshold: [1000, 5000], - index: ['.kibana_1'], - timeField: 'alert', - }, - }); - await pageObjects.common.navigateToApp('triggersActions'); - await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - - const searchResults = await pageObjects.triggersActionsUI.getAlertsList(); - expect(searchResults).to.eql([ - { - name: createdAlert.name, - tagsText: 'foo, bar', - alertType: 'Index threshold', - interval: '1m', - }, - ]); - - const editLink = await testSubjects.findAll('alertsTableCell-editLink'); - await editLink[0].click(); - - const throttleInputToUnsetValue = await testSubjects.find('throttleInput'); - - expect(await throttleInputToUnsetValue.getAttribute('value')).to.eql('10'); - await throttleInputToUnsetValue.click(); - await throttleInputToUnsetValue.clearValueWithKeyboard(); - - expect(await throttleInputToUnsetValue.getAttribute('value')).to.eql(''); - - await find.clickByCssSelector('[data-test-subj="saveEditedAlertButton"]:not(disabled)'); - - expect(await pageObjects.common.closeToast()).to.eql(`Updated '${createdAlert.name}'`); - - await pageObjects.common.navigateToApp('triggersActions'); - await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - await (await testSubjects.findAll('alertsTableCell-editLink'))[0].click(); - const throttleInput = await testSubjects.find('throttleInput'); - expect(await throttleInput.getAttribute('value')).to.eql(''); - }); - - it('should reset alert when canceling an edit', async () => { - const createdAlert = await createAlert({ - alertTypeId: '.index-threshold', - name: generateUniqueKey(), - params: { - aggType: 'count', - termSize: 5, - thresholdComparator: '>', - timeWindowSize: 5, - timeWindowUnit: 'm', - groupBy: 'all', - threshold: [1000, 5000], - index: ['.kibana_1'], - timeField: 'alert', - }, - }); - await pageObjects.common.navigateToApp('triggersActions'); - await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name); - - const editLink = await testSubjects.findAll('alertsTableCell-editLink'); - await editLink[0].click(); - - const updatedAlertName = `Changed Alert Name ${generateUniqueKey()}`; - await testSubjects.setValue('alertNameInput', updatedAlertName); - - await testSubjects.click('cancelSaveEditedAlertButton'); - await find.waitForDeletedByCssSelector('[data-test-subj="cancelSaveEditedAlertButton"]'); - - const editLinkPostCancel = await testSubjects.findAll('alertsTableCell-editLink'); - await editLinkPostCancel[0].click(); - - const nameInputAfterCancel = await testSubjects.find('alertNameInput'); - const textAfterCancel = await nameInputAfterCancel.getAttribute('value'); - expect(textAfterCancel).to.eql(createdAlert.name); - }); - it('should search for tags', async () => { const createdAlert = await createAlert(); await pageObjects.common.navigateToApp('triggersActions'); From 497c5da763be143d65106e292e9d45bc7496c7b9 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 30 Apr 2020 14:07:32 +0100 Subject: [PATCH 06/47] [ML] Moving get filters capability to admin (#64879) * [ML] Moving get filters capability to admin * updating test --- x-pack/plugins/ml/common/types/capabilities.ts | 4 ++-- .../lib/capabilities/check_capabilities.test.ts | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/ml/common/types/capabilities.ts b/x-pack/plugins/ml/common/types/capabilities.ts index da5fd3ac252096..572217ce16eee2 100644 --- a/x-pack/plugins/ml/common/types/capabilities.ts +++ b/x-pack/plugins/ml/common/types/capabilities.ts @@ -15,8 +15,6 @@ export const userMlCapabilities = { canGetCalendars: false, // File Data Visualizer canFindFileStructure: false, - // Filters - canGetFilters: false, // Data Frame Analytics canGetDataFrameAnalytics: false, // Annotations @@ -38,6 +36,8 @@ export const adminMlCapabilities = { canStartStopDatafeed: false, canUpdateDatafeed: false, canPreviewDatafeed: false, + // Filters + canGetFilters: false, // Calendars canCreateCalendar: false, canDeleteCalendar: false, diff --git a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts index b6e95ae8373ee1..746c9da47d0adb 100644 --- a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts +++ b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts @@ -64,7 +64,6 @@ describe('check_capabilities', () => { expect(capabilities.canGetDatafeeds).toBe(true); expect(capabilities.canGetCalendars).toBe(true); expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canGetDataFrameAnalytics).toBe(true); expect(capabilities.canGetAnnotations).toBe(true); expect(capabilities.canCreateAnnotation).toBe(true); @@ -81,6 +80,7 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); expect(capabilities.canCreateFilter).toBe(false); @@ -113,7 +113,6 @@ describe('check_capabilities', () => { expect(capabilities.canGetDatafeeds).toBe(true); expect(capabilities.canGetCalendars).toBe(true); expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canGetDataFrameAnalytics).toBe(true); expect(capabilities.canGetAnnotations).toBe(true); expect(capabilities.canCreateAnnotation).toBe(true); @@ -130,6 +129,7 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDatafeed).toBe(true); expect(capabilities.canUpdateDatafeed).toBe(true); expect(capabilities.canPreviewDatafeed).toBe(true); + expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canCreateCalendar).toBe(true); expect(capabilities.canDeleteCalendar).toBe(true); expect(capabilities.canCreateFilter).toBe(true); @@ -162,7 +162,6 @@ describe('check_capabilities', () => { expect(capabilities.canGetDatafeeds).toBe(true); expect(capabilities.canGetCalendars).toBe(true); expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canGetDataFrameAnalytics).toBe(true); expect(capabilities.canGetAnnotations).toBe(true); expect(capabilities.canCreateAnnotation).toBe(false); @@ -177,6 +176,7 @@ describe('check_capabilities', () => { expect(capabilities.canUpdateJob).toBe(false); expect(capabilities.canCreateDatafeed).toBe(false); expect(capabilities.canDeleteDatafeed).toBe(false); + expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); expect(capabilities.canCreateCalendar).toBe(false); @@ -211,7 +211,6 @@ describe('check_capabilities', () => { expect(capabilities.canGetDatafeeds).toBe(true); expect(capabilities.canGetCalendars).toBe(true); expect(capabilities.canFindFileStructure).toBe(true); - expect(capabilities.canGetFilters).toBe(true); expect(capabilities.canGetDataFrameAnalytics).toBe(true); expect(capabilities.canGetAnnotations).toBe(true); expect(capabilities.canCreateAnnotation).toBe(false); @@ -228,6 +227,7 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); expect(capabilities.canCreateFilter).toBe(false); @@ -260,7 +260,6 @@ describe('check_capabilities', () => { expect(capabilities.canGetDatafeeds).toBe(false); expect(capabilities.canGetCalendars).toBe(false); expect(capabilities.canFindFileStructure).toBe(false); - expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canGetDataFrameAnalytics).toBe(false); expect(capabilities.canGetAnnotations).toBe(false); expect(capabilities.canCreateAnnotation).toBe(false); @@ -277,6 +276,7 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); expect(capabilities.canCreateFilter).toBe(false); @@ -311,7 +311,6 @@ describe('check_capabilities', () => { expect(capabilities.canGetDatafeeds).toBe(false); expect(capabilities.canGetCalendars).toBe(false); expect(capabilities.canFindFileStructure).toBe(false); - expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canGetDataFrameAnalytics).toBe(false); expect(capabilities.canGetAnnotations).toBe(false); expect(capabilities.canCreateAnnotation).toBe(false); @@ -328,6 +327,7 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDatafeed).toBe(false); expect(capabilities.canUpdateDatafeed).toBe(false); expect(capabilities.canPreviewDatafeed).toBe(false); + expect(capabilities.canGetFilters).toBe(false); expect(capabilities.canCreateCalendar).toBe(false); expect(capabilities.canDeleteCalendar).toBe(false); expect(capabilities.canCreateFilter).toBe(false); From 60ef51b9122ca6f60590da95f4f6bc75d9f48073 Mon Sep 17 00:00:00 2001 From: Henry Harding Date: Thu, 30 Apr 2020 09:28:12 -0400 Subject: [PATCH 07/47] Feature/send feedback link (#64845) * use fixed table layout * add alpha messaging flyout * Added alpha badge + data streams link * Update x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_flyout.tsx Co-Authored-By: Jen Huang * Update x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_flyout.tsx Co-Authored-By: Jen Huang * Update x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/alpha_flyout.tsx Co-Authored-By: Jen Huang * Update x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/index.tsx Co-Authored-By: Jen Huang * remove small tags * change messaging from alpha to experimental * add period * remove unused imports * fixed i18n ids * add send feedback link to header * rename "ingest management" to "ingest manager" Co-authored-by: Jen Huang Co-authored-by: Elastic Machine --- .../components/settings_flyout.tsx | 2 +- .../ingest_manager/layouts/default.tsx | 28 +++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/settings_flyout.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/settings_flyout.tsx index 92146e9ee56793..9863463e68a016 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/settings_flyout.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/settings_flyout.tsx @@ -209,7 +209,7 @@ export const SettingFlyout: React.FunctionComponent = ({ onClose }) => {

diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx index 10245e73520f75..4a9cfe02b74ace 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx @@ -96,12 +96,28 @@ export const DefaultLayout: React.FunctionComponent = ({ section, childre - setIsSettingsFlyoutOpen(true)}> - - + + + + + + + + setIsSettingsFlyoutOpen(true)}> + + + + From 1736005e5e733212f508fa36ee6d348a00bf6e93 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 30 Apr 2020 15:45:52 +0200 Subject: [PATCH 08/47] [Uptime] Remove hard coded value for monitor states histograms (#64396) --- x-pack/plugins/uptime/common/constants/index.ts | 2 +- x-pack/plugins/uptime/common/constants/query.ts | 7 ------- .../server/lib/requests/search/enrich_monitor_groups.ts | 4 ++-- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/uptime/common/constants/index.ts b/x-pack/plugins/uptime/common/constants/index.ts index 72d498056d6b3c..00baa39044a557 100644 --- a/x-pack/plugins/uptime/common/constants/index.ts +++ b/x-pack/plugins/uptime/common/constants/index.ts @@ -11,6 +11,6 @@ export { CONTEXT_DEFAULTS } from './context_defaults'; export * from './capabilities'; export * from './settings_defaults'; export { PLUGIN } from './plugin'; -export { QUERY, STATES } from './query'; +export { QUERY } from './query'; export * from './ui'; export * from './rest_api'; diff --git a/x-pack/plugins/uptime/common/constants/query.ts b/x-pack/plugins/uptime/common/constants/query.ts index d728f114aae76e..21574f1d8b27e0 100644 --- a/x-pack/plugins/uptime/common/constants/query.ts +++ b/x-pack/plugins/uptime/common/constants/query.ts @@ -25,10 +25,3 @@ export const QUERY = { 'error.type', ], }; - -export const STATES = { - // Number of results returned for a states query - LEGACY_STATES_QUERY_SIZE: 10, - // The maximum number of monitors that should be supported - MAX_MONITORS: 35000, -}; diff --git a/x-pack/plugins/uptime/server/lib/requests/search/enrich_monitor_groups.ts b/x-pack/plugins/uptime/server/lib/requests/search/enrich_monitor_groups.ts index d21259fad77a66..8612d71dfe9394 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/enrich_monitor_groups.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/enrich_monitor_groups.ts @@ -6,7 +6,7 @@ import { get, sortBy } from 'lodash'; import { QueryContext } from './query_context'; -import { QUERY, STATES } from '../../../../common/constants'; +import { QUERY } from '../../../../common/constants'; import { Check, Histogram, @@ -314,7 +314,7 @@ const getHistogramForMonitors = async ( by_id: { terms: { field: 'monitor.id', - size: STATES.LEGACY_STATES_QUERY_SIZE, + size: queryContext.size, }, aggs: { histogram: { From 027c8a8d752e1f90a133ca5a6c9bd7ca969791c8 Mon Sep 17 00:00:00 2001 From: Robert Austin Date: Thu, 30 Apr 2020 09:47:43 -0400 Subject: [PATCH 09/47] [Endpoint] Remove todos, urls to issues (#64833) --- .../public/applications/endpoint/store/hosts/middleware.ts | 1 - x-pack/plugins/endpoint/server/routes/alerts/list/lib/index.ts | 2 -- x-pack/plugins/endpoint/server/routes/metadata/index.ts | 1 - 3 files changed, 4 deletions(-) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts index d1b9a2cde4b319..bcfd6b96c9eb88 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.ts @@ -70,7 +70,6 @@ export const hostMiddlewareFactory: ImmutableMiddlewareFactory = core type: 'serverReturnedHostDetails', payload: response, }); - // FIXME: once we have the API implementation in place, we should call it parallel with the above api call and then dispatch this with the results of the second call dispatch({ type: 'serverReturnedHostPolicyResponse', payload: { diff --git a/x-pack/plugins/endpoint/server/routes/alerts/list/lib/index.ts b/x-pack/plugins/endpoint/server/routes/alerts/list/lib/index.ts index 92bd07a813d264..114251820ce4b4 100644 --- a/x-pack/plugins/endpoint/server/routes/alerts/list/lib/index.ts +++ b/x-pack/plugins/endpoint/server/routes/alerts/list/lib/index.ts @@ -60,8 +60,6 @@ export const getRequestData = async ( reqData.fromIndex = reqData.pageIndex * reqData.pageSize; } - // See: https://github.com/elastic/elasticsearch-js/issues/662 - // and https://github.com/elastic/endpoint-app-team/issues/221 if ( reqData.searchBefore !== undefined && reqData.searchBefore[0] === '' && diff --git a/x-pack/plugins/endpoint/server/routes/metadata/index.ts b/x-pack/plugins/endpoint/server/routes/metadata/index.ts index 99dc4ac9f9e333..08950930441df0 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata/index.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata/index.ts @@ -171,7 +171,6 @@ async function enrichHostMetadata( try { /** * Get agent status by elastic agent id if available or use the host id. - * https://github.com/elastic/endpoint-app-team/issues/354 */ if (!elasticAgentId) { From d75328c476aca4afb56090e930a735c43025e7af Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Thu, 30 Apr 2020 10:34:44 -0400 Subject: [PATCH 10/47] [Ingest] Allow aggent to send metadata compliant with ECS (#64452) --- .../common/types/models/agent.ts | 11 +++-- .../ingest_manager/components/search_bar.tsx | 1 - .../agent_details_page/components/helper.ts | 47 +++++++++++++++++++ .../components/metadata_flyout.tsx | 6 ++- .../components/metadata_form.tsx | 11 +++-- .../sections/fleet/agent_list_page/index.tsx | 2 +- .../ingest_manager/types/index.ts | 1 + .../ingest_manager/server/saved_objects.ts | 4 +- .../server/services/agents/checkin.ts | 3 +- .../server/services/agents/crud.ts | 2 +- .../server/services/agents/enroll.ts | 4 +- .../server/services/agents/saved_objects.ts | 4 +- .../server/services/agents/status.test.ts | 8 ++-- .../ingest_manager/server/types/index.tsx | 1 + .../es_archives/fleet/agents/data.json | 16 +++---- .../es_archives/fleet/agents/mappings.json | 4 +- 16 files changed, 91 insertions(+), 34 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/helper.ts diff --git a/x-pack/plugins/ingest_manager/common/types/models/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index fcd3955f3a32fc..e3ca7635fdb407 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -60,6 +60,11 @@ export interface AgentEvent { export interface AgentEventSOAttributes extends AgentEvent, SavedObjectAttributes {} +type MetadataValue = string | AgentMetadata; + +export interface AgentMetadata { + [x: string]: MetadataValue; +} interface AgentBase { type: AgentType; active: boolean; @@ -72,19 +77,17 @@ interface AgentBase { config_revision?: number | null; config_newest_revision?: number; last_checkin?: string; + user_provided_metadata: AgentMetadata; + local_metadata: AgentMetadata; } export interface Agent extends AgentBase { id: string; current_error_events: AgentEvent[]; - user_provided_metadata: Record; - local_metadata: Record; access_api_key?: string; status?: string; } export interface AgentSOAttributes extends AgentBase, SavedObjectAttributes { - user_provided_metadata: string; - local_metadata: string; current_error_events?: string; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/search_bar.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/search_bar.tsx index 4429b9d8e6b829..1c9bd9107515d3 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/search_bar.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/components/search_bar.tsx @@ -92,7 +92,6 @@ function useSuggestions(fieldPrefix: string, search: string) { const res = (await data.indexPatterns.getFieldsForWildcard({ pattern: INDEX_NAME, })) as IFieldType[]; - if (!data || !data.autocomplete) { throw new Error('Missing data plugin'); } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/helper.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/helper.ts new file mode 100644 index 00000000000000..508190fef0fc2b --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/helper.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AgentMetadata } from '../../../../types'; + +export function flattenMetadata(metadata: AgentMetadata) { + return Object.entries(metadata).reduce((acc, [key, value]) => { + if (typeof value === 'string') { + acc[key] = value; + + return acc; + } + + Object.entries(flattenMetadata(value)).forEach(([flattenedKey, flattenedValue]) => { + acc[`${key}.${flattenedKey}`] = flattenedValue; + }); + + return acc; + }, {} as { [k: string]: string }); +} +export function unflattenMetadata(flattened: { [k: string]: string }) { + const metadata: AgentMetadata = {}; + + Object.entries(flattened).forEach(([flattenedKey, flattenedValue]) => { + const keyParts = flattenedKey.split('.'); + const lastKey = keyParts.pop(); + + if (!lastKey) { + throw new Error('Invalid metadata'); + } + + let metadataPart = metadata; + keyParts.forEach(keyPart => { + if (!metadataPart[keyPart]) { + metadataPart[keyPart] = {}; + } + + metadataPart = metadataPart[keyPart] as AgentMetadata; + }); + metadataPart[lastKey] = flattenedValue; + }); + + return metadata; +} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/metadata_flyout.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/metadata_flyout.tsx index ee43385e601c28..aa6da36f8fb6c7 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/metadata_flyout.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/metadata_flyout.tsx @@ -17,11 +17,13 @@ import { } from '@elastic/eui'; import { MetadataForm } from './metadata_form'; import { Agent } from '../../../../types'; +import { flattenMetadata } from './helper'; interface Props { agent: Agent; flyout: { hide: () => void }; } + export const AgentMetadataFlyout: React.FunctionComponent = ({ agent, flyout }) => { const mapMetadata = (obj: { [key: string]: string } | undefined) => { return Object.keys(obj || {}).map(key => ({ @@ -30,8 +32,8 @@ export const AgentMetadataFlyout: React.FunctionComponent = ({ agent, fly })); }; - const localItems = mapMetadata(agent.local_metadata); - const userProvidedItems = mapMetadata(agent.user_provided_metadata); + const localItems = mapMetadata(flattenMetadata(agent.local_metadata)); + const userProvidedItems = mapMetadata(flattenMetadata(agent.user_provided_metadata)); return ( flyout.hide()} size="s" aria-labelledby="flyoutTitle"> diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/metadata_form.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/metadata_form.tsx index ce28bbdc590b06..af7e8c674db4c0 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/metadata_form.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/metadata_form.tsx @@ -22,6 +22,7 @@ import { useAgentRefresh } from '../hooks'; import { useInput, sendRequest } from '../../../../hooks'; import { Agent } from '../../../../types'; import { agentRouteService } from '../../../../services'; +import { flattenMetadata, unflattenMetadata } from './helper'; function useAddMetadataForm(agent: Agent, done: () => void) { const refreshAgent = useAgentRefresh(); @@ -66,15 +67,17 @@ function useAddMetadataForm(agent: Agent, done: () => void) { isLoading: true, }); + const metadata = unflattenMetadata({ + ...flattenMetadata(agent.user_provided_metadata), + [keyInput.value]: valueInput.value, + }); + try { const { error } = await sendRequest({ path: agentRouteService.getUpdatePath(agent.id), method: 'put', body: JSON.stringify({ - user_provided_metadata: { - ...agent.user_provided_metadata, - [keyInput.value]: valueInput.value, - }, + user_provided_metadata: metadata, }), }); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx index 23fe18b82468cb..05264c157434e0 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx @@ -238,7 +238,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { const columns = [ { - field: 'local_metadata.host', + field: 'local_metadata.host.hostname', name: i18n.translate('xpack.ingestManager.agentList.hostColumnTitle', { defaultMessage: 'Host', }), diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts index 1508f4dfaa628b..d6483479a3f2fe 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts @@ -8,6 +8,7 @@ export { entries, // Object types Agent, + AgentMetadata, AgentConfig, NewAgentConfig, AgentEvent, diff --git a/x-pack/plugins/ingest_manager/server/saved_objects.ts b/x-pack/plugins/ingest_manager/server/saved_objects.ts index 90fe68e61bb1b4..89d8b9e173ffe0 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects.ts @@ -56,8 +56,8 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = { enrolled_at: { type: 'date' }, access_api_key_id: { type: 'keyword' }, version: { type: 'keyword' }, - user_provided_metadata: { type: 'text' }, - local_metadata: { type: 'text' }, + user_provided_metadata: { type: 'flattened' }, + local_metadata: { type: 'flattened' }, config_id: { type: 'keyword' }, last_updated: { type: 'date' }, last_checkin: { type: 'date' }, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts index c96a81ed9b7587..9b1565e7d74aab 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts @@ -11,6 +11,7 @@ import { AgentAction, AgentSOAttributes, AgentEventSOAttributes, + AgentMetadata, } from '../../types'; import { agentConfigService } from '../agent_config'; @@ -28,7 +29,7 @@ export async function agentCheckin( const updateData: { last_checkin: string; default_api_key?: string; - local_metadata?: string; + local_metadata?: AgentMetadata; current_error_events?: string; } = { last_checkin: new Date().toISOString(), diff --git a/x-pack/plugins/ingest_manager/server/services/agents/crud.ts b/x-pack/plugins/ingest_manager/server/services/agents/crud.ts index 175b92b75aca05..43fd5a3ce0ac94 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/crud.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/crud.ts @@ -103,7 +103,7 @@ export async function updateAgent( } ) { await soClient.update(AGENT_SAVED_OBJECT_TYPE, agentId, { - user_provided_metadata: JSON.stringify(data.userProvidedMetatada), + user_provided_metadata: data.userProvidedMetatada, }); } diff --git a/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts b/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts index a34d2e03e9b3de..81afa70ecb818f 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts @@ -32,8 +32,8 @@ export async function enroll( config_id: configId, type, enrolled_at: enrolledAt, - user_provided_metadata: JSON.stringify(metadata?.userProvided ?? {}), - local_metadata: JSON.stringify(metadata?.local ?? {}), + user_provided_metadata: metadata?.userProvided ?? {}, + local_metadata: metadata?.local ?? {}, current_error_events: undefined, access_api_key_id: undefined, last_checkin: undefined, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts b/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts index b182662e0fb4e2..11beba1cd7e43e 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts @@ -19,8 +19,8 @@ export function savedObjectToAgent(so: SavedObject): Agent { current_error_events: so.attributes.current_error_events ? JSON.parse(so.attributes.current_error_events) : [], - local_metadata: JSON.parse(so.attributes.local_metadata), - user_provided_metadata: JSON.parse(so.attributes.user_provided_metadata), + local_metadata: so.attributes.local_metadata, + user_provided_metadata: so.attributes.user_provided_metadata, access_api_key: undefined, status: undefined, }; diff --git a/x-pack/plugins/ingest_manager/server/services/agents/status.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/status.test.ts index b6de083cbe0cb2..8140b1e6de470f 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/status.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/status.test.ts @@ -18,8 +18,8 @@ describe('Agent status service', () => { type: AGENT_TYPE_PERMANENT, attributes: { active: false, - local_metadata: '{}', - user_provided_metadata: '{}', + local_metadata: {}, + user_provided_metadata: {}, }, } as SavedObject); const status = await getAgentStatusById(mockSavedObjectsClient, 'id'); @@ -33,8 +33,8 @@ describe('Agent status service', () => { type: AGENT_TYPE_PERMANENT, attributes: { active: true, - local_metadata: '{}', - user_provided_metadata: '{}', + local_metadata: {}, + user_provided_metadata: {}, }, } as SavedObject); const status = await getAgentStatusById(mockSavedObjectsClient, 'id'); diff --git a/x-pack/plugins/ingest_manager/server/types/index.tsx b/x-pack/plugins/ingest_manager/server/types/index.tsx index a7019ebc0a271f..27ed1de8499871 100644 --- a/x-pack/plugins/ingest_manager/server/types/index.tsx +++ b/x-pack/plugins/ingest_manager/server/types/index.tsx @@ -8,6 +8,7 @@ import { ScopedClusterClient } from 'src/core/server'; export { // Object types Agent, + AgentMetadata, AgentSOAttributes, AgentStatus, AgentType, diff --git a/x-pack/test/functional/es_archives/fleet/agents/data.json b/x-pack/test/functional/es_archives/fleet/agents/data.json index 3fe4f828ba128b..d22e3cd3fecdda 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/data.json +++ b/x-pack/test/functional/es_archives/fleet/agents/data.json @@ -11,8 +11,8 @@ "shared_id": "agent1_filebeat", "config_id": "1", "type": "PERMANENT", - "local_metadata": "{}", - "user_provided_metadata": "{}" + "local_metadata": {}, + "user_provided_metadata": {} } } } @@ -30,8 +30,8 @@ "active": true, "shared_id": "agent2_filebeat", "type": "PERMANENT", - "local_metadata": "{}", - "user_provided_metadata": "{}" + "local_metadata": {}, + "user_provided_metadata": {} } } } @@ -49,8 +49,8 @@ "active": true, "shared_id": "agent3_metricbeat", "type": "PERMANENT", - "local_metadata": "{}", - "user_provided_metadata": "{}" + "local_metadata": {}, + "user_provided_metadata": {} } } } @@ -68,8 +68,8 @@ "active": true, "shared_id": "agent4_metricbeat", "type": "PERMANENT", - "local_metadata": "{}", - "user_provided_metadata": "{}" + "local_metadata": {}, + "user_provided_metadata": {} } } } diff --git a/x-pack/test/functional/es_archives/fleet/agents/mappings.json b/x-pack/test/functional/es_archives/fleet/agents/mappings.json index 5d5d373797d4cf..409cc3c689eafc 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/mappings.json +++ b/x-pack/test/functional/es_archives/fleet/agents/mappings.json @@ -227,7 +227,7 @@ "type": "date" }, "local_metadata": { - "type": "text" + "type": "flattened" }, "shared_id": { "type": "keyword" @@ -239,7 +239,7 @@ "type": "date" }, "user_provided_metadata": { - "type": "text" + "type": "flattened" }, "version": { "type": "keyword" From 793d6f5689c675b878a821226bcaf482ca4f8c98 Mon Sep 17 00:00:00 2001 From: John Schulz Date: Thu, 30 Apr 2020 10:50:55 -0400 Subject: [PATCH 11/47] Always add Manager setup route. Fleet, if enabled (#64859) Co-authored-by: Elastic Machine --- x-pack/plugins/ingest_manager/server/plugin.ts | 2 +- .../plugins/ingest_manager/server/routes/setup/index.ts | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/plugin.ts b/x-pack/plugins/ingest_manager/server/plugin.ts index 097825e0b69e1d..3448685d1f279f 100644 --- a/x-pack/plugins/ingest_manager/server/plugin.ts +++ b/x-pack/plugins/ingest_manager/server/plugin.ts @@ -150,6 +150,7 @@ export class IngestManagerPlugin const config = await this.config$.pipe(first()).toPromise(); // Register routes + registerSetupRoutes(router, config); registerAgentConfigRoutes(router); registerDatasourceRoutes(router); registerOutputRoutes(router); @@ -162,7 +163,6 @@ export class IngestManagerPlugin } if (config.fleet.enabled) { - registerSetupRoutes(router); registerAgentRoutes(router); registerEnrollmentApiKeyRoutes(router); registerInstallScriptRoutes({ diff --git a/x-pack/plugins/ingest_manager/server/routes/setup/index.ts b/x-pack/plugins/ingest_manager/server/routes/setup/index.ts index edc9a0a268161e..5ee7ee7733220d 100644 --- a/x-pack/plugins/ingest_manager/server/routes/setup/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/setup/index.ts @@ -5,13 +5,14 @@ */ import { IRouter } from 'src/core/server'; import { PLUGIN_ID, FLEET_SETUP_API_ROUTES, SETUP_API_ROUTE } from '../../constants'; +import { IngestManagerConfigType } from '../../../common'; import { getFleetSetupHandler, createFleetSetupHandler, ingestManagerSetupHandler, } from './handlers'; -export const registerRoutes = (router: IRouter) => { +export const registerRoutes = (router: IRouter, config: IngestManagerConfigType) => { // Ingest manager setup router.post( { @@ -23,6 +24,11 @@ export const registerRoutes = (router: IRouter) => { }, ingestManagerSetupHandler ); + + if (!config.fleet.enabled) { + return; + } + // Get Fleet setup router.get( { From 988b93edca7267bc20b902846591f71e2378d338 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Thu, 30 Apr 2020 10:52:10 -0400 Subject: [PATCH 12/47] [Lens] Hide some suggestions in preparation for pie charts (#64740) * [Lens] Hide some suggestions in preparation for pie charts * Suggest reordering the X and Break down by axis * More tweaks --- .../datatable_visualization/visualization.tsx | 4 +- .../indexpattern_suggestions.test.tsx | 56 +++++++++++++++ .../indexpattern_suggestions.ts | 2 +- x-pack/plugins/lens/public/types.ts | 9 ++- .../xy_visualization/xy_suggestions.test.ts | 72 ++++++++++++++++++- .../public/xy_visualization/xy_suggestions.ts | 26 +++++-- 6 files changed, 158 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx index 48729448b2ea57..21bbcce68bf361 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx @@ -126,8 +126,8 @@ export const datatableVisualization: Visualization< ], }, previewIcon: chartTableSVG, - // dont show suggestions for reduced versions or single-line tables - hide: table.changeType === 'reduced' || !table.isMultiRow, + // tables are hidden from suggestion bar, but used for drag & drop and chart switching + hide: true, }, ]; }, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index 02471b935c97c0..f26fd39a60c0e8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -1552,6 +1552,62 @@ describe('IndexPattern Data Source suggestions', () => { expect(suggestions[0].table.columns.length).toBe(1); expect(suggestions[0].table.columns[0].operation.label).toBe('Sum of field1'); }); + + it('contains a reordering suggestion when there are exactly 2 buckets', () => { + const initialState = testInitialState(); + const state: IndexPatternPrivateState = { + indexPatternRefs: [], + existingFields: {}, + currentIndexPatternId: '1', + indexPatterns: expectedIndexPatterns, + showEmptyFields: true, + layers: { + first: { + ...initialState.layers.first, + columns: { + id1: { + label: 'Date histogram', + dataType: 'date', + isBucketed: true, + + operationType: 'date_histogram', + sourceField: 'field2', + params: { + interval: 'd', + }, + }, + id2: { + label: 'Top 5', + dataType: 'string', + isBucketed: true, + + operationType: 'terms', + sourceField: 'field1', + params: { size: 5, orderBy: { type: 'alphabetical' }, orderDirection: 'asc' }, + }, + id3: { + label: 'Average of field1', + dataType: 'number', + isBucketed: false, + + operationType: 'avg', + sourceField: 'field1', + }, + }, + columnOrder: ['id1', 'id2', 'id3'], + }, + }, + }; + + const suggestions = getDatasourceSuggestionsFromCurrentState(state); + expect(suggestions).toContainEqual( + expect.objectContaining({ + table: expect.objectContaining({ + changeType: 'reorder', + }), + }) + ); + }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index 44963722f8afcb..487c1bf759fc23 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -486,7 +486,7 @@ function createChangedNestingSuggestion(state: IndexPatternPrivateState, layerId layerId, updatedLayer, label: getNestedTitle([layer.columns[secondBucket], layer.columns[firstBucket]]), - changeType: 'extended', + changeType: 'reorder', }); } diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index ed0af8545f0122..04efc642793b08 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -103,9 +103,16 @@ export interface TableSuggestion { * * `unchanged` means the table is the same in the currently active configuration * * `reduced` means the table is a reduced version of the currently active table (some columns dropped, but not all of them) * * `extended` means the table is an extended version of the currently active table (added one or multiple additional columns) + * * `reorder` means the table columns have changed order, which change the data as well * * `layers` means the change is a change to the layer structure, not to the table */ -export type TableChangeType = 'initial' | 'unchanged' | 'reduced' | 'extended' | 'layers'; +export type TableChangeType = + | 'initial' + | 'unchanged' + | 'reduced' + | 'extended' + | 'reorder' + | 'layers'; export interface DatasourceSuggestion { state: T; diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts index ddbd9d11b5fada..73ff88e97f4797 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.test.ts @@ -186,7 +186,7 @@ describe('xy_suggestions', () => { isMultiRow: true, columns: [numCol('price'), numCol('quantity'), dateCol('date'), strCol('product')], layerId: 'first', - changeType: 'unchanged', + changeType: 'extended', label: 'Datasource title', }, keptLayerIds: [], @@ -196,6 +196,34 @@ describe('xy_suggestions', () => { expect(suggestion.title).toEqual('Datasource title'); }); + test('suggests only stacked bar chart when xy chart is inactive', () => { + const [suggestion, ...rest] = getSuggestions({ + table: { + isMultiRow: true, + columns: [dateCol('date'), numCol('price')], + layerId: 'first', + changeType: 'unchanged', + label: 'Datasource title', + }, + keptLayerIds: [], + }); + + expect(rest).toHaveLength(0); + expect(suggestion.title).toEqual('Bar chart'); + expect(suggestion.state).toEqual( + expect.objectContaining({ + layers: [ + expect.objectContaining({ + seriesType: 'bar_stacked', + xAccessor: 'date', + accessors: ['price'], + splitAccessor: undefined, + }), + ], + }) + ); + }); + test('hides reduced suggestions if there is a current state', () => { const [suggestion, ...rest] = getSuggestions({ table: { @@ -224,7 +252,7 @@ describe('xy_suggestions', () => { expect(suggestion.hide).toBeTruthy(); }); - test('does not hide reduced suggestions if xy visualization is not active', () => { + test('hides reduced suggestions if xy visualization is not active', () => { const [suggestion, ...rest] = getSuggestions({ table: { isMultiRow: true, @@ -236,7 +264,7 @@ describe('xy_suggestions', () => { }); expect(rest).toHaveLength(0); - expect(suggestion.hide).toBeFalsy(); + expect(suggestion.hide).toBeTruthy(); }); test('only makes a seriesType suggestion for unchanged table without split', () => { @@ -419,6 +447,44 @@ describe('xy_suggestions', () => { }); }); + test('changes column mappings when suggestion is reorder', () => { + const currentState: XYState = { + legend: { isVisible: true, position: 'bottom' }, + preferredSeriesType: 'bar', + layers: [ + { + accessors: ['price'], + layerId: 'first', + seriesType: 'bar', + splitAccessor: 'category', + xAccessor: 'product', + }, + ], + }; + const [suggestion, ...rest] = getSuggestions({ + table: { + isMultiRow: true, + columns: [strCol('category'), strCol('product'), numCol('price')], + layerId: 'first', + changeType: 'reorder', + }, + state: currentState, + keptLayerIds: [], + }); + + expect(rest).toHaveLength(0); + expect(suggestion.state).toEqual({ + ...currentState, + layers: [ + { + ...currentState.layers[0], + xAccessor: 'category', + splitAccessor: 'product', + }, + ], + }); + }); + test('overwrites column to dimension mappings if a date dimension is added', () => { (generateId as jest.Mock).mockReturnValueOnce('dummyCol'); const currentState: XYState = { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts index 5e9311bb1e9283..abd76403440642 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts +++ b/x-pack/plugins/lens/public/xy_visualization/xy_suggestions.ts @@ -99,11 +99,14 @@ function getBucketMappings(table: TableSuggestion, currentState?: State) { // reverse the buckets before prioritization to always use the most inner // bucket of the highest-prioritized group as x value (don't use nested // buckets as split series) - const prioritizedBuckets = prioritizeColumns(buckets.reverse()); + const prioritizedBuckets = prioritizeColumns([...buckets].reverse()); if (!currentLayer || table.changeType === 'initial') { return prioritizedBuckets; } + if (table.changeType === 'reorder') { + return buckets; + } // if existing table is just modified, try to map buckets to the current dimensions const currentXColumnIndex = prioritizedBuckets.findIndex( @@ -175,12 +178,24 @@ function getSuggestionsForLayer({ keptLayerIds, }; - const isSameState = currentState && changeType === 'unchanged'; + // handles the simplest cases, acting as a chart switcher + if (!currentState && changeType === 'unchanged') { + return [ + { + ...buildSuggestion(options), + title: i18n.translate('xpack.lens.xySuggestions.barChartTitle', { + defaultMessage: 'Bar chart', + }), + }, + ]; + } + const isSameState = currentState && changeType === 'unchanged'; if (!isSameState) { return buildSuggestion(options); } + // Suggestions are either changing the data, or changing the way the data is used const sameStateSuggestions: Array> = []; // if current state is using the same data, suggest same chart with different presentational configuration @@ -374,8 +389,11 @@ function buildSuggestion({ return { title, score: getScore(yValues, splitBy, changeType), - // don't advertise chart of same type but with less data - hide: currentState && changeType === 'reduced', + hide: + // Only advertise very clear changes when XY chart is not active + (!currentState && changeType !== 'unchanged' && changeType !== 'extended') || + // Don't advertise removing dimensions + (currentState && changeType === 'reduced'), state, previewIcon: getIconForSeries(seriesType), }; From d3ba5b5a554ccde6ead6960102f6ec62e2670530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Thu, 30 Apr 2020 11:33:51 -0400 Subject: [PATCH 13/47] Update event log ILM policy defaults (#64675) * Initial work * Update docs * Add delete phase mention to docs Co-authored-by: Elastic Machine --- x-pack/plugins/event_log/README.md | 13 ++++++++++--- x-pack/plugins/event_log/server/es/documents.ts | 8 +++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/event_log/README.md b/x-pack/plugins/event_log/README.md index 38364033cb70be..941dedc3d1093f 100644 --- a/x-pack/plugins/event_log/README.md +++ b/x-pack/plugins/event_log/README.md @@ -274,10 +274,16 @@ PUT _ilm/policy/event_log_policy "hot": { "actions": { "rollover": { - "max_size": "5GB", + "max_size": "50GB", "max_age": "30d" } } + }, + "delete": { + "min_age": "90d", + "actions": { + "delete": {} + } } } } @@ -285,10 +291,11 @@ PUT _ilm/policy/event_log_policy ``` This means that ILM would "rollover" the current index, say -`.kibana-event-log-000001` by creating a new index `.kibana-event-log-000002`, +`.kibana-event-log-8.0.0-000001` by creating a new index `.kibana-event-log-8.0.0-000002`, which would "inherit" everything from the index template, and then ILM will set the write index of the the alias to the new index. This would happen -when the original index grew past 5 GB, or was created more than 30 days ago. +when the original index grew past 50 GB, or was created more than 30 days ago. +After rollover, the indices will be removed after 90 days to avoid disks to fill up. For more relevant information on ILM, see: [getting started with ILM doc][] and [write index alias behavior][]: diff --git a/x-pack/plugins/event_log/server/es/documents.ts b/x-pack/plugins/event_log/server/es/documents.ts index a6af209d6d3a02..91b3db554964f1 100644 --- a/x-pack/plugins/event_log/server/es/documents.ts +++ b/x-pack/plugins/event_log/server/es/documents.ts @@ -31,12 +31,18 @@ export function getIlmPolicy() { hot: { actions: { rollover: { - max_size: '5GB', + max_size: '50GB', max_age: '30d', // max_docs: 1, // you know, for testing }, }, }, + delete: { + min_age: '90d', + actions: { + delete: {}, + }, + }, }, }, }; From c131cb341b9fcc5ce340445c1562813182ff246a Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper Date: Thu, 30 Apr 2020 10:51:05 -0500 Subject: [PATCH 14/47] Refactor action messaging to report on No Data state (#64365) --- .../alerting/metrics/expression.tsx | 26 +++++ .../metrics/metric_threshold_alert_type.ts | 6 +- .../lib/alerting/metric_threshold/messages.ts | 109 ++++++++++++++++++ .../metric_threshold_executor.test.ts | 50 ++++++-- .../metric_threshold_executor.ts | 67 +++++++---- .../register_metric_threshold_alert_type.ts | 27 ++--- .../alerting/metric_threshold/test_mocks.ts | 8 ++ 7 files changed, 239 insertions(+), 54 deletions(-) create mode 100644 x-pack/plugins/infra/server/lib/alerting/metric_threshold/messages.ts diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx b/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx index d4d53b81109c61..f2ac34ccb752fd 100644 --- a/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx +++ b/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx @@ -13,6 +13,9 @@ import { EuiText, EuiFormRow, EuiButtonEmpty, + EuiCheckbox, + EuiToolTip, + EuiIcon, EuiFieldSearch, } from '@elastic/eui'; import { IFieldType } from 'src/plugins/data/public'; @@ -57,6 +60,7 @@ interface Props { groupBy?: string; filterQuery?: string; sourceId?: string; + alertOnNoData?: boolean; }; alertsContext: AlertsContextValue; setAlertParams(key: string, value: any): void; @@ -282,6 +286,28 @@ export const Expressions: React.FC = props => { + + + {i18n.translate('xpack.infra.metrics.alertFlyout.alertOnNoData', { + defaultMessage: "Alert me if there's no data", + })}{' '} + + + + + } + checked={alertParams.alertOnNoData} + onChange={e => setAlertParams('alertOnNoData', e.target.checked)} + /> + { + const gtText = i18n.translate('xpack.infra.metrics.alerting.threshold.gtComparator', { + defaultMessage: 'greater than', + }); + const ltText = i18n.translate('xpack.infra.metrics.alerting.threshold.ltComparator', { + defaultMessage: 'less than', + }); + const eqText = i18n.translate('xpack.infra.metrics.alerting.threshold.eqComparator', { + defaultMessage: 'equal to', + }); + + switch (comparator) { + case Comparator.BETWEEN: + return i18n.translate('xpack.infra.metrics.alerting.threshold.betweenComparator', { + defaultMessage: 'between', + }); + case Comparator.OUTSIDE_RANGE: + return i18n.translate('xpack.infra.metrics.alerting.threshold.outsideRangeComparator', { + defaultMessage: 'not between', + }); + case Comparator.GT: + return gtText; + case Comparator.LT: + return ltText; + case Comparator.GT_OR_EQ: + case Comparator.LT_OR_EQ: + if (threshold[0] === currentValue) return eqText; + else if (threshold[0] < currentValue) return ltText; + return gtText; + } +}; + +const thresholdToI18n = ([a, b]: number[]) => { + if (typeof b === 'undefined') return a; + return i18n.translate('xpack.infra.metrics.alerting.threshold.thresholdRange', { + defaultMessage: '{a} and {b}', + values: { a, b }, + }); +}; + +export const buildFiredAlertReason: (alertResult: { + metric: string; + comparator: Comparator; + threshold: number[]; + currentValue: number; +}) => string = ({ metric, comparator, threshold, currentValue }) => + i18n.translate('xpack.infra.metrics.alerting.threshold.firedAlertReason', { + defaultMessage: + '{metric} is {comparator} a threshold of {threshold} (current value is {currentValue})', + values: { + metric, + comparator: comparatorToI18n(comparator, threshold, currentValue), + threshold: thresholdToI18n(threshold), + currentValue, + }, + }); + +export const buildNoDataAlertReason: (alertResult: { + metric: string; + timeSize: number; + timeUnit: string; +}) => string = ({ metric, timeSize, timeUnit }) => + i18n.translate('xpack.infra.metrics.alerting.threshold.noDataAlertReason', { + defaultMessage: '{metric} has reported no data over the past {interval}', + values: { + metric, + interval: `${timeSize}${timeUnit}`, + }, + }); + +export const buildErrorAlertReason = (metric: string) => + i18n.translate('xpack.infra.metrics.alerting.threshold.errorAlertReason', { + defaultMessage: 'Elasticsearch failed when attempting to query data for {metric}', + values: { + metric, + }, + }); diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index 24b6ba2ec378bc..0007b8bd719f44 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -34,6 +34,8 @@ services.callCluster.mockImplementation(async (_: string, { body, index }: any) } if (metric === 'test.metric.2') { return mocks.alternateMetricResponse; + } else if (metric === 'test.metric.3') { + return mocks.emptyMetricResponse; } return mocks.basicMetricResponse; }); @@ -161,9 +163,9 @@ describe('The metric threshold alert type', () => { await execute(Comparator.GT, [0.75]); const { action } = mostRecentAction(instanceID); expect(action.group).toBe('*'); - expect(action.valueOf.condition0).toBe(1); - expect(action.thresholdOf.condition0).toStrictEqual([0.75]); - expect(action.metricOf.condition0).toBe('test.metric.1'); + expect(action.reason).toContain('current value is 1'); + expect(action.reason).toContain('threshold of 0.75'); + expect(action.reason).toContain('test.metric.1'); }); test('fetches the index pattern dynamically', async () => { await execute(Comparator.LT, [17], 'alternate'); @@ -271,12 +273,14 @@ describe('The metric threshold alert type', () => { const instanceID = 'test-*'; await execute(Comparator.GT_OR_EQ, [1.0], [3.0]); const { action } = mostRecentAction(instanceID); - expect(action.valueOf.condition0).toBe(1); - expect(action.valueOf.condition1).toBe(3.5); - expect(action.thresholdOf.condition0).toStrictEqual([1.0]); - expect(action.thresholdOf.condition1).toStrictEqual([3.0]); - expect(action.metricOf.condition0).toBe('test.metric.1'); - expect(action.metricOf.condition1).toBe('test.metric.2'); + const reasons = action.reason.split('\n'); + expect(reasons.length).toBe(2); + expect(reasons[0]).toContain('test.metric.1'); + expect(reasons[1]).toContain('test.metric.2'); + expect(reasons[0]).toContain('current value is 1'); + expect(reasons[1]).toContain('current value is 3.5'); + expect(reasons[0]).toContain('threshold of 1'); + expect(reasons[1]).toContain('threshold of 3'); }); }); describe('querying with the count aggregator', () => { @@ -305,4 +309,32 @@ describe('The metric threshold alert type', () => { expect(getState(instanceID).alertState).toBe(AlertStates.OK); }); }); + describe("querying a metric that hasn't reported data", () => { + const instanceID = 'test-*'; + const execute = (alertOnNoData: boolean) => + executor({ + services, + params: { + criteria: [ + { + ...baseCriterion, + comparator: Comparator.GT, + threshold: 1, + metric: 'test.metric.3', + }, + ], + alertOnNoData, + }, + }); + test('sends a No Data alert when configured to do so', async () => { + await execute(true); + expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); + expect(getState(instanceID).alertState).toBe(AlertStates.NO_DATA); + }); + test('does not send a No Data alert when not configured to do so', async () => { + await execute(false); + expect(mostRecentAction(instanceID)).toBe(undefined); + expect(getState(instanceID).alertState).toBe(AlertStates.NO_DATA); + }); + }); }); diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index cf691f73bdc2cc..bd77e5e2daf42b 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -12,6 +12,13 @@ import { createAfterKeyHandler } from '../../../utils/create_afterkey_handler'; import { getAllCompositeData } from '../../../utils/get_all_composite_data'; import { networkTraffic } from '../../../../common/inventory_models/shared/metrics/snapshot/network_traffic'; import { MetricExpressionParams, Comparator, Aggregators, AlertStates } from './types'; +import { + buildErrorAlertReason, + buildFiredAlertReason, + buildNoDataAlertReason, + DOCUMENT_COUNT_I18N, + stateToAlertMessage, +} from './messages'; import { AlertServices, AlertExecutorOptions } from '../../../../../alerting/server'; import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds'; import { getDateHistogramOffset } from '../../snapshot/query_helpers'; @@ -258,24 +265,14 @@ const comparatorMap = { [Comparator.LT_OR_EQ]: (a: number, [b]: number[]) => a <= b, }; -const mapToConditionsLookup = ( - list: any[], - mapFn: (value: any, index: number, array: any[]) => unknown -) => - list - .map(mapFn) - .reduce( - (result: Record, value, i) => ({ ...result, [`condition${i}`]: value }), - {} - ); - export const createMetricThresholdExecutor = (alertUUID: string) => async function({ services, params }: AlertExecutorOptions) { - const { criteria, groupBy, filterQuery, sourceId } = params as { + const { criteria, groupBy, filterQuery, sourceId, alertOnNoData } = params as { criteria: MetricExpressionParams[]; groupBy: string | undefined; filterQuery: string | undefined; sourceId?: string; + alertOnNoData: boolean; }; const alertResults = await Promise.all( @@ -286,9 +283,11 @@ export const createMetricThresholdExecutor = (alertUUID: string) => const { threshold, comparator } = criterion; const comparisonFunction = comparatorMap[comparator]; return mapValues(currentValues, value => ({ + ...criterion, + metric: criterion.metric ?? DOCUMENT_COUNT_I18N, + currentValue: value, shouldFire: value !== undefined && value !== null && comparisonFunction(value, threshold), - currentValue: value, isNoData: value === null, isError: value === undefined, })); @@ -306,23 +305,43 @@ export const createMetricThresholdExecutor = (alertUUID: string) => // whole alert is in a No Data/Error state const isNoData = alertResults.some(result => result[group].isNoData); const isError = alertResults.some(result => result[group].isError); - if (shouldAlertFire) { + + const nextState = isError + ? AlertStates.ERROR + : isNoData + ? AlertStates.NO_DATA + : shouldAlertFire + ? AlertStates.ALERT + : AlertStates.OK; + + let reason; + if (nextState === AlertStates.ALERT) { + reason = alertResults.map(result => buildFiredAlertReason(result[group])).join('\n'); + } + if (alertOnNoData) { + if (nextState === AlertStates.NO_DATA) { + reason = alertResults + .filter(result => result[group].isNoData) + .map(result => buildNoDataAlertReason(result[group])) + .join('\n'); + } else if (nextState === AlertStates.ERROR) { + reason = alertResults + .filter(result => result[group].isError) + .map(result => buildErrorAlertReason(result[group].metric)) + .join('\n'); + } + } + if (reason) { alertInstance.scheduleActions(FIRED_ACTIONS.id, { group, - valueOf: mapToConditionsLookup(alertResults, result => result[group].currentValue), - thresholdOf: mapToConditionsLookup(criteria, criterion => criterion.threshold), - metricOf: mapToConditionsLookup(criteria, criterion => criterion.metric), + alertState: stateToAlertMessage[nextState], + reason, }); } + // Future use: ability to fetch display current alert state alertInstance.replaceState({ - alertState: isError - ? AlertStates.ERROR - : isNoData - ? AlertStates.NO_DATA - : shouldAlertFire - ? AlertStates.ALERT - : AlertStates.OK, + alertState: nextState, }); } }; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts index 3415ae9873bfb1..029491c1168cfb 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts @@ -55,27 +55,18 @@ export async function registerMetricThresholdAlertType( } ); - const valueOfActionVariableDescription = i18n.translate( - 'xpack.infra.metrics.alerting.threshold.alerting.valueOfActionVariableDescription', + const alertStateActionVariableDescription = i18n.translate( + 'xpack.infra.metrics.alerting.threshold.alerting.alertStateActionVariableDescription', { - defaultMessage: - 'Record of the current value of the watched metric; grouped by condition, i.e valueOf.condition0, valueOf.condition1, etc.', - } - ); - - const thresholdOfActionVariableDescription = i18n.translate( - 'xpack.infra.metrics.alerting.threshold.alerting.thresholdOfActionVariableDescription', - { - defaultMessage: - 'Record of the alerting threshold; grouped by condition, i.e thresholdOf.condition0, thresholdOf.condition1, etc.', + defaultMessage: 'Current state of the alert', } ); - const metricOfActionVariableDescription = i18n.translate( - 'xpack.infra.metrics.alerting.threshold.alerting.metricOfActionVariableDescription', + const reasonActionVariableDescription = i18n.translate( + 'xpack.infra.metrics.alerting.threshold.alerting.reasonActionVariableDescription', { defaultMessage: - 'Record of the watched metric; grouped by condition, i.e metricOf.condition0, metricOf.condition1, etc.', + 'A description of why the alert is in this state, including which metrics have crossed which thresholds', } ); @@ -88,6 +79,7 @@ export async function registerMetricThresholdAlertType( groupBy: schema.maybe(schema.string()), filterQuery: schema.maybe(schema.string()), sourceId: schema.string(), + alertOnNoData: schema.maybe(schema.boolean()), }), }, defaultActionGroupId: FIRED_ACTIONS.id, @@ -96,9 +88,8 @@ export async function registerMetricThresholdAlertType( actionVariables: { context: [ { name: 'group', description: groupActionVariableDescription }, - { name: 'valueOf', description: valueOfActionVariableDescription }, - { name: 'thresholdOf', description: thresholdOfActionVariableDescription }, - { name: 'metricOf', description: metricOfActionVariableDescription }, + { name: 'alertState', description: alertStateActionVariableDescription }, + { name: 'reason', description: reasonActionVariableDescription }, ], }, }); diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/test_mocks.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/test_mocks.ts index 66e0a363c89833..fa55f80e472de3 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/test_mocks.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/test_mocks.ts @@ -53,6 +53,14 @@ export const alternateMetricResponse = { }, }; +export const emptyMetricResponse = { + aggregations: { + aggregatedIntervals: { + buckets: [], + }, + }, +}; + export const basicCompositeResponse = { aggregations: { groupings: { From 671d750c03068696abae097c83ffbc05d03cba3a Mon Sep 17 00:00:00 2001 From: The SpaceCake Project Date: Thu, 30 Apr 2020 12:28:55 -0400 Subject: [PATCH 15/47] lookback adjust (#64837) increase lookback to 15 minutes for latency in endpoint signal rules --- ...elastic_endpoint_security_adversary_behavior_detected.json | 2 +- .../elastic_endpoint_security_cred_dumping_detected.json | 4 ++-- .../elastic_endpoint_security_cred_dumping_prevented.json | 2 +- .../elastic_endpoint_security_cred_manipulation_detected.json | 2 +- ...elastic_endpoint_security_cred_manipulation_prevented.json | 2 +- .../elastic_endpoint_security_exploit_detected.json | 2 +- .../elastic_endpoint_security_exploit_prevented.json | 2 +- .../elastic_endpoint_security_malware_detected.json | 2 +- .../elastic_endpoint_security_malware_prevented.json | 2 +- .../elastic_endpoint_security_permission_theft_detected.json | 2 +- .../elastic_endpoint_security_permission_theft_prevented.json | 2 +- .../elastic_endpoint_security_process_injection_detected.json | 2 +- ...elastic_endpoint_security_process_injection_prevented.json | 2 +- .../elastic_endpoint_security_ransomware_detected.json | 2 +- .../elastic_endpoint_security_ransomware_prevented.json | 2 +- 15 files changed, 16 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_adversary_behavior_detected.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_adversary_behavior_detected.json index cfc322788d4be9..c83c0e01d7fa04 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_adversary_behavior_detected.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_adversary_behavior_detected.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint detected an Adversary Behavior. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_detected.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_detected.json index 0647fe9c9ce109..18472abbd70d77 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_detected.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_detected.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint detected Credential Dumping. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], @@ -17,4 +17,4 @@ ], "type": "query", "version": 2 -} \ No newline at end of file +} diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_prevented.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_prevented.json index 036c88688d9bdd..03024ad15396e8 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_prevented.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_prevented.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint prevented Credential Dumping. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_detected.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_detected.json index 0fe610d5511527..e5a128029f5859 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_detected.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_detected.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint detected Credential Manipulation. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_prevented.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_prevented.json index a317c77bcd90aa..1c05743fae62f7 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_prevented.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_prevented.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint prevented Credential Manipulation. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_detected.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_detected.json index 97640c0cea9b22..3396a8563ba1c6 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_detected.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_detected.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint detected an Exploit. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_prevented.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_prevented.json index 069687a5af00fd..2f70c539414c62 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_prevented.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_prevented.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint prevented an Exploit. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_detected.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_detected.json index a7d3371190cedd..cbf6c286a439f0 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_detected.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_detected.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint detected Malware. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_prevented.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_prevented.json index dd7bf72c34f900..49c7c160e5dafb 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_prevented.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_prevented.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint prevented Malware. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_detected.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_detected.json index a8e102cc4619d1..e836bd037ddc54 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_detected.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_detected.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint detected Permission Theft. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_prevented.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_prevented.json index c97330f2349eb6..e9ac8d7ba6686e 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_prevented.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_prevented.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint prevented Permission Theft. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_detected.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_detected.json index e644c0e8d66eb4..8e25832b0e89a7 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_detected.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_detected.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint detected Process Injection. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_prevented.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_prevented.json index 61cbe267f9a465..a59428275ca228 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_prevented.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_prevented.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint prevented Process Injection. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_detected.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_detected.json index 0e88b26cb2c758..22091d8c9b68f4 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_detected.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_detected.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint detected Ransomware. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], diff --git a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_prevented.json b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_prevented.json index ba341f059f26d5..947bfcbba39a0e 100644 --- a/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_prevented.json +++ b/x-pack/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_prevented.json @@ -1,6 +1,6 @@ { "description": "Elastic Endpoint prevented Ransomware. Click the Elastic Endpoint icon in the event.module column or the link in the rule.reference column in the External Alerts tab of the SIEM Detections page for additional information.", - "from": "now-660s", + "from": "now-15m", "index": [ "endgame-*" ], From 9112b6c1f1ef6def9b1dc83f18276d9cbd24046b Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Thu, 30 Apr 2020 10:32:26 -0600 Subject: [PATCH 16/47] Avoid race condition between HttpServer.stop() and HttpServerSetup methods (#64487) --- src/core/server/http/http_server.test.ts | 58 ++++++++++++++++++++++++ src/core/server/http/http_server.ts | 28 +++++++++++- 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/src/core/server/http/http_server.test.ts b/src/core/server/http/http_server.test.ts index 27db79bb94d252..4fb433b5c77ba5 100644 --- a/src/core/server/http/http_server.test.ts +++ b/src/core/server/http/http_server.test.ts @@ -1068,6 +1068,14 @@ describe('setup contract', () => { await create(); expect(create()).rejects.toThrowError('A cookieSessionStorageFactory was already created'); }); + + it('does not throw if called after stop', async () => { + const { createCookieSessionStorageFactory } = await server.setup(config); + await server.stop(); + expect(() => { + createCookieSessionStorageFactory(cookieOptions); + }).not.toThrow(); + }); }); describe('#isTlsEnabled', () => { @@ -1113,4 +1121,54 @@ describe('setup contract', () => { expect(getServerInfo().protocol).toEqual('https'); }); }); + + describe('#registerStaticDir', () => { + it('does not throw if called after stop', async () => { + const { registerStaticDir } = await server.setup(config); + await server.stop(); + expect(() => { + registerStaticDir('/path1/{path*}', '/path/to/resource'); + }).not.toThrow(); + }); + }); + + describe('#registerOnPreAuth', () => { + test('does not throw if called after stop', async () => { + const { registerOnPreAuth } = await server.setup(config); + await server.stop(); + expect(() => { + registerOnPreAuth((req, res) => res.unauthorized()); + }).not.toThrow(); + }); + }); + + describe('#registerOnPostAuth', () => { + test('does not throw if called after stop', async () => { + const { registerOnPostAuth } = await server.setup(config); + await server.stop(); + expect(() => { + registerOnPostAuth((req, res) => res.unauthorized()); + }).not.toThrow(); + }); + }); + + describe('#registerOnPreResponse', () => { + test('does not throw if called after stop', async () => { + const { registerOnPreResponse } = await server.setup(config); + await server.stop(); + expect(() => { + registerOnPreResponse((req, res, t) => t.next()); + }).not.toThrow(); + }); + }); + + describe('#registerAuth', () => { + test('does not throw if called after stop', async () => { + const { registerAuth } = await server.setup(config); + await server.stop(); + expect(() => { + registerAuth((req, res) => res.unauthorized()); + }).not.toThrow(); + }); + }); }); diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index 77d3d99fb48cbd..92ac5220735a12 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -74,6 +74,7 @@ export class HttpServer { private registeredRouters = new Set(); private authRegistered = false; private cookieSessionStorageCreated = false; + private stopped = false; private readonly log: Logger; private readonly authState: AuthStateStorage; @@ -144,6 +145,10 @@ export class HttpServer { if (this.server === undefined) { throw new Error('Http server is not setup up yet'); } + if (this.stopped) { + this.log.warn(`start called after stop`); + return; + } this.log.debug('starting http server'); for (const router of this.registeredRouters) { @@ -189,13 +194,13 @@ export class HttpServer { } public async stop() { + this.stopped = true; if (this.server === undefined) { return; } this.log.debug('stopping http server'); await this.server.stop(); - this.server = undefined; } private getAuthOption( @@ -234,6 +239,9 @@ export class HttpServer { if (this.server === undefined) { throw new Error('Server is not created yet'); } + if (this.stopped) { + this.log.warn(`setupConditionalCompression called after stop`); + } const { enabled, referrerWhitelist: list } = config.compression; if (!enabled) { @@ -261,6 +269,9 @@ export class HttpServer { if (this.server === undefined) { throw new Error('Server is not created yet'); } + if (this.stopped) { + this.log.warn(`registerOnPostAuth called after stop`); + } this.server.ext('onPostAuth', adoptToHapiOnPostAuthFormat(fn, this.log)); } @@ -269,6 +280,9 @@ export class HttpServer { if (this.server === undefined) { throw new Error('Server is not created yet'); } + if (this.stopped) { + this.log.warn(`registerOnPreAuth called after stop`); + } this.server.ext('onRequest', adoptToHapiOnPreAuthFormat(fn, this.log)); } @@ -277,6 +291,9 @@ export class HttpServer { if (this.server === undefined) { throw new Error('Server is not created yet'); } + if (this.stopped) { + this.log.warn(`registerOnPreResponse called after stop`); + } this.server.ext('onPreResponse', adoptToHapiOnPreResponseFormat(fn, this.log)); } @@ -288,6 +305,9 @@ export class HttpServer { if (this.server === undefined) { throw new Error('Server is not created yet'); } + if (this.stopped) { + this.log.warn(`createCookieSessionStorageFactory called after stop`); + } if (this.cookieSessionStorageCreated) { throw new Error('A cookieSessionStorageFactory was already created'); } @@ -305,6 +325,9 @@ export class HttpServer { if (this.server === undefined) { throw new Error('Server is not created yet'); } + if (this.stopped) { + this.log.warn(`registerAuth called after stop`); + } if (this.authRegistered) { throw new Error('Auth interceptor was already registered'); } @@ -348,6 +371,9 @@ export class HttpServer { if (this.server === undefined) { throw new Error('Http server is not setup up yet'); } + if (this.stopped) { + this.log.warn(`registerStaticDir called after stop`); + } this.server.route({ path, From a7291fa8c82f06c6d2f488c3d5694f71893ff52e Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Thu, 30 Apr 2020 13:03:08 -0400 Subject: [PATCH 17/47] [EPM] Adding support for nested fields (#64829) * Allowing nested types to be merged with group * Adding nested to case and handling other fields * Cleaing up if logic * Removing unneeded if statement * Adding nested type to switch and more tests * Keeping functions immutable --- .../elasticsearch/template/template.test.ts | 106 ++++++++++-- .../epm/elasticsearch/template/template.ts | 43 ++++- .../server/services/epm/fields/field.test.ts | 162 ++++++++++++++++++ .../server/services/epm/fields/field.ts | 54 +++++- 4 files changed, 339 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts index 25180244b02147..cacf84381dd88d 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts @@ -93,7 +93,7 @@ test('tests processing text field with multi fields', () => { const fields: Field[] = safeLoad(textWithMultiFieldsLiteralYml); const processedFields = processFields(fields); const mappings = generateMappings(processedFields); - expect(JSON.stringify(mappings)).toEqual(JSON.stringify(textWithMultiFieldsMapping)); + expect(mappings).toEqual(textWithMultiFieldsMapping); }); test('tests processing keyword field with multi fields', () => { @@ -127,7 +127,7 @@ test('tests processing keyword field with multi fields', () => { const fields: Field[] = safeLoad(keywordWithMultiFieldsLiteralYml); const processedFields = processFields(fields); const mappings = generateMappings(processedFields); - expect(JSON.stringify(mappings)).toEqual(JSON.stringify(keywordWithMultiFieldsMapping)); + expect(mappings).toEqual(keywordWithMultiFieldsMapping); }); test('tests processing keyword field with multi fields with analyzed text field', () => { @@ -159,7 +159,7 @@ test('tests processing keyword field with multi fields with analyzed text field' const fields: Field[] = safeLoad(keywordWithAnalyzedMultiFieldsLiteralYml); const processedFields = processFields(fields); const mappings = generateMappings(processedFields); - expect(JSON.stringify(mappings)).toEqual(JSON.stringify(keywordWithAnalyzedMultiFieldsMapping)); + expect(mappings).toEqual(keywordWithAnalyzedMultiFieldsMapping); }); test('tests processing object field with no other attributes', () => { @@ -177,7 +177,7 @@ test('tests processing object field with no other attributes', () => { const fields: Field[] = safeLoad(objectFieldLiteralYml); const processedFields = processFields(fields); const mappings = generateMappings(processedFields); - expect(JSON.stringify(mappings)).toEqual(JSON.stringify(objectFieldMapping)); + expect(mappings).toEqual(objectFieldMapping); }); test('tests processing object field with enabled set to false', () => { @@ -197,7 +197,7 @@ test('tests processing object field with enabled set to false', () => { const fields: Field[] = safeLoad(objectFieldEnabledFalseLiteralYml); const processedFields = processFields(fields); const mappings = generateMappings(processedFields); - expect(JSON.stringify(mappings)).toEqual(JSON.stringify(objectFieldEnabledFalseMapping)); + expect(mappings).toEqual(objectFieldEnabledFalseMapping); }); test('tests processing object field with dynamic set to false', () => { @@ -217,7 +217,7 @@ test('tests processing object field with dynamic set to false', () => { const fields: Field[] = safeLoad(objectFieldDynamicFalseLiteralYml); const processedFields = processFields(fields); const mappings = generateMappings(processedFields); - expect(JSON.stringify(mappings)).toEqual(JSON.stringify(objectFieldDynamicFalseMapping)); + expect(mappings).toEqual(objectFieldDynamicFalseMapping); }); test('tests processing object field with dynamic set to true', () => { @@ -237,7 +237,7 @@ test('tests processing object field with dynamic set to true', () => { const fields: Field[] = safeLoad(objectFieldDynamicTrueLiteralYml); const processedFields = processFields(fields); const mappings = generateMappings(processedFields); - expect(JSON.stringify(mappings)).toEqual(JSON.stringify(objectFieldDynamicTrueMapping)); + expect(mappings).toEqual(objectFieldDynamicTrueMapping); }); test('tests processing object field with dynamic set to strict', () => { @@ -257,7 +257,7 @@ test('tests processing object field with dynamic set to strict', () => { const fields: Field[] = safeLoad(objectFieldDynamicStrictLiteralYml); const processedFields = processFields(fields); const mappings = generateMappings(processedFields); - expect(JSON.stringify(mappings)).toEqual(JSON.stringify(objectFieldDynamicStrictMapping)); + expect(mappings).toEqual(objectFieldDynamicStrictMapping); }); test('tests processing object field with property', () => { @@ -282,7 +282,7 @@ test('tests processing object field with property', () => { const fields: Field[] = safeLoad(objectFieldWithPropertyLiteralYml); const processedFields = processFields(fields); const mappings = generateMappings(processedFields); - expect(JSON.stringify(mappings)).toEqual(JSON.stringify(objectFieldWithPropertyMapping)); + expect(mappings).toEqual(objectFieldWithPropertyMapping); }); test('tests processing object field with property, reverse order', () => { @@ -291,10 +291,12 @@ test('tests processing object field with property, reverse order', () => { type: keyword - name: a type: object + dynamic: false `; const objectFieldWithPropertyReversedMapping = { properties: { a: { + dynamic: false, properties: { b: { ignore_above: 1024, @@ -307,7 +309,91 @@ test('tests processing object field with property, reverse order', () => { const fields: Field[] = safeLoad(objectFieldWithPropertyReversedLiteralYml); const processedFields = processFields(fields); const mappings = generateMappings(processedFields); - expect(JSON.stringify(mappings)).toEqual(JSON.stringify(objectFieldWithPropertyReversedMapping)); + expect(mappings).toEqual(objectFieldWithPropertyReversedMapping); +}); + +test('tests processing nested field with property', () => { + const nestedYaml = ` + - name: a.b + type: keyword + - name: a + type: nested + dynamic: false + `; + const expectedMapping = { + properties: { + a: { + dynamic: false, + type: 'nested', + properties: { + b: { + ignore_above: 1024, + type: 'keyword', + }, + }, + }, + }, + }; + const fields: Field[] = safeLoad(nestedYaml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(mappings).toEqual(expectedMapping); +}); + +test('tests processing nested field with property, nested field first', () => { + const nestedYaml = ` + - name: a + type: nested + include_in_parent: true + - name: a.b + type: keyword + `; + const expectedMapping = { + properties: { + a: { + include_in_parent: true, + type: 'nested', + properties: { + b: { + ignore_above: 1024, + type: 'keyword', + }, + }, + }, + }, + }; + const fields: Field[] = safeLoad(nestedYaml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(mappings).toEqual(expectedMapping); +}); + +test('tests processing nested leaf field with properties', () => { + const nestedYaml = ` + - name: a + type: object + dynamic: false + - name: a.b + type: nested + enabled: false + `; + const expectedMapping = { + properties: { + a: { + dynamic: false, + properties: { + b: { + enabled: false, + type: 'nested', + }, + }, + }, + }, + }; + const fields: Field[] = safeLoad(nestedYaml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(mappings).toEqual(expectedMapping); }); test('tests constant_keyword field type handling', () => { diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts index 9736f6d1cbd3cd..c45c7e706be588 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts @@ -71,7 +71,14 @@ export function generateMappings(fields: Field[]): IndexTemplateMappings { switch (type) { case 'group': - fieldProps = generateMappings(field.fields!); + fieldProps = { ...generateMappings(field.fields!), ...generateDynamicAndEnabled(field) }; + break; + case 'group-nested': + fieldProps = { + ...generateMappings(field.fields!), + ...generateNestedProps(field), + type: 'nested', + }; break; case 'integer': fieldProps.type = 'long'; @@ -95,13 +102,10 @@ export function generateMappings(fields: Field[]): IndexTemplateMappings { } break; case 'object': - fieldProps.type = 'object'; - if (field.hasOwnProperty('enabled')) { - fieldProps.enabled = field.enabled; - } - if (field.hasOwnProperty('dynamic')) { - fieldProps.dynamic = field.dynamic; - } + fieldProps = { ...fieldProps, ...generateDynamicAndEnabled(field), type: 'object' }; + break; + case 'nested': + fieldProps = { ...fieldProps, ...generateNestedProps(field), type: 'nested' }; break; case 'array': // this assumes array fields were validated in an earlier step @@ -128,6 +132,29 @@ export function generateMappings(fields: Field[]): IndexTemplateMappings { return { properties: props }; } +function generateDynamicAndEnabled(field: Field) { + const props: Properties = {}; + if (field.hasOwnProperty('enabled')) { + props.enabled = field.enabled; + } + if (field.hasOwnProperty('dynamic')) { + props.dynamic = field.dynamic; + } + return props; +} + +function generateNestedProps(field: Field) { + const props = generateDynamicAndEnabled(field); + + if (field.hasOwnProperty('include_in_parent')) { + props.include_in_parent = field.include_in_parent; + } + if (field.hasOwnProperty('include_in_root')) { + props.include_in_root = field.include_in_root; + } + return props; +} + function generateMultiFields(fields: Fields): MultiFields { const multiFields: MultiFields = {}; if (fields) { diff --git a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.test.ts index 42989bb1e3ac91..f0ff4c61254524 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.test.ts @@ -210,4 +210,166 @@ describe('processFields', () => { JSON.stringify(objectFieldWithPropertyExpanded) ); }); + + test('correctly handles properties of object type fields where object comes second', () => { + const nested = [ + { + name: 'a.b', + type: 'keyword', + }, + { + name: 'a', + type: 'object', + dynamic: true, + }, + ]; + + const nestedExpanded = [ + { + name: 'a', + type: 'group', + dynamic: true, + fields: [ + { + name: 'b', + type: 'keyword', + }, + ], + }, + ]; + expect(processFields(nested)).toEqual(nestedExpanded); + }); + + test('correctly handles properties of nested type fields', () => { + const nested = [ + { + name: 'a', + type: 'nested', + dynamic: true, + }, + { + name: 'a.b', + type: 'keyword', + }, + ]; + + const nestedExpanded = [ + { + name: 'a', + type: 'group-nested', + dynamic: true, + fields: [ + { + name: 'b', + type: 'keyword', + }, + ], + }, + ]; + expect(processFields(nested)).toEqual(nestedExpanded); + }); + + test('correctly handles properties of nested type where nested top level comes second', () => { + const nested = [ + { + name: 'a.b', + type: 'keyword', + }, + { + name: 'a', + type: 'nested', + dynamic: true, + }, + ]; + + const nestedExpanded = [ + { + name: 'a', + type: 'group-nested', + dynamic: true, + fields: [ + { + name: 'b', + type: 'keyword', + }, + ], + }, + ]; + expect(processFields(nested)).toEqual(nestedExpanded); + }); + + test('ignores redefinitions of an object field', () => { + const object = [ + { + name: 'a', + type: 'object', + dynamic: true, + }, + { + name: 'a', + type: 'object', + dynamic: false, + }, + ]; + + const objectExpected = [ + { + name: 'a', + type: 'object', + // should preserve the field that was parsed first which had dynamic: true + dynamic: true, + }, + ]; + expect(processFields(object)).toEqual(objectExpected); + }); + + test('ignores redefinitions of a nested field', () => { + const nested = [ + { + name: 'a', + type: 'nested', + dynamic: true, + }, + { + name: 'a', + type: 'nested', + dynamic: false, + }, + ]; + + const nestedExpected = [ + { + name: 'a', + type: 'nested', + // should preserve the field that was parsed first which had dynamic: true + dynamic: true, + }, + ]; + expect(processFields(nested)).toEqual(nestedExpected); + }); + + test('ignores redefinitions of a nested and object field', () => { + const nested = [ + { + name: 'a', + type: 'nested', + dynamic: true, + }, + { + name: 'a', + type: 'object', + dynamic: false, + }, + ]; + + const nestedExpected = [ + { + name: 'a', + type: 'nested', + // should preserve the field that was parsed first which had dynamic: true + dynamic: true, + }, + ]; + expect(processFields(nested)).toEqual(nestedExpected); + }); }); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts index edf7624d3f0d5b..abaf7ab5b0dfc4 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts @@ -28,6 +28,8 @@ export interface Field { object_type?: string; scaling_factor?: number; dynamic?: 'strict' | boolean; + include_in_parent?: boolean; + include_in_root?: boolean; // Kibana specific analyzed?: boolean; @@ -108,18 +110,54 @@ function dedupFields(fields: Fields): Fields { return f.name === field.name; }); if (found) { + // remove name, type, and fields from `field` variable so we avoid merging them into `found` + const { name, type, fields: nestedFields, ...importantFieldProps } = field; + /** + * There are a couple scenarios this if is trying to account for: + * Example 1 + * - name: a.b + * - name: a + * In this scenario found will be `group` and field could be either `object` or `nested` + * Example 2 + * - name: a + * - name: a.b + * In this scenario found could be `object` or `nested` and field will be group + */ if ( - (found.type === 'group' || found.type === 'object') && - field.type === 'group' && - field.fields + // only merge if found is a group and field is object, nested, or group. + // Or if found is object, or nested, and field is a group. + // This is to avoid merging two objects, or nested, or object with a nested. + (found.type === 'group' && + (field.type === 'object' || field.type === 'nested' || field.type === 'group')) || + ((found.type === 'object' || found.type === 'nested') && field.type === 'group') ) { - if (!found.fields) { - found.fields = []; + // if the new field has properties let's dedup and concat them with the already existing found variable in + // the array + if (field.fields) { + // if the found type was object or nested it won't have a fields array so let's initialize it + if (!found.fields) { + found.fields = []; + } + found.fields = dedupFields(found.fields.concat(field.fields)); } - found.type = 'group'; - found.fields = dedupFields(found.fields.concat(field.fields)); + + // if found already had fields or got new ones from the new field coming in we need to assign the right + // type to it + if (found.fields) { + // If this field is supposed to be `nested` and we have fields, we need to preserve the fact that it is + // supposed to be `nested` for when the template is actually generated + if (found.type === 'nested' || field.type === 'nested') { + found.type = 'group-nested'; + } else { + // found was either `group` already or `object` so just set it to `group` + found.type = 'group'; + } + } + // we need to merge in other properties (like `dynamic`) that might exist + Object.assign(found, importantFieldProps); + // if `field.type` wasn't group object or nested, then there's a conflict in types, so lets ignore it } else { - // only 'group' fields can be merged in this way + // only `group`, `object`, and `nested` fields can be merged in this way // XXX: don't abort on error for now // see discussion in https://github.com/elastic/kibana/pull/59894 // throw new Error( From 693a84aace273108d2ebf51b3a8d018cd97ed41c Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 30 Apr 2020 19:16:31 +0200 Subject: [PATCH 18/47] load SettingsOptions component lazily (#64638) Co-authored-by: Elastic Machine --- .../vis_type_markdown/public/markdown_vis.ts | 2 +- .../public/settings_options.tsx | 4 ++- .../public/settings_options_lazy.tsx | 30 +++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 src/plugins/vis_type_markdown/public/settings_options_lazy.tsx diff --git a/src/plugins/vis_type_markdown/public/markdown_vis.ts b/src/plugins/vis_type_markdown/public/markdown_vis.ts index b84d9638eb973d..3309330d7527c9 100644 --- a/src/plugins/vis_type_markdown/public/markdown_vis.ts +++ b/src/plugins/vis_type_markdown/public/markdown_vis.ts @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; import { MarkdownVisWrapper } from './markdown_vis_controller'; import { MarkdownOptions } from './markdown_options'; -import { SettingsOptions } from './settings_options'; +import { SettingsOptions } from './settings_options_lazy'; import { DefaultEditorSize } from '../../vis_default_editor/public'; export const markdownVisDefinition = { diff --git a/src/plugins/vis_type_markdown/public/settings_options.tsx b/src/plugins/vis_type_markdown/public/settings_options.tsx index 6f6a80564ce076..bf4570db5d4a0d 100644 --- a/src/plugins/vis_type_markdown/public/settings_options.tsx +++ b/src/plugins/vis_type_markdown/public/settings_options.tsx @@ -52,4 +52,6 @@ function SettingsOptions({ stateParams, setValue }: VisOptionsProps import('./settings_options')); + +export const SettingsOptions = (props: any) => ( + }> + + +); From 3442c25b9f7bf0dd5a8b6ad614f5e0e64b5d318a Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 30 Apr 2020 19:18:07 +0200 Subject: [PATCH 19/47] lazy load React components + VegaParser (#64749) --- .../vis_type_vega/public/vega_request_handler.ts | 13 +++++++++---- .../vis_type_vega/public/vega_visualization.js | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/plugins/vis_type_vega/public/vega_request_handler.ts b/src/plugins/vis_type_vega/public/vega_request_handler.ts index 196e8fdcbafdac..efc02e368efa8e 100644 --- a/src/plugins/vis_type_vega/public/vega_request_handler.ts +++ b/src/plugins/vis_type_vega/public/vega_request_handler.ts @@ -19,8 +19,6 @@ import { Filter, esQuery, TimeRange, Query } from '../../data/public'; -// @ts-ignore -import { VegaParser } from './data_model/vega_parser'; // @ts-ignore import { SearchCache } from './data_model/search_cache'; // @ts-ignore @@ -46,7 +44,12 @@ export function createVegaRequestHandler({ const { timefilter } = data.query.timefilter; const timeCache = new TimeCache(timefilter, 3 * 1000); - return ({ timeRange, filters, query, visParams }: VegaRequestHandlerParams) => { + return async function vegaRequestHandler({ + timeRange, + filters, + query, + visParams, + }: VegaRequestHandlerParams) { if (!searchCache) { searchCache = new SearchCache(getData().search.__LEGACY.esClient, { max: 10, @@ -58,8 +61,10 @@ export function createVegaRequestHandler({ const esQueryConfigs = esQuery.getEsQueryConfig(uiSettings); const filtersDsl = esQuery.buildEsQuery(undefined, query, filters, esQueryConfigs); + // @ts-ignore + const { VegaParser } = await import('./data_model/vega_parser'); const vp = new VegaParser(visParams.spec, searchCache, timeCache, filtersDsl, serviceSettings); - return vp.parseAsync(); + return await vp.parseAsync(); }; } diff --git a/src/plugins/vis_type_vega/public/vega_visualization.js b/src/plugins/vis_type_vega/public/vega_visualization.js index a6e911de7f0cb0..1fcb89f04457da 100644 --- a/src/plugins/vis_type_vega/public/vega_visualization.js +++ b/src/plugins/vis_type_vega/public/vega_visualization.js @@ -17,8 +17,6 @@ * under the License. */ import { i18n } from '@kbn/i18n'; -import { VegaView } from './vega_view/vega_view'; -import { VegaMapView } from './vega_view/vega_map_view'; import { getNotifications, getData, getSavedObjects } from './services'; export const createVegaVisualization = ({ serviceSettings }) => @@ -117,8 +115,10 @@ export const createVegaVisualization = ({ serviceSettings }) => if (vegaParser.useMap) { const services = { toastService: getNotifications().toasts }; + const { VegaMapView } = await import('./vega_view/vega_map_view'); this._vegaView = new VegaMapView(vegaViewParams, services); } else { + const { VegaView } = await import('./vega_view/vega_view'); this._vegaView = new VegaView(vegaViewParams); } await this._vegaView.init(); From 0efd02b49dbbc825f8694237dc74bfa7685682d3 Mon Sep 17 00:00:00 2001 From: Lee Drengenberg Date: Thu, 30 Apr 2020 17:30:06 +0000 Subject: [PATCH 20/47] change createIndexPattern to do what it takes to make the indexPatternName* (#64646) --- .../apps/discover/_discover_histogram.js | 2 +- .../apps/getting_started/_shakespeare.js | 6 ++--- .../_index_pattern_create_delete.js | 5 +--- test/functional/page_objects/settings_page.ts | 26 ++++++++++++++++--- x-pack/test/functional/apps/graph/graph.ts | 4 +-- 5 files changed, 29 insertions(+), 14 deletions(-) diff --git a/test/functional/apps/discover/_discover_histogram.js b/test/functional/apps/discover/_discover_histogram.js index eeef3333aab0fc..dcd185eba00e6e 100644 --- a/test/functional/apps/discover/_discover_histogram.js +++ b/test/functional/apps/discover/_discover_histogram.js @@ -48,7 +48,7 @@ export default function({ getService, getPageObjects }) { log.debug('create long_window_logstash index pattern'); // NOTE: long_window_logstash load does NOT create index pattern - await PageObjects.settings.createIndexPattern('long-window-logstash-'); + await PageObjects.settings.createIndexPattern('long-window-logstash-*'); await kibanaServer.uiSettings.replace(defaultSettings); await browser.refresh(); diff --git a/test/functional/apps/getting_started/_shakespeare.js b/test/functional/apps/getting_started/_shakespeare.js index 9a4bb0081b7ad1..3a3d6b93e166bf 100644 --- a/test/functional/apps/getting_started/_shakespeare.js +++ b/test/functional/apps/getting_started/_shakespeare.js @@ -59,9 +59,9 @@ export default function({ getService, getPageObjects }) { it('should create shakespeare index pattern', async function() { log.debug('Create shakespeare index pattern'); - await PageObjects.settings.createIndexPattern('shakes', null); + await PageObjects.settings.createIndexPattern('shakespeare', null); const patternName = await PageObjects.settings.getIndexPageHeading(); - expect(patternName).to.be('shakes*'); + expect(patternName).to.be('shakespeare'); }); // https://www.elastic.co/guide/en/kibana/current/tutorial-visualizing.html @@ -74,7 +74,7 @@ export default function({ getService, getPageObjects }) { log.debug('create shakespeare vertical bar chart'); await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVerticalBarChart(); - await PageObjects.visualize.clickNewSearch('shakes*'); + await PageObjects.visualize.clickNewSearch('shakespeare'); await PageObjects.visChart.waitForVisualization(); const expectedChartValues = [111396]; diff --git a/test/functional/apps/management/_index_pattern_create_delete.js b/test/functional/apps/management/_index_pattern_create_delete.js index 616e2297b2f510..2545b8f324d1b9 100644 --- a/test/functional/apps/management/_index_pattern_create_delete.js +++ b/test/functional/apps/management/_index_pattern_create_delete.js @@ -44,10 +44,7 @@ export default function({ getService, getPageObjects }) { it('should handle special charaters in template input', async () => { await PageObjects.settings.clickAddNewIndexPatternButton(); await PageObjects.header.waitUntilLoadingHasFinished(); - await PageObjects.settings.setIndexPatternField({ - indexPatternName: '❤️', - expectWildcard: false, - }); + await PageObjects.settings.setIndexPatternField('❤️'); await PageObjects.header.waitUntilLoadingHasFinished(); await retry.try(async () => { diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts index 8864eda3823efe..81d22838d1e8b7 100644 --- a/test/functional/page_objects/settings_page.ts +++ b/test/functional/page_objects/settings_page.ts @@ -334,7 +334,7 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider } await PageObjects.header.waitUntilLoadingHasFinished(); await retry.try(async () => { - await this.setIndexPatternField({ indexPatternName }); + await this.setIndexPatternField(indexPatternName); }); await PageObjects.common.sleep(2000); await (await this.getCreateIndexPatternGoToStep2Button()).click(); @@ -375,14 +375,32 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider return indexPatternId; } - async setIndexPatternField({ indexPatternName = 'logstash-', expectWildcard = true } = {}) { + async setIndexPatternField(indexPatternName = 'logstash-*') { log.debug(`setIndexPatternField(${indexPatternName})`); const field = await this.getIndexPatternField(); await field.clearValue(); - await field.type(indexPatternName, { charByChar: true }); + if ( + indexPatternName.charAt(0) === '*' && + indexPatternName.charAt(indexPatternName.length - 1) === '*' + ) { + // this is a special case when the index pattern name starts with '*' + // like '*:makelogs-*' where the UI will not append * + await field.type(indexPatternName, { charByChar: true }); + } else if (indexPatternName.charAt(indexPatternName.length - 1) === '*') { + // the common case where the UI will append '*' automatically so we won't type it + const tempName = indexPatternName.slice(0, -1); + await field.type(tempName, { charByChar: true }); + } else { + // case where we don't want the * appended so we'll remove it if it was added + await field.type(indexPatternName, { charByChar: true }); + const tempName = await field.getAttribute('value'); + if (tempName.length > indexPatternName.length) { + await field.type(browser.keys.DELETE, { charByChar: true }); + } + } const currentName = await field.getAttribute('value'); log.debug(`setIndexPatternField set to ${currentName}`); - expect(currentName).to.eql(`${indexPatternName}${expectWildcard ? '*' : ''}`); + expect(currentName).to.eql(indexPatternName); } async getCreateIndexPatternGoToStep2Button() { diff --git a/x-pack/test/functional/apps/graph/graph.ts b/x-pack/test/functional/apps/graph/graph.ts index 2bbc39969370bf..fcf7298c5577ae 100644 --- a/x-pack/test/functional/apps/graph/graph.ts +++ b/x-pack/test/functional/apps/graph/graph.ts @@ -76,7 +76,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { } it('should show correct node labels', async function() { - await PageObjects.graph.selectIndexPattern('secrepo*'); + await PageObjects.graph.selectIndexPattern('secrepo'); await buildGraph(); const { nodes } = await PageObjects.graph.getGraphObjects(); const circlesText = nodes.map(({ label }) => label); @@ -120,7 +120,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { it('should create new Graph workspace', async function() { await PageObjects.graph.newGraph(); - await PageObjects.graph.selectIndexPattern('secrepo*'); + await PageObjects.graph.selectIndexPattern('secrepo'); const { nodes, edges } = await PageObjects.graph.getGraphObjects(); expect(nodes).to.be.empty(); expect(edges).to.be.empty(); From fc2f2446eb73c1af3119db60bdf65d96102de593 Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Thu, 30 Apr 2020 20:08:30 +0200 Subject: [PATCH 21/47] fix: fix migration (#64894) (cherry picked from commit 99b5982f1c6e0940acbe578d735aae31ddc57981) Co-authored-by: Elastic Machine --- x-pack/plugins/lens/server/migrations.test.ts | 2 +- x-pack/plugins/lens/server/migrations.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/server/migrations.test.ts b/x-pack/plugins/lens/server/migrations.test.ts index 4cc330d40efd7f..0541d9636577ba 100644 --- a/x-pack/plugins/lens/server/migrations.test.ts +++ b/x-pack/plugins/lens/server/migrations.test.ts @@ -160,7 +160,7 @@ describe('Lens migrations', () => { }); describe('7.8.0 auto timestamp', () => { - const context = {} as SavedObjectMigrationContext; + const context = ({ log: { warning: () => {} } } as unknown) as SavedObjectMigrationContext; const example = { type: 'lens', diff --git a/x-pack/plugins/lens/server/migrations.ts b/x-pack/plugins/lens/server/migrations.ts index 583fba1a4a9992..a15e2b3692d026 100644 --- a/x-pack/plugins/lens/server/migrations.ts +++ b/x-pack/plugins/lens/server/migrations.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { cloneDeep, flow } from 'lodash'; +import { cloneDeep } from 'lodash'; import { fromExpression, toExpression, Ast, ExpressionFunctionAST } from '@kbn/interpreter/common'; import { SavedObjectMigrationFn } from 'src/core/server'; @@ -156,5 +156,5 @@ export const migrations: Record> = { }, // The order of these migrations matter, since the timefield migration relies on the aggConfigs // sitting directly on the esaggs as an argument and not a nested function (which lens_auto_date was). - '7.8.0': flow(removeLensAutoDate, addTimeFieldToEsaggs), + '7.8.0': (doc, context) => addTimeFieldToEsaggs(removeLensAutoDate(doc, context), context), }; From 8a304623d4bf2e3be16f4c2cd25aa734b0308f5e Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Thu, 30 Apr 2020 11:12:22 -0700 Subject: [PATCH 22/47] [Ingest] Agent config settings UI (#64854) * Remove duplicate donut chart, move used donut chart closer to usage, clean up import paths * Change delete agent config to only single delete and delete all its associated data sources * Initial pass at settings tab * Reuse existing create agent config form instead * Fix delete api * Prevent nav drawer from hiding bottom bar (save action area) content * Remove delete config functionality from list page * Prevent API from deleting config with agents enrolled * Fix namespace populating in form * Display confirmation modal to deploy to agents if agents are detected * Adjust confirm delete copy * Fix i18n checks * Fix type check * Fix it again * De-dupe confirm modal * Fix i18n * Update agent config info after saving * Adjust skip unassign from agent config option schema in delete datasource method --- .../common/types/rest_spec/agent_config.ts | 8 +- .../hooks/use_request/agent_config.ts | 8 +- .../applications/ingest_manager/index.scss | 14 + .../applications/ingest_manager/index.tsx | 1 + .../components/config_delete_provider.tsx | 172 +++---- .../agent_config/components/config_form.tsx | 449 ++++++++++-------- .../confirm_deploy_modal.tsx} | 16 +- .../sections/agent_config/components/index.ts | 1 + .../components/linked_agent_count.tsx | 4 +- .../components/index.ts | 1 - .../create_datasource_page/index.tsx | 28 +- .../details_page/components/config_form.tsx | 94 ---- .../details_page/components/donut_chart.tsx | 65 --- .../details_page/components/edit_config.tsx | 135 ------ .../details_page/components/index.ts | 2 - .../components/settings/index.tsx | 216 +++++++++ .../details_page/components/yaml/index.tsx | 2 +- .../agent_config/details_page/hooks/index.ts | 2 +- .../agent_config/details_page/index.tsx | 33 +- .../edit_datasource_page/index.tsx | 8 +- .../sections/fleet/agent_list_page/index.scss | 6 - .../components/donut_chart.tsx | 0 .../sections/fleet/components/list_layout.tsx | 6 +- .../ingest_manager/types/index.ts | 4 +- .../server/routes/agent_config/handlers.ts | 12 +- .../server/routes/agent_config/index.ts | 4 +- .../server/services/agent_config.ts | 57 ++- .../server/services/datasource.ts | 20 +- .../server/types/rest_spec/agent_config.ts | 4 +- .../translations/translations/ja-JP.json | 26 - .../translations/translations/zh-CN.json | 26 - 31 files changed, 680 insertions(+), 744 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.scss rename x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/{create_datasource_page/components/confirm_modal.tsx => components/confirm_deploy_modal.tsx} (76%) delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/config_form.tsx delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/donut_chart.tsx delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/edit_config.tsx create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/settings/index.tsx delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.scss rename x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/{agent_list_page => }/components/donut_chart.tsx (100%) diff --git a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts index 89d548d11dadb6..82d7fa51b20826 100644 --- a/x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts +++ b/x-pack/plugins/ingest_manager/common/types/rest_spec/agent_config.ts @@ -49,16 +49,16 @@ export interface UpdateAgentConfigResponse { success: boolean; } -export interface DeleteAgentConfigsRequest { +export interface DeleteAgentConfigRequest { body: { - agentConfigIds: string[]; + agentConfigId: string; }; } -export type DeleteAgentConfigsResponse = Array<{ +export interface DeleteAgentConfigResponse { id: string; success: boolean; -}>; +} export interface GetFullAgentConfigRequest { params: { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts index bed3f994005ad2..f80c468677f482 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_request/agent_config.ts @@ -18,8 +18,8 @@ import { CreateAgentConfigResponse, UpdateAgentConfigRequest, UpdateAgentConfigResponse, - DeleteAgentConfigsRequest, - DeleteAgentConfigsResponse, + DeleteAgentConfigRequest, + DeleteAgentConfigResponse, } from '../../types'; export const useGetAgentConfigs = (query: HttpFetchQuery = {}) => { @@ -75,8 +75,8 @@ export const sendUpdateAgentConfig = ( }); }; -export const sendDeleteAgentConfigs = (body: DeleteAgentConfigsRequest['body']) => { - return sendRequest({ +export const sendDeleteAgentConfig = (body: DeleteAgentConfigRequest['body']) => { + return sendRequest({ path: agentConfigRouteService.getDeletePath(), method: 'post', body: JSON.stringify(body), diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.scss b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.scss new file mode 100644 index 00000000000000..fb95b1fa8bbfc6 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.scss @@ -0,0 +1,14 @@ +@import '@elastic/eui/src/components/header/variables'; +@import '@elastic/eui/src/components/nav_drawer/variables'; + +/** + * 1. Hack EUI so the bottom bar doesn't obscure the nav drawer flyout. + */ +.ingestManager__bottomBar { + z-index: 0; /* 1 */ + left: $euiNavDrawerWidthCollapsed; +} + +.ingestManager__bottomBar-isNavDrawerLocked { + left: $euiNavDrawerWidthExpanded; +} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx index 6485862830d8ab..295a35693726f3 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx @@ -23,6 +23,7 @@ import { IngestManagerOverview, EPMApp, AgentConfigApp, FleetApp, DataStreamApp import { CoreContext, DepsContext, ConfigContext, setHttpClient, useConfig } from './hooks'; import { PackageInstallProvider } from './sections/epm/hooks'; import { sendSetup } from './hooks/use_request/setup'; +import './index.scss'; export interface ProtectedRouteProps extends RouteProps { isAllowed?: boolean; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx index 9ae8369abbd52e..d517dde45d5e3c 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_delete_provider.tsx @@ -5,117 +5,92 @@ */ import React, { Fragment, useRef, useState } from 'react'; -import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; +import { EuiConfirmModal, EuiOverlayMask, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { AGENT_SAVED_OBJECT_TYPE } from '../../../constants'; -import { sendDeleteAgentConfigs, useCore, sendRequest } from '../../../hooks'; +import { sendDeleteAgentConfig, useCore, useConfig, sendRequest } from '../../../hooks'; interface Props { - children: (deleteAgentConfigs: deleteAgentConfigs) => React.ReactElement; + children: (deleteAgentConfig: DeleteAgentConfig) => React.ReactElement; } -export type deleteAgentConfigs = (agentConfigs: string[], onSuccess?: OnSuccessCallback) => void; +export type DeleteAgentConfig = (agentConfig: string, onSuccess?: OnSuccessCallback) => void; -type OnSuccessCallback = (agentConfigsUnenrolled: string[]) => void; +type OnSuccessCallback = (agentConfigDeleted: string) => void; export const AgentConfigDeleteProvider: React.FunctionComponent = ({ children }) => { const { notifications } = useCore(); - const [agentConfigs, setAgentConfigs] = useState([]); + const { + fleet: { enabled: isFleetEnabled }, + } = useConfig(); + const [agentConfig, setAgentConfig] = useState(); const [isModalOpen, setIsModalOpen] = useState(false); const [isLoadingAgentsCount, setIsLoadingAgentsCount] = useState(false); const [agentsCount, setAgentsCount] = useState(0); const [isLoading, setIsLoading] = useState(false); const onSuccessCallback = useRef(null); - const deleteAgentConfigsPrompt: deleteAgentConfigs = ( - agentConfigsToDelete, + const deleteAgentConfigPrompt: DeleteAgentConfig = ( + agentConfigToDelete, onSuccess = () => undefined ) => { - if ( - agentConfigsToDelete === undefined || - (Array.isArray(agentConfigsToDelete) && agentConfigsToDelete.length === 0) - ) { - throw new Error('No agent configs specified for deletion'); + if (!agentConfigToDelete) { + throw new Error('No agent config specified for deletion'); } setIsModalOpen(true); - setAgentConfigs(agentConfigsToDelete); - fetchAgentsCount(agentConfigsToDelete); + setAgentConfig(agentConfigToDelete); + fetchAgentsCount(agentConfigToDelete); onSuccessCallback.current = onSuccess; }; const closeModal = () => { - setAgentConfigs([]); + setAgentConfig(undefined); setIsLoading(false); setIsLoadingAgentsCount(false); setIsModalOpen(false); }; - const deleteAgentConfigs = async () => { + const deleteAgentConfig = async () => { setIsLoading(true); try { - const { data } = await sendDeleteAgentConfigs({ - agentConfigIds: agentConfigs, + const { data } = await sendDeleteAgentConfig({ + agentConfigId: agentConfig!, }); - const successfulResults = data?.filter(result => result.success) || []; - const failedResults = data?.filter(result => !result.success) || []; - if (successfulResults.length) { - const hasMultipleSuccesses = successfulResults.length > 1; - const successMessage = hasMultipleSuccesses - ? i18n.translate( - 'xpack.ingestManager.deleteAgentConfigs.successMultipleNotificationTitle', - { - defaultMessage: 'Deleted {count} agent configs', - values: { count: successfulResults.length }, - } - ) - : i18n.translate( - 'xpack.ingestManager.deleteAgentConfigs.successSingleNotificationTitle', - { - defaultMessage: "Deleted agent config '{id}'", - values: { id: successfulResults[0].id }, - } - ); - notifications.toasts.addSuccess(successMessage); + if (data?.success) { + notifications.toasts.addSuccess( + i18n.translate('xpack.ingestManager.deleteAgentConfig.successSingleNotificationTitle', { + defaultMessage: "Deleted agent config '{id}'", + values: { id: agentConfig }, + }) + ); + if (onSuccessCallback.current) { + onSuccessCallback.current(agentConfig!); + } } - if (failedResults.length) { - const hasMultipleFailures = failedResults.length > 1; - const failureMessage = hasMultipleFailures - ? i18n.translate( - 'xpack.ingestManager.deleteAgentConfigs.failureMultipleNotificationTitle', - { - defaultMessage: 'Error deleting {count} agent configs', - values: { count: failedResults.length }, - } - ) - : i18n.translate( - 'xpack.ingestManager.deleteAgentConfigs.failureSingleNotificationTitle', - { - defaultMessage: "Error deleting agent config '{id}'", - values: { id: failedResults[0].id }, - } - ); - notifications.toasts.addDanger(failureMessage); - } - - if (onSuccessCallback.current) { - onSuccessCallback.current(successfulResults.map(result => result.id)); + if (!data?.success) { + notifications.toasts.addDanger( + i18n.translate('xpack.ingestManager.deleteAgentConfig.failureSingleNotificationTitle', { + defaultMessage: "Error deleting agent config '{id}'", + values: { id: agentConfig }, + }) + ); } } catch (e) { notifications.toasts.addDanger( - i18n.translate('xpack.ingestManager.deleteAgentConfigs.fatalErrorNotificationTitle', { - defaultMessage: 'Error deleting agent configs', + i18n.translate('xpack.ingestManager.deleteAgentConfig.fatalErrorNotificationTitle', { + defaultMessage: 'Error deleting agent config', }) ); } closeModal(); }; - const fetchAgentsCount = async (agentConfigsToCheck: string[]) => { - if (isLoadingAgentsCount) { + const fetchAgentsCount = async (agentConfigToCheck: string) => { + if (!isFleetEnabled || isLoadingAgentsCount) { return; } setIsLoadingAgentsCount(true); @@ -123,7 +98,7 @@ export const AgentConfigDeleteProvider: React.FunctionComponent = ({ chil path: `/api/ingest_manager/fleet/agents`, method: 'get', query: { - kuery: `${AGENT_SAVED_OBJECT_TYPE}.config_id : (${agentConfigsToCheck.join(' or ')})`, + kuery: `${AGENT_SAVED_OBJECT_TYPE}.config_id : ${agentConfigToCheck}`, }, }); setAgentsCount(data?.total || 0); @@ -140,68 +115,61 @@ export const AgentConfigDeleteProvider: React.FunctionComponent = ({ chil } onCancel={closeModal} - onConfirm={deleteAgentConfigs} + onConfirm={deleteAgentConfig} cancelButtonText={ } confirmButtonText={ isLoading || isLoadingAgentsCount ? ( - ) : agentsCount ? ( - ) : ( ) } buttonColor="danger" - confirmButtonDisabled={isLoading || isLoadingAgentsCount} + confirmButtonDisabled={isLoading || isLoadingAgentsCount || !!agentsCount} > {isLoadingAgentsCount ? ( ) : agentsCount ? ( - + + + ) : ( )} @@ -211,7 +179,7 @@ export const AgentConfigDeleteProvider: React.FunctionComponent = ({ chil return ( - {children(deleteAgentConfigsPrompt)} + {children(deleteAgentConfigPrompt)} {renderModal()} ); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx index 92c44d86e47c65..c55d6009074b0a 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/config_form.tsx @@ -8,8 +8,7 @@ import React, { useMemo, useState } from 'react'; import { EuiAccordion, EuiFieldText, - EuiFlexGroup, - EuiFlexItem, + EuiDescribedFormGroup, EuiForm, EuiFormRow, EuiHorizontalRule, @@ -19,11 +18,13 @@ import { EuiComboBox, EuiIconTip, EuiCheckboxGroup, + EuiButton, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; -import { NewAgentConfig } from '../../../types'; +import { NewAgentConfig, AgentConfig } from '../../../types'; +import { AgentConfigDeleteProvider } from './config_delete_provider'; interface ValidationResults { [key: string]: JSX.Element[]; @@ -36,7 +37,7 @@ const StyledEuiAccordion = styled(EuiAccordion)` `; export const agentConfigFormValidation = ( - agentConfig: Partial + agentConfig: Partial ): ValidationResults => { const errors: ValidationResults = {}; @@ -53,11 +54,13 @@ export const agentConfigFormValidation = ( }; interface Props { - agentConfig: Partial; - updateAgentConfig: (u: Partial) => void; + agentConfig: Partial; + updateAgentConfig: (u: Partial) => void; withSysMonitoring: boolean; updateSysMonitoring: (newValue: boolean) => void; validation: ValidationResults; + isEditing?: boolean; + onDelete?: () => void; } export const AgentConfigForm: React.FunctionComponent = ({ @@ -66,9 +69,11 @@ export const AgentConfigForm: React.FunctionComponent = ({ withSysMonitoring, updateSysMonitoring, validation, + isEditing = false, + onDelete = () => {}, }) => { const [touchedFields, setTouchedFields] = useState<{ [key: string]: boolean }>({}); - const [showNamespace, setShowNamespace] = useState(false); + const [showNamespace, setShowNamespace] = useState(!!agentConfig.namespace); const fields: Array<{ name: 'name' | 'description' | 'namespace'; label: JSX.Element; @@ -105,209 +110,281 @@ export const AgentConfigForm: React.FunctionComponent = ({ ]; }, []); - return ( - - {fields.map(({ name, label, placeholder }) => { - return ( - - updateAgentConfig({ [name]: e.target.value })} - isInvalid={Boolean(touchedFields[name] && validation[name])} - onBlur={() => setTouchedFields({ ...touchedFields, [name]: true })} - placeholder={placeholder} - /> - - ); - })} + const generalSettingsWrapper = (children: JSX.Element[]) => ( + + + + } + description={ + + } + > + {children} + + ); + + const generalFields = fields.map(({ name, label, placeholder }) => { + return ( + fullWidth + key={name} + label={label} + error={touchedFields[name] && validation[name] ? validation[name] : null} + isInvalid={Boolean(touchedFields[name] && validation[name])} + > + updateAgentConfig({ [name]: e.target.value })} + isInvalid={Boolean(touchedFields[name] && validation[name])} + onBlur={() => setTouchedFields({ ...touchedFields, [name]: true })} + placeholder={placeholder} + /> + + ); + }); + + const advancedOptionsContent = ( + <> + - + + } + description={ + } > - {' '} - - + } - checked={withSysMonitoring} + checked={showNamespace} onChange={() => { - updateSysMonitoring(!withSysMonitoring); + setShowNamespace(!showNamespace); + if (showNamespace) { + updateAgentConfig({ namespace: '' }); + } }} /> - - - - + + + { + updateAgentConfig({ namespace: value }); + }} + onChange={selectedOptions => { + updateAgentConfig({ + namespace: (selectedOptions.length ? selectedOptions[0] : '') as string, + }); + }} + isInvalid={Boolean(touchedFields.namespace && validation.namespace)} + onBlur={() => setTouchedFields({ ...touchedFields, namespace: true })} + /> + + + )} + + + + + } + description={ } - buttonClassName="ingest-active-button" > - - - - -

- -

-
- - + { + acc[key] = true; + return acc; + }, + { logs: false, metrics: false } + )} + onChange={id => { + if (id !== 'logs' && id !== 'metrics') { + return; + } + + const hasLogs = + agentConfig.monitoring_enabled && agentConfig.monitoring_enabled.indexOf(id) >= 0; + + const previousValues = agentConfig.monitoring_enabled || []; + updateAgentConfig({ + monitoring_enabled: hasLogs + ? previousValues.filter(type => type !== id) + : [...previousValues, id], + }); + }} + /> +
+ {isEditing && 'id' in agentConfig ? ( + + + + } + description={ + <> + + + + {deleteAgentConfigPrompt => { + return ( + deleteAgentConfigPrompt(agentConfig.id!, onDelete)} + > + + + ); + }} + + {agentConfig.is_default ? ( + <> + + + + + + ) : null} + + } + /> + ) : null} + + ); + + return ( + + {!isEditing ? generalFields : generalSettingsWrapper(generalFields)} + {!isEditing ? ( + - - - - } - checked={showNamespace} - onChange={() => { - setShowNamespace(!showNamespace); - if (showNamespace) { - updateAgentConfig({ namespace: '' }); - } - }} - /> - {showNamespace && ( + } + > + - - - { - updateAgentConfig({ namespace: value }); - }} - onChange={selectedOptions => { - updateAgentConfig({ - namespace: (selectedOptions.length ? selectedOptions[0] : '') as string, - }); - }} - isInvalid={Boolean(touchedFields.namespace && validation.namespace)} - onBlur={() => setTouchedFields({ ...touchedFields, namespace: true })} - /> - - - )} - - - - - - -

{' '} + -

-
- - + + } + checked={withSysMonitoring} + onChange={() => { + updateSysMonitoring(!withSysMonitoring); + }} + /> +
+ ) : null} + {!isEditing ? ( + <> + + + - - - - { - acc[key] = true; - return acc; - }, - { logs: false, metrics: false } - )} - onChange={id => { - if (id !== 'logs' && id !== 'metrics') { - return; - } - - const hasLogs = - agentConfig.monitoring_enabled && agentConfig.monitoring_enabled.indexOf(id) >= 0; - - const previousValues = agentConfig.monitoring_enabled || []; - updateAgentConfig({ - monitoring_enabled: hasLogs - ? previousValues.filter(type => type !== id) - : [...previousValues, id], - }); - }} - /> - - - + } + buttonClassName="ingest-active-button" + > + + {advancedOptionsContent} +
+ + ) : ( + advancedOptionsContent + )} ); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/confirm_modal.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/confirm_deploy_modal.tsx similarity index 76% rename from x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/confirm_modal.tsx rename to x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/confirm_deploy_modal.tsx index aa7eab8f5be8d0..a503beeffa8b4d 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/confirm_modal.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/confirm_deploy_modal.tsx @@ -8,9 +8,9 @@ import React from 'react'; import { EuiCallOut, EuiOverlayMask, EuiConfirmModal, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { AgentConfig } from '../../../../types'; +import { AgentConfig } from '../../../types'; -export const ConfirmCreateDatasourceModal: React.FunctionComponent<{ +export const ConfirmDeployConfigModal: React.FunctionComponent<{ onConfirm: () => void; onCancel: () => void; agentCount: number; @@ -21,7 +21,7 @@ export const ConfirmCreateDatasourceModal: React.FunctionComponent<{ } @@ -29,13 +29,13 @@ export const ConfirmCreateDatasourceModal: React.FunctionComponent<{ onConfirm={onConfirm} cancelButtonText={ } confirmButtonText={ } @@ -43,7 +43,7 @@ export const ConfirmCreateDatasourceModal: React.FunctionComponent<{ > diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/index.ts index a0fdc656dd7ed1..c1811b99588a82 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/index.ts @@ -7,3 +7,4 @@ export { AgentConfigForm, agentConfigFormValidation } from './config_form'; export { AgentConfigDeleteProvider } from './config_delete_provider'; export { LinkedAgentCount } from './linked_agent_count'; +export { ConfirmDeployConfigModal } from './confirm_deploy_modal'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/linked_agent_count.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/linked_agent_count.tsx index ec66108c60f680..3860439f26d44a 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/linked_agent_count.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/components/linked_agent_count.tsx @@ -8,7 +8,7 @@ import React, { memo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiLink } from '@elastic/eui'; import { useLink } from '../../../hooks'; -import { FLEET_AGENTS_PATH } from '../../../constants'; +import { FLEET_AGENTS_PATH, AGENT_SAVED_OBJECT_TYPE } from '../../../constants'; export const LinkedAgentCount = memo<{ count: number; agentConfigId: string }>( ({ count, agentConfigId }) => { @@ -21,7 +21,7 @@ export const LinkedAgentCount = memo<{ count: number; agentConfigId: string }>( /> ); return count > 0 ? ( - + {displayValue} ) : ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/index.ts index aa564690a6092f..3bfca756689115 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/index.ts @@ -5,5 +5,4 @@ */ export { CreateDatasourcePageLayout } from './layout'; export { DatasourceInputPanel } from './datasource_input_panel'; -export { ConfirmCreateDatasourceModal } from './confirm_modal'; export { DatasourceInputVarField } from './datasource_input_var_field'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx index 8e7042c1275ad9..5b7553dd8cf92e 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx @@ -27,7 +27,8 @@ import { sendGetAgentStatus, } from '../../../hooks'; import { useLinks as useEPMLinks } from '../../epm/hooks'; -import { CreateDatasourcePageLayout, ConfirmCreateDatasourceModal } from './components'; +import { ConfirmDeployConfigModal } from '../components'; +import { CreateDatasourcePageLayout } from './components'; import { CreateDatasourceFrom, DatasourceFormState } from './types'; import { DatasourceValidationResults, validateDatasource, validationHasErrors } from './services'; import { StepSelectPackage } from './step_select_package'; @@ -36,7 +37,10 @@ import { StepConfigureDatasource } from './step_configure_datasource'; import { StepDefineDatasource } from './step_define_datasource'; export const CreateDatasourcePage: React.FunctionComponent = () => { - const { notifications } = useCore(); + const { + notifications, + chrome: { getIsNavDrawerLocked$ }, + } = useCore(); const { fleet: { enabled: isFleetEnabled }, } = useConfig(); @@ -45,6 +49,15 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { } = useRouteMatch(); const history = useHistory(); const from: CreateDatasourceFrom = configId ? 'config' : 'package'; + const [isNavDrawerLocked, setIsNavDrawerLocked] = useState(false); + + useEffect(() => { + const subscription = getIsNavDrawerLocked$().subscribe((newIsNavDrawerLocked: boolean) => { + setIsNavDrawerLocked(newIsNavDrawerLocked); + }); + + return () => subscription.unsubscribe(); + }); // Agent config and package info states const [agentConfig, setAgentConfig] = useState(); @@ -269,7 +282,7 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { return ( {formState === 'CONFIRM' && agentConfig && ( - { )} - + diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/config_form.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/config_form.tsx deleted file mode 100644 index c4f8d944ceb142..00000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/config_form.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useState } from 'react'; -import { EuiFieldText, EuiForm, EuiFormRow } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { AgentConfig } from '../../../../types'; - -interface ValidationResults { - [key: string]: JSX.Element[]; -} - -export const configFormValidation = (config: Partial): ValidationResults => { - const errors: ValidationResults = {}; - - if (!config.name?.trim()) { - errors.name = [ - , - ]; - } - - return errors; -}; - -interface Props { - config: Partial; - updateConfig: (u: Partial) => void; - validation: ValidationResults; -} - -export const ConfigForm: React.FunctionComponent = ({ - config, - updateConfig, - validation, -}) => { - const [touchedFields, setTouchedFields] = useState<{ [key: string]: boolean }>({}); - const fields: Array<{ name: 'name' | 'description' | 'namespace'; label: JSX.Element }> = [ - { - name: 'name', - label: ( - - ), - }, - { - name: 'description', - label: ( - - ), - }, - { - name: 'namespace', - label: ( - - ), - }, - ]; - - return ( - - {fields.map(({ name, label }) => { - return ( - - updateConfig({ [name]: e.target.value })} - isInvalid={Boolean(touchedFields[name] && validation[name])} - onBlur={() => setTouchedFields({ ...touchedFields, [name]: true })} - /> - - ); - })} - - ); -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/donut_chart.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/donut_chart.tsx deleted file mode 100644 index 408ccc6e951f68..00000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/donut_chart.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { useEffect, useRef } from 'react'; -import d3 from 'd3'; -import { EuiFlexItem } from '@elastic/eui'; - -interface DonutChartProps { - data: { - [key: string]: number; - }; - height: number; - width: number; -} - -export const DonutChart = ({ height, width, data }: DonutChartProps) => { - const chartElement = useRef(null); - - useEffect(() => { - if (chartElement.current !== null) { - // we must remove any existing paths before painting - d3.selectAll('g').remove(); - const svgElement = d3 - .select(chartElement.current) - .append('g') - .attr('transform', `translate(${width / 2}, ${height / 2})`); - const color = d3.scale - .ordinal() - // @ts-ignore - .domain(data) - .range(['#017D73', '#98A2B3', '#BD271E']); - const pieGenerator = d3.layout - .pie() - .value(({ value }: any) => value) - // these start/end angles will reverse the direction of the pie, - // which matches our design - .startAngle(2 * Math.PI) - .endAngle(0); - - svgElement - .selectAll('g') - // @ts-ignore - .data(pieGenerator(d3.entries(data))) - .enter() - .append('path') - .attr( - 'd', - // @ts-ignore attr does not expect a param of type Arc but it behaves as desired - d3.svg - .arc() - .innerRadius(width * 0.28) - .outerRadius(Math.min(width, height) / 2 - 10) - ) - .attr('fill', (d: any) => color(d.data.key)); - } - }, [data, height, width]); - return ( - - - - ); -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/edit_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/edit_config.tsx deleted file mode 100644 index 65eb86d7d871f2..00000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/edit_config.tsx +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import React, { useState } from 'react'; -import { - EuiFlyout, - EuiFlyoutHeader, - EuiTitle, - EuiFlyoutBody, - EuiFlyoutFooter, - EuiFlexGroup, - EuiFlexItem, - EuiButtonEmpty, - EuiButton, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { useCore, sendRequest } from '../../../../hooks'; -import { agentConfigRouteService } from '../../../../services'; -import { AgentConfig } from '../../../../types'; -import { ConfigForm, configFormValidation } from './config_form'; - -interface Props { - agentConfig: AgentConfig; - onClose: () => void; -} - -export const EditConfigFlyout: React.FunctionComponent = ({ - agentConfig: originalAgentConfig, - onClose, -}) => { - const { notifications } = useCore(); - const [config, setConfig] = useState>({ - name: originalAgentConfig.name, - description: originalAgentConfig.description, - }); - const [isLoading, setIsLoading] = useState(false); - const updateConfig = (updatedFields: Partial) => { - setConfig({ - ...config, - ...updatedFields, - }); - }; - const validation = configFormValidation(config); - - const header = ( - - -

- -

-
-
- ); - - const body = ( - - - - ); - - const footer = ( - - - - - - - - - 0} - onClick={async () => { - setIsLoading(true); - try { - const { error } = await sendRequest({ - path: agentConfigRouteService.getUpdatePath(originalAgentConfig.id), - method: 'put', - body: JSON.stringify(config), - }); - if (!error) { - notifications.toasts.addSuccess( - i18n.translate('xpack.ingestManager.editConfig.successNotificationTitle', { - defaultMessage: "Agent config '{name}' updated", - values: { name: config.name }, - }) - ); - } else { - notifications.toasts.addDanger( - error - ? error.message - : i18n.translate('xpack.ingestManager.editConfig.errorNotificationTitle', { - defaultMessage: 'Unable to update agent config', - }) - ); - } - } catch (e) { - notifications.toasts.addDanger( - i18n.translate('xpack.ingestManager.editConfig.errorNotificationTitle', { - defaultMessage: 'Unable to update agent config', - }) - ); - } - setIsLoading(false); - onClose(); - }} - > - - - - - - ); - - return ( - - {header} - {body} - {footer} - - ); -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/index.ts index 918b361a60d790..0123bd46c16e7e 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/index.ts @@ -4,5 +4,3 @@ * you may not use this file except in compliance with the Elastic License. */ export { DatasourcesTable } from './datasources/datasources_table'; -export { DonutChart } from './donut_chart'; -export { EditConfigFlyout } from './edit_config'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/settings/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/settings/index.tsx new file mode 100644 index 00000000000000..2d9d29bfc1ac7e --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/settings/index.tsx @@ -0,0 +1,216 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { memo, useState, useEffect } from 'react'; +import { useHistory } from 'react-router-dom'; +import styled from 'styled-components'; +import { EuiBottomBar, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { AGENT_CONFIG_PATH } from '../../../../../constants'; +import { AgentConfig } from '../../../../../types'; +import { + useCore, + useCapabilities, + sendUpdateAgentConfig, + useConfig, + sendGetAgentStatus, +} from '../../../../../hooks'; +import { + AgentConfigForm, + agentConfigFormValidation, + ConfirmDeployConfigModal, +} from '../../../components'; +import { useConfigRefresh } from '../../hooks'; + +const FormWrapper = styled.div` + max-width: 800px; + margin-right: auto; + margin-left: auto; +`; + +export const ConfigSettingsView = memo<{ config: AgentConfig }>( + ({ config: originalAgentConfig }) => { + const { + notifications, + chrome: { getIsNavDrawerLocked$ }, + } = useCore(); + const { + fleet: { enabled: isFleetEnabled }, + } = useConfig(); + const history = useHistory(); + const hasWriteCapabilites = useCapabilities().write; + const refreshConfig = useConfigRefresh(); + const [isNavDrawerLocked, setIsNavDrawerLocked] = useState(false); + const [agentConfig, setAgentConfig] = useState({ + ...originalAgentConfig, + }); + const [isLoading, setIsLoading] = useState(false); + const [hasChanges, setHasChanges] = useState(false); + const [agentCount, setAgentCount] = useState(0); + const [withSysMonitoring, setWithSysMonitoring] = useState(true); + const validation = agentConfigFormValidation(agentConfig); + + useEffect(() => { + const subscription = getIsNavDrawerLocked$().subscribe((newIsNavDrawerLocked: boolean) => { + setIsNavDrawerLocked(newIsNavDrawerLocked); + }); + + return () => subscription.unsubscribe(); + }); + + const updateAgentConfig = (updatedFields: Partial) => { + setAgentConfig({ + ...agentConfig, + ...updatedFields, + }); + setHasChanges(true); + }; + + const submitUpdateAgentConfig = async () => { + setIsLoading(true); + try { + const { name, description, namespace, monitoring_enabled } = agentConfig; + const { data, error } = await sendUpdateAgentConfig(agentConfig.id, { + name, + description, + namespace, + monitoring_enabled, + }); + if (data?.success) { + notifications.toasts.addSuccess( + i18n.translate('xpack.ingestManager.editAgentConfig.successNotificationTitle', { + defaultMessage: "Successfully updated '{name}' settings", + values: { name: agentConfig.name }, + }) + ); + refreshConfig(); + setHasChanges(false); + } else { + notifications.toasts.addDanger( + error + ? error.message + : i18n.translate('xpack.ingestManager.editAgentConfig.errorNotificationTitle', { + defaultMessage: 'Unable to update agent config', + }) + ); + } + } catch (e) { + notifications.toasts.addDanger( + i18n.translate('xpack.ingestManager.editAgentConfig.errorNotificationTitle', { + defaultMessage: 'Unable to update agent config', + }) + ); + } + setIsLoading(false); + }; + + const onSubmit = async () => { + // Retrieve agent count if fleet is enabled + if (isFleetEnabled) { + setIsLoading(true); + const { data } = await sendGetAgentStatus({ configId: agentConfig.id }); + if (data?.results.total) { + setAgentCount(data.results.total); + } else { + await submitUpdateAgentConfig(); + } + } else { + await submitUpdateAgentConfig(); + } + }; + + return ( + + {agentCount ? ( + { + setAgentCount(0); + submitUpdateAgentConfig(); + }} + onCancel={() => { + setAgentCount(0); + setIsLoading(false); + }} + /> + ) : null} + setWithSysMonitoring(newValue)} + validation={validation} + isEditing={true} + onDelete={() => { + history.push(AGENT_CONFIG_PATH); + }} + /> + {hasChanges ? ( + + + + + + + + + { + setAgentConfig({ ...originalAgentConfig }); + setHasChanges(false); + }} + > + + + + + 0 + } + iconType="save" + color="primary" + fill + > + {isLoading ? ( + + ) : ( + + )} + + + + + + + ) : null} + + ); + } +); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx index f1d7bd5dbc0396..9f2088521ed389 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/yaml/index.tsx @@ -15,7 +15,7 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; -import { AgentConfig } from '../../../../../../../../common/types/models'; +import { AgentConfig } from '../../../../../types'; import { useGetOneAgentConfigFull, useGetEnrollmentAPIKeys, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/hooks/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/hooks/index.ts index 19be93676a7346..76c6d64eb9e07a 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/hooks/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/hooks/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ export { useGetAgentStatus, AgentStatusRefreshContext } from './use_agent_status'; -export { ConfigRefreshContext } from './use_config'; +export { ConfigRefreshContext, useConfigRefresh } from './use_config'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/index.tsx index 450f86df5c03aa..20a39724ce23c7 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/index.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, memo, useCallback, useMemo, useState } from 'react'; +import React, { Fragment, memo, useMemo, useState } from 'react'; import { Redirect, useRouteMatch, Switch, Route } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedDate } from '@kbn/i18n/react'; @@ -26,12 +26,12 @@ import { useGetOneAgentConfig } from '../../../hooks'; import { Loading } from '../../../components'; import { WithHeaderLayout } from '../../../layouts'; import { ConfigRefreshContext, useGetAgentStatus, AgentStatusRefreshContext } from './hooks'; -import { EditConfigFlyout } from './components'; import { LinkedAgentCount } from '../components'; import { useAgentConfigLink } from './hooks/use_details_uri'; import { DETAILS_ROUTER_PATH, DETAILS_ROUTER_SUB_PATH } from './constants'; import { ConfigDatasourcesView } from './components/datasources'; import { ConfigYamlView } from './components/yaml'; +import { ConfigSettingsView } from './components/settings'; const Divider = styled.div` width: 0; @@ -70,14 +70,6 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { const configDetailsYamlLink = useAgentConfigLink('details-yaml', { configId }); const configDetailsSettingsLink = useAgentConfigLink('details-settings', { configId }); - // Flyout states - const [isEditConfigFlyoutOpen, setIsEditConfigFlyoutOpen] = useState(false); - - const refreshData = useCallback(() => { - refreshAgentConfig(); - refreshAgentStatus(); - }, [refreshAgentConfig, refreshAgentStatus]); - const headerLeftContent = useMemo( () => ( @@ -196,7 +188,7 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { return [ { id: 'datasources', - name: i18n.translate('xpack.ingestManager.configDetails.subTabs.datasouces', { + name: i18n.translate('xpack.ingestManager.configDetails.subTabs.datasourcesTabText', { defaultMessage: 'Data sources', }), href: configDetailsLink, @@ -204,15 +196,15 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { }, { id: 'yaml', - name: i18n.translate('xpack.ingestManager.configDetails.subTabs.yamlFile', { - defaultMessage: 'YAML File', + name: i18n.translate('xpack.ingestManager.configDetails.subTabs.yamlTabText', { + defaultMessage: 'YAML', }), href: configDetailsYamlLink, isSelected: tabId === 'yaml', }, { id: 'settings', - name: i18n.translate('xpack.ingestManager.configDetails.subTabs.settings', { + name: i18n.translate('xpack.ingestManager.configDetails.subTabs.settingsTabText', { defaultMessage: 'Settings', }), href: configDetailsSettingsLink, @@ -269,16 +261,6 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { rightColumn={headerRightContent} tabs={(headerTabs as unknown) as EuiTabProps[]} > - {isEditConfigFlyoutOpen ? ( - { - setIsEditConfigFlyoutOpen(false); - refreshData(); - }} - agentConfig={agentConfig} - /> - ) : null} - { { - // TODO: Settings implementation tracked via: https://github.com/elastic/kibana/issues/57959 - return
Settings placeholder
; + return ; }} /> { ) : ( <> {formState === 'CONFIRM' && ( - listAgents(soClient, { - showInactive: true, + showInactive: false, perPage: 0, page: 1, kuery: `${AGENT_SAVED_OBJECT_TYPE}.config_id:${agentConfig.id}`, @@ -179,13 +179,13 @@ export const updateAgentConfigHandler: RequestHandler< export const deleteAgentConfigsHandler: RequestHandler< unknown, unknown, - TypeOf + TypeOf > = async (context, request, response) => { const soClient = context.core.savedObjects.client; try { - const body: DeleteAgentConfigsResponse = await agentConfigService.delete( + const body: DeleteAgentConfigResponse = await agentConfigService.delete( soClient, - request.body.agentConfigIds + request.body.agentConfigId ); return response.ok({ body, diff --git a/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts index b8e827974ff81a..e630f3c9595903 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent_config/index.ts @@ -10,7 +10,7 @@ import { GetOneAgentConfigRequestSchema, CreateAgentConfigRequestSchema, UpdateAgentConfigRequestSchema, - DeleteAgentConfigsRequestSchema, + DeleteAgentConfigRequestSchema, GetFullAgentConfigRequestSchema, } from '../../types'; import { @@ -67,7 +67,7 @@ export const registerRoutes = (router: IRouter) => { router.post( { path: AGENT_CONFIG_API_ROUTES.DELETE_PATTERN, - validate: DeleteAgentConfigsRequestSchema, + validate: DeleteAgentConfigRequestSchema, options: { tags: [`access:${PLUGIN_ID}-all`] }, }, deleteAgentConfigsHandler diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config.ts b/x-pack/plugins/ingest_manager/server/services/agent_config.ts index 7ab6ef1920c18a..5ecbaff8ad71e1 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_config.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_config.ts @@ -6,7 +6,11 @@ import { uniq } from 'lodash'; import { SavedObjectsClientContract } from 'src/core/server'; import { AuthenticatedUser } from '../../../security/server'; -import { DEFAULT_AGENT_CONFIG, AGENT_CONFIG_SAVED_OBJECT_TYPE } from '../constants'; +import { + DEFAULT_AGENT_CONFIG, + AGENT_CONFIG_SAVED_OBJECT_TYPE, + AGENT_SAVED_OBJECT_TYPE, +} from '../constants'; import { Datasource, NewAgentConfig, @@ -15,7 +19,8 @@ import { AgentConfigStatus, ListWithKuery, } from '../types'; -import { DeleteAgentConfigsResponse, storedDatasourceToAgentDatasource } from '../../common'; +import { DeleteAgentConfigResponse, storedDatasourceToAgentDatasource } from '../../common'; +import { listAgents } from './agents'; import { datasourceService } from './datasource'; import { outputService } from './output'; import { agentConfigUpdateEventHandler } from './agent_config_update'; @@ -256,32 +261,40 @@ class AgentConfigService { public async delete( soClient: SavedObjectsClientContract, - ids: string[] - ): Promise { - const result: DeleteAgentConfigsResponse = []; - const defaultConfigId = await this.getDefaultAgentConfigId(soClient); + id: string + ): Promise { + const config = await this.get(soClient, id, false); + if (!config) { + throw new Error('Agent configuration not found'); + } - if (ids.includes(defaultConfigId)) { + const defaultConfigId = await this.getDefaultAgentConfigId(soClient); + if (id === defaultConfigId) { throw new Error('The default agent configuration cannot be deleted'); } - for (const id of ids) { - try { - await soClient.delete(SAVED_OBJECT_TYPE, id); - await this.triggerAgentConfigUpdatedEvent(soClient, 'deleted', id); - result.push({ - id, - success: true, - }); - } catch (e) { - result.push({ - id, - success: false, - }); - } + const { total } = await listAgents(soClient, { + showInactive: false, + perPage: 0, + page: 1, + kuery: `${AGENT_SAVED_OBJECT_TYPE}.config_id:${id}`, + }); + + if (total > 0) { + throw new Error('Cannot delete agent config that is assigned to agent(s)'); } - return result; + if (config.datasources && config.datasources.length) { + await datasourceService.delete(soClient, config.datasources as string[], { + skipUnassignFromAgentConfigs: true, + }); + } + await soClient.delete(SAVED_OBJECT_TYPE, id); + await this.triggerAgentConfigUpdatedEvent(soClient, 'deleted', id); + return { + id, + success: true, + }; } public async getFullConfig( diff --git a/x-pack/plugins/ingest_manager/server/services/datasource.ts b/x-pack/plugins/ingest_manager/server/services/datasource.ts index 0a5ba43e40fba9..affd9b2755881a 100644 --- a/x-pack/plugins/ingest_manager/server/services/datasource.ts +++ b/x-pack/plugins/ingest_manager/server/services/datasource.ts @@ -145,7 +145,7 @@ class DatasourceService { public async delete( soClient: SavedObjectsClientContract, ids: string[], - options?: { user?: AuthenticatedUser } + options?: { user?: AuthenticatedUser; skipUnassignFromAgentConfigs?: boolean } ): Promise { const result: DeleteDatasourcesResponse = []; @@ -155,14 +155,16 @@ class DatasourceService { if (!oldDatasource) { throw new Error('Datasource not found'); } - await agentConfigService.unassignDatasources( - soClient, - oldDatasource.config_id, - [oldDatasource.id], - { - user: options?.user, - } - ); + if (!options?.skipUnassignFromAgentConfigs) { + await agentConfigService.unassignDatasources( + soClient, + oldDatasource.config_id, + [oldDatasource.id], + { + user: options?.user, + } + ); + } await soClient.delete(SAVED_OBJECT_TYPE, id); result.push({ id, diff --git a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent_config.ts b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent_config.ts index 0d223f028fc883..ab97ddc0ba7235 100644 --- a/x-pack/plugins/ingest_manager/server/types/rest_spec/agent_config.ts +++ b/x-pack/plugins/ingest_manager/server/types/rest_spec/agent_config.ts @@ -29,9 +29,9 @@ export const UpdateAgentConfigRequestSchema = { body: NewAgentConfigSchema, }; -export const DeleteAgentConfigsRequestSchema = { +export const DeleteAgentConfigRequestSchema = { body: schema.object({ - agentConfigIds: schema.arrayOf(schema.string()), + agentConfigId: schema.string(), }), }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index a3011ab5bdfa9e..bf257d66a59bc2 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -8317,9 +8317,6 @@ "xpack.ingestManager.configDetails.datasourcesTable.namespaceColumnTitle": "名前空間", "xpack.ingestManager.configDetails.datasourcesTable.packageNameColumnTitle": "パッケージ", "xpack.ingestManager.configDetails.datasourcesTable.streamsCountColumnTitle": "ストリーム", - "xpack.ingestManager.configDetails.subTabs.datasouces": "データソース", - "xpack.ingestManager.configDetails.subTabs.settings": "設定", - "xpack.ingestManager.configDetails.subTabs.yamlFile": "YAML ファイル", "xpack.ingestManager.configDetails.summary.datasources": "データソース", "xpack.ingestManager.configDetails.summary.lastUpdated": "最終更新日:", "xpack.ingestManager.configDetails.summary.revision": "リビジョン", @@ -8329,10 +8326,6 @@ "xpack.ingestManager.configDetailsDatasources.createFirstButtonText": "データソースを作成", "xpack.ingestManager.configDetailsDatasources.createFirstMessage": "この構成にはデータソースはまだありません。", "xpack.ingestManager.configDetailsDatasources.createFirstTitle": "初めてのデーソースを作成する", - "xpack.ingestManager.configForm.descriptionFieldLabel": "説明", - "xpack.ingestManager.configForm.nameFieldLabel": "名前", - "xpack.ingestManager.configForm.nameRequiredErrorMessage": "構成名が必要です", - "xpack.ingestManager.configForm.namespaceFieldLabel": "名前空間", "xpack.ingestManager.createAgentConfig.cancelButtonLabel": "キャンセル", "xpack.ingestManager.createAgentConfig.errorNotificationTitle": "エージェント構成を作成できません", "xpack.ingestManager.createAgentConfig.flyoutTitle": "エージェント構成を作成", @@ -8364,20 +8357,6 @@ "xpack.ingestManager.createDatasource.stepSelectPackage.errorLoadingPackagesTitle": "パッケージの読み込みエラー", "xpack.ingestManager.createDatasource.stepSelectPackage.errorLoadingSelectedPackageTitle": "選択したパッケージの読み込みエラー", "xpack.ingestManager.createDatasource.stepSelectPackage.filterPackagesInputPlaceholder": "パッケージの検索", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.affectedAgentsMessage": "{agentsCount, plural, one {# エージェントを} other {# エージェントを}}{agentConfigsCount, plural, one {このエージェント構成に} other {これらのエージェント構成に}}割り当てました。 {agentsCount, plural, one {このエージェント} other {これらのエージェント}}の登録が解除されます。", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.cancelButtonLabel": "キャンセル", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.confirmAndReassignButtonLabel": "{agentConfigsCount, plural, one {エージェント構成} other {エージェント構成}} and unenroll {agentsCount, plural, one {エージェント} other {エージェント}} を削除", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.confirmButtonLabel": "{agentConfigsCount, plural, one {エージェント構成} other {エージェント構成}}を削除", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.deleteMultipleTitle": "{count, plural, one {this agent config} other {# agent configs}} を削除しますか?", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.loadingAgentsCountMessage": "影響があるエージェントの数を確認中...", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.loadingButtonLabel": "読み込み中...", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.noAffectedAgentsMessage": "{agentConfigsCount, plural, one {this agent config} other {these agentConfigs}}に割り当てられたエージェントはありません。", - "xpack.ingestManager.deleteAgentConfigs.failureMultipleNotificationTitle": "{count} 件のエージェント構成の削除エラー", - "xpack.ingestManager.deleteAgentConfigs.failureSingleNotificationTitle": "エージェント構成「{id}」の削除エラー", - "xpack.ingestManager.deleteAgentConfigs.fatalErrorNotificationTitle": "エージェント構成の削除エラー", - "xpack.ingestManager.deleteAgentConfigs.successMultipleNotificationTitle": "{count} 件のエージェント構成を削除しました", - "xpack.ingestManager.deleteAgentConfigs.successSingleNotificationTitle": "エージェント構成「{id}」を削除しました", - "xpack.ingestManager.deleteApiKeys.confirmModal.cancelButtonLabel": "キャンセル", "xpack.ingestManager.deleteDatasource.confirmModal.affectedAgentsMessage": "{agentConfigName} が一部のエージェントで既に使用されていることをフリートが検出しました。", "xpack.ingestManager.deleteDatasource.confirmModal.affectedAgentsTitle": "このアクションは {agentsCount} {agentsCount, plural, one {# エージェント} other {# エージェント}}に影響します", "xpack.ingestManager.deleteDatasource.confirmModal.cancelButtonLabel": "キャンセル", @@ -8393,11 +8372,6 @@ "xpack.ingestManager.deleteDatasource.successSingleNotificationTitle": "データソース「{id}」を削除しました", "xpack.ingestManager.disabledSecurityDescription": "Elastic Fleet を使用するには、Kibana と Elasticsearch でセキュリティを有効にする必要があります。", "xpack.ingestManager.disabledSecurityTitle": "セキュリティが有効ではありません", - "xpack.ingestManager.editConfig.cancelButtonLabel": "キャンセル", - "xpack.ingestManager.editConfig.errorNotificationTitle": "エージェント構成を作成できません", - "xpack.ingestManager.editConfig.flyoutTitle": "構成を編集", - "xpack.ingestManager.editConfig.submitButtonLabel": "更新", - "xpack.ingestManager.editConfig.successNotificationTitle": "エージェント構成「{name}」を更新しました", "xpack.ingestManager.enrollmentApiKeyForm.namePlaceholder": "名前を選択", "xpack.ingestManager.enrollmentApiKeyList.createNewButton": "新規キーを作成", "xpack.ingestManager.enrollmentApiKeyList.useExistingsButton": "既存のキーを使用", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index e373d05a7d8516..323ddfda32d703 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -8320,9 +8320,6 @@ "xpack.ingestManager.configDetails.datasourcesTable.namespaceColumnTitle": "命名空间", "xpack.ingestManager.configDetails.datasourcesTable.packageNameColumnTitle": "软件包", "xpack.ingestManager.configDetails.datasourcesTable.streamsCountColumnTitle": "流计数", - "xpack.ingestManager.configDetails.subTabs.datasouces": "数据源", - "xpack.ingestManager.configDetails.subTabs.settings": "设置", - "xpack.ingestManager.configDetails.subTabs.yamlFile": "YAML 文件", "xpack.ingestManager.configDetails.summary.datasources": "数据源", "xpack.ingestManager.configDetails.summary.lastUpdated": "最后更新时间", "xpack.ingestManager.configDetails.summary.revision": "修订", @@ -8332,10 +8329,6 @@ "xpack.ingestManager.configDetailsDatasources.createFirstButtonText": "创建数据源", "xpack.ingestManager.configDetailsDatasources.createFirstMessage": "此配置尚未有任何数据源。", "xpack.ingestManager.configDetailsDatasources.createFirstTitle": "创建您的首个数据源", - "xpack.ingestManager.configForm.descriptionFieldLabel": "描述", - "xpack.ingestManager.configForm.nameFieldLabel": "名称", - "xpack.ingestManager.configForm.nameRequiredErrorMessage": "配置名称必填", - "xpack.ingestManager.configForm.namespaceFieldLabel": "命名空间", "xpack.ingestManager.createAgentConfig.cancelButtonLabel": "取消", "xpack.ingestManager.createAgentConfig.errorNotificationTitle": "无法创建代理配置", "xpack.ingestManager.createAgentConfig.flyoutTitle": "创建代理配置", @@ -8367,20 +8360,6 @@ "xpack.ingestManager.createDatasource.stepSelectPackage.errorLoadingPackagesTitle": "加载软件包时出错", "xpack.ingestManager.createDatasource.stepSelectPackage.errorLoadingSelectedPackageTitle": "加载选定软件包时出错", "xpack.ingestManager.createDatasource.stepSelectPackage.filterPackagesInputPlaceholder": "搜索软件包", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.affectedAgentsMessage": "{agentsCount, plural, one {# 个代理} other {# 个代理}}已分配{agentConfigsCount, plural, one {给此代理配置} other {给这些代理配置}}。将取消注册{agentsCount, plural, one {此代理} other {这些代理}}。", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.cancelButtonLabel": "取消", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.confirmAndReassignButtonLabel": "删除{agentConfigsCount, plural, one {代理配置} other {代理配置}}并取消注册{agentsCount, plural, one {代理} other {代理}}", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.confirmButtonLabel": "删除{agentConfigsCount, plural, one {代理配置} other {代理配置}}", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.deleteMultipleTitle": "删除{count, plural, one {此代理配置} other {# 个代理配置}}?", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.loadingAgentsCountMessage": "正在检查受影响代理数量……", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.loadingButtonLabel": "正在加载……", - "xpack.ingestManager.deleteAgentConfigs.confirmModal.noAffectedAgentsMessage": "没有代理分配给{agentConfigsCount, plural, one {此代理配置} other {这些代理配置}}。", - "xpack.ingestManager.deleteAgentConfigs.failureMultipleNotificationTitle": "删除 {count} 个代理配置时出错", - "xpack.ingestManager.deleteAgentConfigs.failureSingleNotificationTitle": "删除代理配置“{id}”时出错", - "xpack.ingestManager.deleteAgentConfigs.fatalErrorNotificationTitle": "删除代理配置时出错", - "xpack.ingestManager.deleteAgentConfigs.successMultipleNotificationTitle": "已删除 {count} 个代理配置", - "xpack.ingestManager.deleteAgentConfigs.successSingleNotificationTitle": "已删除代理配置“{id}”", - "xpack.ingestManager.deleteApiKeys.confirmModal.cancelButtonLabel": "取消", "xpack.ingestManager.deleteDatasource.confirmModal.affectedAgentsMessage": "Fleet 已检测到 {agentConfigName} 已由您的部分代理使用。", "xpack.ingestManager.deleteDatasource.confirmModal.affectedAgentsTitle": "此操作将影响 {agentsCount} 个 {agentsCount, plural, one {代理} other {代理}}。", "xpack.ingestManager.deleteDatasource.confirmModal.cancelButtonLabel": "取消", @@ -8396,11 +8375,6 @@ "xpack.ingestManager.deleteDatasource.successSingleNotificationTitle": "已删除数据源“{id}”", "xpack.ingestManager.disabledSecurityDescription": "必须在 Kibana 和 Elasticsearch 启用安全性,才能使用 Elastic Fleet。", "xpack.ingestManager.disabledSecurityTitle": "安全性未启用", - "xpack.ingestManager.editConfig.cancelButtonLabel": "取消", - "xpack.ingestManager.editConfig.errorNotificationTitle": "无法更新代理配置", - "xpack.ingestManager.editConfig.flyoutTitle": "编辑配置", - "xpack.ingestManager.editConfig.submitButtonLabel": "更新", - "xpack.ingestManager.editConfig.successNotificationTitle": "代理配置“{name}”已更新", "xpack.ingestManager.enrollmentApiKeyForm.namePlaceholder": "选择名称", "xpack.ingestManager.enrollmentApiKeyList.createNewButton": "创建新密钥", "xpack.ingestManager.enrollmentApiKeyList.useExistingsButton": "使用现有密钥", From b93427b7b65662ae082b06a9f9fb4d77c3acb81d Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 30 Apr 2020 19:35:20 +0100 Subject: [PATCH 23/47] chore(NA): ignore server watch for md and test.tsx files (#64797) * chore(NA): avoid unnecessary server watches on md and test.tsx files * chore(NA): include debug log files --- src/cli/cluster/cluster_manager.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cli/cluster/cluster_manager.ts b/src/cli/cluster/cluster_manager.ts index 32a23d74dbda44..f16ba61234a950 100644 --- a/src/cli/cluster/cluster_manager.ts +++ b/src/cli/cluster/cluster_manager.ts @@ -259,7 +259,9 @@ export class ClusterManager { const ignorePaths = [ /[\\\/](\..*|node_modules|bower_components|target|public|__[a-z0-9_]+__|coverage)([\\\/]|$)/, - /\.test\.(js|ts)$/, + /\.test\.(js|tsx?)$/, + /\.md$/, + /debug\.log$/, ...pluginInternalDirsIgnore, fromRoot('src/legacy/server/sass/__tmp__'), fromRoot('x-pack/legacy/plugins/reporting/.chromium'), From 5887c97d751fd639c4e24e5499b4ceed32fa4fcc Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Thu, 30 Apr 2020 20:36:50 +0200 Subject: [PATCH 24/47] [Lens] Allow user to drag and select a subset of the timeline in the chart (aka brush interaction) (#62636) * feat: brushing basic example for time histogram * test: added * refactor: simplify the structure * refactor: move to inline function * refactor * refactor * Always use time field from index pattern * types * use the meta.aggConfigParams for timefieldName * fix: test snapshot update * Update embeddable.tsx removing commented code * fix: moment remov * fix: corrections for adapting to timepicker on every timefield * fix: fix single bar condition * types Co-authored-by: Elastic Machine Co-authored-by: Wylie Conlon --- .../embeddable/embeddable.tsx | 4 +- .../public/{xy_visualization => }/services.ts | 4 +- .../__snapshots__/xy_expression.test.tsx.snap | 7 + .../lens/public/xy_visualization/index.ts | 2 +- .../public/xy_visualization/to_expression.ts | 2 +- .../xy_visualization/xy_expression.test.tsx | 174 +++++++++++++++++- .../public/xy_visualization/xy_expression.tsx | 85 +++++++-- 7 files changed, 253 insertions(+), 25 deletions(-) rename x-pack/plugins/lens/public/{xy_visualization => }/services.ts (71%) diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index 0ef5f6d1a54706..6cd15e3c93e4ed 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -96,9 +96,7 @@ export class Embeddable extends AbstractEmbeddable { const operation = datasource.getOperationForColumnId(accessor); - if (operation && operation.label) { + if (operation?.label) { columnToLabel[accessor] = operation.label; } }); diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx index 8db00aba0e36d3..dd2f32b63e34a5 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.test.tsx @@ -27,6 +27,145 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers'; const executeTriggerActions = jest.fn(); +const dateHistogramData: LensMultiTable = { + type: 'lens_multitable', + tables: { + timeLayer: { + type: 'kibana_datatable', + rows: [ + { + xAccessorId: 1585758120000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585758360000, + splitAccessorId: "Women's Accessories", + yAccessorId: 1, + }, + { + xAccessorId: 1585758360000, + splitAccessorId: "Women's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + { + xAccessorId: 1585759380000, + splitAccessorId: "Women's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760700000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760760000, + splitAccessorId: "Men's Clothing", + yAccessorId: 1, + }, + { + xAccessorId: 1585760760000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + { + xAccessorId: 1585761120000, + splitAccessorId: "Men's Shoes", + yAccessorId: 1, + }, + ], + columns: [ + { + id: 'xAccessorId', + name: 'order_date per minute', + meta: { + type: 'date_histogram', + indexPatternId: 'indexPatternId', + aggConfigParams: { + field: 'order_date', + timeRange: { from: '2020-04-01T16:14:16.246Z', to: '2020-04-01T17:15:41.263Z' }, + useNormalizedEsInterval: true, + scaleMetricValues: false, + interval: '1m', + drop_partials: false, + min_doc_count: 0, + extended_bounds: {}, + }, + }, + formatHint: { id: 'date', params: { pattern: 'HH:mm' } }, + }, + { + id: 'splitAccessorId', + name: 'Top values of category.keyword', + meta: { + type: 'terms', + indexPatternId: 'indexPatternId', + aggConfigParams: { + field: 'category.keyword', + orderBy: 'yAccessorId', + order: 'desc', + size: 3, + otherBucket: false, + otherBucketLabel: 'Other', + missingBucket: false, + missingBucketLabel: 'Missing', + }, + }, + formatHint: { + id: 'terms', + params: { + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: 'Missing', + parsedUrl: { + origin: 'http://localhost:5601', + pathname: '/jiy/app/kibana', + basePath: '/jiy', + }, + }, + }, + }, + { + id: 'yAccessorId', + name: 'Count of records', + meta: { + type: 'count', + indexPatternId: 'indexPatternId', + aggConfigParams: {}, + }, + formatHint: { id: 'number' }, + }, + ], + }, + }, + dateRange: { + fromDate: new Date('2020-04-01T16:14:16.246Z'), + toDate: new Date('2020-04-01T17:15:41.263Z'), + }, +}; + +const dateHistogramLayer: LayerArgs = { + layerId: 'timeLayer', + hide: false, + xAccessor: 'xAccessorId', + yScaleType: 'linear', + xScaleType: 'time', + isHistogram: true, + splitAccessor: 'splitAccessorId', + seriesType: 'bar_stacked', + accessors: ['yAccessorId'], +}; + const createSampleDatatableWithRows = (rows: KibanaDatatableRow[]): KibanaDatatable => ({ type: 'kibana_datatable', columns: [ @@ -284,7 +423,7 @@ describe('xy_expression', () => { Object { "max": 1546491600000, "min": 1546405200000, - "minInterval": 1728000, + "minInterval": undefined, } `); }); @@ -449,6 +588,39 @@ describe('xy_expression', () => { expect(component.find(Settings).prop('rotation')).toEqual(90); }); + test('onBrushEnd returns correct context data for date histogram data', () => { + const { args } = sampleArgs(); + + const wrapper = mountWithIntl( + + ); + + wrapper + .find(Settings) + .first() + .prop('onBrushEnd')!(1585757732783, 1585758880838); + + expect(executeTriggerActions).toHaveBeenCalledWith('SELECT_RANGE_TRIGGER', { + data: { + column: 0, + table: dateHistogramData.tables.timeLayer, + range: [1585757732783, 1585758880838], + }, + timeFieldName: 'order_date', + }); + }); + test('onElementClick returns correct context data', () => { const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1', mark: null }; const series = { diff --git a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx index 85cf5753befd79..2cd9ae7acb203e 100644 --- a/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/xy_expression.tsx @@ -29,14 +29,17 @@ import { import { EuiIcon, EuiText, IconType, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { ValueClickTriggerContext } from '../../../../../src/plugins/embeddable/public'; +import { + ValueClickTriggerContext, + RangeSelectTriggerContext, +} from '../../../../../src/plugins/embeddable/public'; import { VIS_EVENT_TO_TRIGGER } from '../../../../../src/plugins/visualizations/public'; import { LensMultiTable, FormatFactory } from '../types'; import { XYArgs, SeriesType, visualizationTypes } from './types'; import { VisualizationContainer } from '../visualization_container'; import { isHorizontalChart } from './state_helpers'; +import { getExecuteTriggerActions } from '../services'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; -import { getExecuteTriggerActions } from './services'; import { parseInterval } from '../../../../../src/plugins/data/common'; type InferPropType = T extends React.FunctionComponent ? P : T; @@ -218,8 +221,32 @@ export function XYChart({ const xTitle = (xAxisColumn && xAxisColumn.name) || args.xTitle; function calculateMinInterval() { - // add minInterval only for single row value as it cannot be determined from dataset - if (data.dateRange && layers.every(layer => data.tables[layer.layerId].rows.length <= 1)) { + // check all the tables to see if all of the rows have the same timestamp + // that would mean that chart will draw a single bar + const isSingleTimestampInXDomain = () => { + const nonEmptyLayers = layers.filter( + layer => data.tables[layer.layerId].rows.length && layer.xAccessor + ); + + if (!nonEmptyLayers.length) { + return; + } + + const firstRowValue = + data.tables[nonEmptyLayers[0].layerId].rows[0][nonEmptyLayers[0].xAccessor!]; + for (const layer of nonEmptyLayers) { + if ( + layer.xAccessor && + data.tables[layer.layerId].rows.some(row => row[layer.xAccessor!] !== firstRowValue) + ) { + return false; + } + } + return true; + }; + + // add minInterval only for single point in domain + if (data.dateRange && isSingleTimestampInXDomain()) { if (xAxisColumn?.meta?.aggConfigParams?.interval !== 'auto') return parseInterval(xAxisColumn?.meta?.aggConfigParams?.interval)?.asMilliseconds(); @@ -231,14 +258,16 @@ export function XYChart({ return undefined; } - const xDomain = - data.dateRange && layers.every(l => l.xScaleType === 'time') - ? { - min: data.dateRange.fromDate.getTime(), - max: data.dateRange.toDate.getTime(), - minInterval: calculateMinInterval(), - } - : undefined; + const isTimeViz = data.dateRange && layers.every(l => l.xScaleType === 'time'); + + const xDomain = isTimeViz + ? { + min: data.dateRange?.fromDate.getTime(), + max: data.dateRange?.toDate.getTime(), + minInterval: calculateMinInterval(), + } + : undefined; + return ( { + // in the future we want to make it also for histogram + if (!xAxisColumn || !isTimeViz) { + return; + } + + const firstLayerWithData = + layers[layers.findIndex(layer => data.tables[layer.layerId].rows.length)]; + const table = data.tables[firstLayerWithData.layerId]; + + const xAxisColumnIndex = table.columns.findIndex( + el => el.id === firstLayerWithData.xAccessor + ); + const timeFieldName = table.columns[xAxisColumnIndex]?.meta?.aggConfigParams?.field; + + const context: RangeSelectTriggerContext = { + data: { + range: [min, max], + table, + column: xAxisColumnIndex, + }, + timeFieldName, + }; + executeTriggerActions(VIS_EVENT_TO_TRIGGER.brush, context); + }} onElementClick={([[geometry, series]]) => { // for xyChart series is always XYChartSeriesIdentifier and geometry is always type of GeometryValue const xySeries = series as XYChartSeriesIdentifier; @@ -284,10 +338,8 @@ export function XYChart({ }); } - const xAxisFieldName: string | undefined = table.columns.find( - col => col.id === layer.xAccessor - )?.meta?.aggConfigParams?.field; - + const xAxisFieldName = table.columns.find(el => el.id === layer.xAccessor)?.meta + ?.aggConfigParams?.field; const timeFieldName = xDomain && xAxisFieldName; const context: ValueClickTriggerContext = { @@ -301,7 +353,6 @@ export function XYChart({ }, timeFieldName, }; - executeTriggerActions(VIS_EVENT_TO_TRIGGER.filter, context); }} /> From c8b9bdd0ec479f3513223a79c5c63d2dbdcea989 Mon Sep 17 00:00:00 2001 From: spalger Date: Thu, 30 Apr 2020 11:48:41 -0700 Subject: [PATCH 25/47] skip flaky suite (#64473) --- .../apis/management/index_management/indices.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/api_integration/apis/management/index_management/indices.js b/x-pack/test/api_integration/apis/management/index_management/indices.js index d2d07eca475e76..9442beda3501d7 100644 --- a/x-pack/test/api_integration/apis/management/index_management/indices.js +++ b/x-pack/test/api_integration/apis/management/index_management/indices.js @@ -34,7 +34,8 @@ export default function({ getService }) { clearCache, } = registerHelpers({ supertest }); - describe('indices', () => { + // FLAKY: https://github.com/elastic/kibana/issues/64473 + describe.skip('indices', () => { after(() => Promise.all([cleanUpEsResources()])); describe('clear cache', () => { From 0399f70050458b21f9a11d0bee39aa653a8bc97a Mon Sep 17 00:00:00 2001 From: Chris Cowan Date: Thu, 30 Apr 2020 11:52:11 -0700 Subject: [PATCH 26/47] [Metrics UI] Add Charts to Alert Conditions (#64384) * [Metrics UI] Add Charts to Alert Conditions - Reorganize files under public/alerting - Change Metrics Explorer API to force interval - Add charts to expression rows - Allow expression rows to be collapsable - Adding sum aggregation to Metrics Explorer for parity * Adding interval information to Metrics Eexplorer API * Moving data hook into the expression charts component * Revert "Adding interval information to Metrics Eexplorer API" This reverts commit f6e2fc11be9d239dac4518178ed880f167a74f5a. * Reducing the opacity for the threshold areas * Changing darkMode to use alertsContext.uiSettings --- .../index.ts => metrics_explorer.ts} | 2 + .../saved_objects/metrics_explorer_view.ts | 3 + .../components}/alert_dropdown.tsx | 0 .../components}/alert_flyout.tsx | 0 .../components}/expression.tsx | 261 ++------------- .../components/expression_chart.tsx | 302 ++++++++++++++++++ .../components/expression_row.tsx | 237 ++++++++++++++ .../components}/validation.tsx | 0 .../hooks/use_metrics_explorer_chart_data.ts | 59 ++++ .../metric_threshold/index.ts} | 10 +- .../lib/transform_metrics_explorer_data.ts | 31 ++ .../public/alerting/metric_threshold/types.ts | 51 +++ .../infra/public/pages/metrics/index.tsx | 2 +- .../components/waffle/node_context_menu.tsx | 2 +- .../components/aggregation.tsx | 3 + .../components/chart_context_menu.tsx | 2 +- .../components/helpers/create_metric_label.ts | 7 +- .../components/series_chart.tsx | 23 +- .../hooks/use_metrics_explorer_data.ts | 17 +- .../hooks/use_metrics_explorer_options.ts | 1 + x-pack/plugins/infra/public/plugin.ts | 4 +- .../lib/populate_series_with_tsvb_data.ts | 4 +- 22 files changed, 770 insertions(+), 251 deletions(-) rename x-pack/plugins/infra/common/http_api/{metrics_explorer/index.ts => metrics_explorer.ts} (98%) rename x-pack/plugins/infra/public/{components/alerting/metrics => alerting/metric_threshold/components}/alert_dropdown.tsx (100%) rename x-pack/plugins/infra/public/{components/alerting/metrics => alerting/metric_threshold/components}/alert_flyout.tsx (100%) rename x-pack/plugins/infra/public/{components/alerting/metrics => alerting/metric_threshold/components}/expression.tsx (59%) create mode 100644 x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx create mode 100644 x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx rename x-pack/plugins/infra/public/{components/alerting/metrics => alerting/metric_threshold/components}/validation.tsx (100%) create mode 100644 x-pack/plugins/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts rename x-pack/plugins/infra/public/{components/alerting/metrics/metric_threshold_alert_type.ts => alerting/metric_threshold/index.ts} (72%) create mode 100644 x-pack/plugins/infra/public/alerting/metric_threshold/lib/transform_metrics_explorer_data.ts create mode 100644 x-pack/plugins/infra/public/alerting/metric_threshold/types.ts diff --git a/x-pack/plugins/infra/common/http_api/metrics_explorer/index.ts b/x-pack/plugins/infra/common/http_api/metrics_explorer.ts similarity index 98% rename from x-pack/plugins/infra/common/http_api/metrics_explorer/index.ts rename to x-pack/plugins/infra/common/http_api/metrics_explorer.ts index 93655f931f45df..eb5b0bdbcfbc50 100644 --- a/x-pack/plugins/infra/common/http_api/metrics_explorer/index.ts +++ b/x-pack/plugins/infra/common/http_api/metrics_explorer.ts @@ -13,6 +13,7 @@ export const METRIC_EXPLORER_AGGREGATIONS = [ 'cardinality', 'rate', 'count', + 'sum', ] as const; type MetricExplorerAggregations = typeof METRIC_EXPLORER_AGGREGATIONS[number]; @@ -54,6 +55,7 @@ export const metricsExplorerRequestBodyOptionalFieldsRT = rt.partial({ afterKey: rt.union([rt.string, rt.null, rt.undefined]), limit: rt.union([rt.number, rt.null, rt.undefined]), filterQuery: rt.union([rt.string, rt.null, rt.undefined]), + forceInterval: rt.boolean, }); export const metricsExplorerRequestBodyRT = rt.intersection([ diff --git a/x-pack/plugins/infra/common/saved_objects/metrics_explorer_view.ts b/x-pack/plugins/infra/common/saved_objects/metrics_explorer_view.ts index 6eb08eabc15b5d..add6ab0f132b5c 100644 --- a/x-pack/plugins/infra/common/saved_objects/metrics_explorer_view.ts +++ b/x-pack/plugins/infra/common/saved_objects/metrics_explorer_view.ts @@ -35,6 +35,9 @@ export const metricsExplorerViewSavedObjectMappings: { }, options: { properties: { + forceInterval: { + type: 'boolean', + }, metrics: { type: 'nested', properties: { diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/alerting/metrics/alert_dropdown.tsx rename to x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/alert_flyout.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_flyout.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/alerting/metrics/alert_flyout.tsx rename to x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_flyout.tsx diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx similarity index 59% rename from x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx rename to x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx index f2ac34ccb752fd..352ac1927479ea 100644 --- a/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx @@ -6,9 +6,6 @@ import React, { ChangeEvent, useCallback, useMemo, useEffect, useState } from 'react'; import { - EuiFlexGroup, - EuiFlexItem, - EuiButtonIcon, EuiSpacer, EuiText, EuiFormRow, @@ -18,40 +15,28 @@ import { EuiIcon, EuiFieldSearch, } from '@elastic/eui'; -import { IFieldType } from 'src/plugins/data/public'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { - MetricExpressionParams, Comparator, Aggregators, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../server/lib/alerting/metric_threshold/types'; -import { euiStyled } from '../../../../../observability/public'; import { - WhenExpression, - OfExpression, - ThresholdExpression, ForLastExpression, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../../triggers_actions_ui/public/common'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { builtInComparators } from '../../../../../triggers_actions_ui/public/common/constants'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { IErrorObject } from '../../../../../triggers_actions_ui/public/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MetricsExplorerSeries } from '../../../../common/http_api/metrics_explorer'; import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar'; import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; import { MetricsExplorerGroupBy } from '../../../pages/metrics/metrics_explorer/components/group_by'; import { useSourceViaHttp } from '../../../containers/source/use_source_via_http'; - -interface AlertContextMeta { - currentOptions?: Partial; - series?: MetricsExplorerSeries; -} +import { ExpressionRow } from './expression_row'; +import { AlertContextMeta, TimeUnit, MetricExpression } from '../types'; +import { ExpressionChart } from './expression_chart'; interface Props { errors: IErrorObject[]; @@ -67,11 +52,6 @@ interface Props { setAlertProperty(key: string, value: any): void; } -type TimeUnit = 's' | 'm' | 'h' | 'd'; -type MetricExpression = Omit & { - metric?: string; -}; - const defaultExpression = { aggType: Aggregators.AVERAGE, comparator: Comparator.GT, @@ -80,17 +60,6 @@ const defaultExpression = { timeUnit: 'm', } as MetricExpression; -const customComparators = { - ...builtInComparators, - [Comparator.OUTSIDE_RANGE]: { - text: i18n.translate('xpack.infra.metrics.alertFlyout.outsideRangeLabel', { - defaultMessage: 'Is not between', - }), - value: Comparator.OUTSIDE_RANGE, - requiredValues: 2, - }, -}; - export const Expressions: React.FC = props => { const { setAlertParams, alertParams, errors, alertsContext } = props; const { source, createDerivedIndexPattern } = useSourceViaHttp({ @@ -101,7 +70,6 @@ export const Expressions: React.FC = props => { }); const [timeSize, setTimeSize] = useState(1); const [timeUnit, setTimeUnit] = useState('m'); - const derivedIndexPattern = useMemo(() => createDerivedIndexPattern('metrics'), [ createDerivedIndexPattern, ]); @@ -127,14 +95,14 @@ export const Expressions: React.FC = props => { ); const addExpression = useCallback(() => { - const exp = alertParams.criteria.slice(); + const exp = alertParams.criteria?.slice() || []; exp.push(defaultExpression); setAlertParams('criteria', exp); }, [setAlertParams, alertParams.criteria]); const removeExpression = useCallback( (id: number) => { - const exp = alertParams.criteria.slice(); + const exp = alertParams.criteria?.slice() || []; if (exp.length > 1) { exp.splice(id, 1); setAlertParams('criteria', exp); @@ -167,10 +135,11 @@ export const Expressions: React.FC = props => { const updateTimeSize = useCallback( (ts: number | undefined) => { - const criteria = alertParams.criteria.map(c => ({ - ...c, - timeSize: ts, - })); + const criteria = + alertParams.criteria?.map(c => ({ + ...c, + timeSize: ts, + })) || []; setTimeSize(ts || undefined); setAlertParams('criteria', criteria); }, @@ -179,10 +148,11 @@ export const Expressions: React.FC = props => { const updateTimeUnit = useCallback( (tu: string) => { - const criteria = alertParams.criteria.map(c => ({ - ...c, - timeUnit: tu, - })); + const criteria = + alertParams.criteria?.map(c => ({ + ...c, + timeUnit: tu, + })) || []; setTimeUnit(tu as TimeUnit); setAlertParams('criteria', criteria); }, @@ -250,7 +220,7 @@ export const Expressions: React.FC = props => { alertParams.criteria.map((e, idx) => { return ( 1} + canDelete={(alertParams.criteria && alertParams.criteria.length > 1) || false} fields={derivedIndexPattern.fields} remove={removeExpression} addExpression={addExpression} @@ -259,17 +229,28 @@ export const Expressions: React.FC = props => { setAlertParams={updateParams} errors={errors[idx] || emptyError} expression={e || {}} - /> + > + + ); })} - +
+ +
= props => { ); }; - -interface ExpressionRowProps { - fields: IFieldType[]; - expressionId: number; - expression: MetricExpression; - errors: IErrorObject; - canDelete: boolean; - addExpression(): void; - remove(id: number): void; - setAlertParams(id: number, params: MetricExpression): void; -} - -const StyledExpressionRow = euiStyled(EuiFlexGroup)` - display: flex; - flex-wrap: wrap; - margin: 0 -4px; -`; - -const StyledExpression = euiStyled.div` - padding: 0 4px; -`; - -export const ExpressionRow: React.FC = props => { - const { setAlertParams, expression, errors, expressionId, remove, fields, canDelete } = props; - const { - aggType = Aggregators.MAX, - metric, - comparator = Comparator.GT, - threshold = [], - } = expression; - - const updateAggType = useCallback( - (at: string) => { - setAlertParams(expressionId, { - ...expression, - aggType: at as MetricExpression['aggType'], - metric: at === 'count' ? undefined : expression.metric, - }); - }, - [expressionId, expression, setAlertParams] - ); - - const updateMetric = useCallback( - (m?: MetricExpression['metric']) => { - setAlertParams(expressionId, { ...expression, metric: m }); - }, - [expressionId, expression, setAlertParams] - ); - - const updateComparator = useCallback( - (c?: string) => { - setAlertParams(expressionId, { ...expression, comparator: c as Comparator }); - }, - [expressionId, expression, setAlertParams] - ); - - const updateThreshold = useCallback( - t => { - if (t.join() !== expression.threshold.join()) { - setAlertParams(expressionId, { ...expression, threshold: t }); - } - }, - [expressionId, expression, setAlertParams] - ); - - return ( - <> - - - - - - - {aggType !== 'count' && ( - - ({ - normalizedType: f.type, - name: f.name, - }))} - aggType={aggType} - errors={errors} - onChangeSelectedAggField={updateMetric} - /> - - )} - - - - - - {canDelete && ( - - remove(expressionId)} - /> - - )} - - - - ); -}; - -export const aggregationType: { [key: string]: any } = { - avg: { - text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.avg', { - defaultMessage: 'Average', - }), - fieldRequired: true, - validNormalizedTypes: ['number'], - value: Aggregators.AVERAGE, - }, - max: { - text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.max', { - defaultMessage: 'Max', - }), - fieldRequired: true, - validNormalizedTypes: ['number', 'date'], - value: Aggregators.MAX, - }, - min: { - text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.min', { - defaultMessage: 'Min', - }), - fieldRequired: true, - validNormalizedTypes: ['number', 'date'], - value: Aggregators.MIN, - }, - cardinality: { - text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.cardinality', { - defaultMessage: 'Cardinality', - }), - fieldRequired: false, - value: Aggregators.CARDINALITY, - validNormalizedTypes: ['number'], - }, - rate: { - text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.rate', { - defaultMessage: 'Rate', - }), - fieldRequired: false, - value: Aggregators.RATE, - validNormalizedTypes: ['number'], - }, - count: { - text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.count', { - defaultMessage: 'Document count', - }), - fieldRequired: false, - value: Aggregators.COUNT, - validNormalizedTypes: ['number'], - }, -}; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx new file mode 100644 index 00000000000000..a600d59865cccd --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.tsx @@ -0,0 +1,302 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useMemo, useCallback } from 'react'; +import { + Axis, + Chart, + niceTimeFormatter, + Position, + Settings, + TooltipValue, + RectAnnotation, +} from '@elastic/charts'; +import { first, last } from 'lodash'; +import moment from 'moment'; +import { i18n } from '@kbn/i18n'; +import { EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { IIndexPattern } from 'src/plugins/data/public'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; +import { InfraSource } from '../../../../common/http_api/source_api'; +import { + Comparator, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../server/lib/alerting/metric_threshold/types'; +import { MetricsExplorerColor, colorTransformer } from '../../../../common/color_palette'; +import { MetricsExplorerRow, MetricsExplorerAggregation } from '../../../../common/http_api'; +import { MetricExplorerSeriesChart } from '../../../pages/metrics/metrics_explorer/components/series_chart'; +import { MetricExpression, AlertContextMeta } from '../types'; +import { MetricsExplorerChartType } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; +import { getChartTheme } from '../../../pages/metrics/metrics_explorer/components/helpers/get_chart_theme'; +import { createFormatterForMetric } from '../../../pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric'; +import { calculateDomain } from '../../../pages/metrics/metrics_explorer/components/helpers/calculate_domain'; +import { useMetricsExplorerChartData } from '../hooks/use_metrics_explorer_chart_data'; + +interface Props { + context: AlertsContextValue; + expression: MetricExpression; + derivedIndexPattern: IIndexPattern; + source: InfraSource | null; + filterQuery?: string; + groupBy?: string; +} + +const tooltipProps = { + headerFormatter: (tooltipValue: TooltipValue) => + moment(tooltipValue.value).format('Y-MM-DD HH:mm:ss.SSS'), +}; + +const TIME_LABELS = { + s: i18n.translate('xpack.infra.metrics.alerts.timeLabels.seconds', { defaultMessage: 'seconds' }), + m: i18n.translate('xpack.infra.metrics.alerts.timeLabels.minutes', { defaultMessage: 'minutes' }), + h: i18n.translate('xpack.infra.metrics.alerts.timeLabels.hours', { defaultMessage: 'hours' }), + d: i18n.translate('xpack.infra.metrics.alerts.timeLabels.days', { defaultMessage: 'days' }), +}; + +export const ExpressionChart: React.FC = ({ + expression, + context, + derivedIndexPattern, + source, + filterQuery, + groupBy, +}) => { + const { loading, data } = useMetricsExplorerChartData( + expression, + context, + derivedIndexPattern, + source, + filterQuery, + groupBy + ); + + const metric = { + field: expression.metric, + aggregation: expression.aggType as MetricsExplorerAggregation, + color: MetricsExplorerColor.color0, + }; + const isDarkMode = context.uiSettings?.get('theme:darkMode') || false; + const dateFormatter = useMemo(() => { + const firstSeries = data ? first(data.series) : null; + return firstSeries && firstSeries.rows.length > 0 + ? niceTimeFormatter([first(firstSeries.rows).timestamp, last(firstSeries.rows).timestamp]) + : (value: number) => `${value}`; + }, [data]); + + const yAxisFormater = useCallback(createFormatterForMetric(metric), [expression]); + + if (loading || !data) { + return ( + + + + + + ); + } + + const thresholds = expression.threshold.slice().sort(); + + // Creating a custom series where the ID is changed to 0 + // so that we can get a proper domian + const firstSeries = first(data.series); + if (!firstSeries) { + return ( + + Oops, no chart data available + + ); + } + + const series = { + ...firstSeries, + rows: firstSeries.rows.map(row => { + const newRow: MetricsExplorerRow = { + timestamp: row.timestamp, + metric_0: row.metric_0 || null, + }; + thresholds.forEach((thresholdValue, index) => { + newRow[`metric_threshold_${index}`] = thresholdValue; + }); + return newRow; + }), + }; + + const firstTimestamp = first(firstSeries.rows).timestamp; + const lastTimestamp = last(firstSeries.rows).timestamp; + const dataDomain = calculateDomain(series, [metric], false); + const domain = { + max: Math.max(dataDomain.max, last(thresholds) || dataDomain.max) * 1.1, // add 10% headroom. + min: Math.min(dataDomain.min, first(thresholds) || dataDomain.min), + }; + + if (domain.min === first(expression.threshold)) { + domain.min = domain.min * 0.9; + } + + const isAbove = [Comparator.GT, Comparator.GT_OR_EQ].includes(expression.comparator); + const opacity = 0.3; + const timeLabel = TIME_LABELS[expression.timeUnit]; + + return ( + <> + + + + {thresholds.length ? ( + `threshold_${i}`)} + series={series} + stack={false} + opacity={opacity} + /> + ) : null} + {thresholds.length && expression.comparator === Comparator.OUTSIDE_RANGE ? ( + <> + `threshold_${i}`)} + series={series} + stack={false} + opacity={opacity} + /> + + + + ) : null} + {isAbove ? ( + + ) : null} + + + + + +
+ {series.id !== 'ALL' ? ( + + + + ) : ( + + + + )} +
+ + ); +}; + +const EmptyContainer: React.FC = ({ children }) => ( +
+ {children} +
+); + +const ChartContainer: React.FC = ({ children }) => ( +
+ {children} +
+); diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx new file mode 100644 index 00000000000000..8801df380b48d4 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_row.tsx @@ -0,0 +1,237 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useCallback, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiFlexItem, EuiButtonIcon, EuiSpacer } from '@elastic/eui'; +import { IFieldType } from 'src/plugins/data/public'; +import { + WhenExpression, + OfExpression, + ThresholdExpression, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../triggers_actions_ui/public/common'; +import { euiStyled } from '../../../../../observability/public'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { IErrorObject } from '../../../../../triggers_actions_ui/public/types'; +import { MetricExpression, AGGREGATION_TYPES } from '../types'; +import { + Comparator, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../server/lib/alerting/metric_threshold/types'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { builtInComparators } from '../../../../../triggers_actions_ui/public/common/constants'; + +const customComparators = { + ...builtInComparators, + [Comparator.OUTSIDE_RANGE]: { + text: i18n.translate('xpack.infra.metrics.alertFlyout.outsideRangeLabel', { + defaultMessage: 'Is not between', + }), + value: Comparator.OUTSIDE_RANGE, + requiredValues: 2, + }, +}; + +interface ExpressionRowProps { + fields: IFieldType[]; + expressionId: number; + expression: MetricExpression; + errors: IErrorObject; + canDelete: boolean; + addExpression(): void; + remove(id: number): void; + setAlertParams(id: number, params: MetricExpression): void; +} + +const StyledExpressionRow = euiStyled(EuiFlexGroup)` + display: flex; + flex-wrap: wrap; + margin: 0 -4px; +`; + +const StyledExpression = euiStyled.div` + padding: 0 4px; +`; + +export const ExpressionRow: React.FC = props => { + const [isExpanded, setRowState] = useState(true); + const toggleRowState = useCallback(() => setRowState(!isExpanded), [isExpanded]); + const { + children, + setAlertParams, + expression, + errors, + expressionId, + remove, + fields, + canDelete, + } = props; + const { + aggType = AGGREGATION_TYPES.MAX, + metric, + comparator = Comparator.GT, + threshold = [], + } = expression; + + const updateAggType = useCallback( + (at: string) => { + setAlertParams(expressionId, { + ...expression, + aggType: at as MetricExpression['aggType'], + metric: at === 'count' ? undefined : expression.metric, + }); + }, + [expressionId, expression, setAlertParams] + ); + + const updateMetric = useCallback( + (m?: MetricExpression['metric']) => { + setAlertParams(expressionId, { ...expression, metric: m }); + }, + [expressionId, expression, setAlertParams] + ); + + const updateComparator = useCallback( + (c?: string) => { + setAlertParams(expressionId, { ...expression, comparator: c as Comparator }); + }, + [expressionId, expression, setAlertParams] + ); + + const updateThreshold = useCallback( + t => { + if (t.join() !== expression.threshold.join()) { + setAlertParams(expressionId, { ...expression, threshold: t }); + } + }, + [expressionId, expression, setAlertParams] + ); + + return ( + <> + + + + + + + + + + {aggType !== 'count' && ( + + ({ + normalizedType: f.type, + name: f.name, + }))} + aggType={aggType} + errors={errors} + onChangeSelectedAggField={updateMetric} + /> + + )} + + + + + + {canDelete && ( + + remove(expressionId)} + /> + + )} + + {isExpanded ?
{children}
: null} + + + ); +}; + +export const aggregationType: { [key: string]: any } = { + avg: { + text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.avg', { + defaultMessage: 'Average', + }), + fieldRequired: true, + validNormalizedTypes: ['number'], + value: AGGREGATION_TYPES.AVERAGE, + }, + max: { + text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.max', { + defaultMessage: 'Max', + }), + fieldRequired: true, + validNormalizedTypes: ['number', 'date'], + value: AGGREGATION_TYPES.MAX, + }, + min: { + text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.min', { + defaultMessage: 'Min', + }), + fieldRequired: true, + validNormalizedTypes: ['number', 'date'], + value: AGGREGATION_TYPES.MIN, + }, + cardinality: { + text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.cardinality', { + defaultMessage: 'Cardinality', + }), + fieldRequired: false, + value: AGGREGATION_TYPES.CARDINALITY, + validNormalizedTypes: ['number'], + }, + rate: { + text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.rate', { + defaultMessage: 'Rate', + }), + fieldRequired: false, + value: AGGREGATION_TYPES.RATE, + validNormalizedTypes: ['number'], + }, + count: { + text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.count', { + defaultMessage: 'Document count', + }), + fieldRequired: false, + value: AGGREGATION_TYPES.COUNT, + validNormalizedTypes: ['number'], + }, + sum: { + text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.sum', { + defaultMessage: 'Sum', + }), + fieldRequired: false, + value: AGGREGATION_TYPES.SUM, + validNormalizedTypes: ['number'], + }, +}; diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/validation.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/alerting/metrics/validation.tsx rename to x-pack/plugins/infra/public/alerting/metric_threshold/components/validation.tsx diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts new file mode 100644 index 00000000000000..67f66bf742f430 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/hooks/use_metrics_explorer_chart_data.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IIndexPattern } from 'src/plugins/data/public'; +import { useMemo } from 'react'; +import { InfraSource } from '../../../../common/http_api/source_api'; +import { AlertContextMeta, MetricExpression } from '../types'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; +import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; +import { useMetricsExplorerData } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_data'; + +export const useMetricsExplorerChartData = ( + expression: MetricExpression, + context: AlertsContextValue, + derivedIndexPattern: IIndexPattern, + source: InfraSource | null, + filterQuery?: string, + groupBy?: string +) => { + const { timeSize, timeUnit } = expression || { timeSize: 1, timeUnit: 'm' }; + const options: MetricsExplorerOptions = useMemo( + () => ({ + limit: 1, + forceInterval: true, + groupBy, + filterQuery, + metrics: [ + { + field: expression.metric, + aggregation: expression.aggType, + }, + ], + aggregation: expression.aggType || 'avg', + }), + [expression.aggType, expression.metric, filterQuery, groupBy] + ); + const timerange = useMemo( + () => ({ + interval: `>=${timeSize || 1}${timeUnit}`, + from: `now-${(timeSize || 1) * 20}${timeUnit}`, + to: 'now', + }), + [timeSize, timeUnit] + ); + + return useMetricsExplorerData( + options, + source?.configuration, + derivedIndexPattern, + timerange, + null, + null, + context.http.fetch + ); +}; diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/metric_threshold_alert_type.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts similarity index 72% rename from x-pack/plugins/infra/public/components/alerting/metrics/metric_threshold_alert_type.ts rename to x-pack/plugins/infra/public/alerting/metric_threshold/index.ts index 162102ebd86a80..91b9bafad50117 100644 --- a/x-pack/plugins/infra/public/components/alerting/metrics/metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/index.ts @@ -5,13 +5,13 @@ */ import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AlertTypeModel } from '../../../../../triggers_actions_ui/public/types'; -import { Expressions } from './expression'; -import { validateMetricThreshold } from './validation'; +import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; +import { Expressions } from './components/expression'; +import { validateMetricThreshold } from './components/validation'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../../server/lib/alerting/metric_threshold/types'; +import { METRIC_THRESHOLD_ALERT_TYPE_ID } from '../../../server/lib/alerting/metric_threshold/types'; -export function getAlertType(): AlertTypeModel { +export function createMetricThresholdAlertType(): AlertTypeModel { return { id: METRIC_THRESHOLD_ALERT_TYPE_ID, name: i18n.translate('xpack.infra.metrics.alertFlyout.alertName', { diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/lib/transform_metrics_explorer_data.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/lib/transform_metrics_explorer_data.ts new file mode 100644 index 00000000000000..0e631b1e333d75 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/lib/transform_metrics_explorer_data.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { first } from 'lodash'; +import { MetricsExplorerResponse } from '../../../../common/http_api/metrics_explorer'; +import { MetricThresholdAlertParams, ExpressionChartSeries } from '../types'; + +export const transformMetricsExplorerData = ( + params: MetricThresholdAlertParams, + data: MetricsExplorerResponse | null +) => { + const { criteria } = params; + if (criteria && data) { + const firstSeries = first(data.series); + const series = firstSeries.rows.reduce((acc, row) => { + const { timestamp } = row; + criteria.forEach((item, index) => { + if (!acc[index]) { + acc[index] = []; + } + const value = (row[`metric_${index}`] as number) || 0; + acc[index].push({ timestamp, value }); + }); + return acc; + }, [] as ExpressionChartSeries); + return { id: firstSeries.id, series }; + } +}; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/types.ts b/x-pack/plugins/infra/public/alerting/metric_threshold/types.ts new file mode 100644 index 00000000000000..af3baf191bed21 --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/types.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + MetricExpressionParams, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../server/lib/alerting/metric_threshold/types'; +import { MetricsExplorerOptions } from '../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; +import { MetricsExplorerSeries } from '../../../common/http_api/metrics_explorer'; + +export interface AlertContextMeta { + currentOptions?: Partial; + series?: MetricsExplorerSeries; +} + +export type TimeUnit = 's' | 'm' | 'h' | 'd'; +export type MetricExpression = Omit & { + metric?: string; +}; + +export enum AGGREGATION_TYPES { + COUNT = 'count', + AVERAGE = 'avg', + SUM = 'sum', + MIN = 'min', + MAX = 'max', + RATE = 'rate', + CARDINALITY = 'cardinality', +} + +export interface MetricThresholdAlertParams { + criteria?: MetricExpression[]; + groupBy?: string; + filterQuery?: string; + sourceId?: string; +} + +export interface ExpressionChartRow { + timestamp: number; + value: number; +} + +export type ExpressionChartSeries = ExpressionChartRow[][]; + +export interface ExpressionChartData { + id: string; + series: ExpressionChartSeries; +} diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index cc88dd9e0d0f8f..5dc9802fefd254 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -28,7 +28,7 @@ import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { WaffleOptionsProvider } from './inventory_view/hooks/use_waffle_options'; import { WaffleTimeProvider } from './inventory_view/hooks/use_waffle_time'; import { WaffleFiltersProvider } from './inventory_view/hooks/use_waffle_filters'; -import { AlertDropdown } from '../../components/alerting/metrics/alert_dropdown'; +import { AlertDropdown } from '../../alerting/metric_threshold/components/alert_dropdown'; export const InfrastructurePage = ({ match }: RouteComponentProps) => { const uiCapabilities = useKibana().services.application?.capabilities; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx index 275635a33ec266..c528aa885346e4 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo, useState } from 'react'; -import { AlertFlyout } from '../../../../../components/alerting/metrics/alert_flyout'; +import { AlertFlyout } from '../../../../../alerting/metric_threshold/components/alert_flyout'; import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../../../lib/lib'; import { getNodeDetailUrl, getNodeLogsUrl } from '../../../../link_to'; import { createUptimeLink } from '../../lib/create_uptime_link'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/aggregation.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/aggregation.tsx index 0c0f7b33b3a4a0..5a84d204b3b25e 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/aggregation.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/aggregation.tsx @@ -26,6 +26,9 @@ export const MetricsExplorerAggregationPicker = ({ options, onChange }: Props) = ['avg']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.avg', { defaultMessage: 'Average', }), + ['sum']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.sum', { + defaultMessage: 'Sum', + }), ['max']: i18n.translate('xpack.infra.metricsExplorer.aggregationLables.max', { defaultMessage: 'Max', }), diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx index 31086a21ca13f8..e13c9dcc069845 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/chart_context_menu.tsx @@ -14,7 +14,7 @@ import { } from '@elastic/eui'; import DateMath from '@elastic/datemath'; import { Capabilities } from 'src/core/public'; -import { AlertFlyout } from '../../../../components/alerting/metrics/alert_flyout'; +import { AlertFlyout } from '../../../../alerting/metric_threshold/components/alert_flyout'; import { MetricsExplorerSeries } from '../../../../../common/http_api/metrics_explorer'; import { MetricsExplorerOptions, diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_metric_label.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_metric_label.ts index 1607302a6259a5..6343f2848054b7 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_metric_label.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_metric_label.ts @@ -4,8 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { MetricsExplorerMetric } from '../../../../../../common/http_api/metrics_explorer'; +import { MetricsExplorerOptionsMetric } from '../../hooks/use_metrics_explorer_options'; -export const createMetricLabel = (metric: MetricsExplorerMetric) => { +export const createMetricLabel = (metric: MetricsExplorerOptionsMetric) => { + if (metric.label) { + return metric.label; + } return `${metric.aggregation}(${metric.field || ''})`; }; diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/series_chart.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/series_chart.tsx index ad7ce83539526a..3b84fcbc34836b 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/series_chart.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/series_chart.tsx @@ -21,12 +21,15 @@ import { MetricsExplorerChartType, } from '../hooks/use_metrics_explorer_options'; +type NumberOrString = string | number; + interface Props { metric: MetricsExplorerOptionsMetric; - id: string | number; + id: NumberOrString | NumberOrString[]; series: MetricsExplorerSeries; type: MetricsExplorerChartType; stack: boolean; + opacity?: number; } export const MetricExplorerSeriesChart = (props: Props) => { @@ -36,13 +39,17 @@ export const MetricExplorerSeriesChart = (props: Props) => { return ; }; -export const MetricsExplorerAreaChart = ({ metric, id, series, type, stack }: Props) => { +export const MetricsExplorerAreaChart = ({ metric, id, series, type, stack, opacity }: Props) => { const color = (metric.color && colorTransformer(metric.color)) || colorTransformer(MetricsExplorerColor.color0); - const yAccessor = `metric_${id}`; - const chartId = `series-${series.id}-${yAccessor}`; + const yAccessors = Array.isArray(id) + ? id.map(i => `metric_${i}`).slice(id.length - 1, id.length) + : [`metric_${id}`]; + const y0Accessors = + Array.isArray(id) && id.length > 1 ? id.map(i => `metric_${i}`).slice(0, 1) : undefined; + const chartId = `series-${series.id}-${yAccessors.join('-')}`; const seriesAreaStyle: RecursivePartial = { line: { @@ -50,19 +57,21 @@ export const MetricsExplorerAreaChart = ({ metric, id, series, type, stack }: Pr visible: true, }, area: { - opacity: 0.5, + opacity: opacity || 0.5, visible: type === MetricsExplorerChartType.area, }, }; + return ( (null); const [loading, setLoading] = useState(true); const [data, setData] = useState(null); @@ -46,13 +49,17 @@ export function useMetricsExplorerData( if (!from || !to) { throw new Error('Unalble to parse timerange'); } - if (!fetch) { + if (!fetchFn) { throw new Error('HTTP service is unavailable'); } + if (!source) { + throw new Error('Source is unavailable'); + } const response = decodeOrThrow(metricsExplorerResponseRT)( - await fetch('/api/infra/metrics_explorer', { + await fetchFn('/api/infra/metrics_explorer', { method: 'POST', body: JSON.stringify({ + forceInterval: options.forceInterval, metrics: options.aggregation === 'count' ? [{ aggregation: 'count' }] diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.ts index 9d124a6af80123..1b3e809fde61fd 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options.ts @@ -40,6 +40,7 @@ export interface MetricsExplorerOptions { groupBy?: string; filterQuery?: string; aggregation: MetricsExplorerAggregation; + forceInterval?: boolean; } export interface MetricsExplorerTimeOptions { diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts index 40366b2a54f243..8cdfc4f381f436 100644 --- a/x-pack/plugins/infra/public/plugin.ts +++ b/x-pack/plugins/infra/public/plugin.ts @@ -21,8 +21,8 @@ import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/p import { DataEnhancedSetup, DataEnhancedStart } from '../../data_enhanced/public'; import { TriggersAndActionsUIPublicPluginSetup } from '../../../plugins/triggers_actions_ui/public'; -import { getAlertType as getMetricsAlertType } from './components/alerting/metrics/metric_threshold_alert_type'; import { getAlertType as getLogsAlertType } from './components/alerting/logs/log_threshold_alert_type'; +import { createMetricThresholdAlertType } from './alerting/metric_threshold'; export type ClientSetup = void; export type ClientStart = void; @@ -53,8 +53,8 @@ export class Plugin setup(core: CoreSetup, pluginsSetup: ClientPluginsSetup) { registerFeatures(pluginsSetup.home); - pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(getMetricsAlertType()); pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(getLogsAlertType()); + pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(createMetricThresholdAlertType()); core.application.register({ id: 'logs', diff --git a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts index 3517800ea0dd10..e735a26d96a91d 100644 --- a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts +++ b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts @@ -72,7 +72,9 @@ export const populateSeriesWithTSVBData = ( ); if (calculatedInterval) { - model.interval = `>=${calculatedInterval}s`; + model.interval = options.forceInterval + ? options.timerange.interval + : `>=${calculatedInterval}s`; } // Get TSVB results using the model, timerange and filters From b8e0730d0cbd09346af042f70e1f7c02d933d9d3 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Thu, 30 Apr 2020 14:48:37 -0500 Subject: [PATCH 27/47] [SIEM] NP Plugin dependency cleanup (#64842) * Remove static src dependencies from kibana.json We are only importing static code from these plugins, and not consuming their plugin contracts. For this reason, we can safely remove them from kibana.json as that imported code will always be included in our own bundle. * Make usageCollection an optional dependency If it's not enabled, we simply use a noop for our tracker call. * Remove unused plugin contracts These are only needed when we're actually using them in our codebase. For request handler contexts, we only need our kibana.json declaration. * Remove unnecessary try/catch With the addition of the null coalescing, the only chance for an error is in usageCollection. However, if the usageCollection contract changes, we should get a type error long before we see a runtime error. * Improve typings of our Plugin classes * passes missing generic arguments to public plugin interface * passes missing generic arguments to both plugins' CoreSetup types * Don't re-export core types Instead, import them from core as needed. --- x-pack/plugins/siem/kibana.json | 12 ++++++++---- .../plugins/siem/public/lib/telemetry/index.ts | 8 +++----- x-pack/plugins/siem/public/plugin.tsx | 6 +++--- x-pack/plugins/siem/server/lib/compose/kibana.ts | 3 ++- .../lib/framework/kibana_framework_adapter.ts | 3 ++- x-pack/plugins/siem/server/plugin.ts | 16 ++++------------ 6 files changed, 22 insertions(+), 26 deletions(-) diff --git a/x-pack/plugins/siem/kibana.json b/x-pack/plugins/siem/kibana.json index e0ff82eb10eb22..39e50838c1c97a 100644 --- a/x-pack/plugins/siem/kibana.json +++ b/x-pack/plugins/siem/kibana.json @@ -8,18 +8,22 @@ "alerting", "data", "embeddable", - "esUiShared", "features", "home", "inspector", - "kibanaUtils", "licensing", "maps", "triggers_actions_ui", - "uiActions", + "uiActions" + ], + "optionalPlugins": [ + "encryptedSavedObjects", + "ml", + "newsfeed", + "security", + "spaces", "usageCollection" ], - "optionalPlugins": ["encryptedSavedObjects", "ml", "newsfeed", "security", "spaces"], "server": true, "ui": true } diff --git a/x-pack/plugins/siem/public/lib/telemetry/index.ts b/x-pack/plugins/siem/public/lib/telemetry/index.ts index 856b7783a53678..37d181e9b8ad73 100644 --- a/x-pack/plugins/siem/public/lib/telemetry/index.ts +++ b/x-pack/plugins/siem/public/lib/telemetry/index.ts @@ -13,6 +13,8 @@ export { METRIC_TYPE }; type TrackFn = (type: UiStatsMetricType, event: string | string[], count?: number) => void; +const noop = () => {}; + let _track: TrackFn; export const track: TrackFn = (type, event, count) => { @@ -24,11 +26,7 @@ export const track: TrackFn = (type, event, count) => { }; export const initTelemetry = (usageCollection: SetupPlugins['usageCollection'], appId: string) => { - try { - _track = usageCollection.reportUiStats.bind(null, appId); - } catch (error) { - // ignore failed setup here, as we'll just have an inert tracker - } + _track = usageCollection?.reportUiStats?.bind(null, appId) ?? noop; }; export enum TELEMETRY_EVENT { diff --git a/x-pack/plugins/siem/public/plugin.tsx b/x-pack/plugins/siem/public/plugin.tsx index e54c96364ba6b0..f9c44bd341facb 100644 --- a/x-pack/plugins/siem/public/plugin.tsx +++ b/x-pack/plugins/siem/public/plugin.tsx @@ -37,7 +37,7 @@ export interface SetupPlugins { home: HomePublicPluginSetup; security: SecurityPluginSetup; triggers_actions_ui: TriggersActionsSetup; - usageCollection: UsageCollectionSetup; + usageCollection?: UsageCollectionSetup; } export interface StartPlugins { @@ -59,14 +59,14 @@ export interface PluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface PluginStart {} -export class Plugin implements IPlugin { +export class Plugin implements IPlugin { private kibanaVersion: string; constructor(initializerContext: PluginInitializerContext) { this.kibanaVersion = initializerContext.env.packageInfo.version; } - public setup(core: CoreSetup, plugins: SetupPlugins) { + public setup(core: CoreSetup, plugins: SetupPlugins) { initTelemetry(plugins.usageCollection, APP_ID); plugins.home.featureCatalogue.register({ diff --git a/x-pack/plugins/siem/server/lib/compose/kibana.ts b/x-pack/plugins/siem/server/lib/compose/kibana.ts index 4a595032e43eb2..8bc90bed251683 100644 --- a/x-pack/plugins/siem/server/lib/compose/kibana.ts +++ b/x-pack/plugins/siem/server/lib/compose/kibana.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CoreSetup, SetupPlugins } from '../../plugin'; +import { CoreSetup } from '../../../../../../src/core/server'; +import { SetupPlugins } from '../../plugin'; import { Authentications } from '../authentications'; import { ElasticsearchAuthenticationAdapter } from '../authentications/elasticsearch_adapter'; diff --git a/x-pack/plugins/siem/server/lib/framework/kibana_framework_adapter.ts b/x-pack/plugins/siem/server/lib/framework/kibana_framework_adapter.ts index 762416149c0fbb..1e99c40ef57270 100644 --- a/x-pack/plugins/siem/server/lib/framework/kibana_framework_adapter.ts +++ b/x-pack/plugins/siem/server/lib/framework/kibana_framework_adapter.ts @@ -9,6 +9,7 @@ import { GraphQLSchema } from 'graphql'; import { runHttpQuery } from 'apollo-server-core'; import { schema as configSchema } from '@kbn/config-schema'; import { + CoreSetup, IRouter, KibanaResponseFactory, RequestHandlerContext, @@ -16,7 +17,7 @@ import { } from '../../../../../../src/core/server'; import { IndexPatternsFetcher } from '../../../../../../src/plugins/data/server'; import { AuthenticatedUser } from '../../../../security/common/model'; -import { CoreSetup, SetupPlugins } from '../../plugin'; +import { SetupPlugins } from '../../plugin'; import { FrameworkAdapter, diff --git a/x-pack/plugins/siem/server/plugin.ts b/x-pack/plugins/siem/server/plugin.ts index dc484b75ab531a..3ef4b39bd0979c 100644 --- a/x-pack/plugins/siem/server/plugin.ts +++ b/x-pack/plugins/siem/server/plugin.ts @@ -15,16 +15,12 @@ import { PluginInitializerContext, Logger, } from '../../../../src/core/server'; -import { - PluginStartContract as AlertingStart, - PluginSetupContract as AlertingSetup, -} from '../../alerting/server'; +import { PluginSetupContract as AlertingSetup } from '../../alerting/server'; import { SecurityPluginSetup as SecuritySetup } from '../../security/server'; import { PluginSetupContract as FeaturesSetup } from '../../features/server'; import { MlPluginSetup as MlSetup } from '../../ml/server'; import { EncryptedSavedObjectsPluginSetup as EncryptedSavedObjectsSetup } from '../../encrypted_saved_objects/server'; import { SpacesPluginSetup as SpacesSetup } from '../../spaces/server'; -import { PluginStartContract as ActionsStart } from '../../actions/server'; import { LicensingPluginSetup } from '../../licensing/server'; import { initServer } from './init_server'; import { compose } from './lib/compose/kibana'; @@ -40,8 +36,6 @@ import { createConfig$, ConfigType } from './config'; import { initUiSettings } from './ui_settings'; import { APP_ID, APP_ICON } from '../common/constants'; -export { CoreSetup, CoreStart }; - export interface SetupPlugins { alerting: AlertingSetup; encryptedSavedObjects?: EncryptedSavedObjectsSetup; @@ -52,10 +46,8 @@ export interface SetupPlugins { ml?: MlSetup; } -export interface StartPlugins { - actions: ActionsStart; - alerting: AlertingStart; -} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface StartPlugins {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface PluginSetup {} @@ -77,7 +69,7 @@ export class Plugin implements IPlugin, plugins: SetupPlugins) { this.logger.debug('plugin setup'); if (hasListsFeature()) { From 6e3791ea12c4283561e1cdd5284c1589274b2417 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Thu, 30 Apr 2020 15:52:14 -0400 Subject: [PATCH 28/47] Ingest Node Pipelines UI (#62321) --- .github/CODEOWNERS | 1 + x-pack/.i18nrc.json | 1 + x-pack/plugins/ingest_pipelines/README.md | 24 ++ .../ingest_pipelines/common/constants.ts | 18 + .../ingest_pipelines/common/lib/index.ts | 7 + .../common/lib/pipeline_serialization.test.ts | 68 ++++ .../common/lib/pipeline_serialization.ts | 20 ++ .../plugins/ingest_pipelines/common/types.ts | 28 ++ x-pack/plugins/ingest_pipelines/kibana.json | 17 + .../public/application/app.tsx | 101 ++++++ .../public/application/components/index.ts | 7 + .../components/pipeline_form/index.ts | 7 + .../pipeline_form/pipeline_form.tsx | 166 +++++++++ .../pipeline_form/pipeline_form_error.tsx | 34 ++ .../pipeline_form/pipeline_form_fields.tsx | 223 ++++++++++++ .../pipeline_form/pipeline_form_provider.tsx | 18 + .../pipeline_request_flyout/index.ts | 7 + .../pipeline_request_flyout.tsx | 89 +++++ .../pipeline_request_flyout_provider.tsx | 29 ++ .../pipeline_test_flyout/index.ts | 7 + .../pipeline_test_flyout.tsx | 203 +++++++++++ .../pipeline_test_flyout_provider.tsx | 39 +++ .../pipeline_test_flyout/tabs/index.ts | 11 + .../tabs/pipeline_test_tabs.tsx | 60 ++++ .../pipeline_test_flyout/tabs/schema.tsx | 87 +++++ .../tabs/tab_documents.tsx | 152 ++++++++ .../pipeline_test_flyout/tabs/tab_output.tsx | 106 ++++++ .../components/pipeline_form/schema.tsx | 147 ++++++++ .../pipeline_form/test_config_context.tsx | 57 +++ .../public/application/constants/index.ts | 14 + .../public/application/index.tsx | 55 +++ .../public/application/lib/index.ts | 7 + .../public/application/lib/utils.test.ts | 37 ++ .../public/application/lib/utils.ts | 25 ++ .../application/mount_management_section.ts | 35 ++ .../public/application/sections/index.ts | 13 + .../sections/pipelines_clone/index.ts | 7 + .../pipelines_clone/pipelines_clone.tsx | 59 ++++ .../sections/pipelines_create/index.ts | 7 + .../pipelines_create/pipelines_create.tsx | 109 ++++++ .../sections/pipelines_edit/index.ts | 7 + .../pipelines_edit/pipelines_edit.tsx | 151 ++++++++ .../sections/pipelines_list/delete_modal.tsx | 125 +++++++ .../pipelines_list/details_flyout.tsx | 210 +++++++++++ .../pipelines_list/details_json_block.tsx | 31 ++ .../sections/pipelines_list/empty_list.tsx | 29 ++ .../sections/pipelines_list/index.ts | 7 + .../sections/pipelines_list/main.tsx | 207 +++++++++++ .../pipelines_list/not_found_flyout.tsx | 42 +++ .../sections/pipelines_list/table.tsx | 148 ++++++++ .../public/application/services/api.ts | 125 +++++++ .../application/services/breadcrumbs.ts | 71 ++++ .../application/services/documentation.ts | 39 +++ .../public/application/services/index.ts | 13 + .../public/application/services/ui_metric.ts | 32 ++ .../plugins/ingest_pipelines/public/index.ts | 11 + .../plugins/ingest_pipelines/public/plugin.ts | 39 +++ .../ingest_pipelines/public/shared_imports.ts | 54 +++ .../plugins/ingest_pipelines/public/types.ts | 13 + .../plugins/ingest_pipelines/server/index.ts | 12 + .../ingest_pipelines/server/lib/index.ts | 7 + .../server/lib/is_es_error.ts | 13 + .../plugins/ingest_pipelines/server/plugin.ts | 59 ++++ .../server/routes/api/create.ts | 83 +++++ .../server/routes/api/delete.ts | 49 +++ .../ingest_pipelines/server/routes/api/get.ts | 83 +++++ .../server/routes/api/index.ts | 17 + .../server/routes/api/privileges.ts | 62 ++++ .../server/routes/api/simulate.ts | 61 ++++ .../server/routes/api/update.ts | 67 ++++ .../ingest_pipelines/server/routes/index.ts | 27 ++ .../ingest_pipelines/server/services/index.ts | 7 + .../server/services/license.ts | 82 +++++ .../plugins/ingest_pipelines/server/types.ts | 22 ++ .../api_integration/apis/management/index.js | 1 + .../apis/management/ingest_pipelines/index.ts | 12 + .../ingest_pipelines/ingest_pipelines.ts | 330 ++++++++++++++++++ .../ingest_pipelines/lib/elasticsearch.ts | 39 +++ .../management/ingest_pipelines/lib/index.ts | 7 + .../functional/apps/ingest_pipelines/index.ts | 14 + .../apps/ingest_pipelines/ingest_pipelines.ts | 27 ++ x-pack/test/functional/config.js | 5 + x-pack/test/functional/page_objects/index.ts | 2 + .../page_objects/ingest_pipelines_page.ts | 17 + 84 files changed, 4561 insertions(+) create mode 100644 x-pack/plugins/ingest_pipelines/README.md create mode 100644 x-pack/plugins/ingest_pipelines/common/constants.ts create mode 100644 x-pack/plugins/ingest_pipelines/common/lib/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/common/lib/pipeline_serialization.test.ts create mode 100644 x-pack/plugins/ingest_pipelines/common/lib/pipeline_serialization.ts create mode 100644 x-pack/plugins/ingest_pipelines/common/types.ts create mode 100644 x-pack/plugins/ingest_pipelines/kibana.json create mode 100644 x-pack/plugins/ingest_pipelines/public/application/app.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_error.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_provider.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/pipeline_request_flyout.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/pipeline_request_flyout_provider.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/pipeline_test_flyout.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/pipeline_test_flyout_provider.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/pipeline_test_tabs.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/schema.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/tab_documents.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/tab_output.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/schema.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/test_config_context.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/constants/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/index.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/lib/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/lib/utils.test.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/lib/utils.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/mount_management_section.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/delete_modal.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/details_flyout.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/details_json_block.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/empty_list.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/not_found_flyout.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx create mode 100644 x-pack/plugins/ingest_pipelines/public/application/services/api.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/services/breadcrumbs.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/services/documentation.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/services/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/application/services/ui_metric.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/plugin.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/shared_imports.ts create mode 100644 x-pack/plugins/ingest_pipelines/public/types.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/lib/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/lib/is_es_error.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/plugin.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/routes/api/create.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/routes/api/delete.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/routes/api/get.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/routes/api/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/routes/api/privileges.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/routes/api/simulate.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/routes/api/update.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/routes/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/services/index.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/services/license.ts create mode 100644 x-pack/plugins/ingest_pipelines/server/types.ts create mode 100644 x-pack/test/api_integration/apis/management/ingest_pipelines/index.ts create mode 100644 x-pack/test/api_integration/apis/management/ingest_pipelines/ingest_pipelines.ts create mode 100644 x-pack/test/api_integration/apis/management/ingest_pipelines/lib/elasticsearch.ts create mode 100644 x-pack/test/api_integration/apis/management/ingest_pipelines/lib/index.ts create mode 100644 x-pack/test/functional/apps/ingest_pipelines/index.ts create mode 100644 x-pack/test/functional/apps/ingest_pipelines/ingest_pipelines.ts create mode 100644 x-pack/test/functional/page_objects/ingest_pipelines_page.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a97400ee09c0e9..c17538849660ed 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -203,6 +203,7 @@ /x-pack/plugins/snapshot_restore/ @elastic/es-ui /x-pack/plugins/upgrade_assistant/ @elastic/es-ui /x-pack/plugins/watcher/ @elastic/es-ui +/x-pack/plugins/ingest_pipelines/ @elastic/es-ui # Endpoint /x-pack/plugins/endpoint/ @elastic/endpoint-app-team @elastic/siem diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 4acb170d125744..9f43bf8da06010 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -21,6 +21,7 @@ "xpack.indexLifecycleMgmt": "plugins/index_lifecycle_management", "xpack.infra": "plugins/infra", "xpack.ingestManager": "plugins/ingest_manager", + "xpack.ingestPipelines": "plugins/ingest_pipelines", "xpack.lens": "plugins/lens", "xpack.licenseMgmt": "plugins/license_management", "xpack.licensing": "plugins/licensing", diff --git a/x-pack/plugins/ingest_pipelines/README.md b/x-pack/plugins/ingest_pipelines/README.md new file mode 100644 index 00000000000000..a469511bdbbd2c --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/README.md @@ -0,0 +1,24 @@ +# Ingest Node Pipelines UI + +## Summary +The `ingest_pipelines` plugin provides Kibana support for [Elasticsearch's ingest nodes](https://www.elastic.co/guide/en/elasticsearch/reference/master/ingest.html). Please refer to the Elasticsearch documentation for more details. + +This plugin allows Kibana to create, edit, clone and delete ingest node pipelines. It also provides support to simulate a pipeline. + +It requires a Basic license and the following cluster privileges: `manage_pipeline` and `cluster:monitor/nodes/info`. + +--- + +## Development + +A new app called Ingest Node Pipelines is registered in the Management section and follows a typical CRUD UI pattern. The client-side portion of this app lives in [public/application](public/application) and uses endpoints registered in [server/routes/api](server/routes/api). + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions on setting up your development environment. + +### Test coverage + +The app has the following test coverage: + +- Complete API integration tests +- Smoke-level functional test +- Client-integration tests diff --git a/x-pack/plugins/ingest_pipelines/common/constants.ts b/x-pack/plugins/ingest_pipelines/common/constants.ts new file mode 100644 index 00000000000000..edf681c276a842 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/common/constants.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { LicenseType } from '../../licensing/common/types'; + +const basicLicense: LicenseType = 'basic'; + +export const PLUGIN_ID = 'ingest_pipelines'; + +export const PLUGIN_MIN_LICENSE_TYPE = basicLicense; + +export const BASE_PATH = '/management/elasticsearch/ingest_pipelines'; + +export const API_BASE_PATH = '/api/ingest_pipelines'; + +export const APP_CLUSTER_REQUIRED_PRIVILEGES = ['manage_pipeline', 'cluster:monitor/nodes/info']; diff --git a/x-pack/plugins/ingest_pipelines/common/lib/index.ts b/x-pack/plugins/ingest_pipelines/common/lib/index.ts new file mode 100644 index 00000000000000..a976f66bc7c405 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/common/lib/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { deserializePipelines } from './pipeline_serialization'; diff --git a/x-pack/plugins/ingest_pipelines/common/lib/pipeline_serialization.test.ts b/x-pack/plugins/ingest_pipelines/common/lib/pipeline_serialization.test.ts new file mode 100644 index 00000000000000..65d6b6e30497f5 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/common/lib/pipeline_serialization.test.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { deserializePipelines } from './pipeline_serialization'; + +describe('pipeline_serialization', () => { + describe('deserializePipelines()', () => { + it('should deserialize pipelines', () => { + expect( + deserializePipelines({ + pipeline1: { + description: 'pipeline 1 description', + version: 1, + processors: [ + { + script: { + source: 'ctx._type = null', + }, + }, + ], + on_failure: [ + { + set: { + field: 'error.message', + value: '{{ failure_message }}', + }, + }, + ], + }, + pipeline2: { + description: 'pipeline2 description', + version: 1, + processors: [], + }, + }) + ).toEqual([ + { + name: 'pipeline1', + description: 'pipeline 1 description', + version: 1, + processors: [ + { + script: { + source: 'ctx._type = null', + }, + }, + ], + on_failure: [ + { + set: { + field: 'error.message', + value: '{{ failure_message }}', + }, + }, + ], + }, + { + name: 'pipeline2', + description: 'pipeline2 description', + version: 1, + processors: [], + }, + ]); + }); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/common/lib/pipeline_serialization.ts b/x-pack/plugins/ingest_pipelines/common/lib/pipeline_serialization.ts new file mode 100644 index 00000000000000..572f6550760151 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/common/lib/pipeline_serialization.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PipelinesByName, Pipeline } from '../types'; + +export function deserializePipelines(pipelinesByName: PipelinesByName): Pipeline[] { + const pipelineNames: string[] = Object.keys(pipelinesByName); + + const deserializedPipelines = pipelineNames.map((name: string) => { + return { + ...pipelinesByName[name], + name, + }; + }); + + return deserializedPipelines; +} diff --git a/x-pack/plugins/ingest_pipelines/common/types.ts b/x-pack/plugins/ingest_pipelines/common/types.ts new file mode 100644 index 00000000000000..8d77359a7c3c54 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/common/types.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; + * you may not use this file except in compliance with the Elastic License. + */ + +interface Processor { + [key: string]: { + [key: string]: unknown; + }; +} + +export interface Pipeline { + name: string; + description: string; + version?: number; + processors: Processor[]; + on_failure?: Processor[]; +} + +export interface PipelinesByName { + [key: string]: { + description: string; + version?: number; + processors: Processor[]; + on_failure?: Processor[]; + }; +} diff --git a/x-pack/plugins/ingest_pipelines/kibana.json b/x-pack/plugins/ingest_pipelines/kibana.json new file mode 100644 index 00000000000000..ec02c5f80edf97 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/kibana.json @@ -0,0 +1,17 @@ +{ + "id": "ingestPipelines", + "version": "8.0.0", + "server": true, + "ui": true, + "requiredPlugins": [ + "licensing", + "management" + ], + "optionalPlugins": [ + "usageCollection" + ], + "configPath": [ + "xpack", + "ingest_pipelines" + ] +} diff --git a/x-pack/plugins/ingest_pipelines/public/application/app.tsx b/x-pack/plugins/ingest_pipelines/public/application/app.tsx new file mode 100644 index 00000000000000..ba7675b5075960 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/app.tsx @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiPageContent } from '@elastic/eui'; +import React, { FunctionComponent } from 'react'; +import { HashRouter, Switch, Route } from 'react-router-dom'; + +import { BASE_PATH, APP_CLUSTER_REQUIRED_PRIVILEGES } from '../../common/constants'; + +import { + SectionError, + useAuthorizationContext, + WithPrivileges, + SectionLoading, + NotAuthorizedSection, +} from '../shared_imports'; + +import { PipelinesList, PipelinesCreate, PipelinesEdit, PipelinesClone } from './sections'; + +export const AppWithoutRouter = () => ( + + + + + + {/* Catch all */} + + +); + +export const App: FunctionComponent = () => { + const { apiError } = useAuthorizationContext(); + + if (apiError) { + return ( + + } + error={apiError} + /> + ); + } + + return ( + `cluster.${privilege}`)} + > + {({ isLoading, hasPrivileges, privilegesMissing }) => { + if (isLoading) { + return ( + + + + ); + } + + if (!hasPrivileges) { + return ( + + + } + message={ + + } + /> + + ); + } + + return ( + + + + ); + }} + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/index.ts new file mode 100644 index 00000000000000..21a2ee30a84e16 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PipelineForm } from './pipeline_form'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/index.ts new file mode 100644 index 00000000000000..2b007a25667a1f --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PipelineFormProvider as PipelineForm } from './pipeline_form_provider'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.tsx new file mode 100644 index 00000000000000..9082196a48b395 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form.tsx @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; + +import { useForm, Form, FormConfig } from '../../../shared_imports'; +import { Pipeline } from '../../../../common/types'; + +import { PipelineRequestFlyout } from './pipeline_request_flyout'; +import { PipelineTestFlyout } from './pipeline_test_flyout'; +import { PipelineFormFields } from './pipeline_form_fields'; +import { PipelineFormError } from './pipeline_form_error'; +import { pipelineFormSchema } from './schema'; + +export interface PipelineFormProps { + onSave: (pipeline: Pipeline) => void; + onCancel: () => void; + isSaving: boolean; + saveError: any; + defaultValue?: Pipeline; + isEditing?: boolean; +} + +export const PipelineForm: React.FunctionComponent = ({ + defaultValue = { + name: '', + description: '', + processors: '', + on_failure: '', + version: '', + }, + onSave, + isSaving, + saveError, + isEditing, + onCancel, +}) => { + const [isRequestVisible, setIsRequestVisible] = useState(false); + + const [isTestingPipeline, setIsTestingPipeline] = useState(false); + + const handleSave: FormConfig['onSubmit'] = (formData, isValid) => { + if (isValid) { + onSave(formData as Pipeline); + } + }; + + const handleTestPipelineClick = () => { + setIsTestingPipeline(true); + }; + + const { form } = useForm({ + schema: pipelineFormSchema, + defaultValue, + onSubmit: handleSave, + }); + + const saveButtonLabel = isSaving ? ( + + ) : isEditing ? ( + + ) : ( + + ); + + return ( + <> +
+ {/* Request error */} + {saveError && } + + {/* All form fields */} + + + {/* Form submission */} + + + + + + {saveButtonLabel} + + + + + + + + + + + setIsRequestVisible(prevIsRequestVisible => !prevIsRequestVisible)} + > + {isRequestVisible ? ( + + ) : ( + + )} + + + + + {/* ES request flyout */} + {isRequestVisible ? ( + setIsRequestVisible(prevIsRequestVisible => !prevIsRequestVisible)} + /> + ) : null} + + {/* Test pipeline flyout */} + {isTestingPipeline ? ( + { + setIsTestingPipeline(prevIsTestingPipeline => !prevIsTestingPipeline); + }} + /> + ) : null} + + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_error.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_error.tsx new file mode 100644 index 00000000000000..ef0e2737df24d1 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_error.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiSpacer, EuiCallOut } from '@elastic/eui'; + +interface Props { + errorMessage: string; +} + +export const PipelineFormError: React.FunctionComponent = ({ errorMessage }) => { + return ( + <> + + } + color="danger" + iconType="alert" + data-test-subj="savePipelineError" + > +

{errorMessage}

+
+ + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx new file mode 100644 index 00000000000000..045afd52204faf --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_fields.tsx @@ -0,0 +1,223 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { EuiButton, EuiSpacer, EuiSwitch, EuiLink } from '@elastic/eui'; + +import { + getUseField, + getFormRow, + Field, + JsonEditorField, + useKibana, +} from '../../../shared_imports'; + +interface Props { + hasVersion: boolean; + hasOnFailure: boolean; + isTestButtonDisabled: boolean; + onTestPipelineClick: () => void; + isEditing?: boolean; +} + +const UseField = getUseField({ component: Field }); +const FormRow = getFormRow({ titleTag: 'h3' }); + +export const PipelineFormFields: React.FunctionComponent = ({ + isEditing, + hasVersion, + hasOnFailure, + isTestButtonDisabled, + onTestPipelineClick, +}) => { + const { services } = useKibana(); + + const [isVersionVisible, setIsVersionVisible] = useState(hasVersion); + const [isOnFailureEditorVisible, setIsOnFailureEditorVisible] = useState(hasOnFailure); + + return ( + <> + {/* Name field with optional version field */} + } + description={ + <> + + + + } + checked={isVersionVisible} + onChange={e => setIsVersionVisible(e.target.checked)} + data-test-subj="versionToggle" + /> + + } + > + + + {isVersionVisible && ( + + )} + + + {/* Description field */} + + } + description={ + + } + > + + + + {/* Processors field */} + + } + description={ + <> + + {i18n.translate('xpack.ingestPipelines.form.processorsDocumentionLink', { + defaultMessage: 'Learn more.', + })} + + ), + }} + /> + + + + + + + + } + > + + + + {/* On-failure field */} + + } + description={ + <> + + {i18n.translate('xpack.ingestPipelines.form.onFailureDocumentionLink', { + defaultMessage: 'Learn more.', + })} + + ), + }} + /> + + + } + checked={isOnFailureEditorVisible} + onChange={e => setIsOnFailureEditorVisible(e.target.checked)} + data-test-subj="onFailureToggle" + /> + + } + > + {isOnFailureEditorVisible ? ( + + ) : ( + // requires children or a field + // For now, we return an empty
if the editor is not visible +
+ )} + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_provider.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_provider.tsx new file mode 100644 index 00000000000000..57abea2309aa18 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_form_provider.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { PipelineForm as PipelineFormUI, PipelineFormProps } from './pipeline_form'; +import { TestConfigContextProvider } from './test_config_context'; + +export const PipelineFormProvider: React.FunctionComponent = passThroughProps => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/index.ts new file mode 100644 index 00000000000000..9476b65557c543 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PipelineRequestFlyoutProvider as PipelineRequestFlyout } from './pipeline_request_flyout_provider'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/pipeline_request_flyout.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/pipeline_request_flyout.tsx new file mode 100644 index 00000000000000..58e86695808b10 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/pipeline_request_flyout.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useRef } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { + EuiButtonEmpty, + EuiCodeBlock, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; + +import { Pipeline } from '../../../../../common/types'; + +interface Props { + pipeline: Pipeline; + closeFlyout: () => void; +} + +export const PipelineRequestFlyout: React.FunctionComponent = ({ + closeFlyout, + pipeline, +}) => { + const { name, ...pipelineBody } = pipeline; + const endpoint = `PUT _ingest/pipeline/${name || ''}`; + const payload = JSON.stringify(pipelineBody, null, 2); + const request = `${endpoint}\n${payload}`; + // Hack so that copied-to-clipboard value updates as content changes + // Related issue: https://github.com/elastic/eui/issues/3321 + const uuid = useRef(0); + uuid.current++; + + return ( + + + +

+ {name ? ( + + ) : ( + + )} +

+
+
+ + + +

+ +

+
+ + + + {request} + +
+ + + + + + +
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/pipeline_request_flyout_provider.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/pipeline_request_flyout_provider.tsx new file mode 100644 index 00000000000000..6dcedca6085af5 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_request_flyout/pipeline_request_flyout_provider.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState, useEffect } from 'react'; + +import { Pipeline } from '../../../../../common/types'; +import { useFormContext } from '../../../../shared_imports'; +import { PipelineRequestFlyout } from './pipeline_request_flyout'; + +export const PipelineRequestFlyoutProvider = ({ closeFlyout }: { closeFlyout: () => void }) => { + const form = useFormContext(); + const [formData, setFormData] = useState({} as Pipeline); + + useEffect(() => { + const subscription = form.subscribe(async ({ isValid, validate, data }) => { + const isFormValid = isValid ?? (await validate()); + if (isFormValid) { + setFormData(data.format() as Pipeline); + } + }); + + return subscription.unsubscribe; + }, [form]); + + return ; +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/index.ts new file mode 100644 index 00000000000000..38bbc43b469a54 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PipelineTestFlyoutProvider as PipelineTestFlyout } from './pipeline_test_flyout_provider'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/pipeline_test_flyout.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/pipeline_test_flyout.tsx new file mode 100644 index 00000000000000..16f39b2912c1de --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/pipeline_test_flyout.tsx @@ -0,0 +1,203 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState, useEffect, useCallback } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; + +import { + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiSpacer, + EuiTitle, + EuiCallOut, +} from '@elastic/eui'; + +import { useKibana } from '../../../../shared_imports'; +import { Pipeline } from '../../../../../common/types'; +import { Tabs, Tab, OutputTab, DocumentsTab } from './tabs'; +import { useTestConfigContext } from '../test_config_context'; + +export interface PipelineTestFlyoutProps { + closeFlyout: () => void; + pipeline: Pipeline; + isPipelineValid: boolean; +} + +export const PipelineTestFlyout: React.FunctionComponent = ({ + closeFlyout, + pipeline, + isPipelineValid, +}) => { + const { services } = useKibana(); + + const { testConfig } = useTestConfigContext(); + const { documents: cachedDocuments, verbose: cachedVerbose } = testConfig; + + const initialSelectedTab = cachedDocuments ? 'output' : 'documents'; + const [selectedTab, setSelectedTab] = useState(initialSelectedTab); + + const [shouldExecuteImmediately, setShouldExecuteImmediately] = useState(false); + const [isExecuting, setIsExecuting] = useState(false); + const [executeError, setExecuteError] = useState(null); + const [executeOutput, setExecuteOutput] = useState(undefined); + + const handleExecute = useCallback( + async (documents: object[], verbose?: boolean) => { + const { name: pipelineName, ...pipelineDefinition } = pipeline; + + setIsExecuting(true); + setExecuteError(null); + + const { error, data: output } = await services.api.simulatePipeline({ + documents, + verbose, + pipeline: pipelineDefinition, + }); + + setIsExecuting(false); + + if (error) { + setExecuteError(error); + return; + } + + setExecuteOutput(output); + + services.notifications.toasts.addSuccess( + i18n.translate('xpack.ingestPipelines.testPipelineFlyout.successNotificationText', { + defaultMessage: 'Pipeline executed', + }), + { + toastLifeTimeMs: 1000, + } + ); + + setSelectedTab('output'); + }, + [pipeline, services.api, services.notifications.toasts] + ); + + useEffect(() => { + if (cachedDocuments) { + setShouldExecuteImmediately(true); + } + // We only want to know on initial mount if there are cached documents + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + // If the user has already tested the pipeline once, + // use the cached test config and automatically execute the pipeline + if (shouldExecuteImmediately && Object.entries(pipeline).length > 0) { + setShouldExecuteImmediately(false); + handleExecute(cachedDocuments!, cachedVerbose); + } + }, [ + pipeline, + handleExecute, + cachedDocuments, + cachedVerbose, + isExecuting, + shouldExecuteImmediately, + ]); + + let tabContent; + + if (selectedTab === 'output') { + tabContent = ( + + ); + } else { + // default to "documents" tab + tabContent = ( + + ); + } + + return ( + + + +

+ {pipeline.name ? ( + + ) : ( + + )} +

+
+
+ + + !executeOutput && tabId === 'output'} + /> + + + + {/* Execute error */} + {executeError ? ( + <> + + } + color="danger" + iconType="alert" + > +

{executeError.message}

+
+ + + ) : null} + + {/* Invalid pipeline error */} + {!isPipelineValid ? ( + <> + + } + color="danger" + iconType="alert" + /> + + + ) : null} + + {/* Documents or output tab content */} + {tabContent} +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/pipeline_test_flyout_provider.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/pipeline_test_flyout_provider.tsx new file mode 100644 index 00000000000000..351478394595a2 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/pipeline_test_flyout_provider.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState, useEffect } from 'react'; + +import { Pipeline } from '../../../../../common/types'; +import { useFormContext } from '../../../../shared_imports'; +import { PipelineTestFlyout, PipelineTestFlyoutProps } from './pipeline_test_flyout'; + +type Props = Omit; + +export const PipelineTestFlyoutProvider: React.FunctionComponent = ({ closeFlyout }) => { + const form = useFormContext(); + const [formData, setFormData] = useState({} as Pipeline); + const [isFormDataValid, setIsFormDataValid] = useState(false); + + useEffect(() => { + const subscription = form.subscribe(async ({ isValid, validate, data }) => { + const isFormValid = isValid ?? (await validate()); + if (isFormValid) { + setFormData(data.format() as Pipeline); + } + setIsFormDataValid(isFormValid); + }); + + return subscription.unsubscribe; + }, [form]); + + return ( + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/index.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/index.ts new file mode 100644 index 00000000000000..ea8fe2cd923507 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/index.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; + * you may not use this file except in compliance with the Elastic License. + */ + +export { Tabs, Tab } from './pipeline_test_tabs'; + +export { DocumentsTab } from './tab_documents'; + +export { OutputTab } from './tab_output'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/pipeline_test_tabs.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/pipeline_test_tabs.tsx new file mode 100644 index 00000000000000..f720b801227029 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/pipeline_test_tabs.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiTab, EuiTabs } from '@elastic/eui'; + +export type Tab = 'documents' | 'output'; + +interface Props { + onTabChange: (tab: Tab) => void; + selectedTab: Tab; + getIsDisabled: (tab: Tab) => boolean; +} + +export const Tabs: React.FunctionComponent = ({ + onTabChange, + selectedTab, + getIsDisabled, +}) => { + const tabs: Array<{ + id: Tab; + name: React.ReactNode; + }> = [ + { + id: 'documents', + name: ( + + ), + }, + { + id: 'output', + name: ( + + ), + }, + ]; + + return ( + + {tabs.map(tab => ( + onTabChange(tab.id)} + isSelected={tab.id === selectedTab} + key={tab.id} + disabled={getIsDisabled(tab.id)} + data-test-subj={tab.id.toLowerCase() + '_tab'} + > + {tab.name} + + ))} + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/schema.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/schema.tsx new file mode 100644 index 00000000000000..de9910344bd4bc --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/schema.tsx @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; + +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { EuiCode } from '@elastic/eui'; + +import { FormSchema, fieldValidators, ValidationFuncArg } from '../../../../../shared_imports'; +import { parseJson, stringifyJson } from '../../../../lib'; + +const { emptyField, isJsonField } = fieldValidators; + +export const documentsSchema: FormSchema = { + documents: { + label: i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsFieldLabel', + { + defaultMessage: 'Documents', + } + ), + helpText: ( + + {JSON.stringify([ + { + _index: 'index', + _id: 'id', + _source: { + foo: 'bar', + }, + }, + ])} + + ), + }} + /> + ), + serializer: parseJson, + deserializer: stringifyJson, + validations: [ + { + validator: emptyField( + i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsForm.noDocumentsError', + { + defaultMessage: 'Documents are required.', + } + ) + ), + }, + { + validator: isJsonField( + i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsForm.documentsJsonError', + { + defaultMessage: 'The documents JSON is not valid.', + } + ) + ), + }, + { + validator: ({ value }: ValidationFuncArg) => { + const parsedJSON = JSON.parse(value); + + if (!parsedJSON.length) { + return { + message: i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsForm.oneDocumentRequiredError', + { + defaultMessage: 'At least one document is required.', + } + ), + }; + } + }, + }, + ], + }, +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/tab_documents.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/tab_documents.tsx new file mode 100644 index 00000000000000..97bf03dbdc068f --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/tab_documents.tsx @@ -0,0 +1,152 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; + +import { EuiSpacer, EuiText, EuiButton, EuiHorizontalRule, EuiLink } from '@elastic/eui'; + +import { + getUseField, + Field, + JsonEditorField, + Form, + useForm, + FormConfig, + useKibana, +} from '../../../../../shared_imports'; + +import { documentsSchema } from './schema'; +import { useTestConfigContext, TestConfig } from '../../test_config_context'; + +const UseField = getUseField({ component: Field }); + +interface Props { + handleExecute: (documents: object[], verbose: boolean) => void; + isPipelineValid: boolean; + isExecuting: boolean; +} + +export const DocumentsTab: React.FunctionComponent = ({ + isPipelineValid, + handleExecute, + isExecuting, +}) => { + const { services } = useKibana(); + + const { setCurrentTestConfig, testConfig } = useTestConfigContext(); + const { verbose: cachedVerbose, documents: cachedDocuments } = testConfig; + + const executePipeline: FormConfig['onSubmit'] = (formData, isValid) => { + if (!isValid || !isPipelineValid) { + return; + } + + const { documents } = formData as TestConfig; + + // Update context + setCurrentTestConfig({ + ...testConfig, + documents, + }); + + handleExecute(documents!, cachedVerbose); + }; + + const { form } = useForm({ + schema: documentsSchema, + defaultValue: { + documents: cachedDocuments || '', + verbose: cachedVerbose || false, + }, + onSubmit: executePipeline, + }); + + return ( + <> + +

+ + {i18n.translate( + 'xpack.ingestPipelines.testPipelineFlyout.documentsTab.simulateDocumentionLink', + { + defaultMessage: 'Learn more.', + } + )} + + ), + }} + /> +

+
+ + + +
+ {/* Documents editor */} + + + + + +

+ +

+
+ + + + + {isExecuting ? ( + + ) : ( + + )} + + + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/tab_output.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/tab_output.tsx new file mode 100644 index 00000000000000..aa80f8c86ad8bf --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/pipeline_test_flyout/tabs/tab_output.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiCodeBlock, + EuiSpacer, + EuiText, + EuiSwitch, + EuiLink, + EuiIcon, + EuiLoadingSpinner, + EuiIconTip, +} from '@elastic/eui'; +import { useTestConfigContext } from '../../test_config_context'; + +interface Props { + executeOutput?: { docs: object[] }; + handleExecute: (documents: object[], verbose: boolean) => void; + isExecuting: boolean; +} + +export const OutputTab: React.FunctionComponent = ({ + executeOutput, + handleExecute, + isExecuting, +}) => { + const { setCurrentTestConfig, testConfig } = useTestConfigContext(); + const { verbose: cachedVerbose, documents: cachedDocuments } = testConfig; + + const onEnableVerbose = (isVerboseEnabled: boolean) => { + setCurrentTestConfig({ + ...testConfig, + verbose: isVerboseEnabled, + }); + + handleExecute(cachedDocuments!, isVerboseEnabled); + }; + + let content: React.ReactNode | undefined; + + if (isExecuting) { + content = ; + } else if (executeOutput) { + content = ( + + {JSON.stringify(executeOutput, null, 2)} + + ); + } + + return ( + <> + +

+ handleExecute(cachedDocuments!, cachedVerbose)}> + {' '} + + + ), + }} + /> +

+
+ + + + + {' '} + + } + /> + + } + checked={cachedVerbose} + onChange={e => onEnableVerbose(e.target.checked)} + /> + + + + {content} + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/schema.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/schema.tsx new file mode 100644 index 00000000000000..2e2689f41527a0 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/schema.tsx @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiCode } from '@elastic/eui'; + +import { FormSchema, FIELD_TYPES, fieldValidators, fieldFormatters } from '../../../shared_imports'; +import { parseJson, stringifyJson } from '../../lib'; + +const { emptyField, isJsonField } = fieldValidators; +const { toInt } = fieldFormatters; + +export const pipelineFormSchema: FormSchema = { + name: { + type: FIELD_TYPES.TEXT, + label: i18n.translate('xpack.ingestPipelines.form.nameFieldLabel', { + defaultMessage: 'Name', + }), + validations: [ + { + validator: emptyField( + i18n.translate('xpack.ingestPipelines.form.pipelineNameRequiredError', { + defaultMessage: 'A pipeline name is required.', + }) + ), + }, + ], + }, + description: { + type: FIELD_TYPES.TEXTAREA, + label: i18n.translate('xpack.ingestPipelines.form.descriptionFieldLabel', { + defaultMessage: 'Description', + }), + validations: [ + { + validator: emptyField( + i18n.translate('xpack.ingestPipelines.form.pipelineDescriptionRequiredError', { + defaultMessage: 'A pipeline description is required.', + }) + ), + }, + ], + }, + processors: { + label: i18n.translate('xpack.ingestPipelines.form.processorsFieldLabel', { + defaultMessage: 'Processors', + }), + helpText: ( + + {JSON.stringify([ + { + set: { + field: 'foo', + value: 'bar', + }, + }, + ])} + + ), + }} + /> + ), + serializer: parseJson, + deserializer: stringifyJson, + validations: [ + { + validator: emptyField( + i18n.translate('xpack.ingestPipelines.form.processorsRequiredError', { + defaultMessage: 'Processors are required.', + }) + ), + }, + { + validator: isJsonField( + i18n.translate('xpack.ingestPipelines.form.processorsJsonError', { + defaultMessage: 'The processors JSON is not valid.', + }) + ), + }, + ], + }, + on_failure: { + label: i18n.translate('xpack.ingestPipelines.form.onFailureFieldLabel', { + defaultMessage: 'On-failure processors (optional)', + }), + helpText: ( + + {JSON.stringify([ + { + set: { + field: '_index', + value: 'failed-{{ _index }}', + }, + }, + ])} + + ), + }} + /> + ), + serializer: value => { + const result = parseJson(value); + // If an empty array was passed, strip out this value entirely. + if (!result.length) { + return undefined; + } + return result; + }, + deserializer: stringifyJson, + validations: [ + { + validator: validationArg => { + if (!validationArg.value) { + return; + } + return isJsonField( + i18n.translate('xpack.ingestPipelines.form.onFailureProcessorsJsonError', { + defaultMessage: 'The on-failure processors JSON is not valid.', + }) + )(validationArg); + }, + }, + ], + }, + version: { + type: FIELD_TYPES.NUMBER, + label: i18n.translate('xpack.ingestPipelines.form.versionFieldLabel', { + defaultMessage: 'Version (optional)', + }), + formatters: [toInt], + }, +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/test_config_context.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/test_config_context.tsx new file mode 100644 index 00000000000000..6840ebef287966 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_form/test_config_context.tsx @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState, useCallback, useContext } from 'react'; + +export interface TestConfig { + documents?: object[] | undefined; + verbose: boolean; +} + +interface TestConfigContext { + testConfig: TestConfig; + setCurrentTestConfig: (config: TestConfig) => void; +} + +const TEST_CONFIG_DEFAULT_VALUE = { + testConfig: { + verbose: false, + }, + setCurrentTestConfig: () => {}, +}; + +const TestConfigContext = React.createContext(TEST_CONFIG_DEFAULT_VALUE); + +export const useTestConfigContext = () => { + const ctx = useContext(TestConfigContext); + if (!ctx) { + throw new Error( + '"useTestConfigContext" can only be called inside of TestConfigContext.Provider!' + ); + } + return ctx; +}; + +export const TestConfigContextProvider = ({ children }: { children: React.ReactNode }) => { + const [testConfig, setTestConfig] = useState({ + verbose: false, + }); + + const setCurrentTestConfig = useCallback((currentTestConfig: TestConfig): void => { + setTestConfig(currentTestConfig); + }, []); + + return ( + + {children} + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/constants/index.ts b/x-pack/plugins/ingest_pipelines/public/application/constants/index.ts new file mode 100644 index 00000000000000..776d44c8256703 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/constants/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// UI metric constants +export const UIM_APP_NAME = 'ingest_pipelines'; +export const UIM_PIPELINES_LIST_LOAD = 'pipelines_list_load'; +export const UIM_PIPELINE_CREATE = 'pipeline_create'; +export const UIM_PIPELINE_UPDATE = 'pipeline_update'; +export const UIM_PIPELINE_DELETE = 'pipeline_delete'; +export const UIM_PIPELINE_DELETE_MANY = 'pipeline_delete_many'; +export const UIM_PIPELINE_SIMULATE = 'pipeline_simulate'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/index.tsx b/x-pack/plugins/ingest_pipelines/public/application/index.tsx new file mode 100644 index 00000000000000..e43dba4689b443 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/index.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { HttpSetup } from 'kibana/public'; +import React, { ReactNode } from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { NotificationsSetup } from 'kibana/public'; +import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; + +import { API_BASE_PATH } from '../../common/constants'; + +import { AuthorizationProvider } from '../shared_imports'; + +import { App } from './app'; +import { DocumentationService, UiMetricService, ApiService, BreadcrumbService } from './services'; + +export interface AppServices { + breadcrumbs: BreadcrumbService; + metric: UiMetricService; + documentation: DocumentationService; + api: ApiService; + notifications: NotificationsSetup; +} + +export interface CoreServices { + http: HttpSetup; +} + +export const renderApp = ( + element: HTMLElement, + I18nContext: ({ children }: { children: ReactNode }) => JSX.Element, + services: AppServices, + coreServices: CoreServices +) => { + render( + + + + + + + , + element + ); + + return () => { + unmountComponentAtNode(element); + }; +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/lib/index.ts b/x-pack/plugins/ingest_pipelines/public/application/lib/index.ts new file mode 100644 index 00000000000000..1283033267a50e --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/lib/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { stringifyJson, parseJson } from './utils'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/lib/utils.test.ts b/x-pack/plugins/ingest_pipelines/public/application/lib/utils.test.ts new file mode 100644 index 00000000000000..e7eff3bd6ca33d --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/lib/utils.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { stringifyJson, parseJson } from './utils'; + +describe('utils', () => { + describe('stringifyJson()', () => { + it('should stringify a valid JSON array', () => { + expect(stringifyJson([1, 2, 3])).toEqual(`[ + 1, + 2, + 3 +]`); + }); + + it('should return a stringified empty array if the value is not a valid JSON array', () => { + expect(stringifyJson({})).toEqual('[\n\n]'); + }); + }); + + describe('parseJson()', () => { + it('should parse a valid JSON string', () => { + expect(parseJson('[1,2,3]')).toEqual([1, 2, 3]); + expect(parseJson('[{"foo": "bar"}]')).toEqual([{ foo: 'bar' }]); + }); + + it('should convert valid JSON that is not an array to an array', () => { + expect(parseJson('{"foo": "bar"}')).toEqual([{ foo: 'bar' }]); + }); + + it('should return an empty array if invalid JSON string', () => { + expect(parseJson('{invalidJsonString}')).toEqual([]); + }); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/lib/utils.ts b/x-pack/plugins/ingest_pipelines/public/application/lib/utils.ts new file mode 100644 index 00000000000000..fe4e9e65f4b9a5 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/lib/utils.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; + * you may not use this file except in compliance with the Elastic License. + */ + +export const stringifyJson = (json: any): string => + Array.isArray(json) ? JSON.stringify(json, null, 2) : '[\n\n]'; + +export const parseJson = (jsonString: string): object[] => { + let parsedJSON: any; + + try { + parsedJSON = JSON.parse(jsonString); + + if (!Array.isArray(parsedJSON)) { + // Convert object to array + parsedJSON = [parsedJSON]; + } + } catch { + parsedJSON = []; + } + + return parsedJSON; +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/mount_management_section.ts b/x-pack/plugins/ingest_pipelines/public/application/mount_management_section.ts new file mode 100644 index 00000000000000..e36f27cbf5f62f --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/mount_management_section.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { CoreSetup } from 'src/core/public'; +import { ManagementAppMountParams } from 'src/plugins/management/public'; + +import { documentationService, uiMetricService, apiService, breadcrumbService } from './services'; +import { renderApp } from '.'; + +export async function mountManagementSection( + { http, getStartServices, notifications }: CoreSetup, + params: ManagementAppMountParams +) { + const { element, setBreadcrumbs } = params; + const [coreStart] = await getStartServices(); + const { + docLinks, + i18n: { Context: I18nContext }, + } = coreStart; + + documentationService.setup(docLinks); + breadcrumbService.setup(setBreadcrumbs); + + const services = { + breadcrumbs: breadcrumbService, + metric: uiMetricService, + documentation: documentationService, + api: apiService, + notifications, + }; + + return renderApp(element, I18nContext, services, { http }); +} diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/index.ts b/x-pack/plugins/ingest_pipelines/public/application/sections/index.ts new file mode 100644 index 00000000000000..b2925666c57684 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/index.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; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PipelinesList } from './pipelines_list'; + +export { PipelinesCreate } from './pipelines_create'; + +export { PipelinesEdit } from './pipelines_edit'; + +export { PipelinesClone } from './pipelines_clone'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/index.ts b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/index.ts new file mode 100644 index 00000000000000..614a3598d407db --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PipelinesClone } from './pipelines_clone'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx new file mode 100644 index 00000000000000..b3b1217caf8347 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_clone/pipelines_clone.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent, useEffect } from 'react'; +import { RouteComponentProps } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { SectionLoading, useKibana } from '../../../shared_imports'; + +import { PipelinesCreate } from '../pipelines_create'; + +export interface ParamProps { + sourceName: string; +} + +/** + * This section is a wrapper around the create section where we receive a pipeline name + * to load and set as the source pipeline for the {@link PipelinesCreate} form. + */ +export const PipelinesClone: FunctionComponent> = props => { + const { sourceName } = props.match.params; + const { services } = useKibana(); + + const { error, data: pipeline, isLoading, isInitialRequest } = services.api.useLoadPipeline( + decodeURIComponent(sourceName) + ); + + useEffect(() => { + if (error && !isLoading) { + services.notifications!.toasts.addError(error, { + title: i18n.translate('xpack.ingestPipelines.clone.loadSourcePipelineErrorTitle', { + defaultMessage: 'Cannot load {name}.', + values: { name: sourceName }, + }), + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [error, isLoading]); + + if (isLoading && isInitialRequest) { + return ( + + + + ); + } else { + // We still show the create form even if we were not able to load the + // latest pipeline data. + const sourcePipeline = pipeline ? { ...pipeline, name: `${pipeline.name}-copy` } : undefined; + return ; + } +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/index.ts b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/index.ts new file mode 100644 index 00000000000000..374defa8699162 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PipelinesCreate } from './pipelines_create'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx new file mode 100644 index 00000000000000..34a362d596d92b --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_create/pipelines_create.tsx @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useState, useEffect } from 'react'; +import { RouteComponentProps } from 'react-router-dom'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiPageBody, + EuiPageContent, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiSpacer, +} from '@elastic/eui'; + +import { BASE_PATH } from '../../../../common/constants'; +import { Pipeline } from '../../../../common/types'; +import { useKibana } from '../../../shared_imports'; +import { PipelineForm } from '../../components'; + +interface Props { + /** + * This value may be passed in to prepopulate the creation form + */ + sourcePipeline?: Pipeline; +} + +export const PipelinesCreate: React.FunctionComponent = ({ + history, + sourcePipeline, +}) => { + const { services } = useKibana(); + + const [isSaving, setIsSaving] = useState(false); + const [saveError, setSaveError] = useState(null); + + const onSave = async (pipeline: Pipeline) => { + setIsSaving(true); + setSaveError(null); + + const { error } = await services.api.createPipeline(pipeline); + + setIsSaving(false); + + if (error) { + setSaveError(error); + return; + } + + history.push(BASE_PATH + `?pipeline=${pipeline.name}`); + }; + + const onCancel = () => { + history.push(BASE_PATH); + }; + + useEffect(() => { + services.breadcrumbs.setBreadcrumbs('create'); + }, [services]); + + return ( + + + + + + +

+ +

+
+
+ + + + + + +
+
+ + + + +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/index.ts b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/index.ts new file mode 100644 index 00000000000000..26458d23fd6d82 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PipelinesEdit } from './pipelines_edit'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.tsx new file mode 100644 index 00000000000000..99cd8d7eef97bf --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_edit/pipelines_edit.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; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useState, useEffect } from 'react'; +import { RouteComponentProps } from 'react-router-dom'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiPageBody, + EuiPageContent, + EuiSpacer, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, +} from '@elastic/eui'; + +import { EuiCallOut } from '@elastic/eui'; +import { BASE_PATH } from '../../../../common/constants'; +import { Pipeline } from '../../../../common/types'; +import { useKibana, SectionLoading } from '../../../shared_imports'; +import { PipelineForm } from '../../components'; + +interface MatchParams { + name: string; +} + +export const PipelinesEdit: React.FunctionComponent> = ({ + match: { + params: { name }, + }, + history, +}) => { + const { services } = useKibana(); + + const [isSaving, setIsSaving] = useState(false); + const [saveError, setSaveError] = useState(null); + + const decodedPipelineName = decodeURI(decodeURIComponent(name)); + + const { error, data: pipeline, isLoading } = services.api.useLoadPipeline(decodedPipelineName); + + const onSave = async (updatedPipeline: Pipeline) => { + setIsSaving(true); + setSaveError(null); + + const { error: savePipelineError } = await services.api.updatePipeline(updatedPipeline); + + setIsSaving(false); + + if (savePipelineError) { + setSaveError(savePipelineError); + return; + } + + history.push(BASE_PATH + `?pipeline=${updatedPipeline.name}`); + }; + + const onCancel = () => { + history.push(BASE_PATH); + }; + + useEffect(() => { + services.breadcrumbs.setBreadcrumbs('edit'); + }, [services.breadcrumbs]); + + let content: React.ReactNode; + + if (isLoading) { + content = ( + + + + ); + } else if (error) { + content = ( + <> + + } + color="danger" + iconType="alert" + data-test-subj="fetchPipelineError" + > +

{error.message}

+
+ + + ); + } else if (pipeline) { + content = ( + + ); + } + + return ( + + + + + + +

+ +

+
+
+ + + + + + +
+
+ + + + {content} +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/delete_modal.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/delete_modal.tsx new file mode 100644 index 00000000000000..c7736a6c19ba1e --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/delete_modal.tsx @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { useKibana } from '../../../shared_imports'; + +export const PipelineDeleteModal = ({ + pipelinesToDelete, + callback, +}: { + pipelinesToDelete: string[]; + callback: (data?: { hasDeletedPipelines: boolean }) => void; +}) => { + const { services } = useKibana(); + + const numPipelinesToDelete = pipelinesToDelete.length; + + const handleDeletePipelines = () => { + services.api + .deletePipelines(pipelinesToDelete) + .then(({ data: { itemsDeleted, errors }, error }) => { + const hasDeletedPipelines = itemsDeleted && itemsDeleted.length; + + if (hasDeletedPipelines) { + const successMessage = + itemsDeleted.length === 1 + ? i18n.translate( + 'xpack.ingestPipelines.deleteModal.successDeleteSingleNotificationMessageText', + { + defaultMessage: "Deleted pipeline '{pipelineName}'", + values: { pipelineName: pipelinesToDelete[0] }, + } + ) + : i18n.translate( + 'xpack.ingestPipelines.deleteModal.successDeleteMultipleNotificationMessageText', + { + defaultMessage: + 'Deleted {numSuccesses, plural, one {# pipeline} other {# pipelines}}', + values: { numSuccesses: itemsDeleted.length }, + } + ); + + callback({ hasDeletedPipelines }); + services.notifications.toasts.addSuccess(successMessage); + } + + if (error || errors?.length) { + const hasMultipleErrors = errors?.length > 1 || (error && pipelinesToDelete.length > 1); + const errorMessage = hasMultipleErrors + ? i18n.translate( + 'xpack.ingestPipelines.deleteModal.multipleErrorsNotificationMessageText', + { + defaultMessage: 'Error deleting {count} pipelines', + values: { + count: errors?.length || pipelinesToDelete.length, + }, + } + ) + : i18n.translate('xpack.ingestPipelines.deleteModal.errorNotificationMessageText', { + defaultMessage: "Error deleting pipeline '{name}'", + values: { name: (errors && errors[0].name) || pipelinesToDelete[0] }, + }); + services.notifications.toasts.addDanger(errorMessage); + } + }); + }; + + const handleOnCancel = () => { + callback(); + }; + + return ( + + + } + onCancel={handleOnCancel} + onConfirm={handleDeletePipelines} + cancelButtonText={ + + } + confirmButtonText={ + + } + > + <> +

+ +

+ +
    + {pipelinesToDelete.map(name => ( +
  • {name}
  • + ))} +
+ +
+
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/details_flyout.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/details_flyout.tsx new file mode 100644 index 00000000000000..98243a5149c0df --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/details_flyout.tsx @@ -0,0 +1,210 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiTitle, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiIcon, + EuiPopover, + EuiContextMenu, + EuiButton, +} from '@elastic/eui'; + +import { Pipeline } from '../../../../common/types'; + +import { PipelineDetailsJsonBlock } from './details_json_block'; + +export interface Props { + pipeline: Pipeline; + onEditClick: (pipelineName: string) => void; + onCloneClick: (pipelineName: string) => void; + onDeleteClick: (pipelineName: string[]) => void; + onClose: () => void; +} + +export const PipelineDetailsFlyout: FunctionComponent = ({ + pipeline, + onClose, + onEditClick, + onCloneClick, + onDeleteClick, +}) => { + const [showPopover, setShowPopover] = useState(false); + const actionMenuItems = [ + /** + * Edit pipeline + */ + { + name: i18n.translate('xpack.ingestPipelines.list.pipelineDetails.editActionLabel', { + defaultMessage: 'Edit', + }), + icon: , + onClick: () => onEditClick(pipeline.name), + }, + /** + * Clone pipeline + */ + { + name: i18n.translate('xpack.ingestPipelines.list.pipelineDetails.cloneActionLabel', { + defaultMessage: 'Clone', + }), + icon: , + onClick: () => onCloneClick(pipeline.name), + }, + /** + * Delete pipeline + */ + { + name: i18n.translate('xpack.ingestPipelines.list.pipelineDetails.deleteActionLabel', { + defaultMessage: 'Delete', + }), + icon: , + onClick: () => onDeleteClick([pipeline.name]), + }, + ]; + + const managePipelineButton = ( + setShowPopover(previousBool => !previousBool)} + iconType="arrowUp" + iconSide="right" + fill + > + {i18n.translate('xpack.ingestPipelines.list.pipelineDetails.managePipelineButtonLabel', { + defaultMessage: 'Manage', + })} + + ); + + return ( + + + +

{pipeline.name}

+
+
+ + + + {/* Pipeline description */} + + {i18n.translate('xpack.ingestPipelines.list.pipelineDetails.descriptionTitle', { + defaultMessage: 'Description', + })} + + + {pipeline.description ?? ''} + + + {/* Pipeline version */} + {pipeline.version && ( + <> + + {i18n.translate('xpack.ingestPipelines.list.pipelineDetails.versionTitle', { + defaultMessage: 'Version', + })} + + + {String(pipeline.version)} + + + )} + + {/* Processors JSON */} + + {i18n.translate('xpack.ingestPipelines.list.pipelineDetails.processorsTitle', { + defaultMessage: 'Processors JSON', + })} + + + + + + {/* On Failure Processor JSON */} + {pipeline.on_failure?.length && ( + <> + + {i18n.translate( + 'xpack.ingestPipelines.list.pipelineDetails.failureProcessorsTitle', + { + defaultMessage: 'On failure processors JSON', + } + )} + + + + + + )} + + + + + + + + {i18n.translate('xpack.ingestPipelines.list.pipelineDetails.closeButtonLabel', { + defaultMessage: 'Close', + })} + + + + + setShowPopover(false)} + button={managePipelineButton} + panelPaddingSize="none" + withTitle + repositionOnScroll + > + + + + + + +
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/details_json_block.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/details_json_block.tsx new file mode 100644 index 00000000000000..6c44336c7547d4 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/details_json_block.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent, useRef } from 'react'; +import { EuiCodeBlock } from '@elastic/eui'; + +export interface Props { + json: Record; +} + +export const PipelineDetailsJsonBlock: FunctionComponent = ({ json }) => { + // Hack so copied-to-clipboard value updates as content changes + // Related issue: https://github.com/elastic/eui/issues/3321 + const uuid = useRef(0); + uuid.current++; + + return ( + 0 ? 300 : undefined} + isCopyable + key={uuid.current} + > + {JSON.stringify(json, null, 2)} + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/empty_list.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/empty_list.tsx new file mode 100644 index 00000000000000..ef64fb33a6a55d --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/empty_list.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { FunctionComponent } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; +import { BASE_PATH } from '../../../../common/constants'; + +export const EmptyList: FunctionComponent = () => ( + + {i18n.translate('xpack.ingestPipelines.list.table.emptyPromptTitle', { + defaultMessage: 'Start by creating a pipeline', + })} + + } + actions={ + + {i18n.translate('xpack.ingestPipelines.list.table.emptyPrompt.createButtonLabel', { + defaultMessage: 'Create a pipeline', + })} + + } + /> +); diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/index.ts b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/index.ts new file mode 100644 index 00000000000000..a541e3bb85fd07 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { PipelinesList } from './main'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx new file mode 100644 index 00000000000000..c90ac2714a95a2 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/main.tsx @@ -0,0 +1,207 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect, useState } from 'react'; +import { RouteComponentProps } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Location } from 'history'; +import { parse } from 'query-string'; + +import { + EuiPageBody, + EuiPageContent, + EuiTitle, + EuiFlexGroup, + EuiFlexItem, + EuiButtonEmpty, + EuiCallOut, +} from '@elastic/eui'; + +import { EuiSpacer, EuiText } from '@elastic/eui'; + +import { Pipeline } from '../../../../common/types'; +import { BASE_PATH } from '../../../../common/constants'; +import { useKibana, SectionLoading } from '../../../shared_imports'; +import { UIM_PIPELINES_LIST_LOAD } from '../../constants'; + +import { EmptyList } from './empty_list'; +import { PipelineTable } from './table'; +import { PipelineDetailsFlyout } from './details_flyout'; +import { PipelineNotFoundFlyout } from './not_found_flyout'; +import { PipelineDeleteModal } from './delete_modal'; + +const getPipelineNameFromLocation = (location: Location) => { + const { pipeline } = parse(location.search.substring(1)); + return pipeline; +}; + +export const PipelinesList: React.FunctionComponent = ({ + history, + location, +}) => { + const { services } = useKibana(); + const pipelineNameFromLocation = getPipelineNameFromLocation(location); + + const [selectedPipeline, setSelectedPipeline] = useState(undefined); + const [showFlyout, setShowFlyout] = useState(false); + + const [pipelinesToDelete, setPipelinesToDelete] = useState([]); + + const { data, isLoading, error, sendRequest } = services.api.useLoadPipelines(); + + // Track component loaded + useEffect(() => { + services.metric.trackUiMetric(UIM_PIPELINES_LIST_LOAD); + services.breadcrumbs.setBreadcrumbs('home'); + }, [services.metric, services.breadcrumbs]); + + useEffect(() => { + if (pipelineNameFromLocation && data?.length) { + const pipeline = data.find(p => p.name === pipelineNameFromLocation); + setSelectedPipeline(pipeline); + setShowFlyout(true); + } + }, [pipelineNameFromLocation, data]); + + const goToEditPipeline = (name: string) => { + history.push(`${BASE_PATH}/edit/${encodeURIComponent(name)}`); + }; + + const goToClonePipeline = (name: string) => { + history.push(`${BASE_PATH}/create/${encodeURIComponent(name)}`); + }; + + const goHome = () => { + setShowFlyout(false); + history.push(BASE_PATH); + }; + + let content: React.ReactNode; + + if (isLoading) { + content = ( + + + + ); + } else if (data?.length) { + content = ( + + ); + } else { + content = ; + } + + const renderFlyout = (): React.ReactNode => { + if (!showFlyout) { + return; + } + if (selectedPipeline) { + return ( + { + setSelectedPipeline(undefined); + goHome(); + }} + onEditClick={goToEditPipeline} + onCloneClick={goToClonePipeline} + onDeleteClick={setPipelinesToDelete} + /> + ); + } else { + // Somehow we triggered show pipeline details, but do not have a pipeline. + // We assume not found. + return ( + { + goHome(); + }} + pipelineName={pipelineNameFromLocation} + /> + ); + } + }; + + return ( + <> + + + + + +

+ +

+
+ + + + + +
+
+ + + + + + + + {/* Error call out for pipeline table */} + {error ? ( + + ) : ( + content + )} +
+
+ {renderFlyout()} + {pipelinesToDelete?.length > 0 ? ( + { + if (deleteResponse?.hasDeletedPipelines) { + // reload pipelines list + sendRequest(); + } + setPipelinesToDelete([]); + setSelectedPipeline(undefined); + }} + pipelinesToDelete={pipelinesToDelete} + /> + ) : null} + + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/not_found_flyout.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/not_found_flyout.tsx new file mode 100644 index 00000000000000..b967e54187ced1 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/not_found_flyout.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FunctionComponent } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFlyout, EuiFlyoutBody, EuiCallOut } from '@elastic/eui'; +import { EuiFlyoutHeader, EuiTitle } from '@elastic/eui'; + +interface Props { + onClose: () => void; + pipelineName: string | string[] | null | undefined; +} + +export const PipelineNotFoundFlyout: FunctionComponent = ({ onClose, pipelineName }) => { + return ( + + + {pipelineName && ( + +

{pipelineName}

+
+ )} +
+ + + + } + color="danger" + iconType="alert" + /> + +
+ ); +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx new file mode 100644 index 00000000000000..c93285289ff39a --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/sections/pipelines_list/table.tsx @@ -0,0 +1,148 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { FunctionComponent, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiInMemoryTable, EuiLink, EuiButton, EuiInMemoryTableProps } from '@elastic/eui'; + +import { BASE_PATH } from '../../../../common/constants'; +import { Pipeline } from '../../../../common/types'; + +export interface Props { + pipelines: Pipeline[]; + onReloadClick: () => void; + onEditPipelineClick: (pipelineName: string) => void; + onClonePipelineClick: (pipelineName: string) => void; + onDeletePipelineClick: (pipelineName: string[]) => void; +} + +export const PipelineTable: FunctionComponent = ({ + pipelines, + onReloadClick, + onEditPipelineClick, + onClonePipelineClick, + onDeletePipelineClick, +}) => { + const [selection, setSelection] = useState([]); + + const tableProps: EuiInMemoryTableProps = { + itemId: 'name', + isSelectable: true, + sorting: { sort: { field: 'name', direction: 'asc' } }, + selection: { + onSelectionChange: setSelection, + }, + search: { + toolsLeft: + selection.length > 0 ? ( + onDeletePipelineClick(selection.map(pipeline => pipeline.name))} + color="danger" + > + + + ) : ( + undefined + ), + toolsRight: [ + + {i18n.translate('xpack.ingestPipelines.list.table.reloadButtonLabel', { + defaultMessage: 'Reload', + })} + , + + {i18n.translate('xpack.ingestPipelines.list.table.createPipelineButtonLabel', { + defaultMessage: 'Create a pipeline', + })} + , + ], + box: { + incremental: true, + }, + }, + pagination: { + initialPageSize: 10, + pageSizeOptions: [10, 20, 50], + }, + columns: [ + { + field: 'name', + name: i18n.translate('xpack.ingestPipelines.list.table.nameColumnTitle', { + defaultMessage: 'Name', + }), + sortable: true, + render: (name: string) => {name}, + }, + { + name: ( + + ), + actions: [ + { + isPrimary: true, + name: i18n.translate('xpack.ingestPipelines.list.table.editActionLabel', { + defaultMessage: 'Edit', + }), + description: i18n.translate('xpack.ingestPipelines.list.table.editActionDescription', { + defaultMessage: 'Edit this pipeline', + }), + type: 'icon', + icon: 'pencil', + onClick: ({ name }) => onEditPipelineClick(name), + }, + { + name: i18n.translate('xpack.ingestPipelines.list.table.cloneActionLabel', { + defaultMessage: 'Clone', + }), + description: i18n.translate('xpack.ingestPipelines.list.table.cloneActionDescription', { + defaultMessage: 'Clone this pipeline', + }), + type: 'icon', + icon: 'copy', + onClick: ({ name }) => onClonePipelineClick(name), + }, + { + isPrimary: true, + name: i18n.translate('xpack.ingestPipelines.list.table.deleteActionLabel', { + defaultMessage: 'Delete', + }), + description: i18n.translate( + 'xpack.ingestPipelines.list.table.deleteActionDescription', + { defaultMessage: 'Delete this pipeline' } + ), + type: 'icon', + icon: 'trash', + color: 'danger', + onClick: ({ name }) => onDeletePipelineClick([name]), + }, + ], + }, + ], + items: pipelines ?? [], + }; + + return ; +}; diff --git a/x-pack/plugins/ingest_pipelines/public/application/services/api.ts b/x-pack/plugins/ingest_pipelines/public/application/services/api.ts new file mode 100644 index 00000000000000..13eb96e78adae8 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/services/api.ts @@ -0,0 +1,125 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { HttpSetup } from 'src/core/public'; + +import { Pipeline } from '../../../common/types'; +import { API_BASE_PATH } from '../../../common/constants'; +import { + UseRequestConfig, + SendRequestConfig, + SendRequestResponse, + sendRequest as _sendRequest, + useRequest as _useRequest, +} from '../../shared_imports'; +import { UiMetricService } from './ui_metric'; +import { + UIM_PIPELINE_CREATE, + UIM_PIPELINE_UPDATE, + UIM_PIPELINE_DELETE, + UIM_PIPELINE_DELETE_MANY, + UIM_PIPELINE_SIMULATE, +} from '../constants'; + +export class ApiService { + private client: HttpSetup | undefined; + private uiMetricService: UiMetricService | undefined; + + private useRequest(config: UseRequestConfig) { + if (!this.client) { + throw new Error('Api service has not be initialized.'); + } + return _useRequest(this.client, config); + } + + private sendRequest( + config: SendRequestConfig + ): Promise> { + if (!this.client) { + throw new Error('Api service has not be initialized.'); + } + return _sendRequest(this.client, config); + } + + private trackUiMetric(eventName: string) { + if (!this.uiMetricService) { + throw new Error('UI metric service has not be initialized.'); + } + return this.uiMetricService.trackUiMetric(eventName); + } + + public setup(httpClient: HttpSetup, uiMetricService: UiMetricService): void { + this.client = httpClient; + this.uiMetricService = uiMetricService; + } + + public useLoadPipelines() { + return this.useRequest({ + path: API_BASE_PATH, + method: 'get', + }); + } + + public useLoadPipeline(name: string) { + return this.useRequest({ + path: `${API_BASE_PATH}/${encodeURIComponent(name)}`, + method: 'get', + }); + } + + public async createPipeline(pipeline: Pipeline) { + const result = await this.sendRequest({ + path: API_BASE_PATH, + method: 'post', + body: JSON.stringify(pipeline), + }); + + this.trackUiMetric(UIM_PIPELINE_CREATE); + + return result; + } + + public async updatePipeline(pipeline: Pipeline) { + const { name, ...body } = pipeline; + const result = await this.sendRequest({ + path: `${API_BASE_PATH}/${encodeURIComponent(name)}`, + method: 'put', + body: JSON.stringify(body), + }); + + this.trackUiMetric(UIM_PIPELINE_UPDATE); + + return result; + } + + public async deletePipelines(names: string[]) { + const result = this.sendRequest({ + path: `${API_BASE_PATH}/${names.map(name => encodeURIComponent(name)).join(',')}`, + method: 'delete', + }); + + this.trackUiMetric(names.length > 1 ? UIM_PIPELINE_DELETE_MANY : UIM_PIPELINE_DELETE); + + return result; + } + + public async simulatePipeline(testConfig: { + documents: object[]; + verbose?: boolean; + pipeline: Omit; + }) { + const result = await this.sendRequest({ + path: `${API_BASE_PATH}/simulate`, + method: 'post', + body: JSON.stringify(testConfig), + }); + + this.trackUiMetric(UIM_PIPELINE_SIMULATE); + + return result; + } +} + +export const apiService = new ApiService(); diff --git a/x-pack/plugins/ingest_pipelines/public/application/services/breadcrumbs.ts b/x-pack/plugins/ingest_pipelines/public/application/services/breadcrumbs.ts new file mode 100644 index 00000000000000..1ccdbbad9b1bbc --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/services/breadcrumbs.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; +import { BASE_PATH } from '../../../common/constants'; +import { ManagementAppMountParams } from '../../../../../../src/plugins/management/public'; + +type SetBreadcrumbs = ManagementAppMountParams['setBreadcrumbs']; + +const homeBreadcrumbText = i18n.translate('xpack.ingestPipelines.breadcrumb.pipelinesLabel', { + defaultMessage: 'Ingest Node Pipelines', +}); + +export class BreadcrumbService { + private breadcrumbs: { + [key: string]: Array<{ + text: string; + href?: string; + }>; + } = { + home: [ + { + text: homeBreadcrumbText, + }, + ], + create: [ + { + text: homeBreadcrumbText, + href: `#${BASE_PATH}`, + }, + { + text: i18n.translate('xpack.ingestPipelines.breadcrumb.createPipelineLabel', { + defaultMessage: 'Create pipeline', + }), + }, + ], + edit: [ + { + text: homeBreadcrumbText, + href: `#${BASE_PATH}`, + }, + { + text: i18n.translate('xpack.ingestPipelines.breadcrumb.editPipelineLabel', { + defaultMessage: 'Edit pipeline', + }), + }, + ], + }; + + private setBreadcrumbsHandler?: SetBreadcrumbs; + + public setup(setBreadcrumbsHandler: SetBreadcrumbs): void { + this.setBreadcrumbsHandler = setBreadcrumbsHandler; + } + + public setBreadcrumbs(type: 'create' | 'home' | 'edit'): void { + if (!this.setBreadcrumbsHandler) { + throw new Error('Breadcrumb service has not been initialized'); + } + + const newBreadcrumbs = this.breadcrumbs[type] + ? [...this.breadcrumbs[type]] + : [...this.breadcrumbs.home]; + + this.setBreadcrumbsHandler(newBreadcrumbs); + } +} + +export const breadcrumbService = new BreadcrumbService(); diff --git a/x-pack/plugins/ingest_pipelines/public/application/services/documentation.ts b/x-pack/plugins/ingest_pipelines/public/application/services/documentation.ts new file mode 100644 index 00000000000000..05fdc4b1dfb84c --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/services/documentation.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ +import { DocLinksStart } from 'src/core/public'; + +export class DocumentationService { + private esDocBasePath: string = ''; + + public setup(docLinks: DocLinksStart): void { + const { DOC_LINK_VERSION, ELASTIC_WEBSITE_URL } = docLinks; + const docsBase = `${ELASTIC_WEBSITE_URL}guide/en`; + + this.esDocBasePath = `${docsBase}/elasticsearch/reference/${DOC_LINK_VERSION}`; + } + + public getIngestNodeUrl() { + return `${this.esDocBasePath}/ingest.html`; + } + + public getProcessorsUrl() { + return `${this.esDocBasePath}/ingest-processors.html`; + } + + public getHandlingFailureUrl() { + return `${this.esDocBasePath}/handling-failure-in-pipelines.html`; + } + + public getPutPipelineApiUrl() { + return `${this.esDocBasePath}/put-pipeline-api.html`; + } + + public getSimulatePipelineApiUrl() { + return `${this.esDocBasePath}/simulate-pipeline-api.html`; + } +} + +export const documentationService = new DocumentationService(); diff --git a/x-pack/plugins/ingest_pipelines/public/application/services/index.ts b/x-pack/plugins/ingest_pipelines/public/application/services/index.ts new file mode 100644 index 00000000000000..f03a7824f83642 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/services/index.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; + * you may not use this file except in compliance with the Elastic License. + */ + +export { documentationService, DocumentationService } from './documentation'; + +export { uiMetricService, UiMetricService } from './ui_metric'; + +export { apiService, ApiService } from './api'; + +export { breadcrumbService, BreadcrumbService } from './breadcrumbs'; diff --git a/x-pack/plugins/ingest_pipelines/public/application/services/ui_metric.ts b/x-pack/plugins/ingest_pipelines/public/application/services/ui_metric.ts new file mode 100644 index 00000000000000..f99bb9ba331d2e --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/services/ui_metric.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; + +import { UIM_APP_NAME } from '../constants'; + +export class UiMetricService { + private usageCollection: UsageCollectionSetup | undefined; + + public setup(usageCollection: UsageCollectionSetup) { + this.usageCollection = usageCollection; + } + + private track(name: string) { + if (!this.usageCollection) { + // Usage collection is an optional plugin and might be disabled + return; + } + + const { reportUiStats, METRIC_TYPE } = this.usageCollection; + reportUiStats(UIM_APP_NAME, METRIC_TYPE.COUNT, name); + } + + public trackUiMetric(eventName: string) { + return this.track(eventName); + } +} + +export const uiMetricService = new UiMetricService(); diff --git a/x-pack/plugins/ingest_pipelines/public/index.ts b/x-pack/plugins/ingest_pipelines/public/index.ts new file mode 100644 index 00000000000000..72479737038041 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/index.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IngestPipelinesPlugin } from './plugin'; + +export function plugin() { + return new IngestPipelinesPlugin(); +} diff --git a/x-pack/plugins/ingest_pipelines/public/plugin.ts b/x-pack/plugins/ingest_pipelines/public/plugin.ts new file mode 100644 index 00000000000000..e9f5fd6c7f57c6 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/plugin.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { CoreSetup, Plugin } from 'src/core/public'; + +import { PLUGIN_ID } from '../common/constants'; +import { uiMetricService, apiService } from './application/services'; +import { Dependencies } from './types'; + +export class IngestPipelinesPlugin implements Plugin { + public setup(coreSetup: CoreSetup, plugins: Dependencies): void { + const { management, usageCollection } = plugins; + const { http } = coreSetup; + + // Initialize services + uiMetricService.setup(usageCollection); + apiService.setup(http, uiMetricService); + + management.sections.getSection('elasticsearch')!.registerApp({ + id: PLUGIN_ID, + title: i18n.translate('xpack.ingestPipelines.appTitle', { + defaultMessage: 'Ingest Node Pipelines', + }), + mount: async params => { + const { mountManagementSection } = await import('./application/mount_management_section'); + + return await mountManagementSection(coreSetup, params); + }, + }); + } + + public start() {} + + public stop() {} +} diff --git a/x-pack/plugins/ingest_pipelines/public/shared_imports.ts b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts new file mode 100644 index 00000000000000..cfa946ff942ecd --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/shared_imports.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { useKibana as _useKibana } from '../../../../src/plugins/kibana_react/public'; +import { AppServices } from './application'; + +export { + SendRequestConfig, + SendRequestResponse, + UseRequestConfig, + sendRequest, + useRequest, +} from '../../../../src/plugins/es_ui_shared/public/request/np_ready_request'; + +export { + FormSchema, + FIELD_TYPES, + FormConfig, + useForm, + Form, + getUseField, + ValidationFuncArg, + useFormContext, +} from '../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; + +export { + fieldFormatters, + fieldValidators, +} from '../../../../src/plugins/es_ui_shared/static/forms/helpers'; + +export { + getFormRow, + Field, + JsonEditorField, +} from '../../../../src/plugins/es_ui_shared/static/forms/components'; + +export { + isJSON, + isEmptyString, +} from '../../../../src/plugins/es_ui_shared/static/validators/string'; + +export { + SectionLoading, + WithPrivileges, + AuthorizationProvider, + SectionError, + Error, + useAuthorizationContext, + NotAuthorizedSection, +} from '../../../../src/plugins/es_ui_shared/public'; + +export const useKibana = () => _useKibana(); diff --git a/x-pack/plugins/ingest_pipelines/public/types.ts b/x-pack/plugins/ingest_pipelines/public/types.ts new file mode 100644 index 00000000000000..91783ea04fa9a6 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/types.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ManagementSetup } from 'src/plugins/management/public'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; + +export interface Dependencies { + management: ManagementSetup; + usageCollection: UsageCollectionSetup; +} diff --git a/x-pack/plugins/ingest_pipelines/server/index.ts b/x-pack/plugins/ingest_pipelines/server/index.ts new file mode 100644 index 00000000000000..dc162a5d67cb67 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializerContext } from '../../../../src/core/server'; +import { IngestPipelinesPlugin } from './plugin'; + +export function plugin(initializerContext: PluginInitializerContext) { + return new IngestPipelinesPlugin(initializerContext); +} diff --git a/x-pack/plugins/ingest_pipelines/server/lib/index.ts b/x-pack/plugins/ingest_pipelines/server/lib/index.ts new file mode 100644 index 00000000000000..a9a3c61472d8c7 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/lib/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { isEsError } from './is_es_error'; diff --git a/x-pack/plugins/ingest_pipelines/server/lib/is_es_error.ts b/x-pack/plugins/ingest_pipelines/server/lib/is_es_error.ts new file mode 100644 index 00000000000000..4137293cf39c06 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/lib/is_es_error.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as legacyElasticsearch from 'elasticsearch'; + +const esErrorsParent = legacyElasticsearch.errors._Abstract; + +export function isEsError(err: Error) { + return err instanceof esErrorsParent; +} diff --git a/x-pack/plugins/ingest_pipelines/server/plugin.ts b/x-pack/plugins/ingest_pipelines/server/plugin.ts new file mode 100644 index 00000000000000..b27ca417c3e3cb --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/plugin.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; + +import { PluginInitializerContext, CoreSetup, Plugin, Logger } from 'kibana/server'; + +import { PLUGIN_ID, PLUGIN_MIN_LICENSE_TYPE } from '../common/constants'; + +import { License } from './services'; +import { ApiRoutes } from './routes'; +import { isEsError } from './lib'; +import { Dependencies } from './types'; + +export class IngestPipelinesPlugin implements Plugin { + private readonly logger: Logger; + private readonly license: License; + private readonly apiRoutes: ApiRoutes; + + constructor({ logger }: PluginInitializerContext) { + this.logger = logger.get(); + this.license = new License(); + this.apiRoutes = new ApiRoutes(); + } + + public setup({ http, elasticsearch }: CoreSetup, { licensing }: Dependencies) { + this.logger.debug('ingest_pipelines: setup'); + + const router = http.createRouter(); + + this.license.setup( + { + pluginId: PLUGIN_ID, + minimumLicenseType: PLUGIN_MIN_LICENSE_TYPE, + defaultErrorMessage: i18n.translate('xpack.ingestPipelines.licenseCheckErrorMessage', { + defaultMessage: 'License check failed', + }), + }, + { + licensing, + logger: this.logger, + } + ); + + this.apiRoutes.setup({ + router, + license: this.license, + lib: { + isEsError, + }, + }); + } + + public start() {} + + public stop() {} +} diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/create.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/create.ts new file mode 100644 index 00000000000000..63637eaac765d8 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/create.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; + +import { Pipeline } from '../../../common/types'; +import { API_BASE_PATH } from '../../../common/constants'; +import { RouteDependencies } from '../../types'; + +const bodySchema = schema.object({ + name: schema.string(), + description: schema.string(), + processors: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), + version: schema.maybe(schema.number()), + on_failure: schema.maybe(schema.arrayOf(schema.recordOf(schema.string(), schema.any()))), +}); + +export const registerCreateRoute = ({ + router, + license, + lib: { isEsError }, +}: RouteDependencies): void => { + router.post( + { + path: API_BASE_PATH, + validate: { + body: bodySchema, + }, + }, + license.guardApiRoute(async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient; + const pipeline = req.body as Pipeline; + + const { name, description, processors, version, on_failure } = pipeline; + + try { + // Check that a pipeline with the same name doesn't already exist + const pipelineByName = await callAsCurrentUser('ingest.getPipeline', { id: name }); + + if (pipelineByName[name]) { + return res.conflict({ + body: new Error( + i18n.translate('xpack.ingestPipelines.createRoute.duplicatePipelineIdErrorMessage', { + defaultMessage: "There is already a pipeline with name '{name}'.", + values: { + name, + }, + }) + ), + }); + } + } catch (e) { + // Silently swallow error + } + + try { + const response = await callAsCurrentUser('ingest.putPipeline', { + id: name, + body: { + description, + processors, + version, + on_failure, + }, + }); + + return res.ok({ body: response }); + } catch (error) { + if (isEsError(error)) { + return res.customError({ + statusCode: error.statusCode, + body: error, + }); + } + + return res.internalError({ body: error }); + } + }) + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/delete.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/delete.ts new file mode 100644 index 00000000000000..4664b49a08a501 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/delete.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { schema } from '@kbn/config-schema'; + +import { API_BASE_PATH } from '../../../common/constants'; +import { RouteDependencies } from '../../types'; + +const paramsSchema = schema.object({ + names: schema.string(), +}); + +export const registerDeleteRoute = ({ router, license }: RouteDependencies): void => { + router.delete( + { + path: `${API_BASE_PATH}/{names}`, + validate: { + params: paramsSchema, + }, + }, + license.guardApiRoute(async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient; + const { names } = req.params; + const pipelineNames = names.split(','); + + const response: { itemsDeleted: string[]; errors: any[] } = { + itemsDeleted: [], + errors: [], + }; + + await Promise.all( + pipelineNames.map(pipelineName => { + return callAsCurrentUser('ingest.deletePipeline', { id: pipelineName }) + .then(() => response.itemsDeleted.push(pipelineName)) + .catch(e => + response.errors.push({ + name: pipelineName, + error: e, + }) + ); + }) + ); + + return res.ok({ body: response }); + }) + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/get.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/get.ts new file mode 100644 index 00000000000000..ec922620142728 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/get.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { schema } from '@kbn/config-schema'; + +import { deserializePipelines } from '../../../common/lib'; +import { API_BASE_PATH } from '../../../common/constants'; +import { RouteDependencies } from '../../types'; + +const paramsSchema = schema.object({ + name: schema.string(), +}); + +export const registerGetRoutes = ({ + router, + license, + lib: { isEsError }, +}: RouteDependencies): void => { + // Get all pipelines + router.get( + { path: API_BASE_PATH, validate: false }, + license.guardApiRoute(async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient; + + try { + const pipelines = await callAsCurrentUser('ingest.getPipeline'); + + return res.ok({ body: deserializePipelines(pipelines) }); + } catch (error) { + if (isEsError(error)) { + // ES returns 404 when there are no pipelines + // Instead, we return an empty array and 200 status back to the client + if (error.status === 404) { + return res.ok({ body: [] }); + } + + return res.customError({ + statusCode: error.statusCode, + body: error, + }); + } + + return res.internalError({ body: error }); + } + }) + ); + + // Get single pipeline + router.get( + { + path: `${API_BASE_PATH}/{name}`, + validate: { + params: paramsSchema, + }, + }, + license.guardApiRoute(async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient; + const { name } = req.params; + + try { + const pipeline = await callAsCurrentUser('ingest.getPipeline', { id: name }); + + return res.ok({ + body: { + ...pipeline[name], + name, + }, + }); + } catch (error) { + if (isEsError(error)) { + return res.customError({ + statusCode: error.statusCode, + body: error, + }); + } + + return res.internalError({ body: error }); + } + }) + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/index.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/index.ts new file mode 100644 index 00000000000000..58a4bf56176593 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/index.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; + * you may not use this file except in compliance with the Elastic License. + */ + +export { registerGetRoutes } from './get'; + +export { registerCreateRoute } from './create'; + +export { registerUpdateRoute } from './update'; + +export { registerPrivilegesRoute } from './privileges'; + +export { registerDeleteRoute } from './delete'; + +export { registerSimulateRoute } from './simulate'; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/privileges.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/privileges.ts new file mode 100644 index 00000000000000..2e1c11928959f0 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/privileges.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { RouteDependencies } from '../../types'; +import { API_BASE_PATH, APP_CLUSTER_REQUIRED_PRIVILEGES } from '../../../common/constants'; +import { Privileges } from '../../../../../../src/plugins/es_ui_shared/public'; + +const extractMissingPrivileges = (privilegesObject: { [key: string]: boolean } = {}): string[] => + Object.keys(privilegesObject).reduce((privileges: string[], privilegeName: string): string[] => { + if (!privilegesObject[privilegeName]) { + privileges.push(privilegeName); + } + return privileges; + }, []); + +export const registerPrivilegesRoute = ({ license, router }: RouteDependencies) => { + router.get( + { + path: `${API_BASE_PATH}/privileges`, + validate: false, + }, + license.guardApiRoute(async (ctx, req, res) => { + const { + core: { + elasticsearch: { dataClient }, + }, + } = ctx; + + const privilegesResult: Privileges = { + hasAllPrivileges: true, + missingPrivileges: { + cluster: [], + }, + }; + + try { + const { has_all_requested: hasAllPrivileges, cluster } = await dataClient.callAsCurrentUser( + 'transport.request', + { + path: '/_security/user/_has_privileges', + method: 'POST', + body: { + cluster: APP_CLUSTER_REQUIRED_PRIVILEGES, + }, + } + ); + + if (!hasAllPrivileges) { + privilegesResult.missingPrivileges.cluster = extractMissingPrivileges(cluster); + } + + privilegesResult.hasAllPrivileges = hasAllPrivileges; + + return res.ok({ body: privilegesResult }); + } catch (e) { + return res.internalError(e); + } + }) + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/simulate.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/simulate.ts new file mode 100644 index 00000000000000..ca5fc78d118fdd --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/simulate.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { schema } from '@kbn/config-schema'; + +import { API_BASE_PATH } from '../../../common/constants'; +import { RouteDependencies } from '../../types'; + +const bodySchema = schema.object({ + pipeline: schema.object({ + description: schema.string(), + processors: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), + version: schema.maybe(schema.number()), + on_failure: schema.maybe(schema.arrayOf(schema.recordOf(schema.string(), schema.any()))), + }), + documents: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), + verbose: schema.maybe(schema.boolean()), +}); + +export const registerSimulateRoute = ({ + router, + license, + lib: { isEsError }, +}: RouteDependencies): void => { + router.post( + { + path: `${API_BASE_PATH}/simulate`, + validate: { + body: bodySchema, + }, + }, + license.guardApiRoute(async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient; + + const { pipeline, documents, verbose } = req.body; + + try { + const response = await callAsCurrentUser('ingest.simulate', { + verbose, + body: { + pipeline, + docs: documents, + }, + }); + + return res.ok({ body: response }); + } catch (error) { + if (isEsError(error)) { + return res.customError({ + statusCode: error.statusCode, + body: error, + }); + } + + return res.internalError({ body: error }); + } + }) + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/api/update.ts b/x-pack/plugins/ingest_pipelines/server/routes/api/update.ts new file mode 100644 index 00000000000000..a6fdee47f0ecff --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/routes/api/update.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { schema } from '@kbn/config-schema'; + +import { API_BASE_PATH } from '../../../common/constants'; +import { RouteDependencies } from '../../types'; + +const bodySchema = schema.object({ + description: schema.string(), + processors: schema.arrayOf(schema.recordOf(schema.string(), schema.any())), + version: schema.maybe(schema.number()), + on_failure: schema.maybe(schema.arrayOf(schema.recordOf(schema.string(), schema.any()))), +}); + +const paramsSchema = schema.object({ + name: schema.string(), +}); + +export const registerUpdateRoute = ({ + router, + license, + lib: { isEsError }, +}: RouteDependencies): void => { + router.put( + { + path: `${API_BASE_PATH}/{name}`, + validate: { + body: bodySchema, + params: paramsSchema, + }, + }, + license.guardApiRoute(async (ctx, req, res) => { + const { callAsCurrentUser } = ctx.core.elasticsearch.dataClient; + const { name } = req.params; + const { description, processors, version, on_failure } = req.body; + + try { + // Verify pipeline exists; ES will throw 404 if it doesn't + await callAsCurrentUser('ingest.getPipeline', { id: name }); + + const response = await callAsCurrentUser('ingest.putPipeline', { + id: name, + body: { + description, + processors, + version, + on_failure, + }, + }); + + return res.ok({ body: response }); + } catch (error) { + if (isEsError(error)) { + return res.customError({ + statusCode: error.statusCode, + body: error, + }); + } + + return res.internalError({ body: error }); + } + }) + ); +}; diff --git a/x-pack/plugins/ingest_pipelines/server/routes/index.ts b/x-pack/plugins/ingest_pipelines/server/routes/index.ts new file mode 100644 index 00000000000000..f703a460143f45 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/routes/index.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RouteDependencies } from '../types'; + +import { + registerGetRoutes, + registerCreateRoute, + registerUpdateRoute, + registerPrivilegesRoute, + registerDeleteRoute, + registerSimulateRoute, +} from './api'; + +export class ApiRoutes { + setup(dependencies: RouteDependencies) { + registerGetRoutes(dependencies); + registerCreateRoute(dependencies); + registerUpdateRoute(dependencies); + registerPrivilegesRoute(dependencies); + registerDeleteRoute(dependencies); + registerSimulateRoute(dependencies); + } +} diff --git a/x-pack/plugins/ingest_pipelines/server/services/index.ts b/x-pack/plugins/ingest_pipelines/server/services/index.ts new file mode 100644 index 00000000000000..b7a45e59549eb7 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/services/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { License } from './license'; diff --git a/x-pack/plugins/ingest_pipelines/server/services/license.ts b/x-pack/plugins/ingest_pipelines/server/services/license.ts new file mode 100644 index 00000000000000..0a4748bd0ace08 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/services/license.ts @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { Logger } from 'src/core/server'; +import { + KibanaRequest, + KibanaResponseFactory, + RequestHandler, + RequestHandlerContext, +} from 'kibana/server'; + +import { LicensingPluginSetup } from '../../../licensing/server'; +import { LicenseType } from '../../../licensing/common/types'; + +export interface LicenseStatus { + isValid: boolean; + message?: string; +} + +interface SetupSettings { + pluginId: string; + minimumLicenseType: LicenseType; + defaultErrorMessage: string; +} + +export class License { + private licenseStatus: LicenseStatus = { + isValid: false, + message: 'Invalid License', + }; + + setup( + { pluginId, minimumLicenseType, defaultErrorMessage }: SetupSettings, + { licensing, logger }: { licensing: LicensingPluginSetup; logger: Logger } + ) { + licensing.license$.subscribe(license => { + const { state, message } = license.check(pluginId, minimumLicenseType); + const hasRequiredLicense = state === 'valid'; + + if (hasRequiredLicense) { + this.licenseStatus = { isValid: true }; + } else { + this.licenseStatus = { + isValid: false, + message: message || defaultErrorMessage, + }; + if (message) { + logger.info(message); + } + } + }); + } + + guardApiRoute(handler: RequestHandler) { + const license = this; + + return function licenseCheck( + ctx: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ) { + const licenseStatus = license.getStatus(); + + if (!licenseStatus.isValid) { + return response.customError({ + body: { + message: licenseStatus.message || '', + }, + statusCode: 403, + }); + } + + return handler(ctx, request, response); + }; + } + + getStatus() { + return this.licenseStatus; + } +} diff --git a/x-pack/plugins/ingest_pipelines/server/types.ts b/x-pack/plugins/ingest_pipelines/server/types.ts new file mode 100644 index 00000000000000..0135ae8e2f07d6 --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/server/types.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from 'src/core/server'; +import { LicensingPluginSetup } from '../../licensing/server'; +import { License } from './services'; +import { isEsError } from './lib'; + +export interface Dependencies { + licensing: LicensingPluginSetup; +} + +export interface RouteDependencies { + router: IRouter; + license: License; + lib: { + isEsError: typeof isEsError; + }; +} diff --git a/x-pack/test/api_integration/apis/management/index.js b/x-pack/test/api_integration/apis/management/index.js index 352cd56d0fc9f5..cef2caa9186205 100644 --- a/x-pack/test/api_integration/apis/management/index.js +++ b/x-pack/test/api_integration/apis/management/index.js @@ -12,5 +12,6 @@ export default function({ loadTestFile }) { loadTestFile(require.resolve('./rollup')); loadTestFile(require.resolve('./index_management')); loadTestFile(require.resolve('./index_lifecycle_management')); + loadTestFile(require.resolve('./ingest_pipelines')); }); } diff --git a/x-pack/test/api_integration/apis/management/ingest_pipelines/index.ts b/x-pack/test/api_integration/apis/management/ingest_pipelines/index.ts new file mode 100644 index 00000000000000..ca222ebc2c1e3e --- /dev/null +++ b/x-pack/test/api_integration/apis/management/ingest_pipelines/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function({ loadTestFile }: FtrProviderContext) { + describe('Ingest Node Pipelines', () => { + loadTestFile(require.resolve('./ingest_pipelines')); + }); +} diff --git a/x-pack/test/api_integration/apis/management/ingest_pipelines/ingest_pipelines.ts b/x-pack/test/api_integration/apis/management/ingest_pipelines/ingest_pipelines.ts new file mode 100644 index 00000000000000..88a78d048a3b60 --- /dev/null +++ b/x-pack/test/api_integration/apis/management/ingest_pipelines/ingest_pipelines.ts @@ -0,0 +1,330 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { registerEsHelpers } from './lib'; + +import { FtrProviderContext } from '../../../ftr_provider_context'; + +const API_BASE_PATH = '/api/ingest_pipelines'; + +export default function({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + const { createPipeline, deletePipeline } = registerEsHelpers(getService); + + describe('Pipelines', function() { + describe('Create', () => { + const PIPELINE_ID = 'test_create_pipeline'; + after(() => deletePipeline(PIPELINE_ID)); + + it('should create a pipeline', async () => { + const { body } = await supertest + .post(API_BASE_PATH) + .set('kbn-xsrf', 'xxx') + .send({ + name: PIPELINE_ID, + description: 'test pipeline description', + processors: [ + { + script: { + source: 'ctx._type = null', + }, + }, + ], + on_failure: [ + { + set: { + field: 'error.message', + value: '{{ failure_message }}', + }, + }, + ], + version: 1, + }) + .expect(200); + + expect(body).to.eql({ + acknowledged: true, + }); + }); + + it('should not allow creation of an existing pipeline', async () => { + const { body } = await supertest + .post(API_BASE_PATH) + .set('kbn-xsrf', 'xxx') + .send({ + name: PIPELINE_ID, + description: 'test pipeline description', + processors: [ + { + script: { + source: 'ctx._type = null', + }, + }, + ], + version: 1, + }) + .expect(409); + + expect(body).to.eql({ + statusCode: 409, + error: 'Conflict', + message: `There is already a pipeline with name '${PIPELINE_ID}'.`, + }); + }); + }); + + describe('Update', () => { + const PIPELINE_ID = 'test_update_pipeline'; + const PIPELINE = { + description: 'test pipeline description', + processors: [ + { + script: { + source: 'ctx._type = null', + }, + }, + ], + version: 1, + }; + + before(() => createPipeline({ body: PIPELINE, id: PIPELINE_ID })); + after(() => deletePipeline(PIPELINE_ID)); + + it('should allow an existing pipeline to be updated', async () => { + const uri = `${API_BASE_PATH}/${PIPELINE_ID}`; + + const { body } = await supertest + .put(uri) + .set('kbn-xsrf', 'xxx') + .send({ + ...PIPELINE, + description: 'updated test pipeline description', + }) + .expect(200); + + expect(body).to.eql({ + acknowledged: true, + }); + }); + + it('should not allow a non-existing pipeline to be updated', async () => { + const uri = `${API_BASE_PATH}/pipeline_does_not_exist`; + + const { body } = await supertest + .put(uri) + .set('kbn-xsrf', 'xxx') + .send({ + ...PIPELINE, + description: 'updated test pipeline description', + }) + .expect(404); + + expect(body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Not Found', + }); + }); + }); + + describe('Get', () => { + const PIPELINE_ID = 'test_pipeline'; + const PIPELINE = { + description: 'test pipeline description', + processors: [ + { + script: { + source: 'ctx._type = null', + }, + }, + ], + version: 1, + }; + + before(() => createPipeline({ body: PIPELINE, id: PIPELINE_ID })); + after(() => deletePipeline(PIPELINE_ID)); + + describe('all pipelines', () => { + it('should return an array of pipelines', async () => { + const { body } = await supertest + .get(API_BASE_PATH) + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(Array.isArray(body)).to.be(true); + + // There are some pipelines created OOTB with ES + // To not be dependent on these, we only confirm the pipeline we created as part of the test exists + const testPipeline = body.find(({ name }: { name: string }) => name === PIPELINE_ID); + + expect(testPipeline).to.eql({ + ...PIPELINE, + name: PIPELINE_ID, + }); + }); + }); + + describe('one pipeline', () => { + it('should return a single pipeline', async () => { + const uri = `${API_BASE_PATH}/${PIPELINE_ID}`; + + const { body } = await supertest + .get(uri) + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(body).to.eql({ + ...PIPELINE, + name: PIPELINE_ID, + }); + }); + }); + }); + + describe('Delete', () => { + const PIPELINE = { + description: 'test pipeline description', + processors: [ + { + script: { + source: 'ctx._type = null', + }, + }, + ], + version: 1, + }; + + it('should delete a pipeline', async () => { + // Create pipeline to be deleted + const PIPELINE_ID = 'test_delete_pipeline'; + createPipeline({ body: PIPELINE, id: PIPELINE_ID }); + + const uri = `${API_BASE_PATH}/${PIPELINE_ID}`; + + const { body } = await supertest + .delete(uri) + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(body).to.eql({ + itemsDeleted: [PIPELINE_ID], + errors: [], + }); + }); + + it('should delete multiple pipelines', async () => { + // Create pipelines to be deleted + const PIPELINE_ONE_ID = 'test_delete_pipeline_1'; + const PIPELINE_TWO_ID = 'test_delete_pipeline_2'; + createPipeline({ body: PIPELINE, id: PIPELINE_ONE_ID }); + createPipeline({ body: PIPELINE, id: PIPELINE_TWO_ID }); + + const uri = `${API_BASE_PATH}/${PIPELINE_ONE_ID},${PIPELINE_TWO_ID}`; + + const { + body: { itemsDeleted, errors }, + } = await supertest + .delete(uri) + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(errors).to.eql([]); + + // The itemsDeleted array order isn't guaranteed, so we assert against each pipeline name instead + [PIPELINE_ONE_ID, PIPELINE_TWO_ID].forEach(pipelineName => { + expect(itemsDeleted.includes(pipelineName)).to.be(true); + }); + }); + + it('should return an error for any pipelines not sucessfully deleted', async () => { + const PIPELINE_DOES_NOT_EXIST = 'pipeline_does_not_exist'; + + // Create pipeline to be deleted + const PIPELINE_ONE_ID = 'test_delete_pipeline_1'; + createPipeline({ body: PIPELINE, id: PIPELINE_ONE_ID }); + + const uri = `${API_BASE_PATH}/${PIPELINE_ONE_ID},${PIPELINE_DOES_NOT_EXIST}`; + + const { body } = await supertest + .delete(uri) + .set('kbn-xsrf', 'xxx') + .expect(200); + + expect(body).to.eql({ + itemsDeleted: [PIPELINE_ONE_ID], + errors: [ + { + name: PIPELINE_DOES_NOT_EXIST, + error: { + msg: '[resource_not_found_exception] pipeline [pipeline_does_not_exist] is missing', + path: '/_ingest/pipeline/pipeline_does_not_exist', + query: {}, + statusCode: 404, + response: JSON.stringify({ + error: { + root_cause: [ + { + type: 'resource_not_found_exception', + reason: 'pipeline [pipeline_does_not_exist] is missing', + }, + ], + type: 'resource_not_found_exception', + reason: 'pipeline [pipeline_does_not_exist] is missing', + }, + status: 404, + }), + }, + }, + ], + }); + }); + }); + + describe('Simulate', () => { + it('should successfully simulate a pipeline', async () => { + const { body } = await supertest + .post(`${API_BASE_PATH}/simulate`) + .set('kbn-xsrf', 'xxx') + .send({ + pipeline: { + description: 'test simulate pipeline description', + processors: [ + { + set: { + field: 'field2', + value: '_value', + }, + }, + ], + }, + documents: [ + { + _index: 'index', + _id: 'id', + _source: { + foo: 'bar', + }, + }, + { + _index: 'index', + _id: 'id', + _source: { + foo: 'rab', + }, + }, + ], + }) + .expect(200); + + // The simulate ES response is quite long and includes timestamps + // so for now, we just confirm the docs array is returned with the correct length + expect(body.docs?.length).to.eql(2); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/management/ingest_pipelines/lib/elasticsearch.ts b/x-pack/test/api_integration/apis/management/ingest_pipelines/lib/elasticsearch.ts new file mode 100644 index 00000000000000..2f42596a66b546 --- /dev/null +++ b/x-pack/test/api_integration/apis/management/ingest_pipelines/lib/elasticsearch.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +interface Processor { + [key: string]: { + [key: string]: unknown; + }; +} + +interface Pipeline { + id: string; + body: { + description: string; + processors: Processor[]; + version?: number; + }; +} + +/** + * Helpers to create and delete pipelines on the Elasticsearch instance + * during our tests. + * @param {ElasticsearchClient} es The Elasticsearch client instance + */ +export const registerEsHelpers = (getService: FtrProviderContext['getService']) => { + const es = getService('legacyEs'); + + const createPipeline = (pipeline: Pipeline) => es.ingest.putPipeline(pipeline); + + const deletePipeline = (pipelineId: string) => es.ingest.deletePipeline({ id: pipelineId }); + + return { + createPipeline, + deletePipeline, + }; +}; diff --git a/x-pack/test/api_integration/apis/management/ingest_pipelines/lib/index.ts b/x-pack/test/api_integration/apis/management/ingest_pipelines/lib/index.ts new file mode 100644 index 00000000000000..66ea0fe40c4cea --- /dev/null +++ b/x-pack/test/api_integration/apis/management/ingest_pipelines/lib/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { registerEsHelpers } from './elasticsearch'; diff --git a/x-pack/test/functional/apps/ingest_pipelines/index.ts b/x-pack/test/functional/apps/ingest_pipelines/index.ts new file mode 100644 index 00000000000000..87e7e70c0b5e09 --- /dev/null +++ b/x-pack/test/functional/apps/ingest_pipelines/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ loadTestFile }: FtrProviderContext) => { + describe('Ingest pipelines app', function() { + this.tags('ciGroup3'); + loadTestFile(require.resolve('./ingest_pipelines')); + }); +}; diff --git a/x-pack/test/functional/apps/ingest_pipelines/ingest_pipelines.ts b/x-pack/test/functional/apps/ingest_pipelines/ingest_pipelines.ts new file mode 100644 index 00000000000000..1b22f8f35d7ad2 --- /dev/null +++ b/x-pack/test/functional/apps/ingest_pipelines/ingest_pipelines.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const pageObjects = getPageObjects(['common', 'ingestPipelines']); + const log = getService('log'); + + describe('Ingest Pipelines', function() { + this.tags('smoke'); + before(async () => { + await pageObjects.common.navigateToApp('ingestPipelines'); + }); + + it('Loads the app', async () => { + await log.debug('Checking for section heading to say Ingest Node Pipelines.'); + + const headingText = await pageObjects.ingestPipelines.sectionHeadingText(); + expect(headingText).to.be('Ingest Node Pipelines'); + }); + }); +}; diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index 2c6238704bea0f..f6b80b1b9fc679 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -53,6 +53,7 @@ export default async function({ readConfigFile }) { resolve(__dirname, './apps/index_patterns'), resolve(__dirname, './apps/index_management'), resolve(__dirname, './apps/index_lifecycle_management'), + resolve(__dirname, './apps/ingest_pipelines'), resolve(__dirname, './apps/snapshot_restore'), resolve(__dirname, './apps/cross_cluster_replication'), resolve(__dirname, './apps/remote_clusters'), @@ -175,6 +176,10 @@ export default async function({ readConfigFile }) { pathname: '/app/kibana', hash: '/management/elasticsearch/index_lifecycle_management', }, + ingestPipelines: { + pathname: '/app/kibana', + hash: '/management/elasticsearch/ingest_pipelines', + }, snapshotRestore: { pathname: '/app/kibana', hash: '/management/elasticsearch/snapshot_restore', diff --git a/x-pack/test/functional/page_objects/index.ts b/x-pack/test/functional/page_objects/index.ts index 4b8c2944ef190f..833cc452a5d31e 100644 --- a/x-pack/test/functional/page_objects/index.ts +++ b/x-pack/test/functional/page_objects/index.ts @@ -45,6 +45,7 @@ import { LensPageProvider } from './lens_page'; import { InfraMetricExplorerProvider } from './infra_metric_explorer'; import { RoleMappingsPageProvider } from './role_mappings_page'; import { SpaceSelectorPageProvider } from './space_selector_page'; +import { IngestPipelinesPageProvider } from './ingest_pipelines_page'; // just like services, PageObjects are defined as a map of // names to Providers. Merge in Kibana's or pick specific ones @@ -78,4 +79,5 @@ export const pageObjects = { copySavedObjectsToSpace: CopySavedObjectsToSpacePageProvider, lens: LensPageProvider, roleMappings: RoleMappingsPageProvider, + ingestPipelines: IngestPipelinesPageProvider, }; diff --git a/x-pack/test/functional/page_objects/ingest_pipelines_page.ts b/x-pack/test/functional/page_objects/ingest_pipelines_page.ts new file mode 100644 index 00000000000000..abc85277a36176 --- /dev/null +++ b/x-pack/test/functional/page_objects/ingest_pipelines_page.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; + +export function IngestPipelinesPageProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async sectionHeadingText() { + return await testSubjects.getVisibleText('appTitle'); + }, + }; +} From 59315bc84d3fbf8bde3b7bebdf0308b1749ed978 Mon Sep 17 00:00:00 2001 From: igoristic Date: Thu, 30 Apr 2020 15:59:35 -0400 Subject: [PATCH 29/47] [Monitoring] NP Migration complete client cutover (#62908) * Final phase before the complete cutover * NP migration * lint fix * More NP stuff * Moved Stack Monitoring client plugin outside legacy and fixed all tests * ... * Removed unused files * Fix for main links * Fixed more tests * Fixed redirect when clicking on SM icon again * Code review feedback * Addressed code review feedback * Fixed return value --- .eslintignore | 1 + .eslintrc.js | 6 + .github/CODEOWNERS | 1 + src/dev/precommit_hook/casing_check_config.js | 12 +- x-pack/legacy/plugins/monitoring/.agignore | 3 - .../__tests__/format_timestamp_to_duration.js | 128 - .../monitoring/common/cancel_promise.ts | 70 - .../plugins/monitoring/common/constants.ts | 268 -- .../common/format_timestamp_to_duration.js | 54 - .../plugins/monitoring/common/formatting.js | 35 - .../monitoring/{config.js => config.ts} | 2 +- x-pack/legacy/plugins/monitoring/index.js | 57 - x-pack/legacy/plugins/monitoring/index.ts | 41 + .../components/chart/chart_target.test.js | 76 - .../public/components/license/index.js | 91 - .../monitoring/public/directives/all.js | 10 - .../monitoring/public/filters/index.js | 30 - .../hacks/__tests__/toggle_app_link_in_nav.js | 9 - .../public/hacks/toggle_app_link_in_nav.js | 16 - .../monitoring/public/icons/monitoring.svg | 16 - .../plugins/monitoring/public/legacy.ts | 27 - .../np_imports/angular/angular_config.ts | 157 - .../public/np_imports/angular/index.ts | 48 - .../public/np_imports/angular/modules.ts | 148 - .../np_imports/angular/providers/promises.js | 116 - .../public/np_imports/legacy_imports.ts | 22 - .../public/np_imports/ui/capabilities.ts | 8 - .../monitoring/public/np_imports/ui/chrome.ts | 33 - .../public/np_imports/ui/modules.ts | 55 - .../public/np_imports/ui/timefilter.ts | 31 - .../monitoring/public/np_ready/plugin.ts | 28 - .../monitoring/public/register_feature.ts | 30 - .../monitoring/public/services/breadcrumbs.js | 10 - .../monitoring/public/services/executor.js | 10 - .../public/views/kibana/instance/index.js | 153 - .../public/views/loading/index.html | 5 - .../monitoring/public/views/loading/index.js | 51 - .../legacy/plugins/monitoring/ui_exports.js | 65 - .../server/routes/api/v1/settings.js | 2 +- x-pack/plugins/monitoring/kibana.json | 6 +- .../plugins/monitoring/public/_hacks.scss | 0 .../monitoring/public/angular/app_modules.ts | 248 ++ .../public/angular/helpers}/routes.ts | 19 +- .../public/angular/helpers}/utils.ts | 0 .../monitoring/public/angular/index.ts | 68 + .../public}/angular/providers/private.js | 2 + .../public/angular/providers/url.js | 217 ++ .../alerts/__snapshots__/status.test.tsx.snap | 0 .../alerts/__tests__/map_severity.js | 0 .../public/components/alerts/alerts.js | 8 +- .../__snapshots__/configuration.test.tsx.snap | 0 .../__snapshots__/step1.test.tsx.snap | 0 .../__snapshots__/step2.test.tsx.snap | 0 .../__snapshots__/step3.test.tsx.snap | 0 .../configuration/configuration.test.tsx | 16 +- .../alerts/configuration/configuration.tsx | 8 +- .../components/alerts/configuration/index.ts | 0 .../alerts/configuration/step1.test.tsx | 84 +- .../components/alerts/configuration/step1.tsx | 12 +- .../alerts/configuration/step2.test.tsx | 0 .../components/alerts/configuration/step2.tsx | 0 .../alerts/configuration/step3.test.tsx | 0 .../components/alerts/configuration/step3.tsx | 0 .../components/alerts/formatted_alert.js | 0 .../public/components/alerts/index.js | 0 .../components/alerts/manage_email_action.tsx | 2 +- .../public/components/alerts/map_severity.js | 0 .../public/components/alerts/status.test.tsx | 18 +- .../public/components/alerts/status.tsx | 16 +- .../public/components/apm/instance/index.js | 0 .../components/apm/instance/instance.js | 0 .../public/components/apm/instance/status.js | 0 .../public/components/apm/instances/index.js | 0 .../components/apm/instances/instances.js | 0 .../public/components/apm/instances/status.js | 0 .../public/components/apm/overview/index.js | 0 .../public/components/apm/status_icon.js | 2 +- .../public/components/beats/beat/beat.js | 0 .../public/components/beats/beat/index.js | 0 .../public/components/beats/index.js | 0 .../public/components/beats/listing/index.js | 0 .../components/beats/listing/listing.js | 6 +- .../__snapshots__/latest_active.test.js.snap | 0 .../__snapshots__/latest_types.test.js.snap | 0 .../latest_versions.test.js.snap | 0 .../__snapshots__/overview.test.js.snap | 0 .../public/components/beats/overview/index.js | 0 .../beats/overview/latest_active.js | 0 .../beats/overview/latest_active.test.js | 0 .../components/beats/overview/latest_types.js | 0 .../beats/overview/latest_types.test.js | 0 .../beats/overview/latest_versions.js | 0 .../beats/overview/latest_versions.test.js | 0 .../components/beats/overview/overview.js | 0 .../beats/overview/overview.test.js | 8 +- .../public/components/beats/stats.js | 2 +- .../components/chart/__tests__/get_color.js | 0 .../chart/__tests__/get_last_value.js | 0 .../components/chart/__tests__/get_title.js | 0 .../chart/__tests__/get_values_for_legend.js | 0 .../public/components/chart/_chart.scss | 0 .../public/components/chart/_index.scss | 0 .../public/components/chart/chart_target.js | 2 +- .../public/components/chart/event_bus.js | 0 .../components/chart/get_chart_options.js | 4 +- .../public/components/chart/get_color.js | 0 .../public/components/chart/get_last_value.js | 0 .../public/components/chart/get_title.js | 0 .../public/components/chart/get_units.js | 0 .../components/chart/get_values_for_legend.js | 0 .../components/chart/horizontal_legend.js | 0 .../public/components/chart/index.js | 0 .../public/components/chart/info_tooltip.js | 0 .../components/chart/monitoring_timeseries.js | 0 .../chart/monitoring_timeseries_container.js | 0 .../components/chart/timeseries_container.js | 0 .../chart/timeseries_visualization.js | 0 .../cluster/listing/alerts_indicator.js | 2 +- .../components/cluster/listing/index.js | 0 .../components/cluster/listing/listing.js | 19 +- .../__snapshots__/helpers.test.js.snap | 0 .../overview/__tests__/helpers.test.js | 2 +- .../cluster/overview/alerts_panel.js | 4 +- .../components/cluster/overview/apm_panel.js | 2 +- .../cluster/overview/beats_panel.js | 2 +- .../cluster/overview/elasticsearch_panel.js | 2 +- .../components/cluster/overview/helpers.js | 0 .../components/cluster/overview/index.js | 0 .../cluster/overview/kibana_panel.js | 2 +- .../cluster/overview/license_text.js | 0 .../cluster/overview/logstash_panel.js | 2 +- .../public/components/cluster/status_icon.js | 0 .../ccr/__snapshots__/ccr.test.js.snap | 0 .../components/elasticsearch/ccr/ccr.js | 3 +- .../components/elasticsearch/ccr/ccr.test.js | 0 .../components/elasticsearch/ccr/index.js | 0 .../components/elasticsearch/ccr/index.scss} | 0 .../__snapshots__/ccr_shard.test.js.snap | 0 .../elasticsearch/ccr_shard/ccr_shard.js | 4 +- .../elasticsearch/ccr_shard/ccr_shard.test.js | 11 +- .../elasticsearch/ccr_shard/index.js | 0 .../elasticsearch/ccr_shard/status.js | 0 .../elasticsearch/cluster_status/index.js | 0 .../public/components/elasticsearch/index.js | 0 .../elasticsearch/index/advanced.js | 0 .../components/elasticsearch/index/index.js | 0 .../index_detail_status/index.js | 0 .../components/elasticsearch/indices/index.js | 0 .../elasticsearch/indices/indices.js | 0 .../ml_job_listing/status_icon.js | 2 +- .../components/elasticsearch/node/advanced.js | 0 .../components/elasticsearch/node/index.js | 0 .../components/elasticsearch/node/node.js | 0 .../elasticsearch/node/status_icon.js | 0 .../elasticsearch/node_detail_status/index.js | 0 .../__snapshots__/cells.test.js.snap | 0 .../nodes/__tests__/cells.test.js | 2 +- .../components/elasticsearch/nodes/cells.js | 0 .../components/elasticsearch/nodes/index.js | 0 .../components/elasticsearch/nodes/nodes.js | 0 .../elasticsearch/overview/index.js | 0 .../elasticsearch/overview/overview.js | 0 .../elasticsearch/shard_activity/index.js | 0 .../shard_activity/parse_props.js | 6 +- .../elasticsearch/shard_activity/progress.js | 0 .../shard_activity/recovery_index.js | 0 .../shard_activity/shard_activity.js | 2 +- .../elasticsearch/shard_activity/snapshot.js | 0 .../shard_activity/source_destination.js | 0 .../shard_activity/source_tooltip.js | 0 .../shard_activity/total_time.js | 0 .../shard_allocation/_index.scss | 0 .../shard_allocation/_shard_allocation.scss | 0 .../__snapshots__/shard.test.js.snap | 0 .../shard_allocation/components/assigned.js | 0 .../components/cluster_view.js | 0 .../shard_allocation/components/shard.js | 0 .../shard_allocation/components/shard.test.js | 0 .../shard_allocation/components/table_body.js | 0 .../shard_allocation/components/table_head.js | 0 .../shard_allocation/components/unassigned.js | 0 .../elasticsearch/shard_allocation/index.js | 0 .../shard_allocation/lib/calculate_class.js | 0 .../shard_allocation/lib/decorate_shards.js | 0 .../lib/decorate_shards.test.js | 0 .../lib/generate_query_and_link.js | 0 .../lib/has_primary_children.js | 0 .../shard_allocation/lib/has_unassigned.js | 0 .../shard_allocation/lib/labels.js | 0 .../shard_allocation/lib/vents.js | 0 .../shard_allocation/shard_allocation.js | 0 .../transformers/indices_by_nodes.js | 0 .../transformers/nodes_by_indices.js | 0 .../components/elasticsearch/status_icon.js | 0 .../monitoring/public/components/index.js | 0 .../components/kibana/cluster_status/index.js | 0 .../components/kibana/detail_status/index.js | 0 .../components/kibana/instances/index.js | 0 .../components/kibana/instances/instances.js | 2 +- .../public/components/kibana/status_icon.js | 2 +- .../public/components/license/index.js | 201 ++ .../logs/__snapshots__/logs.test.js.snap | 0 .../logs/__snapshots__/reason.test.js.snap | 0 .../public/components/logs/index.js | 0 .../monitoring/public/components/logs/logs.js | 9 +- .../public/components/logs/logs.test.js | 23 +- .../public/components/logs/reason.js | 3 +- .../public/components/logs/reason.test.js | 12 +- .../logstash/cluster_status/index.js | 0 .../logstash/detail_status/index.js | 0 .../__snapshots__/listing.test.js.snap | 0 .../components/logstash/listing/index.js | 0 .../components/logstash/listing/listing.js | 0 .../logstash/listing/listing.test.js | 0 .../components/logstash/overview/index.js | 0 .../components/logstash/overview/overview.js | 0 .../logstash/pipeline_listing/index.js | 0 .../pipeline_listing/pipeline_listing.js | 2 +- .../logstash/pipeline_viewer/index.js | 0 .../models/__tests__/config.js | 0 .../models/__tests__/pipeline_state.js | 0 .../logstash/pipeline_viewer/models/config.js | 0 .../models/graph/__tests__/boolean_edge.js | 0 .../models/graph/__tests__/edge.js | 0 .../models/graph/__tests__/edge_factory.js | 0 .../models/graph/__tests__/if_vertex.js | 0 .../models/graph/__tests__/index.js | 0 .../models/graph/__tests__/plugin_vertex.js | 0 .../models/graph/__tests__/queue_vertex.js | 0 .../models/graph/__tests__/vertex.js | 0 .../models/graph/__tests__/vertex_factory.js | 0 .../models/graph/boolean_edge.js | 0 .../pipeline_viewer/models/graph/edge.js | 0 .../models/graph/edge_factory.js | 0 .../pipeline_viewer/models/graph/if_vertex.js | 0 .../pipeline_viewer/models/graph/index.js | 0 .../models/graph/plugin_vertex.js | 0 .../models/graph/queue_vertex.js | 0 .../pipeline_viewer/models/graph/vertex.js | 0 .../models/graph/vertex_factory.js | 0 .../pipeline_viewer/models/list/element.js | 0 .../models/list/else_element.js | 0 .../models/list/else_element.test.js | 0 .../models/list/flatten_pipeline_section.js | 0 .../list/flatten_pipeline_section.test.js | 0 .../pipeline_viewer/models/list/if_element.js | 0 .../models/list/if_element.test.js | 0 .../pipeline_viewer/models/list/index.js | 0 .../pipeline_viewer/models/list/list.js | 0 .../pipeline_viewer/models/list/list.test.js | 0 .../models/list/plugin_element.js | 0 .../models/list/plugin_element.test.js | 0 .../models/pipeline/__tests__/if_statement.js | 0 .../pipeline/__tests__/make_statement.js | 0 .../models/pipeline/__tests__/pipeline.js | 0 .../pipeline/__tests__/plugin_statement.js | 0 .../models/pipeline/__tests__/queue.js | 0 .../models/pipeline/__tests__/statement.js | 0 .../models/pipeline/__tests__/utils.js | 0 .../models/pipeline/if_statement.js | 0 .../pipeline_viewer/models/pipeline/index.js | 0 .../models/pipeline/make_statement.js | 0 .../models/pipeline/pipeline.js | 0 .../models/pipeline/plugin_statement.js | 0 .../pipeline_viewer/models/pipeline/queue.js | 0 .../models/pipeline/statement.js | 0 .../pipeline_viewer/models/pipeline/utils.js | 0 .../pipeline_viewer/models/pipeline_state.js | 0 .../collapsible_statement.test.js.snap | 0 .../__snapshots__/detail_drawer.test.js.snap | 0 .../__snapshots__/metric.test.js.snap | 0 .../pipeline_viewer.test.js.snap | 0 .../plugin_statement.test.js.snap | 0 .../__test__/__snapshots__/queue.test.js.snap | 0 .../__snapshots__/statement.test.js.snap | 0 .../__snapshots__/statement_list.test.js.snap | 0 .../statement_list_heading.test.js.snap | 0 .../statement_section.test.js.snap | 0 .../__test__/collapsible_statement.test.js | 0 .../views/__test__/detail_drawer.test.js | 4 + .../views/__test__/metric.test.js | 0 .../views/__test__/pipeline_viewer.test.js | 4 + .../views/__test__/plugin_statement.test.js | 0 .../views/__test__/queue.test.js | 0 .../views/__test__/statement.test.js | 0 .../views/__test__/statement_list.test.js | 0 .../__test__/statement_list_heading.test.js | 0 .../views/__test__/statement_section.test.js | 0 .../pipeline_viewer/views/_index.scss | 0 .../views/_pipeline_viewer.scss | 0 .../views/collapsible_statement.js | 0 .../pipeline_viewer/views/detail_drawer.js | 0 .../logstash/pipeline_viewer/views/index.js | 0 .../logstash/pipeline_viewer/views/metric.js | 0 .../pipeline_viewer/views/pipeline_viewer.js | 0 .../pipeline_viewer/views/plugin_statement.js | 0 .../logstash/pipeline_viewer/views/queue.js | 0 .../pipeline_viewer/views/statement.js | 0 .../pipeline_viewer/views/statement_list.js | 0 .../views/statement_list_heading.js | 0 .../views/statement_section.js | 0 .../metricbeat_migration/constants.js | 0 .../flyout/__snapshots__/flyout.test.js.snap | 0 .../metricbeat_migration/flyout/flyout.js | 5 +- .../flyout/flyout.test.js | 13 +- .../metricbeat_migration/flyout/index.js | 0 ...isable_internal_collection_instructions.js | 0 .../apm/enable_metricbeat_instructions.js | 3 +- .../instruction_steps/apm/index.js | 0 .../beats/common_beats_instructions.js | 0 ...isable_internal_collection_instructions.js | 0 .../beats/enable_metricbeat_instructions.js | 3 +- .../instruction_steps/beats/index.js | 0 .../instruction_steps/common_instructions.js | 0 .../components/monospace/index.js | 0 .../components/monospace/monospace.js | 0 ...isable_internal_collection_instructions.js | 0 .../enable_metricbeat_instructions.js | 3 +- .../instruction_steps/elasticsearch/index.js | 0 .../get_instruction_steps.js | 0 .../instruction_steps/index.js | 0 ...isable_internal_collection_instructions.js | 0 .../kibana/enable_metricbeat_instructions.js | 3 +- .../instruction_steps/kibana/index.js | 0 ...isable_internal_collection_instructions.js | 0 .../enable_metricbeat_instructions.js | 3 +- .../instruction_steps/logstash/index.js | 0 .../__snapshots__/checker_errors.test.js.snap | 0 .../__snapshots__/no_data.test.js.snap | 0 .../no_data/__tests__/checker_errors.test.js | 2 +- .../no_data/__tests__/no_data.test.js | 8 +- .../public/components/no_data/_index.scss | 0 .../public/components/no_data/_no_data.scss | 0 .../no_data/blurbs/changes_needed.js | 0 .../no_data/blurbs/cloud_deployment.js | 0 .../public/components/no_data/blurbs/index.js | 0 .../components/no_data/blurbs/looking_for.js | 0 .../components/no_data/blurbs/what_is.js | 0 .../components/no_data/checker_errors.js | 0 .../components/no_data/checking_settings.js | 0 .../collection_enabled.test.js.snap | 0 .../__tests__/collection_enabled.test.js | 2 +- .../collection_enabled/collection_enabled.js | 0 .../collection_interval.test.js.snap | 0 .../__tests__/collection_interval.test.js | 2 +- .../collection_interval.js | 0 .../__snapshots__/exporters.test.js.snap | 0 .../exporters/__tests__/exporters.test.js | 2 +- .../explanations/exporters/exporters.js | 0 .../components/no_data/explanations/index.js | 0 .../__snapshots__/plugin_enabled.test.js.snap | 0 .../__tests__/plugin_enabled.test.js | 2 +- .../plugin_enabled/plugin_enabled.js | 0 .../public/components/no_data/index.js | 0 .../public/components/no_data/no_data.js | 0 .../__snapshots__/reason_found.test.js.snap | 0 .../__snapshots__/we_tried.test.js.snap | 0 .../reasons/__tests__/reason_found.test.js | 2 +- .../reasons/__tests__/we_tried.test.js | 2 +- .../components/no_data/reasons/index.js | 0 .../no_data/reasons/reason_found.js | 0 .../components/no_data/reasons/we_tried.js | 0 .../__snapshots__/page_loading.test.js.snap | 0 .../__tests__/page_loading.test.js | 2 +- .../public/components/page_loading/index.js | 0 .../__snapshots__/setup_mode.test.js.snap | 0 .../public/components/renderers/index.js | 0 .../components/renderers/lib/find_new_uuid.js | 0 .../public/components/renderers/setup_mode.js | 0 .../components/renderers/setup_mode.test.js | 0 .../__snapshots__/badge.test.js.snap | 0 .../__snapshots__/enter_button.test.tsx.snap | 0 .../__snapshots__/formatting.test.js.snap | 0 .../listing_callout.test.js.snap | 0 .../__snapshots__/tooltip.test.js.snap | 0 .../components/setup_mode/_enter_button.scss | 0 .../public/components/setup_mode/_index.scss | 0 .../public/components/setup_mode/badge.js | 0 .../components/setup_mode/badge.test.js | 0 .../setup_mode/enter_button.test.tsx | 0 .../components/setup_mode/enter_button.tsx | 0 .../components/setup_mode/formatting.js | 0 .../components/setup_mode/formatting.test.js | 0 .../components/setup_mode/listing_callout.js | 0 .../setup_mode/listing_callout.test.js | 0 .../public/components/setup_mode/tooltip.js | 0 .../components/setup_mode/tooltip.test.js | 0 .../plugins/xpack_main/jquery_flot.js | 0 .../__test__/__snapshots__/index.test.js.snap | 0 .../sparkline/__test__/index.test.js | 4 + .../public/components/sparkline/_index.scss | 0 .../components/sparkline/_sparkline.scss | 0 .../public/components/sparkline/index.js | 0 .../sparkline/sparkline_flot_chart.js | 2 +- .../public/components/status_icon/_index.scss | 0 .../components/status_icon/_status_icon.scss | 0 .../public/components/status_icon/index.js | 0 .../__snapshots__/summary_status.test.js.snap | 0 .../components/summary_status/_index.scss | 0 .../summary_status/_summary_status.scss | 0 .../public/components/summary_status/index.js | 0 .../summary_status/summary_status.js | 0 .../summary_status/summary_status.test.js | 2 +- .../public/components/table/_index.scss | 0 .../public/components/table/_table.scss | 0 .../public/components/table/eui_table.js | 0 .../public/components/table/eui_table_ssp.js | 0 .../public/components/table/index.js | 0 .../public/components/table/storage.js | 0 .../public/directives/beats/beat}/index.js | 19 +- .../directives/beats/overview}/index.js | 18 +- .../elasticsearch/ml_job_listing/index.js | 94 +- .../__tests__/monitoring_main_controller.js | 0 .../public/directives/main/index.html | 0 .../public/directives/main/index.js | 25 +- .../monitoring/public/icons/health-gray.svg | 0 .../monitoring/public/icons/health-green.svg | 0 .../monitoring/public/icons/health-red.svg | 0 .../monitoring/public/icons/health-yellow.svg | 0 .../plugins/monitoring/public/index.scss | 7 + .../monitoring/public}/index.ts | 2 +- .../plugins/monitoring/public/jest.helpers.ts | 9 - .../plugins/monitoring/public/legacy_shims.ts | 83 + .../public/lib/__tests__/format_number.js | 0 .../public/lib/ajax_error_handler.tsx | 11 +- .../public/lib/calculate_shard_stats.js | 0 .../__tests__/enabler.test.js | 0 .../__tests__/settings_checker.test.js | 0 .../__tests__/start_checks.test.js | 0 .../checkers/cluster_settings.js | 0 .../checkers/node_settings.js | 0 .../checkers/settings_checker.js | 0 .../lib/elasticsearch_settings/enabler.js | 0 .../lib/elasticsearch_settings/index.js | 0 .../elasticsearch_settings/start_checks.js | 0 .../public/lib/ensure_minimum_time.js | 0 .../public/lib/ensure_minimum_time.test.js | 0 .../monitoring/public/lib/extract_ip.js | 0 .../monitoring/public/lib/form_validation.ts | 0 .../monitoring/public/lib/format_number.js | 0 .../public/lib/get_cluster_from_clusters.js | 0 .../monitoring/public/lib/get_page_data.js | 4 +- .../public/lib/get_safe_for_external_link.ts | 0 .../public/lib/jquery_flot/flot-charts/API.md | 1498 ++++++++ .../lib/jquery_flot/flot-charts/index.js | 48 + .../flot-charts/jquery.colorhelpers.js | 180 + .../flot-charts/jquery.flot.canvas.js | 345 ++ .../flot-charts/jquery.flot.categories.js | 190 + .../flot-charts/jquery.flot.crosshair.js | 176 + .../flot-charts/jquery.flot.errorbars.js | 353 ++ .../flot-charts/jquery.flot.fillbetween.js | 226 ++ .../flot-charts/jquery.flot.image.js | 241 ++ .../jquery_flot/flot-charts/jquery.flot.js | 3168 +++++++++++++++++ .../flot-charts/jquery.flot.log.js | 163 + .../flot-charts/jquery.flot.navigate.js | 346 ++ .../flot-charts/jquery.flot.pie.js | 824 +++++ .../flot-charts/jquery.flot.resize.js | 59 + .../flot-charts/jquery.flot.selection.js | 360 ++ .../flot-charts/jquery.flot.stack.js | 188 + .../flot-charts/jquery.flot.symbol.js | 71 + .../flot-charts/jquery.flot.threshold.js | 142 + .../flot-charts/jquery.flot.time.js | 473 +++ .../public/lib/jquery_flot}/index.js | 2 +- .../public/lib/jquery_flot/jquery_flot.js | 19 + .../lib/logstash/__tests__/pipelines.js | 0 .../public/lib/logstash/pipelines.js | 0 .../monitoring/public/lib/route_init.js | 2 +- .../monitoring/public/lib/setup_mode.test.js | 112 +- .../monitoring/public/lib/setup_mode.tsx | 39 +- x-pack/plugins/monitoring/public/plugin.ts | 147 + .../public/services/__tests__/breadcrumbs.js | 4 +- .../public/services/__tests__/executor.js} | 12 +- .../public/services/breadcrumbs.js} | 4 +- .../monitoring/public/services/clusters.js | 12 +- .../monitoring/public/services/executor.js} | 55 +- .../monitoring/public/services/features.js | 6 +- .../monitoring/public/services/license.js | 6 +- .../monitoring/public/services/title.js | 23 +- x-pack/plugins/monitoring/public/types.ts | 21 + x-pack/plugins/monitoring/public/url_state.ts | 169 + .../public/views/__tests__/base_controller.js | 7 +- .../views/__tests__/base_table_controller.js | 0 .../public/views/access_denied/index.html | 0 .../public/views/access_denied/index.js | 6 +- .../monitoring/public/views/alerts/index.html | 0 .../monitoring/public/views/alerts/index.js | 41 +- .../plugins/monitoring/public/views/all.js | 1 - .../public/views/apm/instance/index.html | 0 .../public/views/apm/instance/index.js | 19 +- .../public/views/apm/instances/index.html | 0 .../public/views/apm/instances/index.js | 47 +- .../public/views/apm/overview/index.html | 0 .../public/views/apm/overview/index.js | 11 +- .../public/views/base_controller.js | 100 +- .../public/views/base_eui_table_controller.js | 2 +- .../public/views/base_table_controller.js | 2 +- .../public/views/beats/beat/get_page_data.js | 6 +- .../public/views/beats/beat/index.html | 0 .../public/views/beats/beat/index.js | 4 +- .../views/beats/listing/get_page_data.js | 6 +- .../public/views/beats/listing/index.html | 0 .../public/views/beats/listing/index.js | 53 +- .../views/beats/overview/get_page_data.js | 6 +- .../public/views/beats/overview/index.html | 0 .../public/views/beats/overview/index.js | 4 +- .../public/views/cluster/listing/index.html | 0 .../public/views/cluster/listing/index.js | 35 +- .../public/views/cluster/overview/index.html | 0 .../public/views/cluster/overview/index.js | 47 +- .../views/elasticsearch/ccr/get_page_data.js | 6 +- .../public/views/elasticsearch/ccr/index.html | 0 .../public/views/elasticsearch/ccr/index.js | 11 +- .../elasticsearch/ccr/shard/get_page_data.js | 6 +- .../views/elasticsearch/ccr/shard/index.html | 0 .../views/elasticsearch/ccr/shard/index.js | 11 +- .../elasticsearch/index/advanced/index.html | 0 .../elasticsearch/index/advanced/index.js | 25 +- .../views/elasticsearch/index/index.html | 0 .../public/views/elasticsearch/index/index.js | 31 +- .../views/elasticsearch/indices/index.html | 0 .../views/elasticsearch/indices/index.js | 25 +- .../elasticsearch/ml_jobs/get_page_data.js | 6 +- .../views/elasticsearch/ml_jobs/index.html | 0 .../views/elasticsearch/ml_jobs/index.js | 4 +- .../elasticsearch/node/advanced/index.html | 0 .../elasticsearch/node/advanced/index.js | 25 +- .../views/elasticsearch/node/get_page_data.js | 6 +- .../views/elasticsearch/node/index.html | 0 .../public/views/elasticsearch/node/index.js | 25 +- .../views/elasticsearch/nodes/index.html | 0 .../public/views/elasticsearch/nodes/index.js | 49 +- .../elasticsearch/overview/controller.js | 27 +- .../views/elasticsearch/overview/index.html | 0 .../views/elasticsearch/overview/index.js | 4 +- .../plugins/monitoring/public/views/index.js | 0 .../public/views/kibana/instance/index.html | 0 .../public/views/kibana/instance/index.js | 150 + .../views/kibana/instances/get_page_data.js | 6 +- .../public/views/kibana/instances/index.html | 0 .../public/views/kibana/instances/index.js | 55 +- .../public/views/kibana/overview/index.html | 0 .../public/views/kibana/overview/index.js | 65 +- .../public/views/license/controller.js | 34 +- .../public/views/license/index.html | 0 .../monitoring/public/views/license/index.js | 4 +- .../views/logstash/node/advanced/index.html | 0 .../views/logstash/node/advanced/index.js | 61 +- .../public/views/logstash/node/index.html | 0 .../public/views/logstash/node/index.js | 61 +- .../views/logstash/node/pipelines/index.html | 0 .../views/logstash/node/pipelines/index.js | 45 +- .../views/logstash/nodes/get_page_data.js | 6 +- .../public/views/logstash/nodes/index.html | 0 .../public/views/logstash/nodes/index.js | 47 +- .../public/views/logstash/overview/index.html | 0 .../public/views/logstash/overview/index.js | 25 +- .../public/views/logstash/pipeline/index.html | 0 .../public/views/logstash/pipeline/index.js | 47 +- .../views/logstash/pipelines/index.html | 0 .../public/views/logstash/pipelines/index.js | 41 +- .../no_data/__tests__/model_updater.test.js | 0 .../public/views/no_data/controller.js | 25 +- .../public/views/no_data/index.html | 0 .../monitoring/public/views/no_data/index.js | 12 +- .../public/views/no_data/model_updater.js | 0 x-pack/plugins/monitoring/server/index.ts | 6 + .../spaces_usage_collector.ts | 2 +- .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - 569 files changed, 11410 insertions(+), 2967 deletions(-) delete mode 100644 x-pack/legacy/plugins/monitoring/.agignore delete mode 100644 x-pack/legacy/plugins/monitoring/common/__tests__/format_timestamp_to_duration.js delete mode 100644 x-pack/legacy/plugins/monitoring/common/cancel_promise.ts delete mode 100644 x-pack/legacy/plugins/monitoring/common/constants.ts delete mode 100644 x-pack/legacy/plugins/monitoring/common/format_timestamp_to_duration.js delete mode 100644 x-pack/legacy/plugins/monitoring/common/formatting.js rename x-pack/legacy/plugins/monitoring/{config.js => config.ts} (99%) delete mode 100644 x-pack/legacy/plugins/monitoring/index.js create mode 100644 x-pack/legacy/plugins/monitoring/index.ts delete mode 100644 x-pack/legacy/plugins/monitoring/public/components/chart/chart_target.test.js delete mode 100644 x-pack/legacy/plugins/monitoring/public/components/license/index.js delete mode 100644 x-pack/legacy/plugins/monitoring/public/directives/all.js delete mode 100644 x-pack/legacy/plugins/monitoring/public/filters/index.js delete mode 100644 x-pack/legacy/plugins/monitoring/public/hacks/__tests__/toggle_app_link_in_nav.js delete mode 100644 x-pack/legacy/plugins/monitoring/public/hacks/toggle_app_link_in_nav.js delete mode 100644 x-pack/legacy/plugins/monitoring/public/icons/monitoring.svg delete mode 100644 x-pack/legacy/plugins/monitoring/public/legacy.ts delete mode 100644 x-pack/legacy/plugins/monitoring/public/np_imports/angular/angular_config.ts delete mode 100644 x-pack/legacy/plugins/monitoring/public/np_imports/angular/index.ts delete mode 100644 x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts delete mode 100644 x-pack/legacy/plugins/monitoring/public/np_imports/angular/providers/promises.js delete mode 100644 x-pack/legacy/plugins/monitoring/public/np_imports/legacy_imports.ts delete mode 100644 x-pack/legacy/plugins/monitoring/public/np_imports/ui/capabilities.ts delete mode 100644 x-pack/legacy/plugins/monitoring/public/np_imports/ui/chrome.ts delete mode 100644 x-pack/legacy/plugins/monitoring/public/np_imports/ui/modules.ts delete mode 100644 x-pack/legacy/plugins/monitoring/public/np_imports/ui/timefilter.ts delete mode 100644 x-pack/legacy/plugins/monitoring/public/np_ready/plugin.ts delete mode 100644 x-pack/legacy/plugins/monitoring/public/register_feature.ts delete mode 100644 x-pack/legacy/plugins/monitoring/public/services/breadcrumbs.js delete mode 100644 x-pack/legacy/plugins/monitoring/public/services/executor.js delete mode 100644 x-pack/legacy/plugins/monitoring/public/views/kibana/instance/index.js delete mode 100644 x-pack/legacy/plugins/monitoring/public/views/loading/index.html delete mode 100644 x-pack/legacy/plugins/monitoring/public/views/loading/index.js delete mode 100644 x-pack/legacy/plugins/monitoring/ui_exports.js rename x-pack/{legacy => }/plugins/monitoring/public/_hacks.scss (100%) create mode 100644 x-pack/plugins/monitoring/public/angular/app_modules.ts rename x-pack/{legacy/plugins/monitoring/public/np_imports/ui => plugins/monitoring/public/angular/helpers}/routes.ts (62%) rename x-pack/{legacy/plugins/monitoring/public/np_imports/ui => plugins/monitoring/public/angular/helpers}/utils.ts (100%) create mode 100644 x-pack/plugins/monitoring/public/angular/index.ts rename x-pack/{legacy/plugins/monitoring/public/np_imports => plugins/monitoring/public}/angular/providers/private.js (99%) create mode 100644 x-pack/plugins/monitoring/public/angular/providers/url.js rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/__snapshots__/status.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/__tests__/map_severity.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/alerts.js (95%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/__snapshots__/configuration.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step1.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step2.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step3.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/configuration.test.tsx (91%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/configuration.tsx (96%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/index.ts (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/step1.test.tsx (83%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/step1.tsx (97%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/step2.test.tsx (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/step2.tsx (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/step3.test.tsx (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/configuration/step3.tsx (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/formatted_alert.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/manage_email_action.tsx (99%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/map_severity.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/status.test.tsx (83%) rename x-pack/{legacy => }/plugins/monitoring/public/components/alerts/status.tsx (93%) rename x-pack/{legacy => }/plugins/monitoring/public/components/apm/instance/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/apm/instance/instance.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/apm/instance/status.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/apm/instances/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/apm/instances/instances.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/apm/instances/status.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/apm/overview/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/apm/status_icon.js (91%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/beat/beat.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/beat/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/listing/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/listing/listing.js (96%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_active.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_types.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_versions.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/__snapshots__/overview.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/latest_active.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/latest_active.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/latest_types.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/latest_types.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/latest_versions.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/latest_versions.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/overview.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/overview/overview.test.js (93%) rename x-pack/{legacy => }/plugins/monitoring/public/components/beats/stats.js (95%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/__tests__/get_color.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/__tests__/get_last_value.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/__tests__/get_title.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/__tests__/get_values_for_legend.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/_chart.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/_index.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/chart_target.js (99%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/event_bus.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/get_chart_options.js (92%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/get_color.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/get_last_value.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/get_title.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/get_units.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/get_values_for_legend.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/horizontal_legend.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/info_tooltip.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/monitoring_timeseries.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/monitoring_timeseries_container.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/timeseries_container.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/chart/timeseries_visualization.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/listing/alerts_indicator.js (97%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/listing/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/listing/listing.js (95%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/overview/__tests__/__snapshots__/helpers.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/overview/__tests__/helpers.test.js (93%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/overview/alerts_panel.js (97%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/overview/apm_panel.js (98%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/overview/beats_panel.js (98%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js (99%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/overview/helpers.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/overview/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/overview/kibana_panel.js (98%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/overview/license_text.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/overview/logstash_panel.js (99%) rename x-pack/{legacy => }/plugins/monitoring/public/components/cluster/status_icon.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/ccr/ccr.js (99%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/ccr/ccr.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/ccr/index.js (100%) rename x-pack/{legacy/plugins/monitoring/public/components/elasticsearch/ccr/ccr.css => plugins/monitoring/public/components/elasticsearch/ccr/index.scss} (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.js (97%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.test.js (88%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/ccr_shard/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/ccr_shard/status.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/cluster_status/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/index/advanced.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/index/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/index_detail_status/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/indices/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/indices/indices.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/ml_job_listing/status_icon.js (93%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/node/advanced.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/node/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/node/node.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/node/status_icon.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/node_detail_status/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js (95%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/nodes/cells.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/nodes/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/overview/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/overview/overview.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_activity/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_activity/parse_props.js (92%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_activity/progress.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_activity/recovery_index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_activity/shard_activity.js (98%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_activity/snapshot.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_activity/source_destination.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_activity/source_tooltip.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_activity/total_time.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/_index.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/_shard_allocation.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/__snapshots__/shard.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/assigned.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/cluster_view.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/shard.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/shard.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_body.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/unassigned.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/calculate_class.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/decorate_shards.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/decorate_shards.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/generate_query_and_link.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_primary_children.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_unassigned.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/labels.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/vents.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/indices_by_nodes.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/elasticsearch/status_icon.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/kibana/cluster_status/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/kibana/detail_status/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/kibana/instances/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/kibana/instances/instances.js (99%) rename x-pack/{legacy => }/plugins/monitoring/public/components/kibana/status_icon.js (91%) create mode 100644 x-pack/plugins/monitoring/public/components/license/index.js rename x-pack/{legacy => }/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logs/__snapshots__/reason.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logs/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logs/logs.js (95%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logs/logs.test.js (95%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logs/reason.js (98%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logs/reason.test.js (93%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/cluster_status/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/detail_status/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/listing/__snapshots__/listing.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/listing/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/listing/listing.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/listing/listing.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/overview/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/overview/overview.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_listing/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_listing/pipeline_listing.js (98%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/__tests__/config.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/__tests__/pipeline_state.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/config.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/boolean_edge.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/edge.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/edge_factory.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/if_vertex.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/plugin_vertex.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/queue_vertex.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/vertex.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/vertex_factory.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/boolean_edge.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/edge.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/edge_factory.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/if_vertex.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/plugin_vertex.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/queue_vertex.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/vertex.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/vertex_factory.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/element.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/else_element.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/else_element.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/flatten_pipeline_section.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/flatten_pipeline_section.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/if_element.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/if_element.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/list.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/list.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/plugin_element.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/plugin_element.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/if_statement.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/make_statement.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/pipeline.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/plugin_statement.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/queue.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/statement.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/utils.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/if_statement.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/make_statement.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/pipeline.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/plugin_statement.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/queue.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/statement.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/utils.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline_state.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/collapsible_statement.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/detail_drawer.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/metric.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/pipeline_viewer.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/plugin_statement.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/queue.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list_heading.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_section.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/collapsible_statement.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/detail_drawer.test.js (98%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/metric.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/pipeline_viewer.test.js (94%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/plugin_statement.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/queue.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list_heading.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_section.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_index.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_pipeline_viewer.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/collapsible_statement.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/pipeline_viewer.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list_heading.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_section.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/constants.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/flyout/__snapshots__/flyout.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.js (98%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.test.js (95%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/flyout/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/disable_internal_collection_instructions.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/enable_metricbeat_instructions.js (97%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/common_beats_instructions.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/disable_internal_collection_instructions.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/enable_metricbeat_instructions.js (98%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/common_instructions.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/components/monospace/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/components/monospace/monospace.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/disable_internal_collection_instructions.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/enable_metricbeat_instructions.js (97%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/get_instruction_steps.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/disable_internal_collection_instructions.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/enable_metricbeat_instructions.js (97%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/disable_internal_collection_instructions.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/enable_metricbeat_instructions.js (97%) rename x-pack/{legacy => }/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/checker_errors.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/__tests__/checker_errors.test.js (92%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/__tests__/no_data.test.js (84%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/_index.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/_no_data.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/blurbs/changes_needed.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/blurbs/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/blurbs/looking_for.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/blurbs/what_is.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/checker_errors.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/checking_settings.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/__snapshots__/collection_enabled.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/collection_enabled.test.js (93%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/collection_enabled/collection_enabled.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/__snapshots__/collection_interval.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/collection_interval.test.js (96%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/collection_interval/collection_interval.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/__snapshots__/exporters.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/exporters.test.js (93%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/exporters/exporters.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/__snapshots__/plugin_enabled.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/plugin_enabled.test.js (92%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/plugin_enabled.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/no_data.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/reason_found.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/we_tried.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/reasons/__tests__/reason_found.test.js (96%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/reasons/__tests__/we_tried.test.js (85%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/reasons/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/reasons/reason_found.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/no_data/reasons/we_tried.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/page_loading/__tests__/__snapshots__/page_loading.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/page_loading/__tests__/page_loading.test.js (85%) rename x-pack/{legacy => }/plugins/monitoring/public/components/page_loading/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/renderers/__snapshots__/setup_mode.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/renderers/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/renderers/lib/find_new_uuid.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/renderers/setup_mode.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/renderers/setup_mode.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/__snapshots__/badge.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/__snapshots__/enter_button.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/__snapshots__/formatting.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/__snapshots__/listing_callout.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/__snapshots__/tooltip.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/_enter_button.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/_index.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/badge.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/badge.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/enter_button.test.tsx (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/enter_button.tsx (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/formatting.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/formatting.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/listing_callout.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/listing_callout.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/tooltip.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/setup_mode/tooltip.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/sparkline/__mocks__/plugins/xpack_main/jquery_flot.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/sparkline/__test__/__snapshots__/index.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/sparkline/__test__/index.test.js (95%) rename x-pack/{legacy => }/plugins/monitoring/public/components/sparkline/_index.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/sparkline/_sparkline.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/sparkline/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/sparkline/sparkline_flot_chart.js (98%) rename x-pack/{legacy => }/plugins/monitoring/public/components/status_icon/_index.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/status_icon/_status_icon.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/status_icon/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/summary_status/__snapshots__/summary_status.test.js.snap (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/summary_status/_index.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/summary_status/_summary_status.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/summary_status/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/summary_status/summary_status.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/summary_status/summary_status.test.js (95%) rename x-pack/{legacy => }/plugins/monitoring/public/components/table/_index.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/table/_table.scss (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/table/eui_table.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/table/eui_table_ssp.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/table/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/components/table/storage.js (100%) rename x-pack/{legacy/plugins/monitoring/public/directives/beats/overview => plugins/monitoring/public/directives/beats/beat}/index.js (59%) rename x-pack/{legacy/plugins/monitoring/public/directives/beats/beat => plugins/monitoring/public/directives/beats/overview}/index.js (55%) rename x-pack/{legacy => }/plugins/monitoring/public/directives/elasticsearch/ml_job_listing/index.js (63%) rename x-pack/{legacy => }/plugins/monitoring/public/directives/main/__tests__/monitoring_main_controller.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/directives/main/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/directives/main/index.js (93%) rename x-pack/{legacy => }/plugins/monitoring/public/icons/health-gray.svg (100%) rename x-pack/{legacy => }/plugins/monitoring/public/icons/health-green.svg (100%) rename x-pack/{legacy => }/plugins/monitoring/public/icons/health-red.svg (100%) rename x-pack/{legacy => }/plugins/monitoring/public/icons/health-yellow.svg (100%) rename x-pack/{legacy => }/plugins/monitoring/public/index.scss (83%) rename x-pack/{legacy/plugins/monitoring/public/np_ready => plugins/monitoring/public}/index.ts (84%) rename x-pack/{legacy => }/plugins/monitoring/public/jest.helpers.ts (80%) create mode 100644 x-pack/plugins/monitoring/public/legacy_shims.ts rename x-pack/{legacy => }/plugins/monitoring/public/lib/__tests__/format_number.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/ajax_error_handler.tsx (88%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/calculate_shard_stats.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/elasticsearch_settings/__tests__/enabler.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/elasticsearch_settings/__tests__/settings_checker.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/elasticsearch_settings/__tests__/start_checks.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/elasticsearch_settings/checkers/cluster_settings.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/elasticsearch_settings/checkers/node_settings.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/elasticsearch_settings/checkers/settings_checker.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/elasticsearch_settings/enabler.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/elasticsearch_settings/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/elasticsearch_settings/start_checks.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/ensure_minimum_time.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/ensure_minimum_time.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/extract_ip.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/form_validation.ts (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/format_number.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/get_cluster_from_clusters.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/get_page_data.js (87%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/get_safe_for_external_link.ts (100%) create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/API.md create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/index.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.colorhelpers.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.canvas.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.categories.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.crosshair.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.errorbars.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.fillbetween.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.image.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.log.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.navigate.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.pie.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.resize.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.selection.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.stack.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.symbol.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.threshold.js create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.time.js rename x-pack/{legacy/plugins/monitoring/common => plugins/monitoring/public/lib/jquery_flot}/index.js (76%) create mode 100644 x-pack/plugins/monitoring/public/lib/jquery_flot/jquery_flot.js rename x-pack/{legacy => }/plugins/monitoring/public/lib/logstash/__tests__/pipelines.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/logstash/pipelines.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/route_init.js (95%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/setup_mode.test.js (72%) rename x-pack/{legacy => }/plugins/monitoring/public/lib/setup_mode.tsx (82%) create mode 100644 x-pack/plugins/monitoring/public/plugin.ts rename x-pack/{legacy => }/plugins/monitoring/public/services/__tests__/breadcrumbs.js (96%) rename x-pack/{legacy/plugins/monitoring/public/services/__tests__/executor_provider.js => plugins/monitoring/public/services/__tests__/executor.js} (87%) rename x-pack/{legacy/plugins/monitoring/public/services/breadcrumbs_provider.js => plugins/monitoring/public/services/breadcrumbs.js} (98%) rename x-pack/{legacy => }/plugins/monitoring/public/services/clusters.js (77%) rename x-pack/{legacy/plugins/monitoring/public/services/executor_provider.js => plugins/monitoring/public/services/executor.js} (66%) rename x-pack/{legacy => }/plugins/monitoring/public/services/features.js (86%) rename x-pack/{legacy => }/plugins/monitoring/public/services/license.js (88%) rename x-pack/{legacy => }/plugins/monitoring/public/services/title.js (54%) create mode 100644 x-pack/plugins/monitoring/public/types.ts create mode 100644 x-pack/plugins/monitoring/public/url_state.ts rename x-pack/{legacy => }/plugins/monitoring/public/views/__tests__/base_controller.js (95%) rename x-pack/{legacy => }/plugins/monitoring/public/views/__tests__/base_table_controller.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/access_denied/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/access_denied/index.js (89%) rename x-pack/{legacy => }/plugins/monitoring/public/views/alerts/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/alerts/index.js (77%) rename x-pack/{legacy => }/plugins/monitoring/public/views/all.js (98%) rename x-pack/{legacy => }/plugins/monitoring/public/views/apm/instance/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/apm/instance/index.js (83%) rename x-pack/{legacy => }/plugins/monitoring/public/views/apm/instances/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/apm/instances/index.js (70%) rename x-pack/{legacy => }/plugins/monitoring/public/views/apm/overview/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/apm/overview/index.js (81%) rename x-pack/{legacy => }/plugins/monitoring/public/views/base_controller.js (77%) rename x-pack/{legacy => }/plugins/monitoring/public/views/base_eui_table_controller.js (97%) rename x-pack/{legacy => }/plugins/monitoring/public/views/base_table_controller.js (95%) rename x-pack/{legacy => }/plugins/monitoring/public/views/beats/beat/get_page_data.js (82%) rename x-pack/{legacy => }/plugins/monitoring/public/views/beats/beat/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/beats/beat/index.js (92%) rename x-pack/{legacy => }/plugins/monitoring/public/views/beats/listing/get_page_data.js (80%) rename x-pack/{legacy => }/plugins/monitoring/public/views/beats/listing/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/beats/listing/index.js (66%) rename x-pack/{legacy => }/plugins/monitoring/public/views/beats/overview/get_page_data.js (80%) rename x-pack/{legacy => }/plugins/monitoring/public/views/beats/overview/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/beats/overview/index.js (91%) rename x-pack/{legacy => }/plugins/monitoring/public/views/cluster/listing/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/cluster/listing/index.js (75%) rename x-pack/{legacy => }/plugins/monitoring/public/views/cluster/overview/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/cluster/overview/index.js (68%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/ccr/get_page_data.js (80%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/ccr/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/ccr/index.js (83%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/ccr/shard/get_page_data.js (82%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js (86%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/index/advanced/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/index/advanced/index.js (79%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/index/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/index/index.js (80%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/indices/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/indices/index.js (80%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/ml_jobs/get_page_data.js (80%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.js (92%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/node/advanced/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js (78%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/node/get_page_data.js (84%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/node/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/node/index.js (84%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/nodes/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/nodes/index.js (74%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/overview/controller.js (83%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/overview/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/elasticsearch/overview/index.js (84%) rename x-pack/{legacy => }/plugins/monitoring/public/views/index.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/kibana/instance/index.html (100%) create mode 100644 x-pack/plugins/monitoring/public/views/kibana/instance/index.js rename x-pack/{legacy => }/plugins/monitoring/public/views/kibana/instances/get_page_data.js (80%) rename x-pack/{legacy => }/plugins/monitoring/public/views/kibana/instances/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/kibana/instances/index.js (56%) rename x-pack/{legacy => }/plugins/monitoring/public/views/kibana/overview/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/kibana/overview/index.js (58%) rename x-pack/{legacy => }/plugins/monitoring/public/views/license/controller.js (71%) rename x-pack/{legacy => }/plugins/monitoring/public/views/license/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/license/index.js (83%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/node/advanced/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/node/advanced/index.js (65%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/node/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/node/index.js (65%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/node/pipelines/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/node/pipelines/index.js (74%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/nodes/get_page_data.js (80%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/nodes/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/nodes/index.js (57%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/overview/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/overview/index.js (73%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/pipeline/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/pipeline/index.js (77%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/pipelines/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/logstash/pipelines/index.js (75%) rename x-pack/{legacy => }/plugins/monitoring/public/views/no_data/__tests__/model_updater.test.js (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/no_data/controller.js (87%) rename x-pack/{legacy => }/plugins/monitoring/public/views/no_data/index.html (100%) rename x-pack/{legacy => }/plugins/monitoring/public/views/no_data/index.js (63%) rename x-pack/{legacy => }/plugins/monitoring/public/views/no_data/model_updater.js (100%) diff --git a/.eslintignore b/.eslintignore index 2ed9ecf971ff35..4913192e81c1d9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -30,6 +30,7 @@ target /x-pack/legacy/plugins/canvas/canvas_plugin_src/lib/flot-charts /x-pack/legacy/plugins/canvas/shareable_runtime/build /x-pack/legacy/plugins/canvas/storybook +/x-pack/plugins/monitoring/public/lib/jquery_flot /x-pack/legacy/plugins/infra/common/graphql/types.ts /x-pack/legacy/plugins/infra/public/graphql/types.ts /x-pack/legacy/plugins/infra/server/graphql/types.ts diff --git a/.eslintrc.js b/.eslintrc.js index c9b41ec711b7f9..8b33ec83347a85 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -963,6 +963,12 @@ module.exports = { jquery: true, }, }, + { + files: ['x-pack/plugins/monitoring/public/lib/jquery_flot/**/*.js'], + env: { + jquery: true, + }, + }, /** * TSVB overrides diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c17538849660ed..3981a8e1e9afef 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -84,6 +84,7 @@ /x-pack/legacy/plugins/ingest_manager/ @elastic/ingest-management /x-pack/plugins/observability/ @elastic/logs-metrics-ui @elastic/apm-ui @elastic/uptime @elastic/ingest-management /x-pack/legacy/plugins/monitoring/ @elastic/stack-monitoring-ui +/x-pack/plugins/monitoring/ @elastic/stack-monitoring-ui /x-pack/plugins/uptime @elastic/uptime # Machine Learning diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 66296736b3ad04..14e25ab863dbcd 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -38,6 +38,7 @@ export const IGNORE_FILE_GLOBS = [ 'x-pack/legacy/plugins/apm/**/*', 'x-pack/legacy/plugins/canvas/tasks/**/*', 'x-pack/legacy/plugins/canvas/canvas_plugin_src/**/*', + 'x-pack/plugins/monitoring/public/lib/jquery_flot/**/*', '**/.*', '**/{webpackShims,__mocks__}/**/*', 'x-pack/docs/**/*', @@ -160,12 +161,11 @@ export const TEMPORARILY_IGNORED_PATHS = [ 'webpackShims/ui-bootstrap.js', 'x-pack/legacy/plugins/index_management/public/lib/editSettings.js', 'x-pack/legacy/plugins/license_management/public/store/reducers/licenseManagement.js', - 'x-pack/legacy/plugins/monitoring/public/components/sparkline/__mocks__/plugins/xpack_main/jquery_flot.js', - 'x-pack/legacy/plugins/monitoring/public/icons/alert-blue.svg', - 'x-pack/legacy/plugins/monitoring/public/icons/health-gray.svg', - 'x-pack/legacy/plugins/monitoring/public/icons/health-green.svg', - 'x-pack/legacy/plugins/monitoring/public/icons/health-red.svg', - 'x-pack/legacy/plugins/monitoring/public/icons/health-yellow.svg', + 'x-pack/plugins/monitoring/public/components/sparkline/__mocks__/plugins/xpack_main/jquery_flot.js', + 'x-pack/plugins/monitoring/public/icons/health-gray.svg', + 'x-pack/plugins/monitoring/public/icons/health-green.svg', + 'x-pack/plugins/monitoring/public/icons/health-red.svg', + 'x-pack/plugins/monitoring/public/icons/health-yellow.svg', 'x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/pdf/assets/fonts/noto/NotoSansCJKtc-Medium.ttf', 'x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/pdf/assets/fonts/noto/NotoSansCJKtc-Regular.ttf', 'x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/pdf/assets/fonts/roboto/Roboto-Italic.ttf', diff --git a/x-pack/legacy/plugins/monitoring/.agignore b/x-pack/legacy/plugins/monitoring/.agignore deleted file mode 100644 index 10fb4038cbdc26..00000000000000 --- a/x-pack/legacy/plugins/monitoring/.agignore +++ /dev/null @@ -1,3 +0,0 @@ -agent -node_modules -bower_components diff --git a/x-pack/legacy/plugins/monitoring/common/__tests__/format_timestamp_to_duration.js b/x-pack/legacy/plugins/monitoring/common/__tests__/format_timestamp_to_duration.js deleted file mode 100644 index 470d596bd2bdcc..00000000000000 --- a/x-pack/legacy/plugins/monitoring/common/__tests__/format_timestamp_to_duration.js +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import moment from 'moment'; -import { formatTimestampToDuration } from '../format_timestamp_to_duration'; -import { CALCULATE_DURATION_SINCE, CALCULATE_DURATION_UNTIL } from '../constants'; - -const testTime = moment('2010-05-01'); // pick a date where adding/subtracting 2 months formats roundly to '2 months 0 days' -const getTestTime = () => moment(testTime); // clones the obj so it's not mutated with .adds and .subtracts - -/** - * Test the moment-duration-format template - */ -describe('formatTimestampToDuration', () => { - describe('format timestamp to duration - time since', () => { - it('should format timestamp to human-readable duration', () => { - // time inputs are a few "moments" extra from the time advertised by name - const fiftyNineSeconds = getTestTime().subtract(59, 'seconds'); - expect( - formatTimestampToDuration(fiftyNineSeconds, CALCULATE_DURATION_SINCE, getTestTime()) - ).to.be('59 seconds'); - - const fiveMins = getTestTime() - .subtract(5, 'minutes') - .subtract(30, 'seconds'); - expect(formatTimestampToDuration(fiveMins, CALCULATE_DURATION_SINCE, getTestTime())).to.be( - '6 mins' - ); - - const sixHours = getTestTime() - .subtract(6, 'hours') - .subtract(30, 'minutes'); - expect(formatTimestampToDuration(sixHours, CALCULATE_DURATION_SINCE, getTestTime())).to.be( - '6 hrs 30 mins' - ); - - const sevenDays = getTestTime() - .subtract(7, 'days') - .subtract(6, 'hours') - .subtract(18, 'minutes'); - expect(formatTimestampToDuration(sevenDays, CALCULATE_DURATION_SINCE, getTestTime())).to.be( - '7 days 6 hrs 18 mins' - ); - - const eightWeeks = getTestTime() - .subtract(8, 'weeks') - .subtract(7, 'days') - .subtract(6, 'hours') - .subtract(18, 'minutes'); - expect(formatTimestampToDuration(eightWeeks, CALCULATE_DURATION_SINCE, getTestTime())).to.be( - '2 months 2 days' - ); - - const oneHour = getTestTime().subtract(1, 'hour'); // should trim 0 min - expect(formatTimestampToDuration(oneHour, CALCULATE_DURATION_SINCE, getTestTime())).to.be( - '1 hr' - ); - - const oneDay = getTestTime().subtract(1, 'day'); // should trim 0 hrs - expect(formatTimestampToDuration(oneDay, CALCULATE_DURATION_SINCE, getTestTime())).to.be( - '1 day' - ); - - const twoMonths = getTestTime().subtract(2, 'month'); // should trim 0 days - expect(formatTimestampToDuration(twoMonths, CALCULATE_DURATION_SINCE, getTestTime())).to.be( - '2 months' - ); - }); - }); - - describe('format timestamp to duration - time until', () => { - it('should format timestamp to human-readable duration', () => { - // time inputs are a few "moments" extra from the time advertised by name - const fiftyNineSeconds = getTestTime().add(59, 'seconds'); - expect( - formatTimestampToDuration(fiftyNineSeconds, CALCULATE_DURATION_UNTIL, getTestTime()) - ).to.be('59 seconds'); - - const fiveMins = getTestTime().add(10, 'minutes'); - expect(formatTimestampToDuration(fiveMins, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( - '10 mins' - ); - - const sixHours = getTestTime() - .add(6, 'hours') - .add(30, 'minutes'); - expect(formatTimestampToDuration(sixHours, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( - '6 hrs 30 mins' - ); - - const sevenDays = getTestTime() - .add(7, 'days') - .add(6, 'hours') - .add(18, 'minutes'); - expect(formatTimestampToDuration(sevenDays, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( - '7 days 6 hrs 18 mins' - ); - - const eightWeeks = getTestTime() - .add(8, 'weeks') - .add(7, 'days') - .add(6, 'hours') - .add(18, 'minutes'); - expect(formatTimestampToDuration(eightWeeks, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( - '2 months 2 days' - ); - - const oneHour = getTestTime().add(1, 'hour'); // should trim 0 min - expect(formatTimestampToDuration(oneHour, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( - '1 hr' - ); - - const oneDay = getTestTime().add(1, 'day'); // should trim 0 hrs - expect(formatTimestampToDuration(oneDay, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( - '1 day' - ); - - const twoMonths = getTestTime().add(2, 'month'); // should trim 0 days - expect(formatTimestampToDuration(twoMonths, CALCULATE_DURATION_UNTIL, getTestTime())).to.be( - '2 months' - ); - }); - }); -}); diff --git a/x-pack/legacy/plugins/monitoring/common/cancel_promise.ts b/x-pack/legacy/plugins/monitoring/common/cancel_promise.ts deleted file mode 100644 index f100edda507961..00000000000000 --- a/x-pack/legacy/plugins/monitoring/common/cancel_promise.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export enum Status { - Canceled, - Failed, - Resolved, - Awaiting, - Idle, -} - -/** - * Simple [PromiseWithCancel] factory - */ -export class PromiseWithCancel { - private _promise: Promise; - private _status: Status = Status.Idle; - - /** - * @param {Promise} promise Promise you want to cancel / track - */ - constructor(promise: Promise) { - this._promise = promise; - } - - /** - * Cancel the promise in any state - */ - public cancel = (): void => { - this._status = Status.Canceled; - }; - - /** - * @returns status based on [Status] - */ - public status = (): Status => { - return this._status; - }; - - /** - * @returns promise passed in [constructor] - * This sets the state to Status.Awaiting - */ - public promise = (): Promise => { - if (this._status === Status.Canceled) { - throw Error('Getting a canceled promise is not allowed'); - } else if (this._status !== Status.Idle) { - return this._promise; - } - return new Promise((resolve, reject) => { - this._status = Status.Awaiting; - return this._promise - .then(response => { - if (this._status !== Status.Canceled) { - this._status = Status.Resolved; - return resolve(response); - } - }) - .catch(error => { - if (this._status !== Status.Canceled) { - this._status = Status.Failed; - return reject(error); - } - }); - }); - }; -} diff --git a/x-pack/legacy/plugins/monitoring/common/constants.ts b/x-pack/legacy/plugins/monitoring/common/constants.ts deleted file mode 100644 index 36030e1fa7f2a5..00000000000000 --- a/x-pack/legacy/plugins/monitoring/common/constants.ts +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/** - * Helper string to add as a tag in every logging call - */ -export const LOGGING_TAG = 'monitoring'; -/** - * Helper string to add as a tag in every logging call related to Kibana monitoring - */ -export const KIBANA_MONITORING_LOGGING_TAG = 'kibana-monitoring'; - -/** - * The Monitoring API version is the expected API format that we export and expect to import. - * @type {string} - */ -export const MONITORING_SYSTEM_API_VERSION = '7'; -/** - * The type name used within the Monitoring index to publish Kibana ops stats. - * @type {string} - */ -export const KIBANA_STATS_TYPE_MONITORING = 'kibana_stats'; // similar to KIBANA_STATS_TYPE but rolled up into 10s stats from 5s intervals through ops_buffer -/** - * The type name used within the Monitoring index to publish Kibana stats. - * @type {string} - */ -export const KIBANA_SETTINGS_TYPE = 'kibana_settings'; -/** - * The type name used within the Monitoring index to publish Kibana usage stats. - * NOTE: this string shows as-is in the stats API as a field name for the kibana usage stats - * @type {string} - */ -export const KIBANA_USAGE_TYPE = 'kibana'; - -/* - * Key for the localStorage service - */ -export const STORAGE_KEY = 'xpack.monitoring.data'; - -/** - * Units for derivative metric values - */ -export const NORMALIZED_DERIVATIVE_UNIT = '1s'; - -/* - * Values for column sorting in table options - * @type {number} 1 or -1 - */ -export const EUI_SORT_ASCENDING = 'asc'; -export const EUI_SORT_DESCENDING = 'desc'; -export const SORT_ASCENDING = 1; -export const SORT_DESCENDING = -1; - -/* - * Chart colors - * @type {string} - */ -export const CHART_LINE_COLOR = '#d2d2d2'; -export const CHART_TEXT_COLOR = '#9c9c9c'; - -/* - * Number of cluster alerts to show on overview page - * @type {number} - */ -export const CLUSTER_ALERTS_SEARCH_SIZE = 3; - -/* - * Format for moment-duration-format timestamp-to-duration template if the time diffs are gte 1 month - * @type {string} - */ -export const FORMAT_DURATION_TEMPLATE_LONG = 'M [months] d [days]'; - -/* - * Format for moment-duration-format timestamp-to-duration template if the time diffs are lt 1 month but gt 1 minute - * @type {string} - */ -export const FORMAT_DURATION_TEMPLATE_SHORT = ' d [days] h [hrs] m [min]'; - -/* - * Format for moment-duration-format timestamp-to-duration template if the time diffs are lt 1 minute - * @type {string} - */ -export const FORMAT_DURATION_TEMPLATE_TINY = ' s [seconds]'; - -/* - * Simple unique values for Timestamp to duration flags. These are used for - * determining if calculation should be formatted as "time until" (now to - * timestamp) or "time since" (timestamp to now) - */ -export const CALCULATE_DURATION_SINCE = 'since'; -export const CALCULATE_DURATION_UNTIL = 'until'; - -/** - * In order to show ML Jobs tab in the Elasticsearch section / tab navigation, license must be supported - */ -export const ML_SUPPORTED_LICENSES = ['trial', 'platinum', 'enterprise']; - -/** - * Metadata service URLs for the different cloud services that have constant URLs (e.g., unlike GCP, which is a constant prefix). - * - * @type {Object} - */ -export const CLOUD_METADATA_SERVICES = { - // We explicitly call out the version, 2016-09-02, rather than 'latest' to avoid unexpected changes - AWS_URL: 'http://169.254.169.254/2016-09-02/dynamic/instance-identity/document', - - // 2017-04-02 is the first GA release of this API - AZURE_URL: 'http://169.254.169.254/metadata/instance?api-version=2017-04-02', - - // GCP documentation shows both 'metadata.google.internal' (mostly) and '169.254.169.254' (sometimes) - // To bypass potential DNS changes, the IP was used because it's shared with other cloud services - GCP_URL_PREFIX: 'http://169.254.169.254/computeMetadata/v1/instance', -}; - -/** - * Constants used by Logstash monitoring code - */ -export const LOGSTASH = { - MAJOR_VER_REQD_FOR_PIPELINES: 6, - - /* - * Names ES keys on for different Logstash pipeline queues. - * @type {string} - */ - QUEUE_TYPES: { - MEMORY: 'memory', - PERSISTED: 'persisted', - }, -}; - -export const DEBOUNCE_SLOW_MS = 17; // roughly how long it takes to render a frame at 60fps -export const DEBOUNCE_FAST_MS = 10; // roughly how long it takes to render a frame at 100fps - -/** - * Configuration key for setting the email address used for cluster alert notifications. - */ -export const CLUSTER_ALERTS_ADDRESS_CONFIG_KEY = 'cluster_alerts.email_notifications.email_address'; - -export const STANDALONE_CLUSTER_CLUSTER_UUID = '__standalone_cluster__'; - -export const INDEX_PATTERN = '.monitoring-*-6-*,.monitoring-*-7-*'; -export const INDEX_PATTERN_KIBANA = '.monitoring-kibana-6-*,.monitoring-kibana-7-*'; -export const INDEX_PATTERN_LOGSTASH = '.monitoring-logstash-6-*,.monitoring-logstash-7-*'; -export const INDEX_PATTERN_BEATS = '.monitoring-beats-6-*,.monitoring-beats-7-*'; -export const INDEX_ALERTS = '.monitoring-alerts-6,.monitoring-alerts-7'; -export const INDEX_PATTERN_ELASTICSEARCH = '.monitoring-es-6-*,.monitoring-es-7-*'; - -// This is the unique token that exists in monitoring indices collected by metricbeat -export const METRICBEAT_INDEX_NAME_UNIQUE_TOKEN = '-mb-'; - -// We use this for metricbeat migration to identify specific products that we do not have constants for -export const ELASTICSEARCH_SYSTEM_ID = 'elasticsearch'; - -/** - * The id of the infra source owned by the monitoring plugin. - */ -export const INFRA_SOURCE_ID = 'internal-stack-monitoring'; - -/* - * These constants represent code paths within `getClustersFromRequest` - * that an api call wants to invoke. This is meant as an optimization to - * avoid unnecessary ES queries (looking at you logstash) when the data - * is not used. In the long term, it'd be nice to have separate api calls - * instead of this path logic. - */ -export const CODE_PATH_ALL = 'all'; -export const CODE_PATH_ALERTS = 'alerts'; -export const CODE_PATH_KIBANA = 'kibana'; -export const CODE_PATH_ELASTICSEARCH = 'elasticsearch'; -export const CODE_PATH_ML = 'ml'; -export const CODE_PATH_BEATS = 'beats'; -export const CODE_PATH_LOGSTASH = 'logstash'; -export const CODE_PATH_APM = 'apm'; -export const CODE_PATH_LICENSE = 'license'; -export const CODE_PATH_LOGS = 'logs'; - -/** - * The header sent by telemetry service when hitting Elasticsearch to identify query source - * @type {string} - */ -export const TELEMETRY_QUERY_SOURCE = 'TELEMETRY'; - -/** - * The name of the Kibana System ID used to publish and look up Kibana stats through the Monitoring system. - * @type {string} - */ -export const KIBANA_SYSTEM_ID = 'kibana'; - -/** - * The name of the Beats System ID used to publish and look up Beats stats through the Monitoring system. - * @type {string} - */ -export const BEATS_SYSTEM_ID = 'beats'; - -/** - * The name of the Apm System ID used to publish and look up Apm stats through the Monitoring system. - * @type {string} - */ -export const APM_SYSTEM_ID = 'apm'; - -/** - * The name of the Kibana System ID used to look up Logstash stats through the Monitoring system. - * @type {string} - */ -export const LOGSTASH_SYSTEM_ID = 'logstash'; - -/** - * The name of the Kibana System ID used to look up Reporting stats through the Monitoring system. - * @type {string} - */ -export const REPORTING_SYSTEM_ID = 'reporting'; - -/** - * The amount of time, in milliseconds, to wait between collecting kibana stats from es. - * - * Currently 24 hours kept in sync with reporting interval. - * @type {Number} - */ -export const TELEMETRY_COLLECTION_INTERVAL = 86400000; - -/** - * We want to slowly rollout the migration from watcher-based cluster alerts to - * kibana alerts and we only want to enable the kibana alerts once all - * watcher-based cluster alerts have been migrated so this flag will serve - * as the only way to see the new UI and actually run Kibana alerts. It will - * be false until all alerts have been migrated, then it will be removed - */ -export const KIBANA_ALERTING_ENABLED = false; - -/** - * The prefix for all alert types used by monitoring - */ -export const ALERT_TYPE_PREFIX = 'monitoring_'; - -/** - * This is the alert type id for the license expiration alert - */ -export const ALERT_TYPE_LICENSE_EXPIRATION = `${ALERT_TYPE_PREFIX}alert_type_license_expiration`; -/** - * This is the alert type id for the cluster state alert - */ -export const ALERT_TYPE_CLUSTER_STATE = `${ALERT_TYPE_PREFIX}alert_type_cluster_state`; - -/** - * A listing of all alert types - */ -export const ALERT_TYPES = [ALERT_TYPE_LICENSE_EXPIRATION, ALERT_TYPE_CLUSTER_STATE]; - -/** - * Matches the id for the built-in in email action type - * See x-pack/plugins/actions/server/builtin_action_types/email.ts - */ -export const ALERT_ACTION_TYPE_EMAIL = '.email'; - -/** - * The number of alerts that have been migrated - */ -export const NUMBER_OF_MIGRATED_ALERTS = 2; - -/** - * The advanced settings config name for the email address - */ -export const MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS = 'monitoring:alertingEmailAddress'; - -export const ALERT_EMAIL_SERVICES = ['gmail', 'hotmail', 'icloud', 'outlook365', 'ses', 'yahoo']; diff --git a/x-pack/legacy/plugins/monitoring/common/format_timestamp_to_duration.js b/x-pack/legacy/plugins/monitoring/common/format_timestamp_to_duration.js deleted file mode 100644 index 46c8f7db49b0f7..00000000000000 --- a/x-pack/legacy/plugins/monitoring/common/format_timestamp_to_duration.js +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import moment from 'moment'; -import 'moment-duration-format'; -import { - FORMAT_DURATION_TEMPLATE_TINY, - FORMAT_DURATION_TEMPLATE_SHORT, - FORMAT_DURATION_TEMPLATE_LONG, - CALCULATE_DURATION_SINCE, - CALCULATE_DURATION_UNTIL, -} from './constants'; - -/* - * Formats a timestamp string - * @param timestamp: ISO time string - * @param calculationFlag: control "since" or "until" logic - * @param initialTime {Object} moment object (not required) - * @return string - */ -export function formatTimestampToDuration(timestamp, calculationFlag, initialTime) { - initialTime = initialTime || moment(); - let timeDuration; - if (calculationFlag === CALCULATE_DURATION_SINCE) { - timeDuration = moment.duration(initialTime - moment(timestamp)); // since: now - timestamp - } else if (calculationFlag === CALCULATE_DURATION_UNTIL) { - timeDuration = moment.duration(moment(timestamp) - initialTime); // until: timestamp - now - } else { - throw new Error( - '[formatTimestampToDuration] requires a [calculationFlag] parameter to specify format as "since" or "until" the given time.' - ); - } - - // See https://github.com/elastic/x-pack-kibana/issues/3554 - let duration; - if (Math.abs(initialTime.diff(timestamp, 'months')) >= 1) { - // time diff is greater than 1 month, show months / days - duration = moment.duration(timeDuration).format(FORMAT_DURATION_TEMPLATE_LONG); - } else if (Math.abs(initialTime.diff(timestamp, 'minutes')) >= 1) { - // time diff is less than 1 month but greater than a minute, show days / hours / minutes - duration = moment.duration(timeDuration).format(FORMAT_DURATION_TEMPLATE_SHORT); - } else { - // time diff is less than a minute, show seconds - duration = moment.duration(timeDuration).format(FORMAT_DURATION_TEMPLATE_TINY); - } - - return duration - .replace(/ 0 mins$/, '') - .replace(/ 0 hrs$/, '') - .replace(/ 0 days$/, ''); // See https://github.com/jsmreese/moment-duration-format/issues/64 -} diff --git a/x-pack/legacy/plugins/monitoring/common/formatting.js b/x-pack/legacy/plugins/monitoring/common/formatting.js deleted file mode 100644 index ed5d68f942dfd1..00000000000000 --- a/x-pack/legacy/plugins/monitoring/common/formatting.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import moment from 'moment-timezone'; - -export const LARGE_FLOAT = '0,0.[00]'; -export const SMALL_FLOAT = '0.[00]'; -export const LARGE_BYTES = '0,0.0 b'; -export const SMALL_BYTES = '0.0 b'; -export const LARGE_ABBREVIATED = '0,0.[0]a'; - -/** - * Format the {@code date} in the user's expected date/time format using their dateFormat:tz defined time zone. - * @param date Either a numeric Unix timestamp or a {@code Date} object - * @returns The date formatted using 'LL LTS' - */ -export function formatDateTimeLocal(date, timezone) { - if (timezone === 'Browser') { - timezone = moment.tz.guess() || 'utc'; - } - - return moment.tz(date, timezone).format('LL LTS'); -} - -/** - * Shorten a Logstash Pipeline's hash for display purposes - * @param {string} hash The complete hash - * @return {string} The shortened hash - */ -export function shortenPipelineHash(hash) { - return hash.substr(0, 6); -} diff --git a/x-pack/legacy/plugins/monitoring/config.js b/x-pack/legacy/plugins/monitoring/config.ts similarity index 99% rename from x-pack/legacy/plugins/monitoring/config.js rename to x-pack/legacy/plugins/monitoring/config.ts index fd4e6512c50632..0c664fbe1c00c6 100644 --- a/x-pack/legacy/plugins/monitoring/config.js +++ b/x-pack/legacy/plugins/monitoring/config.ts @@ -9,7 +9,7 @@ * @param {Object} Joi - HapiJS Joi module that allows for schema validation * @return {Object} config schema */ -export const config = Joi => { +export const config = (Joi: any) => { const DEFAULT_REQUEST_HEADERS = ['authorization']; return Joi.object({ diff --git a/x-pack/legacy/plugins/monitoring/index.js b/x-pack/legacy/plugins/monitoring/index.js deleted file mode 100644 index ccb45dc1f446ff..00000000000000 --- a/x-pack/legacy/plugins/monitoring/index.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { get } from 'lodash'; -import { resolve } from 'path'; -import { config } from './config'; -import { getUiExports } from './ui_exports'; -import { KIBANA_ALERTING_ENABLED } from './common/constants'; - -/** - * Invokes plugin modules to instantiate the Monitoring plugin for Kibana - * @param kibana {Object} Kibana plugin instance - * @return {Object} Monitoring UI Kibana plugin object - */ -const deps = ['kibana', 'elasticsearch', 'xpack_main']; -if (KIBANA_ALERTING_ENABLED) { - deps.push(...['alerting', 'actions']); -} -export const monitoring = kibana => { - return new kibana.Plugin({ - require: deps, - id: 'monitoring', - configPrefix: 'monitoring', - publicDir: resolve(__dirname, 'public'), - init(server) { - const serverConfig = server.config(); - const npMonitoring = server.newPlatform.setup.plugins.monitoring; - if (npMonitoring) { - const kbnServerStatus = this.kbnServer.status; - npMonitoring.registerLegacyAPI({ - getServerStatus: () => { - const status = kbnServerStatus.toJSON(); - return get(status, 'overall.state'); - }, - }); - } - - server.injectUiAppVars('monitoring', () => { - return { - maxBucketSize: serverConfig.get('monitoring.ui.max_bucket_size'), - minIntervalSeconds: serverConfig.get('monitoring.ui.min_interval_seconds'), - kbnIndex: serverConfig.get('kibana.index'), - showLicenseExpiration: serverConfig.get('monitoring.ui.show_license_expiration'), - showCgroupMetricsElasticsearch: serverConfig.get( - 'monitoring.ui.container.elasticsearch.enabled' - ), - showCgroupMetricsLogstash: serverConfig.get('monitoring.ui.container.logstash.enabled'), // Note, not currently used, but see https://github.com/elastic/x-pack-kibana/issues/1559 part 2 - }; - }); - }, - config, - uiExports: getUiExports(), - }); -}; diff --git a/x-pack/legacy/plugins/monitoring/index.ts b/x-pack/legacy/plugins/monitoring/index.ts new file mode 100644 index 00000000000000..1a0fecb9ef5b57 --- /dev/null +++ b/x-pack/legacy/plugins/monitoring/index.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Hapi from 'hapi'; +import { config } from './config'; +import { KIBANA_ALERTING_ENABLED } from '../../../plugins/monitoring/common/constants'; + +/** + * Invokes plugin modules to instantiate the Monitoring plugin for Kibana + * @param kibana {Object} Kibana plugin instance + * @return {Object} Monitoring UI Kibana plugin object + */ +const deps = ['kibana', 'elasticsearch', 'xpack_main']; +if (KIBANA_ALERTING_ENABLED) { + deps.push(...['alerting', 'actions']); +} +export const monitoring = (kibana: any) => { + return new kibana.Plugin({ + require: deps, + id: 'monitoring', + configPrefix: 'monitoring', + init(server: Hapi.Server) { + const npMonitoring = server.newPlatform.setup.plugins.monitoring as object & { + registerLegacyAPI: (api: unknown) => void; + }; + if (npMonitoring) { + const kbnServerStatus = this.kbnServer.status; + npMonitoring.registerLegacyAPI({ + getServerStatus: () => { + const status = kbnServerStatus.toJSON(); + return status?.overall?.state; + }, + }); + } + }, + config, + }); +}; diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/chart_target.test.js b/x-pack/legacy/plugins/monitoring/public/components/chart/chart_target.test.js deleted file mode 100644 index d8a6f1ad6bd9e8..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/components/chart/chart_target.test.js +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import expect from '@kbn/expect'; -import { shallow } from 'enzyme'; -import { ChartTarget } from './chart_target'; - -const props = { - seriesToShow: ['Max Heap', 'Max Heap Used'], - series: [ - { - color: '#3ebeb0', - label: 'Max Heap', - id: 'Max Heap', - data: [ - [1562958960000, 1037959168], - [1562958990000, 1037959168], - [1562959020000, 1037959168], - ], - }, - { - color: '#3b73ac', - label: 'Max Heap Used', - id: 'Max Heap Used', - data: [ - [1562958960000, 639905768], - [1562958990000, 622312416], - [1562959020000, 555967504], - ], - }, - ], - timeRange: { - min: 1562958939851, - max: 1562962539851, - }, - hasLegend: true, - onBrush: () => void 0, - tickFormatter: () => void 0, - updateLegend: () => void 0, -}; - -jest.mock('../../np_imports/ui/chrome', () => { - return { - getBasePath: () => '', - }; -}); - -// TODO: Skipping for now, seems flaky in New Platform (needs more investigation) -describe.skip('Test legends to toggle series: ', () => { - const ids = props.series.map(item => item.id); - - describe('props.series: ', () => { - it('should toggle based on seriesToShow array', () => { - const component = shallow(); - - const componentClass = component.instance(); - - const seriesA = componentClass.filterData(props.series, [ids[0]]); - expect(seriesA.length).to.be(1); - expect(seriesA[0].id).to.be(ids[0]); - - const seriesB = componentClass.filterData(props.series, [ids[1]]); - expect(seriesB.length).to.be(1); - expect(seriesB[0].id).to.be(ids[1]); - - const seriesAB = componentClass.filterData(props.series, ids); - expect(seriesAB.length).to.be(2); - expect(seriesAB[0].id).to.be(ids[0]); - expect(seriesAB[1].id).to.be(ids[1]); - }); - }); -}); diff --git a/x-pack/legacy/plugins/monitoring/public/components/license/index.js b/x-pack/legacy/plugins/monitoring/public/components/license/index.js deleted file mode 100644 index d43896d5f8d849..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/components/license/index.js +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { - EuiPage, - EuiPageBody, - EuiSpacer, - EuiCodeBlock, - EuiPanel, - EuiText, - EuiLink, - EuiFlexGroup, - EuiFlexItem, - EuiScreenReaderOnly, -} from '@elastic/eui'; -import { LicenseStatus, AddLicense } from 'plugins/xpack_main/components'; -import { FormattedMessage } from '@kbn/i18n/react'; -import chrome from 'plugins/monitoring/np_imports/ui/chrome'; - -const licenseManagement = `${chrome.getBasePath()}/app/kibana#/management/elasticsearch/license_management`; - -const LicenseUpdateInfoForPrimary = ({ isPrimaryCluster, uploadLicensePath }) => { - if (!isPrimaryCluster) { - return null; - } - - // viewed license is for the cluster directly connected to Kibana - return ; -}; - -const LicenseUpdateInfoForRemote = ({ isPrimaryCluster }) => { - if (isPrimaryCluster) { - return null; - } - - // viewed license is for a remote monitored cluster not directly connected to Kibana - return ( - -

- -

- - - {`curl -XPUT -u 'https://:/_license' -H 'Content-Type: application/json' -d @license.json`} - -
- ); -}; - -export function License(props) { - const { status, type, isExpired, expiryDate } = props; - return ( - - -

- -

-
- - - - - - - - - - - - - -

- For more license options please visit  - License Management. -

-
-
-
- ); -} diff --git a/x-pack/legacy/plugins/monitoring/public/directives/all.js b/x-pack/legacy/plugins/monitoring/public/directives/all.js deleted file mode 100644 index 43ad80a7a7e945..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/directives/all.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import './main'; -import './elasticsearch/ml_job_listing'; -import './beats/overview'; -import './beats/beat'; diff --git a/x-pack/legacy/plugins/monitoring/public/filters/index.js b/x-pack/legacy/plugins/monitoring/public/filters/index.js deleted file mode 100644 index a67770ff50dc85..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/filters/index.js +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { capitalize } from 'lodash'; -import { uiModules } from 'plugins/monitoring/np_imports/ui/modules'; -import { formatNumber, formatMetric } from 'plugins/monitoring/lib/format_number'; -import { extractIp } from 'plugins/monitoring/lib/extract_ip'; - -const uiModule = uiModules.get('monitoring/filters', []); - -uiModule.filter('capitalize', function() { - return function(input) { - return capitalize(input.toLowerCase()); - }; -}); - -uiModule.filter('formatNumber', function() { - return formatNumber; -}); - -uiModule.filter('formatMetric', function() { - return formatMetric; -}); - -uiModule.filter('extractIp', function() { - return extractIp; -}); diff --git a/x-pack/legacy/plugins/monitoring/public/hacks/__tests__/toggle_app_link_in_nav.js b/x-pack/legacy/plugins/monitoring/public/hacks/__tests__/toggle_app_link_in_nav.js deleted file mode 100644 index 6ed33714867401..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/hacks/__tests__/toggle_app_link_in_nav.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; - -uiModules.get('kibana').constant('monitoringUiEnabled', true); diff --git a/x-pack/legacy/plugins/monitoring/public/hacks/toggle_app_link_in_nav.js b/x-pack/legacy/plugins/monitoring/public/hacks/toggle_app_link_in_nav.js deleted file mode 100644 index 9448e826f3723a..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/hacks/toggle_app_link_in_nav.js +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'ui/modules'; -import { npStart } from 'ui/new_platform'; - -uiModules.get('monitoring/hacks').run(monitoringUiEnabled => { - if (monitoringUiEnabled) { - return; - } - - npStart.core.chrome.navLinks.update('monitoring', { hidden: true }); -}); diff --git a/x-pack/legacy/plugins/monitoring/public/icons/monitoring.svg b/x-pack/legacy/plugins/monitoring/public/icons/monitoring.svg deleted file mode 100644 index e00faca26a2516..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/icons/monitoring.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/x-pack/legacy/plugins/monitoring/public/legacy.ts b/x-pack/legacy/plugins/monitoring/public/legacy.ts deleted file mode 100644 index 293b6ac7bd8211..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/legacy.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import 'plugins/monitoring/filters'; -import 'plugins/monitoring/services/clusters'; -import 'plugins/monitoring/services/features'; -import 'plugins/monitoring/services/executor'; -import 'plugins/monitoring/services/license'; -import 'plugins/monitoring/services/title'; -import 'plugins/monitoring/services/breadcrumbs'; -import 'plugins/monitoring/directives/all'; -import 'plugins/monitoring/views/all'; -import { npSetup, npStart } from '../public/np_imports/legacy_imports'; -import { plugin } from './np_ready'; -import { localApplicationService } from '../../../../../src/legacy/core_plugins/kibana/public/local_application_service'; - -const pluginInstance = plugin({} as any); -pluginInstance.setup(npSetup.core, npSetup.plugins); -pluginInstance.start(npStart.core, { - ...npStart.plugins, - __LEGACY: { - localApplicationService, - }, -}); diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/angular_config.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/angular/angular_config.ts deleted file mode 100644 index d1849d92479854..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/angular_config.ts +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - ICompileProvider, - IHttpProvider, - IHttpService, - ILocationProvider, - IModule, - IRootScopeService, -} from 'angular'; -import $ from 'jquery'; -import _, { cloneDeep, forOwn, get, set } from 'lodash'; -import * as Rx from 'rxjs'; -import { CoreStart, LegacyCoreStart } from 'kibana/public'; - -const isSystemApiRequest = (request: any) => - Boolean(request && request.headers && !!request.headers['kbn-system-api']); - -export const configureAppAngularModule = (angularModule: IModule, newPlatform: LegacyCoreStart) => { - const legacyMetadata = newPlatform.injectedMetadata.getLegacyMetadata(); - - forOwn(newPlatform.injectedMetadata.getInjectedVars(), (val, name) => { - if (name !== undefined) { - // The legacy platform modifies some of these values, clone to an unfrozen object. - angularModule.value(name, cloneDeep(val)); - } - }); - - angularModule - .value('kbnVersion', newPlatform.injectedMetadata.getKibanaVersion()) - .value('buildNum', legacyMetadata.buildNum) - .value('buildSha', legacyMetadata.buildSha) - .value('serverName', legacyMetadata.serverName) - .value('esUrl', getEsUrl(newPlatform)) - .value('uiCapabilities', newPlatform.application.capabilities) - .config(setupCompileProvider(newPlatform)) - .config(setupLocationProvider()) - .config($setupXsrfRequestInterceptor(newPlatform)) - .run(capture$httpLoadingCount(newPlatform)) - .run($setupUICapabilityRedirect(newPlatform)); -}; - -const getEsUrl = (newPlatform: CoreStart) => { - const a = document.createElement('a'); - a.href = newPlatform.http.basePath.prepend('/elasticsearch'); - const protocolPort = /https/.test(a.protocol) ? 443 : 80; - const port = a.port || protocolPort; - return { - host: a.hostname, - port, - protocol: a.protocol, - pathname: a.pathname, - }; -}; - -const setupCompileProvider = (newPlatform: LegacyCoreStart) => ( - $compileProvider: ICompileProvider -) => { - if (!newPlatform.injectedMetadata.getLegacyMetadata().devMode) { - $compileProvider.debugInfoEnabled(false); - } -}; - -const setupLocationProvider = () => ($locationProvider: ILocationProvider) => { - $locationProvider.html5Mode({ - enabled: false, - requireBase: false, - rewriteLinks: false, - }); - - $locationProvider.hashPrefix(''); -}; - -const $setupXsrfRequestInterceptor = (newPlatform: LegacyCoreStart) => { - const version = newPlatform.injectedMetadata.getLegacyMetadata().version; - - // Configure jQuery prefilter - $.ajaxPrefilter(({ kbnXsrfToken = true }: any, originalOptions, jqXHR) => { - if (kbnXsrfToken) { - jqXHR.setRequestHeader('kbn-version', version); - } - }); - - return ($httpProvider: IHttpProvider) => { - // Configure $httpProvider interceptor - $httpProvider.interceptors.push(() => { - return { - request(opts) { - const { kbnXsrfToken = true } = opts as any; - if (kbnXsrfToken) { - set(opts, ['headers', 'kbn-version'], version); - } - return opts; - }, - }; - }); - }; -}; - -/** - * Injected into angular module by ui/chrome angular integration - * and adds a root-level watcher that will capture the count of - * active $http requests on each digest loop and expose the count to - * the core.loadingCount api - * @param {Angular.Scope} $rootScope - * @param {HttpService} $http - * @return {undefined} - */ -const capture$httpLoadingCount = (newPlatform: CoreStart) => ( - $rootScope: IRootScopeService, - $http: IHttpService -) => { - newPlatform.http.addLoadingCountSource( - new Rx.Observable(observer => { - const unwatch = $rootScope.$watch(() => { - const reqs = $http.pendingRequests || []; - observer.next(reqs.filter(req => !isSystemApiRequest(req)).length); - }); - - return unwatch; - }) - ); -}; - -/** - * integrates with angular to automatically redirect to home if required - * capability is not met - */ -const $setupUICapabilityRedirect = (newPlatform: CoreStart) => ( - $rootScope: IRootScopeService, - $injector: any -) => { - const isKibanaAppRoute = window.location.pathname.endsWith('/app/kibana'); - // this feature only works within kibana app for now after everything is - // switched to the application service, this can be changed to handle all - // apps. - if (!isKibanaAppRoute) { - return; - } - $rootScope.$on( - '$routeChangeStart', - (event, { $$route: route }: { $$route?: { requireUICapability: boolean } } = {}) => { - if (!route || !route.requireUICapability) { - return; - } - - if (!get(newPlatform.application.capabilities, route.requireUICapability)) { - $injector.get('kbnUrl').change('/home'); - event.preventDefault(); - } - } - ); -}; diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/index.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/angular/index.ts deleted file mode 100644 index 8fd8d170bbb406..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import angular, { IModule } from 'angular'; - -import { AppMountContext, LegacyCoreStart } from 'kibana/public'; - -// @ts-ignore TODO: change to absolute path -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -// @ts-ignore TODO: change to absolute path -import chrome from 'plugins/monitoring/np_imports/ui/chrome'; -// @ts-ignore TODO: change to absolute path -import { uiModules } from 'plugins/monitoring/np_imports/ui/modules'; -// @ts-ignore TODO: change to absolute path -import { registerTimefilterWithGlobalState } from 'plugins/monitoring/np_imports/ui/timefilter'; -import { configureAppAngularModule } from './angular_config'; - -import { localAppModule, appModuleName } from './modules'; - -export class AngularApp { - private injector?: angular.auto.IInjectorService; - - constructor({ core }: AppMountContext, { element }: { element: HTMLElement }) { - uiModules.addToModule(); - const app: IModule = localAppModule(core); - app.config(($routeProvider: any) => { - $routeProvider.eagerInstantiationEnabled(false); - uiRoutes.addToProvider($routeProvider); - }); - configureAppAngularModule(app, core as LegacyCoreStart); - registerTimefilterWithGlobalState(app); - const appElement = document.createElement('div'); - appElement.setAttribute('style', 'height: 100%'); - appElement.innerHTML = '
'; - this.injector = angular.bootstrap(appElement, [appModuleName]); - chrome.setInjector(this.injector); - angular.element(element).append(appElement); - } - - public destroy = () => { - if (this.injector) { - this.injector.get('$rootScope').$destroy(); - } - }; -} diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts deleted file mode 100644 index c6031cb2203341..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/modules.ts +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import angular, { IWindowService } from 'angular'; -// required for `ngSanitize` angular module -import 'angular-sanitize'; -import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; - -import { AppMountContext } from 'kibana/public'; -import { Storage } from '../../../../../../../src/plugins/kibana_utils/public'; -import { - createTopNavDirective, - createTopNavHelper, -} from '../../../../../../../src/plugins/kibana_legacy/public'; - -import { - GlobalStateProvider, - StateManagementConfigProvider, - AppStateProvider, - KbnUrlProvider, - npStart, -} from '../legacy_imports'; - -// @ts-ignore -import { PromiseServiceCreator } from './providers/promises'; -// @ts-ignore -import { PrivateProvider } from './providers/private'; -import { getSafeForExternalLink } from '../../lib/get_safe_for_external_link'; - -type IPrivate = (provider: (...injectable: any[]) => T) => T; - -export const appModuleName = 'monitoring'; -const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react']; - -export const localAppModule = (core: AppMountContext['core']) => { - createLocalI18nModule(); - createLocalPrivateModule(); - createLocalPromiseModule(); - createLocalStorage(); - createLocalConfigModule(core); - createLocalKbnUrlModule(); - createLocalStateModule(); - createLocalTopNavModule(npStart.plugins.navigation); - createHrefModule(core); - - const appModule = angular.module(appModuleName, [ - ...thirdPartyAngularDependencies, - 'monitoring/Config', - 'monitoring/I18n', - 'monitoring/Private', - 'monitoring/TopNav', - 'monitoring/State', - 'monitoring/Storage', - 'monitoring/href', - 'monitoring/services', - 'monitoring/filters', - 'monitoring/directives', - ]); - return appModule; -}; - -function createLocalStateModule() { - angular - .module('monitoring/State', [ - 'monitoring/Private', - 'monitoring/Config', - 'monitoring/KbnUrl', - 'monitoring/Promise', - ]) - .factory('AppState', function(Private: IPrivate) { - return Private(AppStateProvider); - }) - .service('globalState', function(Private: IPrivate) { - return Private(GlobalStateProvider); - }); -} - -function createLocalKbnUrlModule() { - angular - .module('monitoring/KbnUrl', ['monitoring/Private', 'ngRoute']) - .service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider)); -} - -function createLocalConfigModule(core: AppMountContext['core']) { - angular - .module('monitoring/Config', ['monitoring/Private']) - .provider('stateManagementConfig', StateManagementConfigProvider) - .provider('config', () => { - return { - $get: () => ({ - get: core.uiSettings.get.bind(core.uiSettings), - }), - }; - }); -} - -function createLocalPromiseModule() { - angular.module('monitoring/Promise', []).service('Promise', PromiseServiceCreator); -} - -function createLocalStorage() { - angular - .module('monitoring/Storage', []) - .service('localStorage', ($window: IWindowService) => new Storage($window.localStorage)) - .service('sessionStorage', ($window: IWindowService) => new Storage($window.sessionStorage)) - .service('sessionTimeout', () => {}); -} - -function createLocalPrivateModule() { - angular.module('monitoring/Private', []).provider('Private', PrivateProvider); -} - -function createLocalTopNavModule({ ui }: any) { - angular - .module('monitoring/TopNav', ['react']) - .directive('kbnTopNav', createTopNavDirective) - .directive('kbnTopNavHelper', createTopNavHelper(ui)); -} - -function createLocalI18nModule() { - angular - .module('monitoring/I18n', []) - .provider('i18n', I18nProvider) - .filter('i18n', i18nFilter) - .directive('i18nId', i18nDirective); -} - -function createHrefModule(core: AppMountContext['core']) { - const name: string = 'kbnHref'; - angular.module('monitoring/href', []).directive(name, () => { - return { - restrict: 'A', - link: { - pre: (_$scope, _$el, $attr) => { - $attr.$observe(name, val => { - if (val) { - const url = getSafeForExternalLink(val as string); - $attr.$set('href', core.http.basePath.prepend(url)); - } - }); - }, - }, - }; - }); -} diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/providers/promises.js b/x-pack/legacy/plugins/monitoring/public/np_imports/angular/providers/promises.js deleted file mode 100644 index 22adccaf3db7fd..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/providers/promises.js +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import _ from 'lodash'; - -export function PromiseServiceCreator($q, $timeout) { - function Promise(fn) { - if (typeof this === 'undefined') - throw new Error('Promise constructor must be called with "new"'); - - const defer = $q.defer(); - try { - fn(defer.resolve, defer.reject); - } catch (e) { - defer.reject(e); - } - return defer.promise; - } - - Promise.all = Promise.props = $q.all; - Promise.resolve = function(val) { - const defer = $q.defer(); - defer.resolve(val); - return defer.promise; - }; - Promise.reject = function(reason) { - const defer = $q.defer(); - defer.reject(reason); - return defer.promise; - }; - Promise.cast = $q.when; - Promise.delay = function(ms) { - return $timeout(_.noop, ms); - }; - Promise.method = function(fn) { - return function() { - const args = Array.prototype.slice.call(arguments); - return Promise.try(fn, args, this); - }; - }; - Promise.nodeify = function(promise, cb) { - promise.then(function(val) { - cb(void 0, val); - }, cb); - }; - Promise.map = function(arr, fn) { - return Promise.all( - arr.map(function(i, el, list) { - return Promise.try(fn, [i, el, list]); - }) - ); - }; - Promise.each = function(arr, fn) { - const queue = arr.slice(0); - let i = 0; - return (function next() { - if (!queue.length) return arr; - return Promise.try(fn, [arr.shift(), i++]).then(next); - })(); - }; - Promise.is = function(obj) { - // $q doesn't create instances of any constructor, promises are just objects with a then function - // https://github.com/angular/angular.js/blob/58f5da86645990ef984353418cd1ed83213b111e/src/ng/q.js#L335 - return obj && typeof obj.then === 'function'; - }; - Promise.halt = _.once(function() { - const promise = new Promise(() => {}); - promise.then = _.constant(promise); - promise.catch = _.constant(promise); - return promise; - }); - Promise.try = function(fn, args, ctx) { - if (typeof fn !== 'function') { - return Promise.reject(new TypeError('fn must be a function')); - } - - let value; - - if (Array.isArray(args)) { - try { - value = fn.apply(ctx, args); - } catch (e) { - return Promise.reject(e); - } - } else { - try { - value = fn.call(ctx, args); - } catch (e) { - return Promise.reject(e); - } - } - - return Promise.resolve(value); - }; - Promise.fromNode = function(takesCbFn) { - return new Promise(function(resolve, reject) { - takesCbFn(function(err, ...results) { - if (err) reject(err); - else if (results.length > 1) resolve(results); - else resolve(results[0]); - }); - }); - }; - Promise.race = function(iterable) { - return new Promise((resolve, reject) => { - for (const i of iterable) { - Promise.resolve(i).then(resolve, reject); - } - }); - }; - - return Promise; -} diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/legacy_imports.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/legacy_imports.ts deleted file mode 100644 index 208b7e2acdb0f7..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/legacy_imports.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/** - * Last remaining 'ui/*' imports that will eventually be shimmed with their np alternatives - */ - -export { npSetup, npStart } from 'ui/new_platform'; -// @ts-ignore -export { GlobalStateProvider } from 'ui/state_management/global_state'; -// @ts-ignore -export { StateManagementConfigProvider } from 'ui/state_management/config_provider'; -// @ts-ignore -export { AppStateProvider } from 'ui/state_management/app_state'; -// @ts-ignore -export { EventsProvider } from 'ui/events'; -// @ts-ignore -export { KbnUrlProvider } from 'ui/url'; -export { registerTimefilterWithGlobalStateFactory } from 'ui/timefilter/setup_router'; diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/ui/capabilities.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/ui/capabilities.ts deleted file mode 100644 index 5aff3025014019..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/ui/capabilities.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { npStart } from '../legacy_imports'; -export const capabilities = { get: () => npStart.core.application.capabilities }; diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/ui/chrome.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/ui/chrome.ts deleted file mode 100644 index f0c5bacabecbf2..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/ui/chrome.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import angular from 'angular'; -import { npStart, npSetup } from '../legacy_imports'; - -type OptionalInjector = void | angular.auto.IInjectorService; - -class Chrome { - private injector?: OptionalInjector; - - public setInjector = (injector: OptionalInjector): void => void (this.injector = injector); - public dangerouslyGetActiveInjector = (): OptionalInjector => this.injector; - - public getBasePath = (): string => npStart.core.http.basePath.get(); - - public getInjected = (name?: string, defaultValue?: any): string | unknown => { - const { getInjectedVar, getInjectedVars } = npSetup.core.injectedMetadata; - return name ? getInjectedVar(name, defaultValue) : getInjectedVars(); - }; - - public get breadcrumbs() { - const set = (...args: any[]) => npStart.core.chrome.setBreadcrumbs.apply(this, args as any); - return { set }; - } -} - -const chrome = new Chrome(); - -export default chrome; // eslint-disable-line import/no-default-export diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/ui/modules.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/ui/modules.ts deleted file mode 100644 index 70201a7906110f..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/ui/modules.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import angular from 'angular'; - -type PrivateProvider = (...args: any) => any; -interface Provider { - name: string; - provider: PrivateProvider; -} - -class Modules { - private _services: Provider[] = []; - private _filters: Provider[] = []; - private _directives: Provider[] = []; - - public get = (_name: string, _dep?: string[]) => { - return this; - }; - - public service = (...args: any) => { - this._services.push(args); - }; - - public filter = (...args: any) => { - this._filters.push(args); - }; - - public directive = (...args: any) => { - this._directives.push(args); - }; - - public addToModule = () => { - angular.module('monitoring/services', []); - angular.module('monitoring/filters', []); - angular.module('monitoring/directives', []); - - this._services.forEach(args => { - angular.module('monitoring/services').service.apply(null, args as any); - }); - - this._filters.forEach(args => { - angular.module('monitoring/filters').filter.apply(null, args as any); - }); - - this._directives.forEach(args => { - angular.module('monitoring/directives').directive.apply(null, args as any); - }); - }; -} - -export const uiModules = new Modules(); diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/ui/timefilter.ts b/x-pack/legacy/plugins/monitoring/public/np_imports/ui/timefilter.ts deleted file mode 100644 index e28699bd126b95..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/ui/timefilter.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { IModule, IRootScopeService } from 'angular'; -import { npStart, registerTimefilterWithGlobalStateFactory } from '../legacy_imports'; - -const { - core: { uiSettings }, -} = npStart; -export const { timefilter } = npStart.plugins.data.query.timefilter; - -uiSettings.overrideLocalDefault( - 'timepicker:refreshIntervalDefaults', - JSON.stringify({ value: 10000, pause: false }) -); -uiSettings.overrideLocalDefault( - 'timepicker:timeDefaults', - JSON.stringify({ from: 'now-1h', to: 'now' }) -); - -export const registerTimefilterWithGlobalState = (app: IModule) => { - app.run((globalState: any, $rootScope: IRootScopeService) => { - globalState.fetch(); - globalState.$inheritedGlobalState = true; - globalState.save(); - registerTimefilterWithGlobalStateFactory(timefilter, globalState, $rootScope); - }); -}; diff --git a/x-pack/legacy/plugins/monitoring/public/np_ready/plugin.ts b/x-pack/legacy/plugins/monitoring/public/np_ready/plugin.ts deleted file mode 100644 index 5598a7a51cf42f..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/np_ready/plugin.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { App, CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/public'; - -export class MonitoringPlugin implements Plugin { - constructor(ctx: PluginInitializerContext) {} - - public setup(core: CoreSetup, plugins: any) { - const app: App = { - id: 'monitoring', - title: 'Monitoring', - mount: async (context, params) => { - const { AngularApp } = await import('../np_imports/angular'); - const monitoringApp = new AngularApp(context, params); - return monitoringApp.destroy; - }, - }; - - core.application.register(app); - } - - public start(core: CoreStart, plugins: any) {} - public stop() {} -} diff --git a/x-pack/legacy/plugins/monitoring/public/register_feature.ts b/x-pack/legacy/plugins/monitoring/public/register_feature.ts deleted file mode 100644 index 9b72e01a193945..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/register_feature.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; -import chrome from 'ui/chrome'; -import { npSetup } from 'ui/new_platform'; -import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public'; - -const { - plugins: { home }, -} = npSetup; - -if (chrome.getInjected('monitoringUiEnabled')) { - home.featureCatalogue.register({ - id: 'monitoring', - title: i18n.translate('xpack.monitoring.monitoringTitle', { - defaultMessage: 'Monitoring', - }), - description: i18n.translate('xpack.monitoring.monitoringDescription', { - defaultMessage: 'Track the real-time health and performance of your Elastic Stack.', - }), - icon: 'monitoringApp', - path: '/app/monitoring', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN, - }); -} diff --git a/x-pack/legacy/plugins/monitoring/public/services/breadcrumbs.js b/x-pack/legacy/plugins/monitoring/public/services/breadcrumbs.js deleted file mode 100644 index d0fe6003863071..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/services/breadcrumbs.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'plugins/monitoring/np_imports/ui/modules'; -import { breadcrumbsProvider } from './breadcrumbs_provider'; -const uiModule = uiModules.get('monitoring/breadcrumbs', []); -uiModule.service('breadcrumbs', breadcrumbsProvider); diff --git a/x-pack/legacy/plugins/monitoring/public/services/executor.js b/x-pack/legacy/plugins/monitoring/public/services/executor.js deleted file mode 100644 index 5004cd02380128..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/services/executor.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { uiModules } from 'plugins/monitoring/np_imports/ui/modules'; -import { executorProvider } from './executor_provider'; -const uiModule = uiModules.get('monitoring/executor', []); -uiModule.service('$executor', executorProvider); diff --git a/x-pack/legacy/plugins/monitoring/public/views/kibana/instance/index.js b/x-pack/legacy/plugins/monitoring/public/views/kibana/instance/index.js deleted file mode 100644 index 6535bd74104450..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/views/kibana/instance/index.js +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -/* - * Kibana Instance - */ -import React from 'react'; -import { get } from 'lodash'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; -import template from './index.html'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; -import { - EuiPage, - EuiPageBody, - EuiPageContent, - EuiSpacer, - EuiFlexGrid, - EuiFlexItem, - EuiPanel, -} from '@elastic/eui'; -import { MonitoringTimeseriesContainer } from '../../../components/chart'; -import { DetailStatus } from 'plugins/monitoring/components/kibana/detail_status'; -import { I18nContext } from 'ui/i18n'; -import { MonitoringViewBaseController } from '../../base_controller'; -import { CODE_PATH_KIBANA } from '../../../../common/constants'; - -function getPageData($injector) { - const $http = $injector.get('$http'); - const globalState = $injector.get('globalState'); - const $route = $injector.get('$route'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/kibana/${$route.current.params.uuid}`; - const timeBounds = timefilter.getBounds(); - - return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, - }) - .then(response => response.data) - .catch(err => { - const Private = $injector.get('Private'); - const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); - return ajaxErrorHandlers(err); - }); -} - -uiRoutes.when('/kibana/instances/:uuid', { - template, - resolve: { - clusters(Private) { - const routeInit = Private(routeInitProvider); - return routeInit({ codePaths: [CODE_PATH_KIBANA] }); - }, - pageData: getPageData, - }, - controllerAs: 'monitoringKibanaInstanceApp', - controller: class extends MonitoringViewBaseController { - constructor($injector, $scope) { - super({ - title: `Kibana - ${get($scope.pageData, 'kibanaSummary.name')}`, - defaultData: {}, - getPageData, - reactNodeId: 'monitoringKibanaInstanceApp', - $scope, - $injector, - }); - - $scope.$watch( - () => this.data, - data => { - if (!data || !data.metrics) { - return; - } - - this.setTitle(`Kibana - ${get(data, 'kibanaSummary.name')}`); - - this.renderReact( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); - } - ); - } - }, -}); diff --git a/x-pack/legacy/plugins/monitoring/public/views/loading/index.html b/x-pack/legacy/plugins/monitoring/public/views/loading/index.html deleted file mode 100644 index 11da26a0ceed24..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/views/loading/index.html +++ /dev/null @@ -1,5 +0,0 @@ - -
-
-
-
diff --git a/x-pack/legacy/plugins/monitoring/public/views/loading/index.js b/x-pack/legacy/plugins/monitoring/public/views/loading/index.js deleted file mode 100644 index 0488683845a7d4..00000000000000 --- a/x-pack/legacy/plugins/monitoring/public/views/loading/index.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { render, unmountComponentAtNode } from 'react-dom'; -import { PageLoading } from 'plugins/monitoring/components'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { I18nContext } from 'ui/i18n'; -import template from './index.html'; -import { CODE_PATH_LICENSE } from '../../../common/constants'; - -const REACT_DOM_ID = 'monitoringLoadingReactApp'; - -uiRoutes.when('/loading', { - template, - controller: class { - constructor($injector, $scope) { - const monitoringClusters = $injector.get('monitoringClusters'); - const kbnUrl = $injector.get('kbnUrl'); - - $scope.$on('$destroy', () => { - unmountComponentAtNode(document.getElementById(REACT_DOM_ID)); - }); - - $scope.$$postDigest(() => { - this.renderReact(); - }); - - monitoringClusters(undefined, undefined, [CODE_PATH_LICENSE]).then(clusters => { - if (clusters && clusters.length) { - kbnUrl.changePath('/home'); - return; - } - kbnUrl.changePath('/no-data'); - return; - }); - } - - renderReact() { - render( - - - , - document.getElementById(REACT_DOM_ID) - ); - } - }, -}); diff --git a/x-pack/legacy/plugins/monitoring/ui_exports.js b/x-pack/legacy/plugins/monitoring/ui_exports.js deleted file mode 100644 index e0c04411ef46b3..00000000000000 --- a/x-pack/legacy/plugins/monitoring/ui_exports.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; -import { resolve } from 'path'; -import { - MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS, - KIBANA_ALERTING_ENABLED, -} from './common/constants'; -import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; - -/** - * Configuration of dependency objects for the UI, which are needed for the - * Monitoring UI app and views and data for outside the monitoring - * app (injectDefaultVars and hacks) - * @return {Object} data per Kibana plugin uiExport schema - */ -export const getUiExports = () => { - const uiSettingDefaults = {}; - if (KIBANA_ALERTING_ENABLED) { - uiSettingDefaults[MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS] = { - name: i18n.translate('xpack.monitoring.alertingEmailAddress.name', { - defaultMessage: 'Alerting email address', - }), - value: '', - description: i18n.translate('xpack.monitoring.alertingEmailAddress.description', { - defaultMessage: `The default email address to receive alerts from Stack Monitoring`, - }), - category: ['monitoring'], - }; - } - - return { - app: { - title: i18n.translate('xpack.monitoring.stackMonitoringTitle', { - defaultMessage: 'Stack Monitoring', - }), - order: 9002, - description: i18n.translate('xpack.monitoring.uiExportsDescription', { - defaultMessage: 'Monitoring for Elastic Stack', - }), - icon: 'plugins/monitoring/icons/monitoring.svg', - euiIconType: 'monitoringApp', - linkToLastSubUrl: false, - main: 'plugins/monitoring/legacy', - category: DEFAULT_APP_CATEGORIES.management, - }, - injectDefaultVars(server) { - const config = server.config(); - return { - monitoringUiEnabled: config.get('monitoring.ui.enabled'), - monitoringLegacyEmailAddress: config.get( - 'monitoring.cluster_alerts.email_notifications.email_address' - ), - }; - }, - uiSettingDefaults, - hacks: ['plugins/monitoring/hacks/toggle_app_link_in_nav'], - home: ['plugins/monitoring/register_feature'], - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - }; -}; diff --git a/x-pack/legacy/plugins/xpack_main/server/routes/api/v1/settings.js b/x-pack/legacy/plugins/xpack_main/server/routes/api/v1/settings.js index c830fc9fcd483e..99071a2f85e136 100644 --- a/x-pack/legacy/plugins/xpack_main/server/routes/api/v1/settings.js +++ b/x-pack/legacy/plugins/xpack_main/server/routes/api/v1/settings.js @@ -6,7 +6,7 @@ import { boomify } from 'boom'; import { get } from 'lodash'; -import { KIBANA_SETTINGS_TYPE } from '../../../../../monitoring/common/constants'; +import { KIBANA_SETTINGS_TYPE } from '../../../../../../../plugins/monitoring/common/constants'; const getClusterUuid = async callCluster => { const { cluster_uuid: uuid } = await callCluster('info', { filterPath: 'cluster_uuid' }); diff --git a/x-pack/plugins/monitoring/kibana.json b/x-pack/plugins/monitoring/kibana.json index bbdf1a2e7cb767..115cc08871ea45 100644 --- a/x-pack/plugins/monitoring/kibana.json +++ b/x-pack/plugins/monitoring/kibana.json @@ -3,8 +3,8 @@ "version": "8.0.0", "kibanaVersion": "kibana", "configPath": ["monitoring"], - "requiredPlugins": ["licensing", "features"], - "optionalPlugins": ["alerting", "actions", "infra", "telemetryCollectionManager", "usageCollection"], + "requiredPlugins": ["licensing", "features", "data", "navigation", "kibanaLegacy"], + "optionalPlugins": ["alerting", "actions", "infra", "telemetryCollectionManager", "usageCollection", "home"], "server": true, - "ui": false + "ui": true } diff --git a/x-pack/legacy/plugins/monitoring/public/_hacks.scss b/x-pack/plugins/monitoring/public/_hacks.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/_hacks.scss rename to x-pack/plugins/monitoring/public/_hacks.scss diff --git a/x-pack/plugins/monitoring/public/angular/app_modules.ts b/x-pack/plugins/monitoring/public/angular/app_modules.ts new file mode 100644 index 00000000000000..3fa79cedf4ce78 --- /dev/null +++ b/x-pack/plugins/monitoring/public/angular/app_modules.ts @@ -0,0 +1,248 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import angular, { IWindowService } from 'angular'; +import '../views/all'; +// required for `ngSanitize` angular module +import 'angular-sanitize'; +import 'angular-route'; +import '../index.scss'; +import { capitalize } from 'lodash'; +import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; +import { AppMountContext } from 'kibana/public'; +import { Storage } from '../../../../../src/plugins/kibana_utils/public'; +import { + createTopNavDirective, + createTopNavHelper, +} from '../../../../../src/plugins/kibana_legacy/public'; +import { MonitoringPluginDependencies } from '../types'; +import { GlobalState } from '../url_state'; +import { getSafeForExternalLink } from '../lib/get_safe_for_external_link'; + +// @ts-ignore +import { formatNumber, formatMetric } from '../lib/format_number'; +// @ts-ignore +import { extractIp } from '../lib/extract_ip'; +// @ts-ignore +import { PrivateProvider } from './providers/private'; +// @ts-ignore +import { KbnUrlProvider } from './providers/url'; +// @ts-ignore +import { breadcrumbsProvider } from '../services/breadcrumbs'; +// @ts-ignore +import { monitoringClustersProvider } from '../services/clusters'; +// @ts-ignore +import { executorProvider } from '../services/executor'; +// @ts-ignore +import { featuresProvider } from '../services/features'; +// @ts-ignore +import { licenseProvider } from '../services/license'; +// @ts-ignore +import { titleProvider } from '../services/title'; +// @ts-ignore +import { monitoringBeatsBeatProvider } from '../directives/beats/beat'; +// @ts-ignore +import { monitoringBeatsOverviewProvider } from '../directives/beats/overview'; +// @ts-ignore +import { monitoringMlListingProvider } from '../directives/elasticsearch/ml_job_listing'; +// @ts-ignore +import { monitoringMainProvider } from '../directives/main'; + +export const appModuleName = 'monitoring'; + +type IPrivate = (provider: (...injectable: unknown[]) => T) => T; + +const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react', 'ui.bootstrap']; + +export const localAppModule = ({ + core, + data: { query }, + navigation, + externalConfig, +}: MonitoringPluginDependencies) => { + createLocalI18nModule(); + createLocalPrivateModule(); + createLocalStorage(); + createLocalConfigModule(core); + createLocalKbnUrlModule(); + createLocalStateModule(query); + createLocalTopNavModule(navigation); + createHrefModule(core); + createMonitoringAppServices(); + createMonitoringAppDirectives(); + createMonitoringAppConfigConstants(externalConfig); + createMonitoringAppFilters(); + + const appModule = angular.module(appModuleName, [ + ...thirdPartyAngularDependencies, + 'monitoring/I18n', + 'monitoring/Private', + 'monitoring/KbnUrl', + 'monitoring/Storage', + 'monitoring/Config', + 'monitoring/State', + 'monitoring/TopNav', + 'monitoring/href', + 'monitoring/constants', + 'monitoring/services', + 'monitoring/filters', + 'monitoring/directives', + ]); + return appModule; +}; + +function createMonitoringAppConfigConstants(keys: MonitoringPluginDependencies['externalConfig']) { + let constantsModule = angular.module('monitoring/constants', []); + keys.map(([key, value]) => (constantsModule = constantsModule.constant(key as string, value))); +} + +function createLocalStateModule(query: any) { + angular + .module('monitoring/State', ['monitoring/Private']) + .service('globalState', function( + Private: IPrivate, + $rootScope: ng.IRootScopeService, + $location: ng.ILocationService + ) { + function GlobalStateProvider(this: any) { + const state = new GlobalState(query, $rootScope, $location, this); + const initialState: any = state.getState(); + for (const key in initialState) { + if (!initialState.hasOwnProperty(key)) { + continue; + } + this[key] = initialState[key]; + } + this.save = () => { + const newState = { ...this }; + delete newState.save; + state.setState(newState); + }; + } + return Private(GlobalStateProvider); + }); +} + +function createLocalKbnUrlModule() { + angular + .module('monitoring/KbnUrl', ['monitoring/Private', 'ngRoute']) + .service('kbnUrl', function(Private: IPrivate) { + return Private(KbnUrlProvider); + }); +} + +function createMonitoringAppServices() { + angular + .module('monitoring/services', ['monitoring/Private']) + .service('breadcrumbs', function(Private: IPrivate) { + return Private(breadcrumbsProvider); + }) + .service('monitoringClusters', function(Private: IPrivate) { + return Private(monitoringClustersProvider); + }) + .service('$executor', function(Private: IPrivate) { + return Private(executorProvider); + }) + .service('features', function(Private: IPrivate) { + return Private(featuresProvider); + }) + .service('license', function(Private: IPrivate) { + return Private(licenseProvider); + }) + .service('title', function(Private: IPrivate) { + return Private(titleProvider); + }); +} + +function createMonitoringAppDirectives() { + angular + .module('monitoring/directives', []) + .directive('monitoringBeatsBeat', monitoringBeatsBeatProvider) + .directive('monitoringBeatsOverview', monitoringBeatsOverviewProvider) + .directive('monitoringMlListing', monitoringMlListingProvider) + .directive('monitoringMain', monitoringMainProvider); +} + +function createMonitoringAppFilters() { + angular + .module('monitoring/filters', []) + .filter('capitalize', function() { + return function(input: string) { + return capitalize(input?.toLowerCase()); + }; + }) + .filter('formatNumber', function() { + return formatNumber; + }) + .filter('formatMetric', function() { + return formatMetric; + }) + .filter('extractIp', function() { + return extractIp; + }); +} + +function createLocalConfigModule(core: MonitoringPluginDependencies['core']) { + angular.module('monitoring/Config', []).provider('config', function() { + return { + $get: () => ({ + get: (key: string) => core.uiSettings?.get(key), + }), + }; + }); +} + +function createLocalStorage() { + angular + .module('monitoring/Storage', []) + .service('localStorage', function($window: IWindowService) { + return new Storage($window.localStorage); + }) + .service('sessionStorage', function($window: IWindowService) { + return new Storage($window.sessionStorage); + }) + .service('sessionTimeout', function() { + return {}; + }); +} + +function createLocalPrivateModule() { + angular.module('monitoring/Private', []).provider('Private', PrivateProvider); +} + +function createLocalTopNavModule({ ui }: MonitoringPluginDependencies['navigation']) { + angular + .module('monitoring/TopNav', ['react']) + .directive('kbnTopNav', createTopNavDirective) + .directive('kbnTopNavHelper', createTopNavHelper(ui)); +} + +function createLocalI18nModule() { + angular + .module('monitoring/I18n', []) + .provider('i18n', I18nProvider) + .filter('i18n', i18nFilter) + .directive('i18nId', i18nDirective); +} + +function createHrefModule(core: AppMountContext['core']) { + const name: string = 'kbnHref'; + angular.module('monitoring/href', []).directive(name, function() { + return { + restrict: 'A', + link: { + pre: (_$scope, _$el, $attr) => { + $attr.$observe(name, val => { + if (val) { + const url = getSafeForExternalLink(val as string); + $attr.$set('href', core.http.basePath.prepend(url)); + } + }); + }, + }, + }; + }); +} diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/ui/routes.ts b/x-pack/plugins/monitoring/public/angular/helpers/routes.ts similarity index 62% rename from x-pack/legacy/plugins/monitoring/public/np_imports/ui/routes.ts rename to x-pack/plugins/monitoring/public/angular/helpers/routes.ts index 22da56a8d184af..b9307e8594a7ae 100644 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/ui/routes.ts +++ b/x-pack/plugins/monitoring/public/angular/helpers/routes.ts @@ -4,36 +4,35 @@ * you may not use this file except in compliance with the Elastic License. */ -type RouteObject = [string, any]; +type RouteObject = [string, { reloadOnSearch: boolean }]; interface Redirect { redirectTo: string; } class Routes { - private _routes: RouteObject[] = []; - private _redirect?: Redirect; + private routes: RouteObject[] = []; + public redirect?: Redirect = { redirectTo: '/no-data' }; public when = (...args: RouteObject) => { const [, routeOptions] = args; routeOptions.reloadOnSearch = false; - this._routes.push(args); + this.routes.push(args); return this; }; public otherwise = (redirect: Redirect) => { - this._redirect = redirect; + this.redirect = redirect; return this; }; public addToProvider = ($routeProvider: any) => { - this._routes.forEach(args => { + this.routes.forEach(args => { $routeProvider.when.apply(this, args); }); - if (this._redirect) { - $routeProvider.otherwise(this._redirect); + if (this.redirect) { + $routeProvider.otherwise(this.redirect); } }; } -const uiRoutes = new Routes(); -export default uiRoutes; // eslint-disable-line import/no-default-export +export const uiRoutes = new Routes(); diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/ui/utils.ts b/x-pack/plugins/monitoring/public/angular/helpers/utils.ts similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/np_imports/ui/utils.ts rename to x-pack/plugins/monitoring/public/angular/helpers/utils.ts diff --git a/x-pack/plugins/monitoring/public/angular/index.ts b/x-pack/plugins/monitoring/public/angular/index.ts new file mode 100644 index 00000000000000..b371503fdb7c95 --- /dev/null +++ b/x-pack/plugins/monitoring/public/angular/index.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import angular, { IModule } from 'angular'; +import { uiRoutes } from './helpers/routes'; +import { Legacy } from '../legacy_shims'; +import { configureAppAngularModule } from '../../../../../src/plugins/kibana_legacy/public'; +import { localAppModule, appModuleName } from './app_modules'; + +import { MonitoringPluginDependencies } from '../types'; + +const APP_WRAPPER_CLASS = 'monitoringApplicationWrapper'; +export class AngularApp { + private injector?: angular.auto.IInjectorService; + + constructor(deps: MonitoringPluginDependencies) { + const { + core, + element, + data, + navigation, + isCloud, + pluginInitializerContext, + externalConfig, + } = deps; + const app: IModule = localAppModule(deps); + app.run(($injector: angular.auto.IInjectorService) => { + this.injector = $injector; + Legacy.init( + { core, element, data, navigation, isCloud, pluginInitializerContext, externalConfig }, + this.injector + ); + }); + + app.config(($routeProvider: unknown) => uiRoutes.addToProvider($routeProvider)); + + const np = { core, env: pluginInitializerContext.env }; + configureAppAngularModule(app, np, true); + const appElement = document.createElement('div'); + appElement.setAttribute('style', 'height: 100%'); + appElement.innerHTML = '
'; + + if (!element.classList.contains(APP_WRAPPER_CLASS)) { + element.classList.add(APP_WRAPPER_CLASS); + } + + angular.bootstrap(appElement, [appModuleName]); + angular.element(element).append(appElement); + } + + public destroy = () => { + if (this.injector) { + this.injector.get('$rootScope').$destroy(); + } + }; + + public applyScope = () => { + if (!this.injector) { + return; + } + + const rootScope = this.injector.get('$rootScope'); + rootScope.$applyAsync(); + }; +} diff --git a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/providers/private.js b/x-pack/plugins/monitoring/public/angular/providers/private.js similarity index 99% rename from x-pack/legacy/plugins/monitoring/public/np_imports/angular/providers/private.js rename to x-pack/plugins/monitoring/public/angular/providers/private.js index 6eae978b828b31..e456f2617f7b88 100644 --- a/x-pack/legacy/plugins/monitoring/public/np_imports/angular/providers/private.js +++ b/x-pack/plugins/monitoring/public/angular/providers/private.js @@ -193,4 +193,6 @@ export function PrivateProvider() { return Private; }, ]; + + return provider; } diff --git a/x-pack/plugins/monitoring/public/angular/providers/url.js b/x-pack/plugins/monitoring/public/angular/providers/url.js new file mode 100644 index 00000000000000..57b63708b546e0 --- /dev/null +++ b/x-pack/plugins/monitoring/public/angular/providers/url.js @@ -0,0 +1,217 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import _ from 'lodash'; + +export function KbnUrlProvider($injector, $location, $rootScope, $parse) { + /** + * the `kbnUrl` service was created to smooth over some of the + * inconsistent behavior that occurs when modifying the url via + * the `$location` api. In general it is recommended that you use + * the `kbnUrl` service any time you want to modify the url. + * + * "features" that `kbnUrl` does it's best to guarantee, which + * are not guaranteed with the `$location` service: + * - calling `kbnUrl.change()` with a url that resolves to the current + * route will force a full transition (rather than just updating the + * properties of the $route object) + * + * Additional features of `kbnUrl` + * - parameterized urls + * - easily include an app state with the url + * + * @type {KbnUrl} + */ + const self = this; + + /** + * Navigate to a url + * + * @param {String} url - the new url, can be a template. See #eval + * @param {Object} [paramObj] - optional set of parameters for the url template + * @return {undefined} + */ + self.change = function(url, paramObj, appState) { + self._changeLocation('url', url, paramObj, false, appState); + }; + + /** + * Same as #change except only changes the url's path, + * leaving the search string and such intact + * + * @param {String} path - the new path, can be a template. See #eval + * @param {Object} [paramObj] - optional set of parameters for the path template + * @return {undefined} + */ + self.changePath = function(path, paramObj) { + self._changeLocation('path', path, paramObj); + }; + + /** + * Same as #change except that it removes the current url from history + * + * @param {String} url - the new url, can be a template. See #eval + * @param {Object} [paramObj] - optional set of parameters for the url template + * @return {undefined} + */ + self.redirect = function(url, paramObj, appState) { + self._changeLocation('url', url, paramObj, true, appState); + }; + + /** + * Same as #redirect except only changes the url's path, + * leaving the search string and such intact + * + * @param {String} path - the new path, can be a template. See #eval + * @param {Object} [paramObj] - optional set of parameters for the path template + * @return {undefined} + */ + self.redirectPath = function(path, paramObj) { + self._changeLocation('path', path, paramObj, true); + }; + + /** + * Evaluate a url template. templates can contain double-curly wrapped + * expressions that are evaluated in the context of the paramObj + * + * @param {String} template - the url template to evaluate + * @param {Object} [paramObj] - the variables to expose to the template + * @return {String} - the evaluated result + * @throws {Error} If any of the expressions can't be parsed. + */ + self.eval = function(template, paramObj) { + paramObj = paramObj || {}; + + return template.replace(/\{\{([^\}]+)\}\}/g, function(match, expr) { + // remove filters + const key = expr.split('|')[0].trim(); + + // verify that the expression can be evaluated + const p = $parse(key)(paramObj); + + // if evaluation can't be made, throw + if (_.isUndefined(p)) { + throw new Error(`Replacement failed, unresolved expression: ${expr}`); + } + + return encodeURIComponent($parse(expr)(paramObj)); + }); + }; + + /** + * convert an object's route to an href, compatible with + * window.location.href= and + * + * @param {Object} obj - any object that list's it's routes at obj.routes{} + * @param {string} route - the route name + * @return {string} - the computed href + */ + self.getRouteHref = function(obj, route) { + return '#' + self.getRouteUrl(obj, route); + }; + + /** + * convert an object's route to a url, compatible with url.change() or $location.url() + * + * @param {Object} obj - any object that list's it's routes at obj.routes{} + * @param {string} route - the route name + * @return {string} - the computed url + */ + self.getRouteUrl = function(obj, route) { + const template = obj && obj.routes && obj.routes[route]; + if (template) return self.eval(template, obj); + }; + + /** + * Similar to getRouteUrl, supports objects which list their routes, + * and redirects to the named route. See #redirect + * + * @param {Object} obj - any object that list's it's routes at obj.routes{} + * @param {string} route - the route name + * @return {undefined} + */ + self.redirectToRoute = function(obj, route) { + self.redirect(self.getRouteUrl(obj, route)); + }; + + /** + * Similar to getRouteUrl, supports objects which list their routes, + * and changes the url to the named route. See #change + * + * @param {Object} obj - any object that list's it's routes at obj.routes{} + * @param {string} route - the route name + * @return {undefined} + */ + self.changeToRoute = function(obj, route) { + self.change(self.getRouteUrl(obj, route)); + }; + + /** + * Removes the given parameter from the url. Does so without modifying the browser + * history. + * @param param + */ + self.removeParam = function(param) { + $location.search(param, null).replace(); + }; + + ///// + // private api + ///// + let reloading; + + self._changeLocation = function(type, url, paramObj, replace, appState) { + const prev = { + path: $location.path(), + search: $location.search(), + }; + + url = self.eval(url, paramObj); + $location[type](url); + if (replace) $location.replace(); + + if (appState) { + $location.search(appState.getQueryParamName(), appState.toQueryParam()); + } + + const next = { + path: $location.path(), + search: $location.search(), + }; + + if ($injector.has('$route')) { + const $route = $injector.get('$route'); + + if (self._shouldForceReload(next, prev, $route)) { + reloading = $rootScope.$on('$locationChangeSuccess', function() { + // call the "unlisten" function returned by $on + reloading(); + reloading = false; + + $route.reload(); + }); + } + } + }; + + // determine if the router will automatically reload the route + self._shouldForceReload = function(next, prev, $route) { + if (reloading) return false; + + const route = $route.current && $route.current.$$route; + if (!route) return false; + + // for the purposes of determining whether the router will + // automatically be reloading, '' and '/' are equal + const nextPath = next.path || '/'; + const prevPath = prev.path || '/'; + if (nextPath !== prevPath) return false; + + const reloadOnSearch = route.reloadOnSearch; + const searchSame = _.isEqual(next.search, prev.search); + return (reloadOnSearch && searchSame) || !reloadOnSearch; + }; +} diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/__snapshots__/status.test.tsx.snap b/x-pack/plugins/monitoring/public/components/alerts/__snapshots__/status.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/__snapshots__/status.test.tsx.snap rename to x-pack/plugins/monitoring/public/components/alerts/__snapshots__/status.test.tsx.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/__tests__/map_severity.js b/x-pack/plugins/monitoring/public/components/alerts/__tests__/map_severity.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/__tests__/map_severity.js rename to x-pack/plugins/monitoring/public/components/alerts/__tests__/map_severity.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/alerts.js b/x-pack/plugins/monitoring/public/components/alerts/alerts.js similarity index 95% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/alerts.js rename to x-pack/plugins/monitoring/public/components/alerts/alerts.js index 95c1af5549198f..a86fdb1041a5c9 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/alerts.js +++ b/x-pack/plugins/monitoring/public/components/alerts/alerts.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import chrome from '../../np_imports/ui/chrome'; +import { Legacy } from '../../legacy_shims'; import { capitalize, get } from 'lodash'; import { formatDateTimeLocal } from '../../../common/formatting'; import { formatTimestampToDuration } from '../../../common'; @@ -16,8 +16,8 @@ import { ALERT_TYPE_CLUSTER_STATE, } from '../../../common/constants'; import { mapSeverity } from './map_severity'; -import { FormattedAlert } from 'plugins/monitoring/components/alerts/formatted_alert'; -import { EuiMonitoringTable } from 'plugins/monitoring/components/table'; +import { FormattedAlert } from '../../components/alerts/formatted_alert'; +import { EuiMonitoringTable } from '../../components/table'; import { EuiHealth, EuiIcon, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -162,7 +162,7 @@ export const Alerts = ({ alerts, angular, sorting, pagination, onTableChange }) category: get(alert, 'metadata.link', get(alert, 'type', null)), })); - const injector = chrome.dangerouslyGetActiveInjector(); + const injector = Legacy.shims.getAngularInjector(); const timezone = injector.get('config').get('dateFormat:tz'); return ( diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/__snapshots__/configuration.test.tsx.snap b/x-pack/plugins/monitoring/public/components/alerts/configuration/__snapshots__/configuration.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/__snapshots__/configuration.test.tsx.snap rename to x-pack/plugins/monitoring/public/components/alerts/configuration/__snapshots__/configuration.test.tsx.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step1.test.tsx.snap b/x-pack/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step1.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step1.test.tsx.snap rename to x-pack/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step1.test.tsx.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step2.test.tsx.snap b/x-pack/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step2.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step2.test.tsx.snap rename to x-pack/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step2.test.tsx.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step3.test.tsx.snap b/x-pack/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step3.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step3.test.tsx.snap rename to x-pack/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step3.test.tsx.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/configuration.test.tsx b/x-pack/plugins/monitoring/public/components/alerts/configuration/configuration.test.tsx similarity index 91% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/configuration.test.tsx rename to x-pack/plugins/monitoring/public/components/alerts/configuration/configuration.test.tsx index 6b7e2391e0301c..2c2d7c6464e1b2 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/configuration.test.tsx +++ b/x-pack/plugins/monitoring/public/components/alerts/configuration/configuration.test.tsx @@ -7,11 +7,15 @@ import React from 'react'; import { mockUseEffects } from '../../../jest.helpers'; import { shallow, ShallowWrapper } from 'enzyme'; -import { kfetch } from 'ui/kfetch'; +import { Legacy } from '../../../legacy_shims'; import { AlertsConfiguration, AlertsConfigurationProps } from './configuration'; -jest.mock('ui/kfetch', () => ({ - kfetch: jest.fn(), +jest.mock('../../../legacy_shims', () => ({ + Legacy: { + shims: { + kfetch: jest.fn(), + }, + }, })); const defaultProps: AlertsConfigurationProps = { @@ -61,7 +65,7 @@ describe('Configuration', () => { beforeEach(async () => { mockUseEffects(2); - (kfetch as jest.Mock).mockImplementation(() => { + (Legacy.shims.kfetch as jest.Mock).mockImplementation(() => { return { data: [ { @@ -101,7 +105,7 @@ describe('Configuration', () => { describe('edit action', () => { let component: ShallowWrapper; beforeEach(async () => { - (kfetch as jest.Mock).mockImplementation(() => { + (Legacy.shims.kfetch as jest.Mock).mockImplementation(() => { return { data: [], }; @@ -124,7 +128,7 @@ describe('Configuration', () => { describe('no email address', () => { let component: ShallowWrapper; beforeEach(async () => { - (kfetch as jest.Mock).mockImplementation(() => { + (Legacy.shims.kfetch as jest.Mock).mockImplementation(() => { return { data: [ { diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/configuration.tsx b/x-pack/plugins/monitoring/public/components/alerts/configuration/configuration.tsx similarity index 96% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/configuration.tsx rename to x-pack/plugins/monitoring/public/components/alerts/configuration/configuration.tsx index eaa474ba177b18..61f86b0f9b609c 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/configuration.tsx +++ b/x-pack/plugins/monitoring/public/components/alerts/configuration/configuration.tsx @@ -5,10 +5,10 @@ */ import React, { ReactNode } from 'react'; -import { kfetch } from 'ui/kfetch'; import { EuiSteps } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ActionResult } from '../../../../../../../plugins/actions/common'; +import { Legacy } from '../../../legacy_shims'; +import { ActionResult } from '../../../../../../plugins/actions/common'; import { ALERT_ACTION_TYPE_EMAIL } from '../../../../common/constants'; import { getMissingFieldErrors } from '../../../lib/form_validation'; import { Step1 } from './step1'; @@ -59,7 +59,7 @@ export const AlertsConfiguration: React.FC = ( }, [emailAddress]); async function fetchEmailActions() { - const kibanaActions = await kfetch({ + const kibanaActions = await Legacy.shims.kfetch({ method: 'GET', pathname: `/api/action/_getAll`, }); @@ -84,7 +84,7 @@ export const AlertsConfiguration: React.FC = ( setShowFormErrors(false); try { - await kfetch({ + await Legacy.shims.kfetch({ method: 'POST', pathname: `/api/monitoring/v1/alerts`, body: JSON.stringify({ selectedEmailActionId, emailAddress }), diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/index.ts b/x-pack/plugins/monitoring/public/components/alerts/configuration/index.ts similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/index.ts rename to x-pack/plugins/monitoring/public/components/alerts/configuration/index.ts diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step1.test.tsx b/x-pack/plugins/monitoring/public/components/alerts/configuration/step1.test.tsx similarity index 83% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step1.test.tsx rename to x-pack/plugins/monitoring/public/components/alerts/configuration/step1.test.tsx index 19a1a61d00a424..5734d379dfb0cf 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step1.test.tsx +++ b/x-pack/plugins/monitoring/public/components/alerts/configuration/step1.test.tsx @@ -49,9 +49,13 @@ describe('Step1', () => { beforeEach(() => { jest.isolateModules(() => { - jest.doMock('ui/kfetch', () => ({ - kfetch: () => { - return {}; + jest.doMock('../../../legacy_shims', () => ({ + Legacy: { + shims: { + kfetch: () => { + return {}; + }, + }, }, })); setModules(); @@ -97,8 +101,12 @@ describe('Step1', () => { it('should send up the create to the server', async () => { const kfetch = jest.fn().mockImplementation(() => {}); jest.isolateModules(() => { - jest.doMock('ui/kfetch', () => ({ - kfetch, + jest.doMock('../../../legacy_shims', () => ({ + Legacy: { + shims: { + kfetch, + }, + }, })); setModules(); }); @@ -152,8 +160,12 @@ describe('Step1', () => { it('should send up the edit to the server', async () => { const kfetch = jest.fn().mockImplementation(() => {}); jest.isolateModules(() => { - jest.doMock('ui/kfetch', () => ({ - kfetch, + jest.doMock('../../../legacy_shims', () => ({ + Legacy: { + shims: { + kfetch, + }, + }, })); setModules(); }); @@ -194,13 +206,17 @@ describe('Step1', () => { describe('testing', () => { it('should allow for testing', async () => { jest.isolateModules(() => { - jest.doMock('ui/kfetch', () => ({ - kfetch: jest.fn().mockImplementation(arg => { - if (arg.pathname === '/api/action/1/_execute') { - return { status: 'ok' }; - } - return {}; - }), + jest.doMock('../../../legacy_shims', () => ({ + Legacy: { + shims: { + kfetch: jest.fn().mockImplementation(arg => { + if (arg.pathname === '/api/action/1/_execute') { + return { status: 'ok' }; + } + return {}; + }), + }, + }, })); setModules(); }); @@ -234,12 +250,16 @@ describe('Step1', () => { it('should show a successful test', async () => { jest.isolateModules(() => { - jest.doMock('ui/kfetch', () => ({ - kfetch: (arg: any) => { - if (arg.pathname === '/api/action/1/_execute') { - return { status: 'ok' }; - } - return {}; + jest.doMock('../../../legacy_shims', () => ({ + Legacy: { + shims: { + kfetch: (arg: any) => { + if (arg.pathname === '/api/action/1/_execute') { + return { status: 'ok' }; + } + return {}; + }, + }, }, })); setModules(); @@ -257,12 +277,16 @@ describe('Step1', () => { it('should show a failed test error', async () => { jest.isolateModules(() => { - jest.doMock('ui/kfetch', () => ({ - kfetch: (arg: any) => { - if (arg.pathname === '/api/action/1/_execute') { - return { message: 'Very detailed error message' }; - } - return {}; + jest.doMock('../../../legacy_shims', () => ({ + Legacy: { + shims: { + kfetch: (arg: any) => { + if (arg.pathname === '/api/action/1/_execute') { + return { message: 'Very detailed error message' }; + } + return {}; + }, + }, }, })); setModules(); @@ -304,8 +328,12 @@ describe('Step1', () => { it('should send up the delete to the server', async () => { const kfetch = jest.fn().mockImplementation(() => {}); jest.isolateModules(() => { - jest.doMock('ui/kfetch', () => ({ - kfetch, + jest.doMock('../../../legacy_shims', () => ({ + Legacy: { + shims: { + kfetch, + }, + }, })); setModules(); }); diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step1.tsx b/x-pack/plugins/monitoring/public/components/alerts/configuration/step1.tsx similarity index 97% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step1.tsx rename to x-pack/plugins/monitoring/public/components/alerts/configuration/step1.tsx index a69bf29dd98742..7953010005885d 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step1.tsx +++ b/x-pack/plugins/monitoring/public/components/alerts/configuration/step1.tsx @@ -16,10 +16,10 @@ import { EuiToolTip, EuiCallOut, } from '@elastic/eui'; -import { kfetch } from 'ui/kfetch'; import { omit, pick } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { ActionResult, BASE_ACTION_API_PATH } from '../../../../../../../plugins/actions/common'; +import { Legacy } from '../../../legacy_shims'; +import { ActionResult, BASE_ACTION_API_PATH } from '../../../../../../plugins/actions/common'; import { ManageEmailAction, EmailActionData } from '../manage_email_action'; import { ALERT_ACTION_TYPE_EMAIL } from '../../../../common/constants'; import { NEW_ACTION_ID } from './configuration'; @@ -42,7 +42,7 @@ export const Step1: React.FC = (props: GetStep1Props) => { async function createEmailAction(data: EmailActionData) { if (props.editAction) { - await kfetch({ + await Legacy.shims.kfetch({ method: 'PUT', pathname: `${BASE_ACTION_API_PATH}/${props.editAction.id}`, body: JSON.stringify({ @@ -53,7 +53,7 @@ export const Step1: React.FC = (props: GetStep1Props) => { }); props.setEditAction(null); } else { - await kfetch({ + await Legacy.shims.kfetch({ method: 'POST', pathname: BASE_ACTION_API_PATH, body: JSON.stringify({ @@ -73,7 +73,7 @@ export const Step1: React.FC = (props: GetStep1Props) => { async function deleteEmailAction(id: string) { setIsDeleting(true); - await kfetch({ + await Legacy.shims.kfetch({ method: 'DELETE', pathname: `${BASE_ACTION_API_PATH}/${id}`, }); @@ -99,7 +99,7 @@ export const Step1: React.FC = (props: GetStep1Props) => { to: [props.emailAddress], }; - const result = await kfetch({ + const result = await Legacy.shims.kfetch({ method: 'POST', pathname: `${BASE_ACTION_API_PATH}/${props.selectedEmailActionId}/_execute`, body: JSON.stringify({ params }), diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step2.test.tsx b/x-pack/plugins/monitoring/public/components/alerts/configuration/step2.test.tsx similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step2.test.tsx rename to x-pack/plugins/monitoring/public/components/alerts/configuration/step2.test.tsx diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step2.tsx b/x-pack/plugins/monitoring/public/components/alerts/configuration/step2.tsx similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step2.tsx rename to x-pack/plugins/monitoring/public/components/alerts/configuration/step2.tsx diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step3.test.tsx b/x-pack/plugins/monitoring/public/components/alerts/configuration/step3.test.tsx similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step3.test.tsx rename to x-pack/plugins/monitoring/public/components/alerts/configuration/step3.test.tsx diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step3.tsx b/x-pack/plugins/monitoring/public/components/alerts/configuration/step3.tsx similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step3.tsx rename to x-pack/plugins/monitoring/public/components/alerts/configuration/step3.tsx diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/formatted_alert.js b/x-pack/plugins/monitoring/public/components/alerts/formatted_alert.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/formatted_alert.js rename to x-pack/plugins/monitoring/public/components/alerts/formatted_alert.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/index.js b/x-pack/plugins/monitoring/public/components/alerts/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/index.js rename to x-pack/plugins/monitoring/public/components/alerts/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/manage_email_action.tsx b/x-pack/plugins/monitoring/public/components/alerts/manage_email_action.tsx similarity index 99% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/manage_email_action.tsx rename to x-pack/plugins/monitoring/public/components/alerts/manage_email_action.tsx index 2bd9804795cb56..3ef9654076340f 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/manage_email_action.tsx +++ b/x-pack/plugins/monitoring/public/components/alerts/manage_email_action.tsx @@ -21,7 +21,7 @@ import { EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ActionResult } from '../../../../../../plugins/actions/common'; +import { ActionResult } from '../../../../../plugins/actions/common'; import { getMissingFieldErrors, hasErrors, getRequiredFieldError } from '../../lib/form_validation'; import { ALERT_EMAIL_SERVICES } from '../../../common/constants'; diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/map_severity.js b/x-pack/plugins/monitoring/public/components/alerts/map_severity.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/map_severity.js rename to x-pack/plugins/monitoring/public/components/alerts/map_severity.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/status.test.tsx b/x-pack/plugins/monitoring/public/components/alerts/status.test.tsx similarity index 83% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/status.test.tsx rename to x-pack/plugins/monitoring/public/components/alerts/status.test.tsx index d3cf4b463a2cc5..a0031f50951bd1 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/status.test.tsx +++ b/x-pack/plugins/monitoring/public/components/alerts/status.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { kfetch } from 'ui/kfetch'; +import { Legacy } from '../../legacy_shims'; import { AlertsStatus, AlertsStatusProps } from './status'; import { ALERT_TYPES } from '../../../common/constants'; import { getSetupModeState } from '../../lib/setup_mode'; @@ -18,8 +18,16 @@ jest.mock('../../lib/setup_mode', () => ({ toggleSetupMode: jest.fn(), })); -jest.mock('ui/kfetch', () => ({ - kfetch: jest.fn(), +jest.mock('../../legacy_shims', () => ({ + Legacy: { + shims: { + kfetch: jest.fn(), + docLinks: { + ELASTIC_WEBSITE_URL: 'https://www.elastic.co/', + DOC_LINK_VERSION: 'current', + }, + }, + }, })); const defaultProps: AlertsStatusProps = { @@ -35,7 +43,7 @@ describe('Status', () => { enabled: false, }); - (kfetch as jest.Mock).mockImplementation(({ pathname }) => { + (Legacy.shims.kfetch as jest.Mock).mockImplementation(({ pathname }) => { if (pathname === '/internal/security/api_key/privileges') { return { areApiKeysEnabled: true }; } @@ -62,7 +70,7 @@ describe('Status', () => { }); it('should render a success message if all alerts have been migrated and in setup mode', async () => { - (kfetch as jest.Mock).mockReturnValue({ + (Legacy.shims.kfetch as jest.Mock).mockReturnValue({ data: ALERT_TYPES.map(type => ({ alertTypeId: type })), }); diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx b/x-pack/plugins/monitoring/public/components/alerts/status.tsx similarity index 93% rename from x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx rename to x-pack/plugins/monitoring/public/components/alerts/status.tsx index 5f5329bf7fff83..cdddbf10313038 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx +++ b/x-pack/plugins/monitoring/public/components/alerts/status.tsx @@ -5,7 +5,6 @@ */ import React, { Fragment } from 'react'; -import { kfetch } from 'ui/kfetch'; import { EuiSpacer, EuiCallOut, @@ -18,8 +17,8 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; -import { Alert, BASE_ALERT_API_PATH } from '../../../../../../plugins/alerting/common'; +import { Legacy } from '../../legacy_shims'; +import { Alert, BASE_ALERT_API_PATH } from '../../../../../plugins/alerting/common'; import { getSetupModeState, addSetupModeCallback, toggleSetupMode } from '../../lib/setup_mode'; import { NUMBER_OF_MIGRATED_ALERTS, ALERT_TYPE_PREFIX } from '../../../common/constants'; import { AlertsConfiguration } from './configuration'; @@ -39,7 +38,10 @@ export const AlertsStatus: React.FC = (props: AlertsStatusPro React.useEffect(() => { async function fetchAlertsStatus() { - const alerts = await kfetch({ method: 'GET', pathname: `${BASE_ALERT_API_PATH}/_find` }); + const alerts = await Legacy.shims.kfetch({ + method: 'GET', + pathname: `${BASE_ALERT_API_PATH}/_find`, + }); const monitoringAlerts = alerts.data.filter((alert: Alert) => alert.alertTypeId.startsWith(ALERT_TYPE_PREFIX) ); @@ -57,7 +59,9 @@ export const AlertsStatus: React.FC = (props: AlertsStatusPro }, [setupModeEnabled, showMigrationFlyout]); async function fetchSecurityConfigured() { - const response = await kfetch({ pathname: '/internal/security/api_key/privileges' }); + const response = await Legacy.shims.kfetch({ + pathname: '/internal/security/api_key/privileges', + }); setIsSecurityConfigured(response.areApiKeysEnabled); } @@ -72,7 +76,7 @@ export const AlertsStatus: React.FC = (props: AlertsStatusPro if (isSecurityConfigured) { return null; } - + const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = Legacy.shims.docLinks; const link = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/security-settings.html#api-key-service-settings`; return ( diff --git a/x-pack/legacy/plugins/monitoring/public/components/apm/instance/index.js b/x-pack/plugins/monitoring/public/components/apm/instance/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/apm/instance/index.js rename to x-pack/plugins/monitoring/public/components/apm/instance/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/apm/instance/instance.js b/x-pack/plugins/monitoring/public/components/apm/instance/instance.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/apm/instance/instance.js rename to x-pack/plugins/monitoring/public/components/apm/instance/instance.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/apm/instance/status.js b/x-pack/plugins/monitoring/public/components/apm/instance/status.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/apm/instance/status.js rename to x-pack/plugins/monitoring/public/components/apm/instance/status.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/apm/instances/index.js b/x-pack/plugins/monitoring/public/components/apm/instances/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/apm/instances/index.js rename to x-pack/plugins/monitoring/public/components/apm/instances/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/apm/instances/instances.js b/x-pack/plugins/monitoring/public/components/apm/instances/instances.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/apm/instances/instances.js rename to x-pack/plugins/monitoring/public/components/apm/instances/instances.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/apm/instances/status.js b/x-pack/plugins/monitoring/public/components/apm/instances/status.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/apm/instances/status.js rename to x-pack/plugins/monitoring/public/components/apm/instances/status.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/apm/overview/index.js b/x-pack/plugins/monitoring/public/components/apm/overview/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/apm/overview/index.js rename to x-pack/plugins/monitoring/public/components/apm/overview/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/apm/status_icon.js b/x-pack/plugins/monitoring/public/components/apm/status_icon.js similarity index 91% rename from x-pack/legacy/plugins/monitoring/public/components/apm/status_icon.js rename to x-pack/plugins/monitoring/public/components/apm/status_icon.js index 2de77b70df6461..073c56217e8df5 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/apm/status_icon.js +++ b/x-pack/plugins/monitoring/public/components/apm/status_icon.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { StatusIcon } from 'plugins/monitoring/components/status_icon'; +import { StatusIcon } from '../../components/status_icon'; import { i18n } from '@kbn/i18n'; export function ApmStatusIcon({ status, availability = true }) { diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/beat/beat.js b/x-pack/plugins/monitoring/public/components/beats/beat/beat.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/beat/beat.js rename to x-pack/plugins/monitoring/public/components/beats/beat/beat.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/beat/index.js b/x-pack/plugins/monitoring/public/components/beats/beat/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/beat/index.js rename to x-pack/plugins/monitoring/public/components/beats/beat/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/index.js b/x-pack/plugins/monitoring/public/components/beats/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/index.js rename to x-pack/plugins/monitoring/public/components/beats/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/listing/index.js b/x-pack/plugins/monitoring/public/components/beats/listing/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/listing/index.js rename to x-pack/plugins/monitoring/public/components/beats/listing/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/listing/listing.js b/x-pack/plugins/monitoring/public/components/beats/listing/listing.js similarity index 96% rename from x-pack/legacy/plugins/monitoring/public/components/beats/listing/listing.js rename to x-pack/plugins/monitoring/public/components/beats/listing/listing.js index a20728eb9a58fc..5863f6e5161ad6 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/beats/listing/listing.js +++ b/x-pack/plugins/monitoring/public/components/beats/listing/listing.js @@ -14,9 +14,9 @@ import { EuiLink, EuiScreenReaderOnly, } from '@elastic/eui'; -import { Stats } from 'plugins/monitoring/components/beats'; -import { formatMetric } from 'plugins/monitoring/lib/format_number'; -import { EuiMonitoringTable } from 'plugins/monitoring/components/table'; +import { Stats } from '../../beats'; +import { formatMetric } from '../../../lib/format_number'; +import { EuiMonitoringTable } from '../../table'; import { i18n } from '@kbn/i18n'; import { BEATS_SYSTEM_ID } from '../../../../common/constants'; import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_active.test.js.snap b/x-pack/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_active.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_active.test.js.snap rename to x-pack/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_active.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_types.test.js.snap b/x-pack/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_types.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_types.test.js.snap rename to x-pack/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_types.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_versions.test.js.snap b/x-pack/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_versions.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_versions.test.js.snap rename to x-pack/plugins/monitoring/public/components/beats/overview/__snapshots__/latest_versions.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/overview.test.js.snap b/x-pack/plugins/monitoring/public/components/beats/overview/__snapshots__/overview.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/__snapshots__/overview.test.js.snap rename to x-pack/plugins/monitoring/public/components/beats/overview/__snapshots__/overview.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/index.js b/x-pack/plugins/monitoring/public/components/beats/overview/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/index.js rename to x-pack/plugins/monitoring/public/components/beats/overview/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/latest_active.js b/x-pack/plugins/monitoring/public/components/beats/overview/latest_active.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/latest_active.js rename to x-pack/plugins/monitoring/public/components/beats/overview/latest_active.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/latest_active.test.js b/x-pack/plugins/monitoring/public/components/beats/overview/latest_active.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/latest_active.test.js rename to x-pack/plugins/monitoring/public/components/beats/overview/latest_active.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/latest_types.js b/x-pack/plugins/monitoring/public/components/beats/overview/latest_types.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/latest_types.js rename to x-pack/plugins/monitoring/public/components/beats/overview/latest_types.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/latest_types.test.js b/x-pack/plugins/monitoring/public/components/beats/overview/latest_types.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/latest_types.test.js rename to x-pack/plugins/monitoring/public/components/beats/overview/latest_types.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/latest_versions.js b/x-pack/plugins/monitoring/public/components/beats/overview/latest_versions.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/latest_versions.js rename to x-pack/plugins/monitoring/public/components/beats/overview/latest_versions.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/latest_versions.test.js b/x-pack/plugins/monitoring/public/components/beats/overview/latest_versions.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/latest_versions.test.js rename to x-pack/plugins/monitoring/public/components/beats/overview/latest_versions.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/overview.js b/x-pack/plugins/monitoring/public/components/beats/overview/overview.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/overview.js rename to x-pack/plugins/monitoring/public/components/beats/overview/overview.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/overview.test.js b/x-pack/plugins/monitoring/public/components/beats/overview/overview.test.js similarity index 93% rename from x-pack/legacy/plugins/monitoring/public/components/beats/overview/overview.test.js rename to x-pack/plugins/monitoring/public/components/beats/overview/overview.test.js index 1947f042b09b7e..006f6ce3db9759 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/beats/overview/overview.test.js +++ b/x-pack/plugins/monitoring/public/components/beats/overview/overview.test.js @@ -10,16 +10,10 @@ import { shallow } from 'enzyme'; jest.mock('../stats', () => ({ Stats: () => 'Stats', })); -jest.mock('../../', () => ({ +jest.mock('../../chart', () => ({ MonitoringTimeseriesContainer: () => 'MonitoringTimeseriesContainer', })); -jest.mock('../../../np_imports/ui/chrome', () => { - return { - getBasePath: () => '', - }; -}); - import { BeatsOverview } from './overview'; describe('Overview', () => { diff --git a/x-pack/legacy/plugins/monitoring/public/components/beats/stats.js b/x-pack/plugins/monitoring/public/components/beats/stats.js similarity index 95% rename from x-pack/legacy/plugins/monitoring/public/components/beats/stats.js rename to x-pack/plugins/monitoring/public/components/beats/stats.js index 672d8a79ca64a6..89ec10bbaf1bb6 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/beats/stats.js +++ b/x-pack/plugins/monitoring/public/components/beats/stats.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { formatMetric } from 'plugins/monitoring/lib/format_number'; +import { formatMetric } from '../../lib/format_number'; import { SummaryStatus } from '../summary_status'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/__tests__/get_color.js b/x-pack/plugins/monitoring/public/components/chart/__tests__/get_color.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/__tests__/get_color.js rename to x-pack/plugins/monitoring/public/components/chart/__tests__/get_color.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/__tests__/get_last_value.js b/x-pack/plugins/monitoring/public/components/chart/__tests__/get_last_value.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/__tests__/get_last_value.js rename to x-pack/plugins/monitoring/public/components/chart/__tests__/get_last_value.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/__tests__/get_title.js b/x-pack/plugins/monitoring/public/components/chart/__tests__/get_title.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/__tests__/get_title.js rename to x-pack/plugins/monitoring/public/components/chart/__tests__/get_title.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/__tests__/get_values_for_legend.js b/x-pack/plugins/monitoring/public/components/chart/__tests__/get_values_for_legend.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/__tests__/get_values_for_legend.js rename to x-pack/plugins/monitoring/public/components/chart/__tests__/get_values_for_legend.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/_chart.scss b/x-pack/plugins/monitoring/public/components/chart/_chart.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/_chart.scss rename to x-pack/plugins/monitoring/public/components/chart/_chart.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/_index.scss b/x-pack/plugins/monitoring/public/components/chart/_index.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/_index.scss rename to x-pack/plugins/monitoring/public/components/chart/_index.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/chart_target.js b/x-pack/plugins/monitoring/public/components/chart/chart_target.js similarity index 99% rename from x-pack/legacy/plugins/monitoring/public/components/chart/chart_target.js rename to x-pack/plugins/monitoring/public/components/chart/chart_target.js index 5c9cddf9c29022..e6a6cc4b77755e 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/chart/chart_target.js +++ b/x-pack/plugins/monitoring/public/components/chart/chart_target.js @@ -6,7 +6,7 @@ import _ from 'lodash'; import React from 'react'; -import $ from 'plugins/xpack_main/jquery_flot'; +import $ from '../../lib/jquery_flot'; import { eventBus } from './event_bus'; import { getChartOptions } from './get_chart_options'; diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/event_bus.js b/x-pack/plugins/monitoring/public/components/chart/event_bus.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/event_bus.js rename to x-pack/plugins/monitoring/public/components/chart/event_bus.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/get_chart_options.js b/x-pack/plugins/monitoring/public/components/chart/get_chart_options.js similarity index 92% rename from x-pack/legacy/plugins/monitoring/public/components/chart/get_chart_options.js rename to x-pack/plugins/monitoring/public/components/chart/get_chart_options.js index 661d51e068201f..81a3260447600e 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/chart/get_chart_options.js +++ b/x-pack/plugins/monitoring/public/components/chart/get_chart_options.js @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import chrome from '../../np_imports/ui/chrome'; +import { Legacy } from '../../legacy_shims'; import { merge } from 'lodash'; import { CHART_LINE_COLOR, CHART_TEXT_COLOR } from '../../../common/constants'; export async function getChartOptions(axisOptions) { - const $injector = chrome.dangerouslyGetActiveInjector(); + const $injector = Legacy.shims.getAngularInjector(); const timezone = $injector.get('config').get('dateFormat:tz'); const opts = { legend: { diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/get_color.js b/x-pack/plugins/monitoring/public/components/chart/get_color.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/get_color.js rename to x-pack/plugins/monitoring/public/components/chart/get_color.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/get_last_value.js b/x-pack/plugins/monitoring/public/components/chart/get_last_value.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/get_last_value.js rename to x-pack/plugins/monitoring/public/components/chart/get_last_value.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/get_title.js b/x-pack/plugins/monitoring/public/components/chart/get_title.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/get_title.js rename to x-pack/plugins/monitoring/public/components/chart/get_title.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/get_units.js b/x-pack/plugins/monitoring/public/components/chart/get_units.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/get_units.js rename to x-pack/plugins/monitoring/public/components/chart/get_units.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/get_values_for_legend.js b/x-pack/plugins/monitoring/public/components/chart/get_values_for_legend.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/get_values_for_legend.js rename to x-pack/plugins/monitoring/public/components/chart/get_values_for_legend.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/horizontal_legend.js b/x-pack/plugins/monitoring/public/components/chart/horizontal_legend.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/horizontal_legend.js rename to x-pack/plugins/monitoring/public/components/chart/horizontal_legend.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/index.js b/x-pack/plugins/monitoring/public/components/chart/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/index.js rename to x-pack/plugins/monitoring/public/components/chart/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/info_tooltip.js b/x-pack/plugins/monitoring/public/components/chart/info_tooltip.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/info_tooltip.js rename to x-pack/plugins/monitoring/public/components/chart/info_tooltip.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/monitoring_timeseries.js b/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/monitoring_timeseries.js rename to x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/monitoring_timeseries_container.js b/x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries_container.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/monitoring_timeseries_container.js rename to x-pack/plugins/monitoring/public/components/chart/monitoring_timeseries_container.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/timeseries_container.js b/x-pack/plugins/monitoring/public/components/chart/timeseries_container.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/timeseries_container.js rename to x-pack/plugins/monitoring/public/components/chart/timeseries_container.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/chart/timeseries_visualization.js b/x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/chart/timeseries_visualization.js rename to x-pack/plugins/monitoring/public/components/chart/timeseries_visualization.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/listing/alerts_indicator.js b/x-pack/plugins/monitoring/public/components/cluster/listing/alerts_indicator.js similarity index 97% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/listing/alerts_indicator.js rename to x-pack/plugins/monitoring/public/components/cluster/listing/alerts_indicator.js index 948f743ba01834..68d7a5a94e42f8 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/cluster/listing/alerts_indicator.js +++ b/x-pack/plugins/monitoring/public/components/cluster/listing/alerts_indicator.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { mapSeverity } from 'plugins/monitoring/components/alerts/map_severity'; +import { mapSeverity } from '../../alerts/map_severity'; import { EuiHealth, EuiToolTip } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/listing/index.js b/x-pack/plugins/monitoring/public/components/cluster/listing/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/listing/index.js rename to x-pack/plugins/monitoring/public/components/cluster/listing/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/listing/listing.js b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js similarity index 95% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/listing/listing.js rename to x-pack/plugins/monitoring/public/components/cluster/listing/listing.js index 4cf74b35957301..feda891c1ce297 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/cluster/listing/listing.js +++ b/x-pack/plugins/monitoring/public/components/cluster/listing/listing.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { Fragment, Component } from 'react'; -import chrome from 'plugins/monitoring/np_imports/ui/chrome'; +import { Legacy } from '../../../legacy_shims'; import moment from 'moment'; import numeral from '@elastic/numeral'; import { capitalize, partial } from 'lodash'; @@ -19,12 +19,11 @@ import { EuiSpacer, EuiIcon, } from '@elastic/eui'; -import { toastNotifications } from 'ui/notify'; -import { EuiMonitoringTable } from 'plugins/monitoring/components/table'; -import { AlertsIndicator } from 'plugins/monitoring/components/cluster/listing/alerts_indicator'; +import { EuiMonitoringTable } from '../../table'; +import { AlertsIndicator } from '../../cluster/listing/alerts_indicator'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public'; +import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public'; import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../../common/constants'; const IsClusterSupported = ({ isSupported, children }) => { @@ -236,13 +235,17 @@ const changeCluster = (scope, globalState, kbnUrl, clusterUuid, ccs) => { globalState.cluster_uuid = clusterUuid; globalState.ccs = ccs; globalState.save(); - kbnUrl.changePath('/overview'); + kbnUrl.redirect('/overview'); }); }; const licenseWarning = (scope, { title, text }) => { scope.$evalAsync(() => { - toastNotifications.addWarning({ title, text, 'data-test-subj': 'monitoringLicenseWarning' }); + Legacy.shims.toastNotifications.addWarning({ + title, + text, + 'data-test-subj': 'monitoringLicenseWarning', + }); }); }; @@ -285,7 +288,7 @@ const handleClickIncompatibleLicense = (scope, clusterName) => { }; const handleClickInvalidLicense = (scope, clusterName) => { - const licensingPath = `${chrome.getBasePath()}/app/kibana#/management/elasticsearch/license_management/home`; + const licensingPath = `${Legacy.shims.getBasePath()}/app/kibana#/management/elasticsearch/license_management/home`; licenseWarning(scope, { title: toMountPoint( diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/__tests__/__snapshots__/helpers.test.js.snap b/x-pack/plugins/monitoring/public/components/cluster/overview/__tests__/__snapshots__/helpers.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/overview/__tests__/__snapshots__/helpers.test.js.snap rename to x-pack/plugins/monitoring/public/components/cluster/overview/__tests__/__snapshots__/helpers.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/__tests__/helpers.test.js b/x-pack/plugins/monitoring/public/components/cluster/overview/__tests__/helpers.test.js similarity index 93% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/overview/__tests__/helpers.test.js rename to x-pack/plugins/monitoring/public/components/cluster/overview/__tests__/helpers.test.js index fea8f0001540af..e09c42bc594290 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/__tests__/helpers.test.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/__tests__/helpers.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { renderWithIntl } from '../../../../../../../../test_utils/enzyme_helpers'; +import { renderWithIntl } from 'test_utils/enzyme_helpers'; import { BytesUsage, BytesPercentageUsage } from '../helpers'; describe('Bytes Usage', () => { diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/alerts_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/alerts_panel.js similarity index 97% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/overview/alerts_panel.js rename to x-pack/plugins/monitoring/public/components/cluster/overview/alerts_panel.js index d87ff98e79be00..6dcd64f875e1ca 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/alerts_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/alerts_panel.js @@ -6,8 +6,8 @@ import React, { Fragment } from 'react'; import moment from 'moment-timezone'; -import { FormattedAlert } from 'plugins/monitoring/components/alerts/formatted_alert'; -import { mapSeverity } from 'plugins/monitoring/components/alerts/map_severity'; +import { FormattedAlert } from '../../alerts/formatted_alert'; +import { mapSeverity } from '../../alerts/map_severity'; import { formatTimestampToDuration } from '../../../../common/format_timestamp_to_duration'; import { CALCULATE_DURATION_SINCE, diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/apm_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js similarity index 98% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/overview/apm_panel.js rename to x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js index 84dc13e9da1de3..97205e0fcd732b 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/apm_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js @@ -7,7 +7,7 @@ import React from 'react'; import moment from 'moment'; import { get } from 'lodash'; -import { formatMetric } from 'plugins/monitoring/lib/format_number'; +import { formatMetric } from '../../../lib/format_number'; import { ClusterItemContainer, BytesUsage, DisabledIfNoDataAndInSetupModeLink } from './helpers'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/beats_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/beats_panel.js similarity index 98% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/overview/beats_panel.js rename to x-pack/plugins/monitoring/public/components/cluster/overview/beats_panel.js index 7406c15f3cf1d9..c20770bdda6b74 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/beats_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/beats_panel.js @@ -6,7 +6,7 @@ import { get } from 'lodash'; import React from 'react'; -import { formatMetric } from 'plugins/monitoring/lib/format_number'; +import { formatMetric } from '../../../lib/format_number'; import { EuiFlexGrid, EuiFlexItem, diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js similarity index 99% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js rename to x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js index 48ff8edaafe60d..41dc662c942116 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js @@ -6,7 +6,7 @@ import React, { Fragment } from 'react'; import { get, capitalize } from 'lodash'; -import { formatNumber } from 'plugins/monitoring/lib/format_number'; +import { formatNumber } from '../../../lib/format_number'; import { ClusterItemContainer, HealthStatusIndicator, diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/helpers.js b/x-pack/plugins/monitoring/public/components/cluster/overview/helpers.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/overview/helpers.js rename to x-pack/plugins/monitoring/public/components/cluster/overview/helpers.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/index.js b/x-pack/plugins/monitoring/public/components/cluster/overview/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/overview/index.js rename to x-pack/plugins/monitoring/public/components/cluster/overview/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/kibana_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js similarity index 98% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/overview/kibana_panel.js rename to x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js index ee17ce446dd6d6..541c240b3c35aa 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/kibana_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { formatNumber } from 'plugins/monitoring/lib/format_number'; +import { formatNumber } from '../../../lib/format_number'; import { ClusterItemContainer, HealthStatusIndicator, diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/license_text.js b/x-pack/plugins/monitoring/public/components/cluster/overview/license_text.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/overview/license_text.js rename to x-pack/plugins/monitoring/public/components/cluster/overview/license_text.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/logstash_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/logstash_panel.js similarity index 99% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/overview/logstash_panel.js rename to x-pack/plugins/monitoring/public/components/cluster/overview/logstash_panel.js index 9e21ef1074ed3b..19a318642aab76 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/logstash_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/logstash_panel.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { formatNumber } from 'plugins/monitoring/lib/format_number'; +import { formatNumber } from '../../../lib/format_number'; import { ClusterItemContainer, BytesPercentageUsage, diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/status_icon.js b/x-pack/plugins/monitoring/public/components/cluster/status_icon.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/cluster/status_icon.js rename to x-pack/plugins/monitoring/public/components/cluster/status_icon.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap rename to x-pack/plugins/monitoring/public/components/elasticsearch/ccr/__snapshots__/ccr.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/ccr.js b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/ccr.js similarity index 99% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/ccr.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/ccr/ccr.js index cddae16f34ecaa..5aad73d7a131eb 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/ccr.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/ccr.js @@ -17,10 +17,9 @@ import { EuiTextColor, EuiScreenReaderOnly, } from '@elastic/eui'; -import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; -import './ccr.css'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; function toSeconds(ms) { return Math.floor(ms / 1000) + 's'; diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/ccr.test.js b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/ccr.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/ccr.test.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/ccr/ccr.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/ccr/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/ccr.css b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr/index.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr/ccr.css rename to x-pack/plugins/monitoring/public/components/elasticsearch/ccr/index.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap rename to x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/__snapshots__/ccr_shard.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.js b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.js similarity index 97% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.js index af0ff323b7ba87..a8aa931bad254c 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.js @@ -5,7 +5,7 @@ */ import React, { Fragment, PureComponent } from 'react'; -import chrome from '../../../np_imports/ui/chrome'; +import { Legacy } from '../../../legacy_shims'; import { EuiPage, EuiPageBody, @@ -93,7 +93,7 @@ export class CcrShard extends PureComponent { renderLatestStat() { const { stat, timestamp } = this.props; - const injector = chrome.dangerouslyGetActiveInjector(); + const injector = Legacy.shims.getAngularInjector(); const timezone = injector.get('config').get('dateFormat:tz'); return ( diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.test.js b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.test.js similarity index 88% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.test.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.test.js index b950c2ca0a6d28..ceb1fd1186eed0 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.test.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/ccr_shard.test.js @@ -8,13 +8,18 @@ import React from 'react'; import { shallow } from 'enzyme'; import { CcrShard } from './ccr_shard'; -jest.mock('../../../np_imports/ui/chrome', () => { +jest.mock('../../../legacy_shims', () => { return { - getBasePath: () => '', - dangerouslyGetActiveInjector: () => ({ get: () => ({ get: () => 'utc' }) }), + Legacy: { + shims: { getAngularInjector: () => ({ get: () => ({ get: () => 'utc' }) }) }, + }, }; }); +jest.mock('../../chart', () => ({ + MonitoringTimeseriesContainer: () => 'MonitoringTimeseriesContainer', +})); + describe('CcrShard', () => { const props = { formattedLeader: 'leader on remote', diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/status.js b/x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/status.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ccr_shard/status.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/ccr_shard/status.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/cluster_status/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/cluster_status/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/cluster_status/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/cluster_status/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/index/advanced.js b/x-pack/plugins/monitoring/public/components/elasticsearch/index/advanced.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/index/advanced.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/index/advanced.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/index/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/index/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/index/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/index/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/index_detail_status/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/index_detail_status/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/index_detail_status/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/index_detail_status/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/indices/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/indices/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/indices/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/indices/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/indices/indices.js b/x-pack/plugins/monitoring/public/components/elasticsearch/indices/indices.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/indices/indices.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/indices/indices.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ml_job_listing/status_icon.js b/x-pack/plugins/monitoring/public/components/elasticsearch/ml_job_listing/status_icon.js similarity index 93% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ml_job_listing/status_icon.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/ml_job_listing/status_icon.js index c2775713171ad8..4d457c943c8722 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/ml_job_listing/status_icon.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/ml_job_listing/status_icon.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { StatusIcon } from 'plugins/monitoring/components/status_icon'; +import { StatusIcon } from '../../status_icon'; import { i18n } from '@kbn/i18n'; export function MachineLearningJobStatusIcon({ status }) { diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/node/advanced.js b/x-pack/plugins/monitoring/public/components/elasticsearch/node/advanced.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/node/advanced.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/node/advanced.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/node/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/node/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/node/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/node/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/node/node.js b/x-pack/plugins/monitoring/public/components/elasticsearch/node/node.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/node/node.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/node/node.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/node/status_icon.js b/x-pack/plugins/monitoring/public/components/elasticsearch/node/status_icon.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/node/status_icon.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/node/status_icon.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/node_detail_status/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/node_detail_status/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/node_detail_status/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/node_detail_status/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap rename to x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/__snapshots__/cells.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js similarity index 95% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js index 50abdcc718bc8b..0c4b4b2b3c3f41 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__tests__/cells.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { renderWithIntl } from '../../../../../../../../test_utils/enzyme_helpers'; +import { renderWithIntl } from 'test_utils/enzyme_helpers'; import { MetricCell } from '../cells'; describe('Node Listing Metric Cell', () => { diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/cells.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/cells.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/nodes/cells.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/nodes/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/overview/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/overview/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/overview/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/overview/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/overview/overview.js b/x-pack/plugins/monitoring/public/components/elasticsearch/overview/overview.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/overview/overview.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/overview/overview.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/parse_props.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/parse_props.js similarity index 92% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/parse_props.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/parse_props.js index 133b520947b1b1..1aaba96ed3da44 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/parse_props.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/parse_props.js @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import chrome from '../../../np_imports/ui/chrome'; +import { Legacy } from '../../../legacy_shims'; import { capitalize } from 'lodash'; -import { formatMetric } from 'plugins/monitoring/lib/format_number'; +import { formatMetric } from '../../../lib/format_number'; import { formatDateTimeLocal } from '../../../../common/formatting'; const getIpAndPort = transport => { @@ -39,7 +39,7 @@ export const parseProps = props => { } = props; const { files, size } = index; - const injector = chrome.dangerouslyGetActiveInjector(); + const injector = Legacy.shims.getAngularInjector(); const timezone = injector.get('config').get('dateFormat:tz'); return { diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/progress.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/progress.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/progress.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/progress.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/recovery_index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/recovery_index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/recovery_index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/recovery_index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/shard_activity.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/shard_activity.js similarity index 98% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/shard_activity.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/shard_activity.js index 6607d236590c1a..d228144778c02a 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/shard_activity.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/shard_activity.js @@ -6,7 +6,7 @@ import React, { Fragment } from 'react'; import { EuiText, EuiTitle, EuiLink, EuiSpacer, EuiSwitch } from '@elastic/eui'; -import { EuiMonitoringTable } from 'plugins/monitoring/components/table'; +import { EuiMonitoringTable } from '../../table'; import { RecoveryIndex } from './recovery_index'; import { TotalTime } from './total_time'; import { SourceDestination } from './source_destination'; diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/snapshot.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/snapshot.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/snapshot.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/snapshot.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/source_destination.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/source_destination.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/source_destination.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/source_destination.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/source_tooltip.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/source_tooltip.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/source_tooltip.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/source_tooltip.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/total_time.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/total_time.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_activity/total_time.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_activity/total_time.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/_index.scss b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/_index.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/_index.scss rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/_index.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/_shard_allocation.scss b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/_shard_allocation.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/_shard_allocation.scss rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/_shard_allocation.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/__snapshots__/shard.test.js.snap b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/__snapshots__/shard.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/__snapshots__/shard.test.js.snap rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/__snapshots__/shard.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/assigned.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/assigned.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/assigned.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/assigned.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/cluster_view.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/cluster_view.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/cluster_view.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/cluster_view.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/shard.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/shard.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/shard.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/shard.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/shard.test.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/shard.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/shard.test.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/shard.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_body.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_body.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_body.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_body.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/table_head.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/unassigned.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/unassigned.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/unassigned.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/components/unassigned.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/index.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/index.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/calculate_class.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/calculate_class.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/calculate_class.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/calculate_class.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/decorate_shards.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/decorate_shards.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/decorate_shards.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/decorate_shards.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/decorate_shards.test.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/decorate_shards.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/decorate_shards.test.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/decorate_shards.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/generate_query_and_link.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/generate_query_and_link.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/generate_query_and_link.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/generate_query_and_link.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_primary_children.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_primary_children.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_primary_children.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_primary_children.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_unassigned.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_unassigned.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_unassigned.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/has_unassigned.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/labels.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/labels.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/labels.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/labels.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/vents.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/vents.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/vents.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/lib/vents.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/shard_allocation.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/indices_by_nodes.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/indices_by_nodes.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/indices_by_nodes.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/indices_by_nodes.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.js b/x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/shard_allocation/transformers/nodes_by_indices.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/elasticsearch/status_icon.js b/x-pack/plugins/monitoring/public/components/elasticsearch/status_icon.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/elasticsearch/status_icon.js rename to x-pack/plugins/monitoring/public/components/elasticsearch/status_icon.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/index.js b/x-pack/plugins/monitoring/public/components/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/index.js rename to x-pack/plugins/monitoring/public/components/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/kibana/cluster_status/index.js b/x-pack/plugins/monitoring/public/components/kibana/cluster_status/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/kibana/cluster_status/index.js rename to x-pack/plugins/monitoring/public/components/kibana/cluster_status/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/kibana/detail_status/index.js b/x-pack/plugins/monitoring/public/components/kibana/detail_status/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/kibana/detail_status/index.js rename to x-pack/plugins/monitoring/public/components/kibana/detail_status/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/kibana/instances/index.js b/x-pack/plugins/monitoring/public/components/kibana/instances/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/kibana/instances/index.js rename to x-pack/plugins/monitoring/public/components/kibana/instances/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/kibana/instances/instances.js b/x-pack/plugins/monitoring/public/components/kibana/instances/instances.js similarity index 99% rename from x-pack/legacy/plugins/monitoring/public/components/kibana/instances/instances.js rename to x-pack/plugins/monitoring/public/components/kibana/instances/instances.js index 3a30bbc38d4260..ed562c7da995b7 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/kibana/instances/instances.js +++ b/x-pack/plugins/monitoring/public/components/kibana/instances/instances.js @@ -19,7 +19,7 @@ import { capitalize, get } from 'lodash'; import { ClusterStatus } from '../cluster_status'; import { EuiMonitoringTable } from '../../table'; import { KibanaStatusIcon } from '../status_icon'; -import { StatusIcon } from 'plugins/monitoring/components/status_icon'; +import { StatusIcon } from '../../status_icon'; import { formatMetric, formatNumber } from '../../../lib/format_number'; import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/legacy/plugins/monitoring/public/components/kibana/status_icon.js b/x-pack/plugins/monitoring/public/components/kibana/status_icon.js similarity index 91% rename from x-pack/legacy/plugins/monitoring/public/components/kibana/status_icon.js rename to x-pack/plugins/monitoring/public/components/kibana/status_icon.js index 87a2ab4cc4713e..3c18197170b502 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/kibana/status_icon.js +++ b/x-pack/plugins/monitoring/public/components/kibana/status_icon.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { StatusIcon } from 'plugins/monitoring/components/status_icon'; +import { StatusIcon } from '../status_icon'; import { i18n } from '@kbn/i18n'; export function KibanaStatusIcon({ status, availability = true }) { diff --git a/x-pack/plugins/monitoring/public/components/license/index.js b/x-pack/plugins/monitoring/public/components/license/index.js new file mode 100644 index 00000000000000..085cc9082cf532 --- /dev/null +++ b/x-pack/plugins/monitoring/public/components/license/index.js @@ -0,0 +1,201 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment } from 'react'; +import { + EuiPage, + EuiPageBody, + EuiSpacer, + EuiCodeBlock, + EuiPanel, + EuiText, + EuiLink, + EuiFlexGroup, + EuiFlexItem, + EuiScreenReaderOnly, + EuiCard, + EuiButton, + EuiIcon, + EuiTitle, + EuiTextAlign, +} from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n/react'; +import { Legacy } from '../../legacy_shims'; + +export const AddLicense = ({ uploadPath }) => { + return ( + + } + description={ + + } + footer={ + + + + } + /> + ); +}; + +export class LicenseStatus extends React.PureComponent { + render() { + const { isExpired, status, type, expiryDate } = this.props; + const typeTitleCase = type.charAt(0).toUpperCase() + type.substr(1).toLowerCase(); + let icon; + let title; + let message; + if (isExpired) { + icon = ; + message = ( + + {expiryDate}, + }} + /> + + ); + title = ( + + ); + } else { + icon = ; + message = expiryDate ? ( + + {expiryDate}, + }} + /> + + ) : ( + + + + ); + title = ( + + ); + } + return ( + + + {icon} + + +

{title}

+
+
+
+ + + + {message} +
+ ); + } +} + +const LicenseUpdateInfoForPrimary = ({ isPrimaryCluster, uploadLicensePath }) => { + if (!isPrimaryCluster) { + return null; + } + + // viewed license is for the cluster directly connected to Kibana + return ; +}; + +const LicenseUpdateInfoForRemote = ({ isPrimaryCluster }) => { + if (isPrimaryCluster) { + return null; + } + + // viewed license is for a remote monitored cluster not directly connected to Kibana + return ( + +

+ +

+ + + {`curl -XPUT -u 'https://:/_license' -H 'Content-Type: application/json' -d @license.json`} + +
+ ); +}; + +export function License(props) { + const { status, type, isExpired, expiryDate } = props; + const licenseManagement = `${Legacy.shims.getBasePath()}/app/kibana#/management/elasticsearch/license_management`; + return ( + + +

+ +

+
+ + + + + + + + + + + + + +

+ For more license options please visit  + License Management. +

+
+
+
+ ); +} diff --git a/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap b/x-pack/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap rename to x-pack/plugins/monitoring/public/components/logs/__snapshots__/logs.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/reason.test.js.snap b/x-pack/plugins/monitoring/public/components/logs/__snapshots__/reason.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logs/__snapshots__/reason.test.js.snap rename to x-pack/plugins/monitoring/public/components/logs/__snapshots__/reason.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logs/index.js b/x-pack/plugins/monitoring/public/components/logs/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logs/index.js rename to x-pack/plugins/monitoring/public/components/logs/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logs/logs.js b/x-pack/plugins/monitoring/public/components/logs/logs.js similarity index 95% rename from x-pack/legacy/plugins/monitoring/public/components/logs/logs.js rename to x-pack/plugins/monitoring/public/components/logs/logs.js index 3590199048352d..eaf92f72103e03 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/logs/logs.js +++ b/x-pack/plugins/monitoring/public/components/logs/logs.js @@ -5,17 +5,16 @@ */ import React, { PureComponent } from 'react'; import { capitalize } from 'lodash'; -import chrome from '../../np_imports/ui/chrome'; +import { Legacy } from '../../legacy_shims'; import { EuiBasicTable, EuiTitle, EuiSpacer, EuiText, EuiCallOut, EuiLink } from '@elastic/eui'; import { INFRA_SOURCE_ID } from '../../../common/constants'; import { formatDateTimeLocal } from '../../../common/formatting'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { Reason } from './reason'; -import { capabilities } from '../../np_imports/ui/capabilities'; const getFormattedDateTimeLocal = timestamp => { - const injector = chrome.dangerouslyGetActiveInjector(); + const injector = Legacy.shims.getAngularInjector(); const timezone = injector.get('config').get('dateFormat:tz'); return formatDateTimeLocal(timestamp, timezone); }; @@ -110,7 +109,7 @@ const clusterColumns = [ ]; function getLogsUiLink(clusterUuid, nodeId, indexUuid) { - const base = `${chrome.getBasePath()}/app/logs/link-to/${INFRA_SOURCE_ID}/logs`; + const base = `${Legacy.shims.getBasePath()}/app/logs/link-to/${INFRA_SOURCE_ID}/logs`; const params = []; if (clusterUuid) { @@ -158,7 +157,7 @@ export class Logs extends PureComponent { } renderCallout() { - const uiCapabilities = capabilities.get(); + const uiCapabilities = Legacy.shims.capabilities.get(); const show = uiCapabilities.logs && uiCapabilities.logs.show; const { logs: { enabled }, diff --git a/x-pack/legacy/plugins/monitoring/public/components/logs/logs.test.js b/x-pack/plugins/monitoring/public/components/logs/logs.test.js similarity index 95% rename from x-pack/legacy/plugins/monitoring/public/components/logs/logs.test.js rename to x-pack/plugins/monitoring/public/components/logs/logs.test.js index 63af8b208fbecc..c0497ee351e34f 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/logs/logs.test.js +++ b/x-pack/plugins/monitoring/public/components/logs/logs.test.js @@ -8,21 +8,16 @@ import React from 'react'; import { shallow } from 'enzyme'; import { Logs } from './logs'; -jest.mock('../../np_imports/ui/chrome', () => { - return { - getBasePath: () => '', - }; -}); - -jest.mock( - '../../np_imports/ui/capabilities', - () => ({ - capabilities: { - get: () => ({ logs: { show: true } }), +jest.mock('../../legacy_shims', () => ({ + Legacy: { + shims: { + getBasePath: () => '', + capabilities: { + get: () => ({ logs: { show: true } }), + }, }, - }), - { virtual: true } -); + }, +})); const logs = { enabled: true, diff --git a/x-pack/legacy/plugins/monitoring/public/components/logs/reason.js b/x-pack/plugins/monitoring/public/components/logs/reason.js similarity index 98% rename from x-pack/legacy/plugins/monitoring/public/components/logs/reason.js rename to x-pack/plugins/monitoring/public/components/logs/reason.js index 91c62ae53a1cad..ad21f7f81d9bdb 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/logs/reason.js +++ b/x-pack/plugins/monitoring/public/components/logs/reason.js @@ -8,10 +8,11 @@ import React from 'react'; import { EuiCallOut, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; +import { Legacy } from '../../legacy_shims'; import { Monospace } from '../metricbeat_migration/instruction_steps/components/monospace/monospace'; export const Reason = ({ reason }) => { + const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = Legacy.shims.docLinks; let title = i18n.translate('xpack.monitoring.logs.reason.defaultTitle', { defaultMessage: 'No log data found', }); diff --git a/x-pack/legacy/plugins/monitoring/public/components/logs/reason.test.js b/x-pack/plugins/monitoring/public/components/logs/reason.test.js similarity index 93% rename from x-pack/legacy/plugins/monitoring/public/components/logs/reason.test.js rename to x-pack/plugins/monitoring/public/components/logs/reason.test.js index c8ed05bd73aded..bd56c733268b73 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/logs/reason.test.js +++ b/x-pack/plugins/monitoring/public/components/logs/reason.test.js @@ -8,9 +8,15 @@ import React from 'react'; import { shallow } from 'enzyme'; import { Reason } from './reason'; -jest.mock('ui/documentation_links', () => ({ - ELASTIC_WEBSITE_URL: 'https://www.elastic.co/', - DOC_LINK_VERSION: 'current', +jest.mock('../../legacy_shims', () => ({ + Legacy: { + shims: { + docLinks: { + ELASTIC_WEBSITE_URL: 'https://www.elastic.co/', + DOC_LINK_VERSION: 'current', + }, + }, + }, })); describe('Logs', () => { diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/cluster_status/index.js b/x-pack/plugins/monitoring/public/components/logstash/cluster_status/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/cluster_status/index.js rename to x-pack/plugins/monitoring/public/components/logstash/cluster_status/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/detail_status/index.js b/x-pack/plugins/monitoring/public/components/logstash/detail_status/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/detail_status/index.js rename to x-pack/plugins/monitoring/public/components/logstash/detail_status/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/listing/__snapshots__/listing.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/listing/__snapshots__/listing.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/listing/__snapshots__/listing.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/listing/__snapshots__/listing.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/listing/index.js b/x-pack/plugins/monitoring/public/components/logstash/listing/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/listing/index.js rename to x-pack/plugins/monitoring/public/components/logstash/listing/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/listing/listing.js b/x-pack/plugins/monitoring/public/components/logstash/listing/listing.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/listing/listing.js rename to x-pack/plugins/monitoring/public/components/logstash/listing/listing.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/listing/listing.test.js b/x-pack/plugins/monitoring/public/components/logstash/listing/listing.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/listing/listing.test.js rename to x-pack/plugins/monitoring/public/components/logstash/listing/listing.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/overview/index.js b/x-pack/plugins/monitoring/public/components/logstash/overview/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/overview/index.js rename to x-pack/plugins/monitoring/public/components/logstash/overview/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/overview/overview.js b/x-pack/plugins/monitoring/public/components/logstash/overview/overview.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/overview/overview.js rename to x-pack/plugins/monitoring/public/components/logstash/overview/overview.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_listing/index.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_listing/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_listing/index.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_listing/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_listing/pipeline_listing.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_listing/pipeline_listing.js similarity index 98% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_listing/pipeline_listing.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_listing/pipeline_listing.js index f8df93d6ee8fb8..132cc7ce131cf2 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_listing/pipeline_listing.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_listing/pipeline_listing.js @@ -20,7 +20,7 @@ import { } from '@elastic/eui'; import { formatMetric } from '../../../lib/format_number'; import { ClusterStatus } from '../cluster_status'; -import { Sparkline } from 'plugins/monitoring/components/sparkline'; +import { Sparkline } from '../../../components/sparkline'; import { EuiMonitoringSSPTable } from '../../table'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/index.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/index.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/__tests__/config.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/__tests__/config.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/__tests__/config.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/__tests__/config.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/__tests__/pipeline_state.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/__tests__/pipeline_state.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/__tests__/pipeline_state.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/__tests__/pipeline_state.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/config.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/config.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/config.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/config.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/boolean_edge.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/boolean_edge.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/boolean_edge.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/boolean_edge.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/edge.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/edge.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/edge.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/edge.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/edge_factory.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/edge_factory.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/edge_factory.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/edge_factory.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/if_vertex.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/if_vertex.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/if_vertex.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/if_vertex.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/index.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/index.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/plugin_vertex.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/plugin_vertex.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/plugin_vertex.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/plugin_vertex.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/queue_vertex.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/queue_vertex.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/queue_vertex.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/queue_vertex.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/vertex.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/vertex.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/vertex.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/vertex.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/vertex_factory.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/vertex_factory.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/vertex_factory.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/__tests__/vertex_factory.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/boolean_edge.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/boolean_edge.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/boolean_edge.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/boolean_edge.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/edge.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/edge.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/edge.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/edge.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/edge_factory.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/edge_factory.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/edge_factory.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/edge_factory.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/if_vertex.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/if_vertex.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/if_vertex.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/if_vertex.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/index.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/index.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/plugin_vertex.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/plugin_vertex.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/plugin_vertex.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/plugin_vertex.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/queue_vertex.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/queue_vertex.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/queue_vertex.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/queue_vertex.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/vertex.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/vertex.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/vertex.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/vertex.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/vertex_factory.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/vertex_factory.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/vertex_factory.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/graph/vertex_factory.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/element.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/element.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/element.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/element.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/else_element.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/else_element.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/else_element.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/else_element.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/else_element.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/else_element.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/else_element.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/else_element.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/flatten_pipeline_section.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/flatten_pipeline_section.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/flatten_pipeline_section.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/flatten_pipeline_section.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/flatten_pipeline_section.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/flatten_pipeline_section.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/flatten_pipeline_section.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/flatten_pipeline_section.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/if_element.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/if_element.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/if_element.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/if_element.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/if_element.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/if_element.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/if_element.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/if_element.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/index.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/index.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/list.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/list.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/list.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/list.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/list.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/list.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/list.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/list.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/plugin_element.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/plugin_element.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/plugin_element.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/plugin_element.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/plugin_element.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/plugin_element.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/plugin_element.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/list/plugin_element.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/if_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/if_statement.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/if_statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/if_statement.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/make_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/make_statement.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/make_statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/make_statement.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/pipeline.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/pipeline.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/pipeline.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/pipeline.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/plugin_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/plugin_statement.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/plugin_statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/plugin_statement.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/queue.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/queue.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/queue.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/queue.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/statement.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/statement.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/utils.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/utils.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/utils.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/__tests__/utils.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/if_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/if_statement.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/if_statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/if_statement.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/index.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/index.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/make_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/make_statement.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/make_statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/make_statement.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/pipeline.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/pipeline.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/pipeline.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/pipeline.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/plugin_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/plugin_statement.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/plugin_statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/plugin_statement.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/queue.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/queue.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/queue.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/queue.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/statement.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/statement.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/utils.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/utils.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/utils.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline/utils.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline_state.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline_state.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline_state.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/models/pipeline_state.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/collapsible_statement.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/collapsible_statement.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/collapsible_statement.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/collapsible_statement.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/detail_drawer.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/detail_drawer.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/detail_drawer.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/detail_drawer.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/metric.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/metric.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/metric.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/metric.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/pipeline_viewer.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/pipeline_viewer.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/pipeline_viewer.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/pipeline_viewer.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/plugin_statement.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/plugin_statement.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/plugin_statement.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/plugin_statement.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/queue.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/queue.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/queue.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/queue.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list_heading.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list_heading.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list_heading.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_list_heading.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_section.test.js.snap b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_section.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_section.test.js.snap rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/__snapshots__/statement_section.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/collapsible_statement.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/collapsible_statement.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/collapsible_statement.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/collapsible_statement.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/detail_drawer.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/detail_drawer.test.js similarity index 98% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/detail_drawer.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/detail_drawer.test.js index 2a65110f811695..09f4d039530382 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/detail_drawer.test.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/detail_drawer.test.js @@ -8,6 +8,10 @@ import React from 'react'; import { DetailDrawer } from '../detail_drawer'; import { shallow } from 'enzyme'; +jest.mock('../../../../sparkline', () => ({ + Sparkline: () => 'Sparkline', +})); + describe('DetailDrawer component', () => { let onHide; let timeseriesTooltipXValueFormatter; diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/metric.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/metric.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/metric.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/metric.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/pipeline_viewer.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/pipeline_viewer.test.js similarity index 94% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/pipeline_viewer.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/pipeline_viewer.test.js index 5013c38ac19211..8c2558bee4e446 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/pipeline_viewer.test.js +++ b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/pipeline_viewer.test.js @@ -8,6 +8,10 @@ import React from 'react'; import { PipelineViewer } from '../pipeline_viewer'; import { shallow } from 'enzyme'; +jest.mock('../../../../sparkline', () => ({ + Sparkline: () => 'Sparkline', +})); + describe('PipelineViewer component', () => { let pipeline; let component; diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/plugin_statement.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/plugin_statement.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/plugin_statement.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/plugin_statement.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/queue.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/queue.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/queue.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/queue.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list_heading.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list_heading.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list_heading.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_list_heading.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_section.test.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_section.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_section.test.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/__test__/statement_section.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_index.scss b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_index.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_index.scss rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_index.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_pipeline_viewer.scss b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_pipeline_viewer.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_pipeline_viewer.scss rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/_pipeline_viewer.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/collapsible_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/collapsible_statement.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/collapsible_statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/collapsible_statement.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/index.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/index.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/metric.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/pipeline_viewer.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/pipeline_viewer.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/pipeline_viewer.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/pipeline_viewer.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/plugin_statement.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/queue.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list_heading.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list_heading.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list_heading.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_list_heading.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_section.js b/x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_section.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_section.js rename to x-pack/plugins/monitoring/public/components/logstash/pipeline_viewer/views/statement_section.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/constants.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/constants.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/constants.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/constants.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/flyout/__snapshots__/flyout.test.js.snap b/x-pack/plugins/monitoring/public/components/metricbeat_migration/flyout/__snapshots__/flyout.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/flyout/__snapshots__/flyout.test.js.snap rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/flyout/__snapshots__/flyout.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.js similarity index 98% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.js index 0a284c102dc9d2..42d0eec7cbed0c 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.js +++ b/x-pack/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.js @@ -25,7 +25,7 @@ import { EuiCheckbox, } from '@elastic/eui'; import { getInstructionSteps } from '../instruction_steps'; -import { Storage } from '../../../../../../../../src/plugins/kibana_utils/public'; +import { Storage } from '../../../../../../../src/plugins/kibana_utils/public'; import { STORAGE_KEY, ELASTICSEARCH_SYSTEM_ID, @@ -38,7 +38,7 @@ import { INSTRUCTION_STEP_ENABLE_METRICBEAT, INSTRUCTION_STEP_DISABLE_INTERNAL, } from '../constants'; -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; +import { Legacy } from '../../../legacy_shims'; import { getIdentifier, formatProductName } from '../../setup_mode/formatting'; const storage = new Storage(window.localStorage); @@ -223,6 +223,7 @@ export class Flyout extends Component { getDocumentationTitle() { const { productName } = this.props; + const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = Legacy.shims.docLinks; let documentationUrl = null; if (productName === KIBANA_SYSTEM_ID) { diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.test.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.test.js similarity index 95% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.test.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.test.js index 3587381f977cbc..61cfb491f0bd05 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.test.js +++ b/x-pack/plugins/monitoring/public/components/metricbeat_migration/flyout/flyout.test.js @@ -16,9 +16,16 @@ import { LOGSTASH_SYSTEM_ID, } from '../../../../common/constants'; -jest.mock('ui/documentation_links', () => ({ - ELASTIC_WEBSITE_URL: 'https://www.elastic.co/', - DOC_LINK_VERSION: 'current', +jest.mock('../../../legacy_shims', () => ({ + Legacy: { + shims: { + kfetch: jest.fn(), + docLinks: { + ELASTIC_WEBSITE_URL: 'https://www.elastic.co/', + DOC_LINK_VERSION: 'current', + }, + }, + }, })); jest.mock('../../../../common', () => ({ diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/flyout/index.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/flyout/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/flyout/index.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/flyout/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/disable_internal_collection_instructions.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/disable_internal_collection_instructions.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/disable_internal_collection_instructions.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/disable_internal_collection_instructions.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/enable_metricbeat_instructions.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/enable_metricbeat_instructions.js similarity index 97% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/enable_metricbeat_instructions.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/enable_metricbeat_instructions.js index 9fa33802b02021..51a7a139aafd0a 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/enable_metricbeat_instructions.js +++ b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/enable_metricbeat_instructions.js @@ -8,10 +8,11 @@ import React, { Fragment } from 'react'; import { EuiSpacer, EuiCodeBlock, EuiLink, EuiText } from '@elastic/eui'; import { Monospace } from '../components/monospace'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; +import { Legacy } from '../../../../legacy_shims'; import { getMigrationStatusStep, getSecurityStep } from '../common_instructions'; export function getApmInstructionsForEnablingMetricbeat(product, _meta, { esMonitoringUrl }) { + const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = Legacy.shims.docLinks; const securitySetup = getSecurityStep( `${ELASTIC_WEBSITE_URL}guide/en/beats/metricbeat/${DOC_LINK_VERSION}/metricbeat-configuration.html` ); diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/index.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/index.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/apm/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/common_beats_instructions.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/common_beats_instructions.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/common_beats_instructions.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/common_beats_instructions.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/disable_internal_collection_instructions.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/disable_internal_collection_instructions.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/disable_internal_collection_instructions.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/disable_internal_collection_instructions.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/enable_metricbeat_instructions.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/enable_metricbeat_instructions.js similarity index 98% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/enable_metricbeat_instructions.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/enable_metricbeat_instructions.js index 56e7c9b5af064b..ddaa610b874a84 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/enable_metricbeat_instructions.js +++ b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/enable_metricbeat_instructions.js @@ -9,10 +9,11 @@ import { EuiSpacer, EuiCodeBlock, EuiLink, EuiCallOut, EuiText } from '@elastic/ import { Monospace } from '../components/monospace'; import { FormattedMessage } from '@kbn/i18n/react'; import { UNDETECTED_BEAT_TYPE, DEFAULT_BEAT_FOR_URLS } from './common_beats_instructions'; -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; +import { Legacy } from '../../../../legacy_shims'; import { getMigrationStatusStep, getSecurityStep } from '../common_instructions'; export function getBeatsInstructionsForEnablingMetricbeat(product, _meta, { esMonitoringUrl }) { + const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = Legacy.shims.docLinks; const beatType = product.beatType; const securitySetup = getSecurityStep( `${ELASTIC_WEBSITE_URL}guide/en/beats/metricbeat/${DOC_LINK_VERSION}/metricbeat-configuration.html` diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/index.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/index.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/beats/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/common_instructions.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/common_instructions.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/common_instructions.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/common_instructions.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/components/monospace/index.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/components/monospace/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/components/monospace/index.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/components/monospace/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/components/monospace/monospace.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/components/monospace/monospace.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/components/monospace/monospace.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/components/monospace/monospace.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/disable_internal_collection_instructions.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/disable_internal_collection_instructions.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/disable_internal_collection_instructions.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/disable_internal_collection_instructions.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/enable_metricbeat_instructions.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/enable_metricbeat_instructions.js similarity index 97% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/enable_metricbeat_instructions.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/enable_metricbeat_instructions.js index 36b3dd21ff43e9..8607739e7090ca 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/enable_metricbeat_instructions.js +++ b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/enable_metricbeat_instructions.js @@ -8,7 +8,7 @@ import React, { Fragment } from 'react'; import { EuiSpacer, EuiCodeBlock, EuiLink, EuiText } from '@elastic/eui'; import { Monospace } from '../components/monospace'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; +import { Legacy } from '../../../../legacy_shims'; import { getSecurityStep, getMigrationStatusStep } from '../common_instructions'; export function getElasticsearchInstructionsForEnablingMetricbeat( @@ -16,6 +16,7 @@ export function getElasticsearchInstructionsForEnablingMetricbeat( _meta, { esMonitoringUrl } ) { + const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = Legacy.shims.docLinks; const securitySetup = getSecurityStep( `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/configuring-metricbeat.html` ); diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/index.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/index.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/elasticsearch/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/get_instruction_steps.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/get_instruction_steps.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/get_instruction_steps.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/get_instruction_steps.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/index.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/index.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/disable_internal_collection_instructions.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/disable_internal_collection_instructions.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/disable_internal_collection_instructions.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/disable_internal_collection_instructions.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/enable_metricbeat_instructions.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/enable_metricbeat_instructions.js similarity index 97% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/enable_metricbeat_instructions.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/enable_metricbeat_instructions.js index 98c75dbfe4b375..eb1aedd47c568c 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/enable_metricbeat_instructions.js +++ b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/enable_metricbeat_instructions.js @@ -8,10 +8,11 @@ import React, { Fragment } from 'react'; import { EuiSpacer, EuiCodeBlock, EuiLink, EuiText } from '@elastic/eui'; import { Monospace } from '../components/monospace'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; +import { Legacy } from '../../../../legacy_shims'; import { getMigrationStatusStep, getSecurityStep } from '../common_instructions'; export function getKibanaInstructionsForEnablingMetricbeat(product, _meta, { esMonitoringUrl }) { + const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = Legacy.shims.docLinks; const securitySetup = getSecurityStep( `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/monitoring-metricbeat.html` ); diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/index.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/index.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/kibana/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/disable_internal_collection_instructions.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/disable_internal_collection_instructions.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/disable_internal_collection_instructions.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/disable_internal_collection_instructions.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/enable_metricbeat_instructions.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/enable_metricbeat_instructions.js similarity index 97% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/enable_metricbeat_instructions.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/enable_metricbeat_instructions.js index 4a36f394e4bd5d..ce542fa8a05e52 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/enable_metricbeat_instructions.js +++ b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/enable_metricbeat_instructions.js @@ -8,10 +8,11 @@ import React, { Fragment } from 'react'; import { EuiSpacer, EuiCodeBlock, EuiLink, EuiText } from '@elastic/eui'; import { Monospace } from '../components/monospace'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; +import { Legacy } from '../../../../legacy_shims'; import { getMigrationStatusStep, getSecurityStep } from '../common_instructions'; export function getLogstashInstructionsForEnablingMetricbeat(product, _meta, { esMonitoringUrl }) { + const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = Legacy.shims.docLinks; const securitySetup = getSecurityStep( `${ELASTIC_WEBSITE_URL}guide/en/logstash/${DOC_LINK_VERSION}/monitoring-with-metricbeat.html` ); diff --git a/x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/index.js b/x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/index.js rename to x-pack/plugins/monitoring/public/components/metricbeat_migration/instruction_steps/logstash/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/checker_errors.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/checker_errors.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/checker_errors.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/checker_errors.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/__tests__/__snapshots__/no_data.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/__tests__/checker_errors.test.js b/x-pack/plugins/monitoring/public/components/no_data/__tests__/checker_errors.test.js similarity index 92% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/__tests__/checker_errors.test.js rename to x-pack/plugins/monitoring/public/components/no_data/__tests__/checker_errors.test.js index 8462d2a6fc87b5..f3cd5ccb9703de 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/no_data/__tests__/checker_errors.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/__tests__/checker_errors.test.js @@ -6,7 +6,7 @@ import React from 'react'; import { boomify, forbidden } from 'boom'; -import { renderWithIntl } from '../../../../../../../test_utils/enzyme_helpers'; +import { renderWithIntl } from 'test_utils/enzyme_helpers'; import { CheckerErrors } from '../checker_errors'; describe('CheckerErrors', () => { diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/__tests__/no_data.test.js b/x-pack/plugins/monitoring/public/components/no_data/__tests__/no_data.test.js similarity index 84% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/__tests__/no_data.test.js rename to x-pack/plugins/monitoring/public/components/no_data/__tests__/no_data.test.js index 81a412a680bc62..5b54df3a828121 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/no_data/__tests__/no_data.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/__tests__/no_data.test.js @@ -5,17 +5,11 @@ */ import React from 'react'; -import { renderWithIntl } from '../../../../../../../test_utils/enzyme_helpers'; +import { renderWithIntl } from 'test_utils/enzyme_helpers'; import { NoData } from '../'; const enabler = {}; -jest.mock('../../../np_imports/ui/chrome', () => { - return { - getBasePath: () => '', - }; -}); - describe('NoData', () => { test('should show text next to the spinner while checking a setting', () => { const component = renderWithIntl( diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/_index.scss b/x-pack/plugins/monitoring/public/components/no_data/_index.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/_index.scss rename to x-pack/plugins/monitoring/public/components/no_data/_index.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/_no_data.scss b/x-pack/plugins/monitoring/public/components/no_data/_no_data.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/_no_data.scss rename to x-pack/plugins/monitoring/public/components/no_data/_no_data.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/blurbs/changes_needed.js b/x-pack/plugins/monitoring/public/components/no_data/blurbs/changes_needed.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/blurbs/changes_needed.js rename to x-pack/plugins/monitoring/public/components/no_data/blurbs/changes_needed.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js b/x-pack/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js rename to x-pack/plugins/monitoring/public/components/no_data/blurbs/cloud_deployment.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/blurbs/index.js b/x-pack/plugins/monitoring/public/components/no_data/blurbs/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/blurbs/index.js rename to x-pack/plugins/monitoring/public/components/no_data/blurbs/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/blurbs/looking_for.js b/x-pack/plugins/monitoring/public/components/no_data/blurbs/looking_for.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/blurbs/looking_for.js rename to x-pack/plugins/monitoring/public/components/no_data/blurbs/looking_for.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/blurbs/what_is.js b/x-pack/plugins/monitoring/public/components/no_data/blurbs/what_is.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/blurbs/what_is.js rename to x-pack/plugins/monitoring/public/components/no_data/blurbs/what_is.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/checker_errors.js b/x-pack/plugins/monitoring/public/components/no_data/checker_errors.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/checker_errors.js rename to x-pack/plugins/monitoring/public/components/no_data/checker_errors.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/checking_settings.js b/x-pack/plugins/monitoring/public/components/no_data/checking_settings.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/checking_settings.js rename to x-pack/plugins/monitoring/public/components/no_data/checking_settings.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/__snapshots__/collection_enabled.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/__snapshots__/collection_enabled.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/__snapshots__/collection_enabled.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/__snapshots__/collection_enabled.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/collection_enabled.test.js b/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/collection_enabled.test.js similarity index 93% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/collection_enabled.test.js rename to x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/collection_enabled.test.js index d2be217ca84982..13a49b1c7b2005 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/collection_enabled.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/__tests__/collection_enabled.test.js @@ -6,7 +6,7 @@ import React from 'react'; import sinon from 'sinon'; -import { mountWithIntl } from '../../../../../../../../../test_utils/enzyme_helpers'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { ExplainCollectionEnabled } from '../collection_enabled'; import { findTestSubject } from '@elastic/eui/lib/test'; diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_enabled/collection_enabled.js b/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/collection_enabled.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_enabled/collection_enabled.js rename to x-pack/plugins/monitoring/public/components/no_data/explanations/collection_enabled/collection_enabled.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/__snapshots__/collection_interval.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/__snapshots__/collection_interval.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/__snapshots__/collection_interval.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/__snapshots__/collection_interval.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/collection_interval.test.js b/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/collection_interval.test.js similarity index 96% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/collection_interval.test.js rename to x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/collection_interval.test.js index 0a50ad44c8b4fe..88185283b6faa4 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/collection_interval.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/__tests__/collection_interval.test.js @@ -6,7 +6,7 @@ import React from 'react'; import sinon from 'sinon'; -import { mountWithIntl } from '../../../../../../../../../test_utils/enzyme_helpers'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { ExplainCollectionInterval } from '../collection_interval'; import { findTestSubject } from '@elastic/eui/lib/test'; diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_interval/collection_interval.js b/x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/collection_interval.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/collection_interval/collection_interval.js rename to x-pack/plugins/monitoring/public/components/no_data/explanations/collection_interval/collection_interval.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/__snapshots__/exporters.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/__snapshots__/exporters.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/__snapshots__/exporters.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/__snapshots__/exporters.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/exporters.test.js b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/exporters.test.js similarity index 93% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/exporters.test.js rename to x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/exporters.test.js index c9147037f0022d..88f1eedfc3bcc6 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/exporters.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/__tests__/exporters.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { renderWithIntl } from '../../../../../../../../../test_utils/enzyme_helpers'; +import { renderWithIntl } from 'test_utils/enzyme_helpers'; import { ExplainExporters, ExplainExportersCloud } from '../exporters'; // Mocking to prevent errors with React portal. diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/exporters/exporters.js b/x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/exporters.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/exporters/exporters.js rename to x-pack/plugins/monitoring/public/components/no_data/explanations/exporters/exporters.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/index.js b/x-pack/plugins/monitoring/public/components/no_data/explanations/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/index.js rename to x-pack/plugins/monitoring/public/components/no_data/explanations/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/__snapshots__/plugin_enabled.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/__snapshots__/plugin_enabled.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/__snapshots__/plugin_enabled.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/__snapshots__/plugin_enabled.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/plugin_enabled.test.js b/x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/plugin_enabled.test.js similarity index 92% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/plugin_enabled.test.js rename to x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/plugin_enabled.test.js index 56536a8e4270b1..ece58ad6aeed2a 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/plugin_enabled.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/__tests__/plugin_enabled.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { renderWithIntl } from '../../../../../../../../../test_utils/enzyme_helpers'; +import { renderWithIntl } from 'test_utils/enzyme_helpers'; import { ExplainPluginEnabled } from '../plugin_enabled'; // Mocking to prevent errors with React portal. diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/plugin_enabled.js b/x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/plugin_enabled.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/plugin_enabled.js rename to x-pack/plugins/monitoring/public/components/no_data/explanations/plugin_enabled/plugin_enabled.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/index.js b/x-pack/plugins/monitoring/public/components/no_data/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/index.js rename to x-pack/plugins/monitoring/public/components/no_data/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/no_data.js b/x-pack/plugins/monitoring/public/components/no_data/no_data.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/no_data.js rename to x-pack/plugins/monitoring/public/components/no_data/no_data.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/reason_found.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/reason_found.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/reason_found.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/reason_found.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/we_tried.test.js.snap b/x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/we_tried.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/we_tried.test.js.snap rename to x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/__snapshots__/we_tried.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/__tests__/reason_found.test.js b/x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/reason_found.test.js similarity index 96% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/__tests__/reason_found.test.js rename to x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/reason_found.test.js index e9b2ff11538abb..7f15cacb1ebb91 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/__tests__/reason_found.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/reason_found.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { renderWithIntl } from '../../../../../../../../test_utils/enzyme_helpers'; +import { renderWithIntl } from 'test_utils/enzyme_helpers'; import { ReasonFound } from '../'; // Mocking to prevent errors with React portal. diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/__tests__/we_tried.test.js b/x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/we_tried.test.js similarity index 85% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/__tests__/we_tried.test.js rename to x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/we_tried.test.js index e382a1c9ea8dbb..95970453d4b7ca 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/__tests__/we_tried.test.js +++ b/x-pack/plugins/monitoring/public/components/no_data/reasons/__tests__/we_tried.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { renderWithIntl } from '../../../../../../../../test_utils/enzyme_helpers'; +import { renderWithIntl } from 'test_utils/enzyme_helpers'; import { WeTried } from '../'; describe('WeTried', () => { diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/index.js b/x-pack/plugins/monitoring/public/components/no_data/reasons/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/index.js rename to x-pack/plugins/monitoring/public/components/no_data/reasons/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/reason_found.js b/x-pack/plugins/monitoring/public/components/no_data/reasons/reason_found.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/reason_found.js rename to x-pack/plugins/monitoring/public/components/no_data/reasons/reason_found.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/we_tried.js b/x-pack/plugins/monitoring/public/components/no_data/reasons/we_tried.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/no_data/reasons/we_tried.js rename to x-pack/plugins/monitoring/public/components/no_data/reasons/we_tried.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/page_loading/__tests__/__snapshots__/page_loading.test.js.snap b/x-pack/plugins/monitoring/public/components/page_loading/__tests__/__snapshots__/page_loading.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/page_loading/__tests__/__snapshots__/page_loading.test.js.snap rename to x-pack/plugins/monitoring/public/components/page_loading/__tests__/__snapshots__/page_loading.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/page_loading/__tests__/page_loading.test.js b/x-pack/plugins/monitoring/public/components/page_loading/__tests__/page_loading.test.js similarity index 85% rename from x-pack/legacy/plugins/monitoring/public/components/page_loading/__tests__/page_loading.test.js rename to x-pack/plugins/monitoring/public/components/page_loading/__tests__/page_loading.test.js index b1ad00ffcc3c68..41f3ef4be969ed 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/page_loading/__tests__/page_loading.test.js +++ b/x-pack/plugins/monitoring/public/components/page_loading/__tests__/page_loading.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { renderWithIntl } from '../../../../../../../test_utils/enzyme_helpers'; +import { renderWithIntl } from 'test_utils/enzyme_helpers'; import { PageLoading } from '../'; describe('PageLoading', () => { diff --git a/x-pack/legacy/plugins/monitoring/public/components/page_loading/index.js b/x-pack/plugins/monitoring/public/components/page_loading/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/page_loading/index.js rename to x-pack/plugins/monitoring/public/components/page_loading/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/renderers/__snapshots__/setup_mode.test.js.snap b/x-pack/plugins/monitoring/public/components/renderers/__snapshots__/setup_mode.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/renderers/__snapshots__/setup_mode.test.js.snap rename to x-pack/plugins/monitoring/public/components/renderers/__snapshots__/setup_mode.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/renderers/index.js b/x-pack/plugins/monitoring/public/components/renderers/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/renderers/index.js rename to x-pack/plugins/monitoring/public/components/renderers/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/renderers/lib/find_new_uuid.js b/x-pack/plugins/monitoring/public/components/renderers/lib/find_new_uuid.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/renderers/lib/find_new_uuid.js rename to x-pack/plugins/monitoring/public/components/renderers/lib/find_new_uuid.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/renderers/setup_mode.js b/x-pack/plugins/monitoring/public/components/renderers/setup_mode.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/renderers/setup_mode.js rename to x-pack/plugins/monitoring/public/components/renderers/setup_mode.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/renderers/setup_mode.test.js b/x-pack/plugins/monitoring/public/components/renderers/setup_mode.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/renderers/setup_mode.test.js rename to x-pack/plugins/monitoring/public/components/renderers/setup_mode.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/__snapshots__/badge.test.js.snap b/x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/badge.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/__snapshots__/badge.test.js.snap rename to x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/badge.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/__snapshots__/enter_button.test.tsx.snap b/x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/enter_button.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/__snapshots__/enter_button.test.tsx.snap rename to x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/enter_button.test.tsx.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/__snapshots__/formatting.test.js.snap b/x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/formatting.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/__snapshots__/formatting.test.js.snap rename to x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/formatting.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/__snapshots__/listing_callout.test.js.snap b/x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/listing_callout.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/__snapshots__/listing_callout.test.js.snap rename to x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/listing_callout.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/__snapshots__/tooltip.test.js.snap b/x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/tooltip.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/__snapshots__/tooltip.test.js.snap rename to x-pack/plugins/monitoring/public/components/setup_mode/__snapshots__/tooltip.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/_enter_button.scss b/x-pack/plugins/monitoring/public/components/setup_mode/_enter_button.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/_enter_button.scss rename to x-pack/plugins/monitoring/public/components/setup_mode/_enter_button.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/_index.scss b/x-pack/plugins/monitoring/public/components/setup_mode/_index.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/_index.scss rename to x-pack/plugins/monitoring/public/components/setup_mode/_index.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/badge.js b/x-pack/plugins/monitoring/public/components/setup_mode/badge.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/badge.js rename to x-pack/plugins/monitoring/public/components/setup_mode/badge.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/badge.test.js b/x-pack/plugins/monitoring/public/components/setup_mode/badge.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/badge.test.js rename to x-pack/plugins/monitoring/public/components/setup_mode/badge.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/enter_button.test.tsx b/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.test.tsx similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/enter_button.test.tsx rename to x-pack/plugins/monitoring/public/components/setup_mode/enter_button.test.tsx diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/enter_button.tsx b/x-pack/plugins/monitoring/public/components/setup_mode/enter_button.tsx similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/enter_button.tsx rename to x-pack/plugins/monitoring/public/components/setup_mode/enter_button.tsx diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/formatting.js b/x-pack/plugins/monitoring/public/components/setup_mode/formatting.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/formatting.js rename to x-pack/plugins/monitoring/public/components/setup_mode/formatting.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/formatting.test.js b/x-pack/plugins/monitoring/public/components/setup_mode/formatting.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/formatting.test.js rename to x-pack/plugins/monitoring/public/components/setup_mode/formatting.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/listing_callout.js b/x-pack/plugins/monitoring/public/components/setup_mode/listing_callout.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/listing_callout.js rename to x-pack/plugins/monitoring/public/components/setup_mode/listing_callout.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/listing_callout.test.js b/x-pack/plugins/monitoring/public/components/setup_mode/listing_callout.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/listing_callout.test.js rename to x-pack/plugins/monitoring/public/components/setup_mode/listing_callout.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/tooltip.js b/x-pack/plugins/monitoring/public/components/setup_mode/tooltip.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/tooltip.js rename to x-pack/plugins/monitoring/public/components/setup_mode/tooltip.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/setup_mode/tooltip.test.js b/x-pack/plugins/monitoring/public/components/setup_mode/tooltip.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/setup_mode/tooltip.test.js rename to x-pack/plugins/monitoring/public/components/setup_mode/tooltip.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/sparkline/__mocks__/plugins/xpack_main/jquery_flot.js b/x-pack/plugins/monitoring/public/components/sparkline/__mocks__/plugins/xpack_main/jquery_flot.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/sparkline/__mocks__/plugins/xpack_main/jquery_flot.js rename to x-pack/plugins/monitoring/public/components/sparkline/__mocks__/plugins/xpack_main/jquery_flot.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/sparkline/__test__/__snapshots__/index.test.js.snap b/x-pack/plugins/monitoring/public/components/sparkline/__test__/__snapshots__/index.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/sparkline/__test__/__snapshots__/index.test.js.snap rename to x-pack/plugins/monitoring/public/components/sparkline/__test__/__snapshots__/index.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/sparkline/__test__/index.test.js b/x-pack/plugins/monitoring/public/components/sparkline/__test__/index.test.js similarity index 95% rename from x-pack/legacy/plugins/monitoring/public/components/sparkline/__test__/index.test.js rename to x-pack/plugins/monitoring/public/components/sparkline/__test__/index.test.js index 22f4787593fec6..ab2cf10b4615d7 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/sparkline/__test__/index.test.js +++ b/x-pack/plugins/monitoring/public/components/sparkline/__test__/index.test.js @@ -9,6 +9,10 @@ import renderer from 'react-test-renderer'; import { shallow } from 'enzyme'; import { Sparkline } from '../'; +jest.mock('../sparkline_flot_chart', () => ({ + SparklineFlotChart: () => 'SparklineFlotChart', +})); + describe('Sparkline component', () => { let component; let renderedComponent; diff --git a/x-pack/legacy/plugins/monitoring/public/components/sparkline/_index.scss b/x-pack/plugins/monitoring/public/components/sparkline/_index.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/sparkline/_index.scss rename to x-pack/plugins/monitoring/public/components/sparkline/_index.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/sparkline/_sparkline.scss b/x-pack/plugins/monitoring/public/components/sparkline/_sparkline.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/sparkline/_sparkline.scss rename to x-pack/plugins/monitoring/public/components/sparkline/_sparkline.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/sparkline/index.js b/x-pack/plugins/monitoring/public/components/sparkline/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/sparkline/index.js rename to x-pack/plugins/monitoring/public/components/sparkline/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/sparkline/sparkline_flot_chart.js b/x-pack/plugins/monitoring/public/components/sparkline/sparkline_flot_chart.js similarity index 98% rename from x-pack/legacy/plugins/monitoring/public/components/sparkline/sparkline_flot_chart.js rename to x-pack/plugins/monitoring/public/components/sparkline/sparkline_flot_chart.js index bb17f464a155ab..82d5f53f9fbd79 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/sparkline/sparkline_flot_chart.js +++ b/x-pack/plugins/monitoring/public/components/sparkline/sparkline_flot_chart.js @@ -5,7 +5,7 @@ */ import { last, isFunction, debounce } from 'lodash'; -import $ from 'plugins/xpack_main/jquery_flot'; +import $ from '../../lib/jquery_flot'; import { DEBOUNCE_FAST_MS } from '../../../common/constants'; /** diff --git a/x-pack/legacy/plugins/monitoring/public/components/status_icon/_index.scss b/x-pack/plugins/monitoring/public/components/status_icon/_index.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/status_icon/_index.scss rename to x-pack/plugins/monitoring/public/components/status_icon/_index.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/status_icon/_status_icon.scss b/x-pack/plugins/monitoring/public/components/status_icon/_status_icon.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/status_icon/_status_icon.scss rename to x-pack/plugins/monitoring/public/components/status_icon/_status_icon.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/status_icon/index.js b/x-pack/plugins/monitoring/public/components/status_icon/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/status_icon/index.js rename to x-pack/plugins/monitoring/public/components/status_icon/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/summary_status/__snapshots__/summary_status.test.js.snap b/x-pack/plugins/monitoring/public/components/summary_status/__snapshots__/summary_status.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/summary_status/__snapshots__/summary_status.test.js.snap rename to x-pack/plugins/monitoring/public/components/summary_status/__snapshots__/summary_status.test.js.snap diff --git a/x-pack/legacy/plugins/monitoring/public/components/summary_status/_index.scss b/x-pack/plugins/monitoring/public/components/summary_status/_index.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/summary_status/_index.scss rename to x-pack/plugins/monitoring/public/components/summary_status/_index.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/summary_status/_summary_status.scss b/x-pack/plugins/monitoring/public/components/summary_status/_summary_status.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/summary_status/_summary_status.scss rename to x-pack/plugins/monitoring/public/components/summary_status/_summary_status.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/summary_status/index.js b/x-pack/plugins/monitoring/public/components/summary_status/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/summary_status/index.js rename to x-pack/plugins/monitoring/public/components/summary_status/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/summary_status/summary_status.js b/x-pack/plugins/monitoring/public/components/summary_status/summary_status.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/summary_status/summary_status.js rename to x-pack/plugins/monitoring/public/components/summary_status/summary_status.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/summary_status/summary_status.test.js b/x-pack/plugins/monitoring/public/components/summary_status/summary_status.test.js similarity index 95% rename from x-pack/legacy/plugins/monitoring/public/components/summary_status/summary_status.test.js rename to x-pack/plugins/monitoring/public/components/summary_status/summary_status.test.js index fe709af2663497..5f4dced47ee6f4 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/summary_status/summary_status.test.js +++ b/x-pack/plugins/monitoring/public/components/summary_status/summary_status.test.js @@ -5,7 +5,7 @@ */ import React from 'react'; -import { renderWithIntl } from '../../../../../../test_utils/enzyme_helpers'; +import { renderWithIntl } from 'test_utils/enzyme_helpers'; import { SummaryStatus } from './summary_status'; jest.mock(`@elastic/eui/lib/components/form/form_row/make_id`, () => () => `generated-id`); diff --git a/x-pack/legacy/plugins/monitoring/public/components/table/_index.scss b/x-pack/plugins/monitoring/public/components/table/_index.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/table/_index.scss rename to x-pack/plugins/monitoring/public/components/table/_index.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/table/_table.scss b/x-pack/plugins/monitoring/public/components/table/_table.scss similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/table/_table.scss rename to x-pack/plugins/monitoring/public/components/table/_table.scss diff --git a/x-pack/legacy/plugins/monitoring/public/components/table/eui_table.js b/x-pack/plugins/monitoring/public/components/table/eui_table.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/table/eui_table.js rename to x-pack/plugins/monitoring/public/components/table/eui_table.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/table/eui_table_ssp.js b/x-pack/plugins/monitoring/public/components/table/eui_table_ssp.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/table/eui_table_ssp.js rename to x-pack/plugins/monitoring/public/components/table/eui_table_ssp.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/table/index.js b/x-pack/plugins/monitoring/public/components/table/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/table/index.js rename to x-pack/plugins/monitoring/public/components/table/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/components/table/storage.js b/x-pack/plugins/monitoring/public/components/table/storage.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/components/table/storage.js rename to x-pack/plugins/monitoring/public/components/table/storage.js diff --git a/x-pack/legacy/plugins/monitoring/public/directives/beats/overview/index.js b/x-pack/plugins/monitoring/public/directives/beats/beat/index.js similarity index 59% rename from x-pack/legacy/plugins/monitoring/public/directives/beats/overview/index.js rename to x-pack/plugins/monitoring/public/directives/beats/beat/index.js index fb78b6a2e03002..103cac98ba5646 100644 --- a/x-pack/legacy/plugins/monitoring/public/directives/beats/overview/index.js +++ b/x-pack/plugins/monitoring/public/directives/beats/beat/index.js @@ -6,12 +6,10 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { uiModules } from 'plugins/monitoring/np_imports/ui/modules'; -import { BeatsOverview } from 'plugins/monitoring/components/beats/overview'; -import { I18nContext } from 'ui/i18n'; +import { Beat } from '../../../components/beats/beat'; -const uiModule = uiModules.get('monitoring/directives', []); -uiModule.directive('monitoringBeatsOverview', () => { +//monitoringBeatsBeat +export function monitoringBeatsBeatProvider() { return { restrict: 'E', scope: { @@ -24,12 +22,15 @@ uiModule.directive('monitoringBeatsOverview', () => { scope.$watch('data', (data = {}) => { render( - - - , + , $el[0] ); }); }, }; -}); +} diff --git a/x-pack/legacy/plugins/monitoring/public/directives/beats/beat/index.js b/x-pack/plugins/monitoring/public/directives/beats/overview/index.js similarity index 55% rename from x-pack/legacy/plugins/monitoring/public/directives/beats/beat/index.js rename to x-pack/plugins/monitoring/public/directives/beats/overview/index.js index c86315fc034823..4faf69e13d02c4 100644 --- a/x-pack/legacy/plugins/monitoring/public/directives/beats/beat/index.js +++ b/x-pack/plugins/monitoring/public/directives/beats/overview/index.js @@ -6,12 +6,9 @@ import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { uiModules } from 'plugins/monitoring/np_imports/ui/modules'; -import { Beat } from 'plugins/monitoring/components/beats/beat'; -import { I18nContext } from 'ui/i18n'; +import { BeatsOverview } from '../../../components/beats/overview'; -const uiModule = uiModules.get('monitoring/directives', []); -uiModule.directive('monitoringBeatsBeat', () => { +export function monitoringBeatsOverviewProvider() { return { restrict: 'E', scope: { @@ -24,17 +21,10 @@ uiModule.directive('monitoringBeatsBeat', () => { scope.$watch('data', (data = {}) => { render( - - - , + , $el[0] ); }); }, }; -}); +} diff --git a/x-pack/legacy/plugins/monitoring/public/directives/elasticsearch/ml_job_listing/index.js b/x-pack/plugins/monitoring/public/directives/elasticsearch/ml_job_listing/index.js similarity index 63% rename from x-pack/legacy/plugins/monitoring/public/directives/elasticsearch/ml_job_listing/index.js rename to x-pack/plugins/monitoring/public/directives/elasticsearch/ml_job_listing/index.js index 8f35bd599ac49d..706d1ac4c0e33e 100644 --- a/x-pack/legacy/plugins/monitoring/public/directives/elasticsearch/ml_job_listing/index.js +++ b/x-pack/plugins/monitoring/public/directives/elasticsearch/ml_job_listing/index.js @@ -8,10 +8,8 @@ import { capitalize } from 'lodash'; import numeral from '@elastic/numeral'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { I18nContext } from 'ui/i18n'; -import { uiModules } from 'plugins/monitoring/np_imports/ui/modules'; -import { EuiMonitoringTable } from 'plugins/monitoring/components/table'; -import { MachineLearningJobStatusIcon } from 'plugins/monitoring/components/elasticsearch/ml_job_listing/status_icon'; +import { EuiMonitoringTable } from '../../../components/table'; +import { MachineLearningJobStatusIcon } from '../../../components/elasticsearch/ml_job_listing/status_icon'; import { LARGE_ABBREVIATED, LARGE_BYTES } from '../../../../common/formatting'; import { EuiLink, EuiPage, EuiPageContent, EuiPageBody, EuiPanel, EuiSpacer } from '@elastic/eui'; import { ClusterStatus } from '../../../components/elasticsearch/cluster_status'; @@ -93,8 +91,8 @@ const getColumns = (kbnUrl, scope) => [ }, ]; -const uiModule = uiModules.get('monitoring/directives', []); -uiModule.directive('monitoringMlListing', kbnUrl => { +//monitoringMlListing +export function monitoringMlListingProvider(kbnUrl) { return { restrict: 'E', scope: { @@ -117,51 +115,49 @@ uiModule.directive('monitoringMlListing', kbnUrl => { scope.$watch('jobs', (jobs = []) => { const mlTable = ( - - - - - - - - - - - - - + + + + + + + + + + + ); render(mlTable, $el[0]); }); }, }; -}); +} diff --git a/x-pack/legacy/plugins/monitoring/public/directives/main/__tests__/monitoring_main_controller.js b/x-pack/plugins/monitoring/public/directives/main/__tests__/monitoring_main_controller.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/directives/main/__tests__/monitoring_main_controller.js rename to x-pack/plugins/monitoring/public/directives/main/__tests__/monitoring_main_controller.js diff --git a/x-pack/legacy/plugins/monitoring/public/directives/main/index.html b/x-pack/plugins/monitoring/public/directives/main/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/directives/main/index.html rename to x-pack/plugins/monitoring/public/directives/main/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/directives/main/index.js b/x-pack/plugins/monitoring/public/directives/main/index.js similarity index 93% rename from x-pack/legacy/plugins/monitoring/public/directives/main/index.js rename to x-pack/plugins/monitoring/public/directives/main/index.js index 4e09225cb85e89..8c28ab6103868f 100644 --- a/x-pack/legacy/plugins/monitoring/public/directives/main/index.js +++ b/x-pack/plugins/monitoring/public/directives/main/index.js @@ -8,9 +8,8 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { EuiSelect, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; -import { uiModules } from 'plugins/monitoring/np_imports/ui/modules'; import template from './index.html'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { Legacy } from '../../legacy_shims'; import { shortenPipelineHash } from '../../../common/formatting'; import { getSetupModeState, initSetupModeState } from '../../lib/setup_mode'; import { Subscription } from 'rxjs'; @@ -77,6 +76,7 @@ export class MonitoringMainController { } addTimerangeObservers = () => { + const timefilter = Legacy.shims.timefilter; this.subscriptions = new Subscription(); const refreshIntervalUpdated = () => { @@ -101,6 +101,7 @@ export class MonitoringMainController { // kick things off from the directive link function setup(options) { + const timefilter = Legacy.shims.timefilter; this._licenseService = options.licenseService; this._breadcrumbsService = options.breadcrumbsService; this._kbnUrlService = options.kbnUrlService; @@ -197,8 +198,7 @@ export class MonitoringMainController { } } -const uiModule = uiModules.get('monitoring/directives', []); -uiModule.directive('monitoringMain', (breadcrumbs, license, kbnUrl, $injector) => { +export function monitoringMainProvider(breadcrumbs, license, kbnUrl, $injector) { const $executor = $injector.get('$executor'); return { @@ -209,7 +209,15 @@ uiModule.directive('monitoringMain', (breadcrumbs, license, kbnUrl, $injector) = controllerAs: 'monitoringMain', bindToController: true, link(scope, _element, attributes, controller) { - controller.addTimerangeObservers(); + scope.$applyAsync(() => { + controller.addTimerangeObservers(); + const setupObj = getSetupObj(); + controller.setup(setupObj); + Object.keys(setupObj.attributes).forEach(key => { + attributes.$observe(key, () => controller.setup(getSetupObj())); + }); + }); + initSetupModeState(scope, $injector, () => { controller.setup(getSetupObj()); }); @@ -244,11 +252,6 @@ uiModule.directive('monitoringMain', (breadcrumbs, license, kbnUrl, $injector) = }; } - const setupObj = getSetupObj(); - controller.setup(setupObj); - Object.keys(setupObj.attributes).forEach(key => { - attributes.$observe(key, () => controller.setup(getSetupObj())); - }); scope.$on('$destroy', () => { controller.pipelineDropdownElement && unmountComponentAtNode(controller.pipelineDropdownElement); @@ -260,4 +263,4 @@ uiModule.directive('monitoringMain', (breadcrumbs, license, kbnUrl, $injector) = }); }, }; -}); +} diff --git a/x-pack/legacy/plugins/monitoring/public/icons/health-gray.svg b/x-pack/plugins/monitoring/public/icons/health-gray.svg similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/icons/health-gray.svg rename to x-pack/plugins/monitoring/public/icons/health-gray.svg diff --git a/x-pack/legacy/plugins/monitoring/public/icons/health-green.svg b/x-pack/plugins/monitoring/public/icons/health-green.svg similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/icons/health-green.svg rename to x-pack/plugins/monitoring/public/icons/health-green.svg diff --git a/x-pack/legacy/plugins/monitoring/public/icons/health-red.svg b/x-pack/plugins/monitoring/public/icons/health-red.svg similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/icons/health-red.svg rename to x-pack/plugins/monitoring/public/icons/health-red.svg diff --git a/x-pack/legacy/plugins/monitoring/public/icons/health-yellow.svg b/x-pack/plugins/monitoring/public/icons/health-yellow.svg similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/icons/health-yellow.svg rename to x-pack/plugins/monitoring/public/icons/health-yellow.svg diff --git a/x-pack/legacy/plugins/monitoring/public/index.scss b/x-pack/plugins/monitoring/public/index.scss similarity index 83% rename from x-pack/legacy/plugins/monitoring/public/index.scss rename to x-pack/plugins/monitoring/public/index.scss index 41bca7774a8b84..4dda80ee7454b6 100644 --- a/x-pack/legacy/plugins/monitoring/public/index.scss +++ b/x-pack/plugins/monitoring/public/index.scss @@ -21,3 +21,10 @@ @import 'components/logstash/pipeline_viewer/views/index'; @import 'components/elasticsearch/shard_allocation/index'; @import 'components/setup_mode/index'; +@import 'components/elasticsearch/ccr/index'; + +.monitoringApplicationWrapper { + display: flex; + flex-direction: column; + flex-grow: 1; +} diff --git a/x-pack/legacy/plugins/monitoring/public/np_ready/index.ts b/x-pack/plugins/monitoring/public/index.ts similarity index 84% rename from x-pack/legacy/plugins/monitoring/public/np_ready/index.ts rename to x-pack/plugins/monitoring/public/index.ts index 80848c497c3701..71c98e8e8b1312 100644 --- a/x-pack/legacy/plugins/monitoring/public/np_ready/index.ts +++ b/x-pack/plugins/monitoring/public/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PluginInitializerContext } from 'src/core/public'; +import { PluginInitializerContext } from '../../../../src/core/public'; import { MonitoringPlugin } from './plugin'; export function plugin(ctx: PluginInitializerContext) { diff --git a/x-pack/legacy/plugins/monitoring/public/jest.helpers.ts b/x-pack/plugins/monitoring/public/jest.helpers.ts similarity index 80% rename from x-pack/legacy/plugins/monitoring/public/jest.helpers.ts rename to x-pack/plugins/monitoring/public/jest.helpers.ts index 46ba603d301382..96fb70e480f532 100644 --- a/x-pack/legacy/plugins/monitoring/public/jest.helpers.ts +++ b/x-pack/plugins/monitoring/public/jest.helpers.ts @@ -25,12 +25,3 @@ export function mockUseEffects(count = 1) { spy.mockImplementationOnce(f => f()); } } - -// export function mockUseEffectForDeps(deps, count = 1) { -// const spy = jest.spyOn(React, 'useEffect'); -// for (let i = 0; i < count; i++) { -// spy.mockImplementationOnce((f, depList) => { - -// }); -// } -// } diff --git a/x-pack/plugins/monitoring/public/legacy_shims.ts b/x-pack/plugins/monitoring/public/legacy_shims.ts new file mode 100644 index 00000000000000..47aa1048c51306 --- /dev/null +++ b/x-pack/plugins/monitoring/public/legacy_shims.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreStart } from 'kibana/public'; +import angular from 'angular'; +import { HttpRequestInit } from '../../../../src/core/public'; +import { MonitoringPluginDependencies } from './types'; + +export interface KFetchQuery { + [key: string]: string | number | boolean | undefined; +} + +export interface KFetchOptions extends HttpRequestInit { + pathname: string; + query?: KFetchQuery; + asSystemRequest?: boolean; +} + +export interface KFetchKibanaOptions { + prependBasePath?: boolean; +} + +export interface IShims { + toastNotifications: CoreStart['notifications']['toasts']; + capabilities: { get: () => CoreStart['application']['capabilities'] }; + getAngularInjector: () => angular.auto.IInjectorService; + getBasePath: () => string; + getInjected: (name: string, defaultValue?: unknown) => unknown; + breadcrumbs: { set: () => void }; + I18nContext: CoreStart['i18n']['Context']; + docLinks: CoreStart['docLinks']; + docTitle: CoreStart['chrome']['docTitle']; + timefilter: MonitoringPluginDependencies['data']['query']['timefilter']['timefilter']; + kfetch: ( + { pathname, ...options }: KFetchOptions, + kfetchOptions?: KFetchKibanaOptions | undefined + ) => Promise; + isCloud: boolean; +} + +export class Legacy { + private static _shims: IShims; + + public static init( + { core, data, isCloud }: MonitoringPluginDependencies, + ngInjector: angular.auto.IInjectorService + ) { + this._shims = { + toastNotifications: core.notifications.toasts, + capabilities: { get: () => core.application.capabilities }, + getAngularInjector: (): angular.auto.IInjectorService => ngInjector, + getBasePath: (): string => core.http.basePath.get(), + getInjected: (name: string, defaultValue?: unknown): string | unknown => + core.injectedMetadata.getInjectedVar(name, defaultValue), + breadcrumbs: { + set: (...args: any[0]) => core.chrome.setBreadcrumbs.apply(this, args), + }, + I18nContext: core.i18n.Context, + docLinks: core.docLinks, + docTitle: core.chrome.docTitle, + timefilter: data.query.timefilter.timefilter, + kfetch: async ( + { pathname, ...options }: KFetchOptions, + kfetchOptions?: KFetchKibanaOptions + ) => + await core.http.fetch(pathname, { + prependBasePath: kfetchOptions?.prependBasePath, + ...options, + }), + isCloud, + }; + } + + public static get shims(): Readonly { + if (!Legacy._shims) { + throw new Error('Legacy needs to be initiated with Legacy.init(...) before use'); + } + return Legacy._shims; + } +} diff --git a/x-pack/legacy/plugins/monitoring/public/lib/__tests__/format_number.js b/x-pack/plugins/monitoring/public/lib/__tests__/format_number.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/lib/__tests__/format_number.js rename to x-pack/plugins/monitoring/public/lib/__tests__/format_number.js diff --git a/x-pack/legacy/plugins/monitoring/public/lib/ajax_error_handler.tsx b/x-pack/plugins/monitoring/public/lib/ajax_error_handler.tsx similarity index 88% rename from x-pack/legacy/plugins/monitoring/public/lib/ajax_error_handler.tsx rename to x-pack/plugins/monitoring/public/lib/ajax_error_handler.tsx index c09014b9a96277..d729457f60df1e 100644 --- a/x-pack/legacy/plugins/monitoring/public/lib/ajax_error_handler.tsx +++ b/x-pack/plugins/monitoring/public/lib/ajax_error_handler.tsx @@ -6,12 +6,11 @@ import React from 'react'; import { contains } from 'lodash'; -import { toastNotifications } from 'ui/notify'; -// @ts-ignore -import { formatMsg } from '../../../../../../src/plugins/kibana_legacy/public'; // eslint-disable-line import/order import { EuiButton, EuiSpacer, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; +import { Legacy } from '../legacy_shims'; +import { formatMsg } from '../../../../../src/plugins/kibana_legacy/public'; +import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; export function formatMonitoringError(err: any) { // TODO: We should stop using Boom for errors and instead write a custom handler to return richer error objects @@ -43,7 +42,7 @@ export function ajaxErrorHandlersProvider($injector: any) { kbnUrl.redirect('access-denied'); } else if (err.status === 404 && !contains(window.location.hash, 'no-data')) { // pass through if this is a 404 and we're already on the no-data page - toastNotifications.addDanger({ + Legacy.shims.toastNotifications.addDanger({ title: toMountPoint( string) + labelBoxBorderColor: color + noColumns: number + position: "ne" or "nw" or "se" or "sw" + margin: number of pixels or [x margin, y margin] + backgroundColor: null or color + backgroundOpacity: number between 0 and 1 + container: null or jQuery object/DOM element/jQuery expression + sorted: null/false, true, "ascending", "descending", "reverse", or a comparator +} +``` + +The legend is generated as a table with the data series labels and +small label boxes with the color of the series. If you want to format +the labels in some way, e.g. make them to links, you can pass in a +function for "labelFormatter". Here's an example that makes them +clickable: + +```js +labelFormatter: function(label, series) { + // series is the series object for the label + return '
' + label + ''; +} +``` + +To prevent a series from showing up in the legend, simply have the function +return null. + +"noColumns" is the number of columns to divide the legend table into. +"position" specifies the overall placement of the legend within the +plot (top-right, top-left, etc.) and margin the distance to the plot +edge (this can be either a number or an array of two numbers like [x, +y]). "backgroundColor" and "backgroundOpacity" specifies the +background. The default is a partly transparent auto-detected +background. + +If you want the legend to appear somewhere else in the DOM, you can +specify "container" as a jQuery object/expression to put the legend +table into. The "position" and "margin" etc. options will then be +ignored. Note that Flot will overwrite the contents of the container. + +Legend entries appear in the same order as their series by default. If "sorted" +is "reverse" then they appear in the opposite order from their series. To sort +them alphabetically, you can specify true, "ascending" or "descending", where +true and "ascending" are equivalent. + +You can also provide your own comparator function that accepts two +objects with "label" and "color" properties, and returns zero if they +are equal, a positive value if the first is greater than the second, +and a negative value if the first is less than the second. + +```js +sorted: function(a, b) { + // sort alphabetically in ascending order + return a.label == b.label ? 0 : ( + a.label > b.label ? 1 : -1 + ) +} +``` + + +## Customizing the axes ## + +```js +xaxis, yaxis: { + show: null or true/false + position: "bottom" or "top" or "left" or "right" + mode: null or "time" ("time" requires jquery.flot.time.js plugin) + timezone: null, "browser" or timezone (only makes sense for mode: "time") + + color: null or color spec + tickColor: null or color spec + font: null or font spec object + + min: null or number + max: null or number + autoscaleMargin: null or number + + transform: null or fn: number -> number + inverseTransform: null or fn: number -> number + + ticks: null or number or ticks array or (fn: axis -> ticks array) + tickSize: number or array + minTickSize: number or array + tickFormatter: (fn: number, object -> string) or string + tickDecimals: null or number + + labelWidth: null or number + labelHeight: null or number + reserveSpace: null or true + + tickLength: null or number + + alignTicksWithAxis: null or number +} +``` + +All axes have the same kind of options. The following describes how to +configure one axis, see below for what to do if you've got more than +one x axis or y axis. + +If you don't set the "show" option (i.e. it is null), visibility is +auto-detected, i.e. the axis will show up if there's data associated +with it. You can override this by setting the "show" option to true or +false. + +The "position" option specifies where the axis is placed, bottom or +top for x axes, left or right for y axes. The "mode" option determines +how the data is interpreted, the default of null means as decimal +numbers. Use "time" for time series data; see the time series data +section. The time plugin (jquery.flot.time.js) is required for time +series support. + +The "color" option determines the color of the line and ticks for the axis, and +defaults to the grid color with transparency. For more fine-grained control you +can also set the color of the ticks separately with "tickColor". + +You can customize the font and color used to draw the axis tick labels with CSS +or directly via the "font" option. When "font" is null - the default - each +tick label is given the 'flot-tick-label' class. For compatibility with Flot +0.7 and earlier the labels are also given the 'tickLabel' class, but this is +deprecated and scheduled to be removed with the release of version 1.0.0. + +To enable more granular control over styles, labels are divided between a set +of text containers, with each holding the labels for one axis. These containers +are given the classes 'flot-[x|y]-axis', and 'flot-[x|y]#-axis', where '#' is +the number of the axis when there are multiple axes. For example, the x-axis +labels for a simple plot with only a single x-axis might look like this: + +```html +
+
January 2013
+ ... +
+``` + +For direct control over label styles you can also provide "font" as an object +with this format: + +```js +{ + size: 11, + lineHeight: 13, + style: "italic", + weight: "bold", + family: "sans-serif", + variant: "small-caps", + color: "#545454" +} +``` + +The size and lineHeight must be expressed in pixels; CSS units such as 'em' +or 'smaller' are not allowed. + +The options "min"/"max" are the precise minimum/maximum value on the +scale. If you don't specify either of them, a value will automatically +be chosen based on the minimum/maximum data values. Note that Flot +always examines all the data values you feed to it, even if a +restriction on another axis may make some of them invisible (this +makes interactive use more stable). + +The "autoscaleMargin" is a bit esoteric: it's the fraction of margin +that the scaling algorithm will add to avoid that the outermost points +ends up on the grid border. Note that this margin is only applied when +a min or max value is not explicitly set. If a margin is specified, +the plot will furthermore extend the axis end-point to the nearest +whole tick. The default value is "null" for the x axes and 0.02 for y +axes which seems appropriate for most cases. + +"transform" and "inverseTransform" are callbacks you can put in to +change the way the data is drawn. You can design a function to +compress or expand certain parts of the axis non-linearly, e.g. +suppress weekends or compress far away points with a logarithm or some +other means. When Flot draws the plot, each value is first put through +the transform function. Here's an example, the x axis can be turned +into a natural logarithm axis with the following code: + +```js +xaxis: { + transform: function (v) { return Math.log(v); }, + inverseTransform: function (v) { return Math.exp(v); } +} +``` + +Similarly, for reversing the y axis so the values appear in inverse +order: + +```js +yaxis: { + transform: function (v) { return -v; }, + inverseTransform: function (v) { return -v; } +} +``` + +Note that for finding extrema, Flot assumes that the transform +function does not reorder values (it should be monotone). + +The inverseTransform is simply the inverse of the transform function +(so v == inverseTransform(transform(v)) for all relevant v). It is +required for converting from canvas coordinates to data coordinates, +e.g. for a mouse interaction where a certain pixel is clicked. If you +don't use any interactive features of Flot, you may not need it. + + +The rest of the options deal with the ticks. + +If you don't specify any ticks, a tick generator algorithm will make +some for you. The algorithm has two passes. It first estimates how +many ticks would be reasonable and uses this number to compute a nice +round tick interval size. Then it generates the ticks. + +You can specify how many ticks the algorithm aims for by setting +"ticks" to a number. The algorithm always tries to generate reasonably +round tick values so even if you ask for three ticks, you might get +five if that fits better with the rounding. If you don't want any +ticks at all, set "ticks" to 0 or an empty array. + +Another option is to skip the rounding part and directly set the tick +interval size with "tickSize". If you set it to 2, you'll get ticks at +2, 4, 6, etc. Alternatively, you can specify that you just don't want +ticks at a size less than a specific tick size with "minTickSize". +Note that for time series, the format is an array like [2, "month"], +see the next section. + +If you want to completely override the tick algorithm, you can specify +an array for "ticks", either like this: + +```js +ticks: [0, 1.2, 2.4] +``` + +Or like this where the labels are also customized: + +```js +ticks: [[0, "zero"], [1.2, "one mark"], [2.4, "two marks"]] +``` + +You can mix the two if you like. + +For extra flexibility you can specify a function as the "ticks" +parameter. The function will be called with an object with the axis +min and max and should return a ticks array. Here's a simplistic tick +generator that spits out intervals of pi, suitable for use on the x +axis for trigonometric functions: + +```js +function piTickGenerator(axis) { + var res = [], i = Math.floor(axis.min / Math.PI); + do { + var v = i * Math.PI; + res.push([v, i + "\u03c0"]); + ++i; + } while (v < axis.max); + return res; +} +``` + +You can control how the ticks look like with "tickDecimals", the +number of decimals to display (default is auto-detected). + +Alternatively, for ultimate control over how ticks are formatted you can +provide a function to "tickFormatter". The function is passed two +parameters, the tick value and an axis object with information, and +should return a string. The default formatter looks like this: + +```js +function formatter(val, axis) { + return val.toFixed(axis.tickDecimals); +} +``` + +The axis object has "min" and "max" with the range of the axis, +"tickDecimals" with the number of decimals to round the value to and +"tickSize" with the size of the interval between ticks as calculated +by the automatic axis scaling algorithm (or specified by you). Here's +an example of a custom formatter: + +```js +function suffixFormatter(val, axis) { + if (val > 1000000) + return (val / 1000000).toFixed(axis.tickDecimals) + " MB"; + else if (val > 1000) + return (val / 1000).toFixed(axis.tickDecimals) + " kB"; + else + return val.toFixed(axis.tickDecimals) + " B"; +} +``` + +"labelWidth" and "labelHeight" specifies a fixed size of the tick +labels in pixels. They're useful in case you need to align several +plots. "reserveSpace" means that even if an axis isn't shown, Flot +should reserve space for it - it is useful in combination with +labelWidth and labelHeight for aligning multi-axis charts. + +"tickLength" is the length of the tick lines in pixels. By default, the +innermost axes will have ticks that extend all across the plot, while +any extra axes use small ticks. A value of null means use the default, +while a number means small ticks of that length - set it to 0 to hide +the lines completely. + +If you set "alignTicksWithAxis" to the number of another axis, e.g. +alignTicksWithAxis: 1, Flot will ensure that the autogenerated ticks +of this axis are aligned with the ticks of the other axis. This may +improve the looks, e.g. if you have one y axis to the left and one to +the right, because the grid lines will then match the ticks in both +ends. The trade-off is that the forced ticks won't necessarily be at +natural places. + + +## Multiple axes ## + +If you need more than one x axis or y axis, you need to specify for +each data series which axis they are to use, as described under the +format of the data series, e.g. { data: [...], yaxis: 2 } specifies +that a series should be plotted against the second y axis. + +To actually configure that axis, you can't use the xaxis/yaxis options +directly - instead there are two arrays in the options: + +```js +xaxes: [] +yaxes: [] +``` + +Here's an example of configuring a single x axis and two y axes (we +can leave options of the first y axis empty as the defaults are fine): + +```js +{ + xaxes: [ { position: "top" } ], + yaxes: [ { }, { position: "right", min: 20 } ] +} +``` + +The arrays get their default values from the xaxis/yaxis settings, so +say you want to have all y axes start at zero, you can simply specify +yaxis: { min: 0 } instead of adding a min parameter to all the axes. + +Generally, the various interfaces in Flot dealing with data points +either accept an xaxis/yaxis parameter to specify which axis number to +use (starting from 1), or lets you specify the coordinate directly as +x2/x3/... or x2axis/x3axis/... instead of "x" or "xaxis". + + +## Time series data ## + +Please note that it is now required to include the time plugin, +jquery.flot.time.js, for time series support. + +Time series are a bit more difficult than scalar data because +calendars don't follow a simple base 10 system. For many cases, Flot +abstracts most of this away, but it can still be a bit difficult to +get the data into Flot. So we'll first discuss the data format. + +The time series support in Flot is based on JavaScript timestamps, +i.e. everywhere a time value is expected or handed over, a JavaScript +timestamp number is used. This is a number, not a Date object. A +JavaScript timestamp is the number of milliseconds since January 1, +1970 00:00:00 UTC. This is almost the same as Unix timestamps, except it's +in milliseconds, so remember to multiply by 1000! + +You can see a timestamp like this + +```js +alert((new Date()).getTime()) +``` + +There are different schools of thought when it comes to display of +timestamps. Many will want the timestamps to be displayed according to +a certain time zone, usually the time zone in which the data has been +produced. Some want the localized experience, where the timestamps are +displayed according to the local time of the visitor. Flot supports +both. Optionally you can include a third-party library to get +additional timezone support. + +Default behavior is that Flot always displays timestamps according to +UTC. The reason being that the core JavaScript Date object does not +support other fixed time zones. Often your data is at another time +zone, so it may take a little bit of tweaking to work around this +limitation. + +The easiest way to think about it is to pretend that the data +production time zone is UTC, even if it isn't. So if you have a +datapoint at 2002-02-20 08:00, you can generate a timestamp for eight +o'clock UTC even if it really happened eight o'clock UTC+0200. + +In PHP you can get an appropriate timestamp with: + +```php +strtotime("2002-02-20 UTC") * 1000 +``` + +In Python you can get it with something like: + +```python +calendar.timegm(datetime_object.timetuple()) * 1000 +``` +In Ruby you can get it using the `#to_i` method on the +[`Time`](http://apidock.com/ruby/Time/to_i) object. If you're using the +`active_support` gem (default for Ruby on Rails applications) `#to_i` is also +available on the `DateTime` and `ActiveSupport::TimeWithZone` objects. You +simply need to multiply the result by 1000: + +```ruby +Time.now.to_i * 1000 # => 1383582043000 +# ActiveSupport examples: +DateTime.now.to_i * 1000 # => 1383582043000 +ActiveSupport::TimeZone.new('Asia/Shanghai').now.to_i * 1000 +# => 1383582043000 +``` + +In .NET you can get it with something like: + +```aspx +public static int GetJavaScriptTimestamp(System.DateTime input) +{ + System.TimeSpan span = new System.TimeSpan(System.DateTime.Parse("1/1/1970").Ticks); + System.DateTime time = input.Subtract(span); + return (long)(time.Ticks / 10000); +} +``` + +JavaScript also has some support for parsing date strings, so it is +possible to generate the timestamps manually client-side. + +If you've already got the real UTC timestamp, it's too late to use the +pretend trick described above. But you can fix up the timestamps by +adding the time zone offset, e.g. for UTC+0200 you would add 2 hours +to the UTC timestamp you got. Then it'll look right on the plot. Most +programming environments have some means of getting the timezone +offset for a specific date (note that you need to get the offset for +each individual timestamp to account for daylight savings). + +The alternative with core JavaScript is to interpret the timestamps +according to the time zone that the visitor is in, which means that +the ticks will shift with the time zone and daylight savings of each +visitor. This behavior is enabled by setting the axis option +"timezone" to the value "browser". + +If you need more time zone functionality than this, there is still +another option. If you include the "timezone-js" library + in the page and set axis.timezone +to a value recognized by said library, Flot will use timezone-js to +interpret the timestamps according to that time zone. + +Once you've gotten the timestamps into the data and specified "time" +as the axis mode, Flot will automatically generate relevant ticks and +format them. As always, you can tweak the ticks via the "ticks" option +- just remember that the values should be timestamps (numbers), not +Date objects. + +Tick generation and formatting can also be controlled separately +through the following axis options: + +```js +minTickSize: array +timeformat: null or format string +monthNames: null or array of size 12 of strings +dayNames: null or array of size 7 of strings +twelveHourClock: boolean +``` + +Here "timeformat" is a format string to use. You might use it like +this: + +```js +xaxis: { + mode: "time", + timeformat: "%Y/%m/%d" +} +``` + +This will result in tick labels like "2000/12/24". A subset of the +standard strftime specifiers are supported (plus the nonstandard %q): + +```js +%a: weekday name (customizable) +%b: month name (customizable) +%d: day of month, zero-padded (01-31) +%e: day of month, space-padded ( 1-31) +%H: hours, 24-hour time, zero-padded (00-23) +%I: hours, 12-hour time, zero-padded (01-12) +%m: month, zero-padded (01-12) +%M: minutes, zero-padded (00-59) +%q: quarter (1-4) +%S: seconds, zero-padded (00-59) +%y: year (two digits) +%Y: year (four digits) +%p: am/pm +%P: AM/PM (uppercase version of %p) +%w: weekday as number (0-6, 0 being Sunday) +``` + +Flot 0.8 switched from %h to the standard %H hours specifier. The %h specifier +is still available, for backwards-compatibility, but is deprecated and +scheduled to be removed permanently with the release of version 1.0. + +You can customize the month names with the "monthNames" option. For +instance, for Danish you might specify: + +```js +monthNames: ["jan", "feb", "mar", "apr", "maj", "jun", "jul", "aug", "sep", "okt", "nov", "dec"] +``` + +Similarly you can customize the weekday names with the "dayNames" +option. An example in French: + +```js +dayNames: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"] +``` + +If you set "twelveHourClock" to true, the autogenerated timestamps +will use 12 hour AM/PM timestamps instead of 24 hour. This only +applies if you have not set "timeformat". Use the "%I" and "%p" or +"%P" options if you want to build your own format string with 12-hour +times. + +If the Date object has a strftime property (and it is a function), it +will be used instead of the built-in formatter. Thus you can include +a strftime library such as http://hacks.bluesmoon.info/strftime/ for +more powerful date/time formatting. + +If everything else fails, you can control the formatting by specifying +a custom tick formatter function as usual. Here's a simple example +which will format December 24 as 24/12: + +```js +tickFormatter: function (val, axis) { + var d = new Date(val); + return d.getUTCDate() + "/" + (d.getUTCMonth() + 1); +} +``` + +Note that for the time mode "tickSize" and "minTickSize" are a bit +special in that they are arrays on the form "[value, unit]" where unit +is one of "second", "minute", "hour", "day", "month" and "year". So +you can specify + +```js +minTickSize: [1, "month"] +``` + +to get a tick interval size of at least 1 month and correspondingly, +if axis.tickSize is [2, "day"] in the tick formatter, the ticks have +been produced with two days in-between. + + +## Customizing the data series ## + +```js +series: { + lines, points, bars: { + show: boolean + lineWidth: number + fill: boolean or number + fillColor: null or color/gradient + } + + lines, bars: { + zero: boolean + } + + points: { + radius: number + symbol: "circle" or function + } + + bars: { + barWidth: number + align: "left", "right" or "center" + horizontal: boolean + } + + lines: { + steps: boolean + } + + shadowSize: number + highlightColor: color or number +} + +colors: [ color1, color2, ... ] +``` + +The options inside "series: {}" are copied to each of the series. So +you can specify that all series should have bars by putting it in the +global options, or override it for individual series by specifying +bars in a particular the series object in the array of data. + +The most important options are "lines", "points" and "bars" that +specify whether and how lines, points and bars should be shown for +each data series. In case you don't specify anything at all, Flot will +default to showing lines (you can turn this off with +lines: { show: false }). You can specify the various types +independently of each other, and Flot will happily draw each of them +in turn (this is probably only useful for lines and points), e.g. + +```js +var options = { + series: { + lines: { show: true, fill: true, fillColor: "rgba(255, 255, 255, 0.8)" }, + points: { show: true, fill: false } + } +}; +``` + +"lineWidth" is the thickness of the line or outline in pixels. You can +set it to 0 to prevent a line or outline from being drawn; this will +also hide the shadow. + +"fill" is whether the shape should be filled. For lines, this produces +area graphs. You can use "fillColor" to specify the color of the fill. +If "fillColor" evaluates to false (default for everything except +points which are filled with white), the fill color is auto-set to the +color of the data series. You can adjust the opacity of the fill by +setting fill to a number between 0 (fully transparent) and 1 (fully +opaque). + +For bars, fillColor can be a gradient, see the gradient documentation +below. "barWidth" is the width of the bars in units of the x axis (or +the y axis if "horizontal" is true), contrary to most other measures +that are specified in pixels. For instance, for time series the unit +is milliseconds so 24 * 60 * 60 * 1000 produces bars with the width of +a day. "align" specifies whether a bar should be left-aligned +(default), right-aligned or centered on top of the value it represents. +When "horizontal" is on, the bars are drawn horizontally, i.e. from the +y axis instead of the x axis; note that the bar end points are still +defined in the same way so you'll probably want to swap the +coordinates if you've been plotting vertical bars first. + +Area and bar charts normally start from zero, regardless of the data's range. +This is because they convey information through size, and starting from a +different value would distort their meaning. In cases where the fill is purely +for decorative purposes, however, "zero" allows you to override this behavior. +It defaults to true for filled lines and bars; setting it to false tells the +series to use the same automatic scaling as an un-filled line. + +For lines, "steps" specifies whether two adjacent data points are +connected with a straight (possibly diagonal) line or with first a +horizontal and then a vertical line. Note that this transforms the +data by adding extra points. + +For points, you can specify the radius and the symbol. The only +built-in symbol type is circles, for other types you can use a plugin +or define them yourself by specifying a callback: + +```js +function cross(ctx, x, y, radius, shadow) { + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.moveTo(x - size, y - size); + ctx.lineTo(x + size, y + size); + ctx.moveTo(x - size, y + size); + ctx.lineTo(x + size, y - size); +} +``` + +The parameters are the drawing context, x and y coordinates of the +center of the point, a radius which corresponds to what the circle +would have used and whether the call is to draw a shadow (due to +limited canvas support, shadows are currently faked through extra +draws). It's good practice to ensure that the area covered by the +symbol is the same as for the circle with the given radius, this +ensures that all symbols have approximately the same visual weight. + +"shadowSize" is the default size of shadows in pixels. Set it to 0 to +remove shadows. + +"highlightColor" is the default color of the translucent overlay used +to highlight the series when the mouse hovers over it. + +The "colors" array specifies a default color theme to get colors for +the data series from. You can specify as many colors as you like, like +this: + +```js +colors: ["#d18b2c", "#dba255", "#919733"] +``` + +If there are more data series than colors, Flot will try to generate +extra colors by lightening and darkening colors in the theme. + + +## Customizing the grid ## + +```js +grid: { + show: boolean + aboveData: boolean + color: color + backgroundColor: color/gradient or null + margin: number or margin object + labelMargin: number + axisMargin: number + markings: array of markings or (fn: axes -> array of markings) + borderWidth: number or object with "top", "right", "bottom" and "left" properties with different widths + borderColor: color or null or object with "top", "right", "bottom" and "left" properties with different colors + minBorderMargin: number or null + clickable: boolean + hoverable: boolean + autoHighlight: boolean + mouseActiveRadius: number +} + +interaction: { + redrawOverlayInterval: number or -1 +} +``` + +The grid is the thing with the axes and a number of ticks. Many of the +things in the grid are configured under the individual axes, but not +all. "color" is the color of the grid itself whereas "backgroundColor" +specifies the background color inside the grid area, here null means +that the background is transparent. You can also set a gradient, see +the gradient documentation below. + +You can turn off the whole grid including tick labels by setting +"show" to false. "aboveData" determines whether the grid is drawn +above the data or below (below is default). + +"margin" is the space in pixels between the canvas edge and the grid, +which can be either a number or an object with individual margins for +each side, in the form: + +```js +margin: { + top: top margin in pixels + left: left margin in pixels + bottom: bottom margin in pixels + right: right margin in pixels +} +``` + +"labelMargin" is the space in pixels between tick labels and axis +line, and "axisMargin" is the space in pixels between axes when there +are two next to each other. + +"borderWidth" is the width of the border around the plot. Set it to 0 +to disable the border. Set it to an object with "top", "right", +"bottom" and "left" properties to use different widths. You can +also set "borderColor" if you want the border to have a different color +than the grid lines. Set it to an object with "top", "right", "bottom" +and "left" properties to use different colors. "minBorderMargin" controls +the default minimum margin around the border - it's used to make sure +that points aren't accidentally clipped by the canvas edge so by default +the value is computed from the point radius. + +"markings" is used to draw simple lines and rectangular areas in the +background of the plot. You can either specify an array of ranges on +the form { xaxis: { from, to }, yaxis: { from, to } } (with multiple +axes, you can specify coordinates for other axes instead, e.g. as +x2axis/x3axis/...) or with a function that returns such an array given +the axes for the plot in an object as the first parameter. + +You can set the color of markings by specifying "color" in the ranges +object. Here's an example array: + +```js +markings: [ { xaxis: { from: 0, to: 2 }, yaxis: { from: 10, to: 10 }, color: "#bb0000" }, ... ] +``` + +If you leave out one of the values, that value is assumed to go to the +border of the plot. So for example if you only specify { xaxis: { +from: 0, to: 2 } } it means an area that extends from the top to the +bottom of the plot in the x range 0-2. + +A line is drawn if from and to are the same, e.g. + +```js +markings: [ { yaxis: { from: 1, to: 1 } }, ... ] +``` + +would draw a line parallel to the x axis at y = 1. You can control the +line width with "lineWidth" in the range object. + +An example function that makes vertical stripes might look like this: + +```js +markings: function (axes) { + var markings = []; + for (var x = Math.floor(axes.xaxis.min); x < axes.xaxis.max; x += 2) + markings.push({ xaxis: { from: x, to: x + 1 } }); + return markings; +} +``` + +If you set "clickable" to true, the plot will listen for click events +on the plot area and fire a "plotclick" event on the placeholder with +a position and a nearby data item object as parameters. The coordinates +are available both in the unit of the axes (not in pixels) and in +global screen coordinates. + +Likewise, if you set "hoverable" to true, the plot will listen for +mouse move events on the plot area and fire a "plothover" event with +the same parameters as the "plotclick" event. If "autoHighlight" is +true (the default), nearby data items are highlighted automatically. +If needed, you can disable highlighting and control it yourself with +the highlight/unhighlight plot methods described elsewhere. + +You can use "plotclick" and "plothover" events like this: + +```js +$.plot($("#placeholder"), [ d ], { grid: { clickable: true } }); + +$("#placeholder").bind("plotclick", function (event, pos, item) { + alert("You clicked at " + pos.x + ", " + pos.y); + // axis coordinates for other axes, if present, are in pos.x2, pos.x3, ... + // if you need global screen coordinates, they are pos.pageX, pos.pageY + + if (item) { + highlight(item.series, item.datapoint); + alert("You clicked a point!"); + } +}); +``` + +The item object in this example is either null or a nearby object on the form: + +```js +item: { + datapoint: the point, e.g. [0, 2] + dataIndex: the index of the point in the data array + series: the series object + seriesIndex: the index of the series + pageX, pageY: the global screen coordinates of the point +} +``` + +For instance, if you have specified the data like this + +```js +$.plot($("#placeholder"), [ { label: "Foo", data: [[0, 10], [7, 3]] } ], ...); +``` + +and the mouse is near the point (7, 3), "datapoint" is [7, 3], +"dataIndex" will be 1, "series" is a normalized series object with +among other things the "Foo" label in series.label and the color in +series.color, and "seriesIndex" is 0. Note that plugins and options +that transform the data can shift the indexes from what you specified +in the original data array. + +If you use the above events to update some other information and want +to clear out that info in case the mouse goes away, you'll probably +also need to listen to "mouseout" events on the placeholder div. + +"mouseActiveRadius" specifies how far the mouse can be from an item +and still activate it. If there are two or more points within this +radius, Flot chooses the closest item. For bars, the top-most bar +(from the latest specified data series) is chosen. + +If you want to disable interactivity for a specific data series, you +can set "hoverable" and "clickable" to false in the options for that +series, like this: + +```js +{ data: [...], label: "Foo", clickable: false } +``` + +"redrawOverlayInterval" specifies the maximum time to delay a redraw +of interactive things (this works as a rate limiting device). The +default is capped to 60 frames per second. You can set it to -1 to +disable the rate limiting. + + +## Specifying gradients ## + +A gradient is specified like this: + +```js +{ colors: [ color1, color2, ... ] } +``` + +For instance, you might specify a background on the grid going from +black to gray like this: + +```js +grid: { + backgroundColor: { colors: ["#000", "#999"] } +} +``` + +For the series you can specify the gradient as an object that +specifies the scaling of the brightness and the opacity of the series +color, e.g. + +```js +{ colors: [{ opacity: 0.8 }, { brightness: 0.6, opacity: 0.8 } ] } +``` + +where the first color simply has its alpha scaled, whereas the second +is also darkened. For instance, for bars the following makes the bars +gradually disappear, without outline: + +```js +bars: { + show: true, + lineWidth: 0, + fill: true, + fillColor: { colors: [ { opacity: 0.8 }, { opacity: 0.1 } ] } +} +``` + +Flot currently only supports vertical gradients drawn from top to +bottom because that's what works with IE. + + +## Plot Methods ## + +The Plot object returned from the plot function has some methods you +can call: + + - highlight(series, datapoint) + + Highlight a specific datapoint in the data series. You can either + specify the actual objects, e.g. if you got them from a + "plotclick" event, or you can specify the indices, e.g. + highlight(1, 3) to highlight the fourth point in the second series + (remember, zero-based indexing). + + - unhighlight(series, datapoint) or unhighlight() + + Remove the highlighting of the point, same parameters as + highlight. + + If you call unhighlight with no parameters, e.g. as + plot.unhighlight(), all current highlights are removed. + + - setData(data) + + You can use this to reset the data used. Note that axis scaling, + ticks, legend etc. will not be recomputed (use setupGrid() to do + that). You'll probably want to call draw() afterwards. + + You can use this function to speed up redrawing a small plot if + you know that the axes won't change. Put in the new data with + setData(newdata), call draw(), and you're good to go. Note that + for large datasets, almost all the time is consumed in draw() + plotting the data so in this case don't bother. + + - setupGrid() + + Recalculate and set axis scaling, ticks, legend etc. + + Note that because of the drawing model of the canvas, this + function will immediately redraw (actually reinsert in the DOM) + the labels and the legend, but not the actual tick lines because + they're drawn on the canvas. You need to call draw() to get the + canvas redrawn. + + - draw() + + Redraws the plot canvas. + + - triggerRedrawOverlay() + + Schedules an update of an overlay canvas used for drawing + interactive things like a selection and point highlights. This + is mostly useful for writing plugins. The redraw doesn't happen + immediately, instead a timer is set to catch multiple successive + redraws (e.g. from a mousemove). You can get to the overlay by + setting up a drawOverlay hook. + + - width()/height() + + Gets the width and height of the plotting area inside the grid. + This is smaller than the canvas or placeholder dimensions as some + extra space is needed (e.g. for labels). + + - offset() + + Returns the offset of the plotting area inside the grid relative + to the document, useful for instance for calculating mouse + positions (event.pageX/Y minus this offset is the pixel position + inside the plot). + + - pointOffset({ x: xpos, y: ypos }) + + Returns the calculated offset of the data point at (x, y) in data + space within the placeholder div. If you are working with multiple + axes, you can specify the x and y axis references, e.g. + + ```js + o = pointOffset({ x: xpos, y: ypos, xaxis: 2, yaxis: 3 }) + // o.left and o.top now contains the offset within the div + ```` + + - resize() + + Tells Flot to resize the drawing canvas to the size of the + placeholder. You need to run setupGrid() and draw() afterwards as + canvas resizing is a destructive operation. This is used + internally by the resize plugin. + + - shutdown() + + Cleans up any event handlers Flot has currently registered. This + is used internally. + +There are also some members that let you peek inside the internal +workings of Flot which is useful in some cases. Note that if you change +something in the objects returned, you're changing the objects used by +Flot to keep track of its state, so be careful. + + - getData() + + Returns an array of the data series currently used in normalized + form with missing settings filled in according to the global + options. So for instance to find out what color Flot has assigned + to the data series, you could do this: + + ```js + var series = plot.getData(); + for (var i = 0; i < series.length; ++i) + alert(series[i].color); + ``` + + A notable other interesting field besides color is datapoints + which has a field "points" with the normalized data points in a + flat array (the field "pointsize" is the increment in the flat + array to get to the next point so for a dataset consisting only of + (x,y) pairs it would be 2). + + - getAxes() + + Gets an object with the axes. The axes are returned as the + attributes of the object, so for instance getAxes().xaxis is the + x axis. + + Various things are stuffed inside an axis object, e.g. you could + use getAxes().xaxis.ticks to find out what the ticks are for the + xaxis. Two other useful attributes are p2c and c2p, functions for + transforming from data point space to the canvas plot space and + back. Both returns values that are offset with the plot offset. + Check the Flot source code for the complete set of attributes (or + output an axis with console.log() and inspect it). + + With multiple axes, the extra axes are returned as x2axis, x3axis, + etc., e.g. getAxes().y2axis is the second y axis. You can check + y2axis.used to see whether the axis is associated with any data + points and y2axis.show to see if it is currently shown. + + - getPlaceholder() + + Returns placeholder that the plot was put into. This can be useful + for plugins for adding DOM elements or firing events. + + - getCanvas() + + Returns the canvas used for drawing in case you need to hack on it + yourself. You'll probably need to get the plot offset too. + + - getPlotOffset() + + Gets the offset that the grid has within the canvas as an object + with distances from the canvas edges as "left", "right", "top", + "bottom". I.e., if you draw a circle on the canvas with the center + placed at (left, top), its center will be at the top-most, left + corner of the grid. + + - getOptions() + + Gets the options for the plot, normalized, with default values + filled in. You get a reference to actual values used by Flot, so + if you modify the values in here, Flot will use the new values. + If you change something, you probably have to call draw() or + setupGrid() or triggerRedrawOverlay() to see the change. + + +## Hooks ## + +In addition to the public methods, the Plot object also has some hooks +that can be used to modify the plotting process. You can install a +callback function at various points in the process, the function then +gets access to the internal data structures in Flot. + +Here's an overview of the phases Flot goes through: + + 1. Plugin initialization, parsing options + + 2. Constructing the canvases used for drawing + + 3. Set data: parsing data specification, calculating colors, + copying raw data points into internal format, + normalizing them, finding max/min for axis auto-scaling + + 4. Grid setup: calculating axis spacing, ticks, inserting tick + labels, the legend + + 5. Draw: drawing the grid, drawing each of the series in turn + + 6. Setting up event handling for interactive features + + 7. Responding to events, if any + + 8. Shutdown: this mostly happens in case a plot is overwritten + +Each hook is simply a function which is put in the appropriate array. +You can add them through the "hooks" option, and they are also available +after the plot is constructed as the "hooks" attribute on the returned +plot object, e.g. + +```js + // define a simple draw hook + function hellohook(plot, canvascontext) { alert("hello!"); }; + + // pass it in, in an array since we might want to specify several + var plot = $.plot(placeholder, data, { hooks: { draw: [hellohook] } }); + + // we can now find it again in plot.hooks.draw[0] unless a plugin + // has added other hooks +``` + +The available hooks are described below. All hook callbacks get the +plot object as first parameter. You can find some examples of defined +hooks in the plugins bundled with Flot. + + - processOptions [phase 1] + + ```function(plot, options)``` + + Called after Flot has parsed and merged options. Useful in the + instance where customizations beyond simple merging of default + values is needed. A plugin might use it to detect that it has been + enabled and then turn on or off other options. + + + - processRawData [phase 3] + + ```function(plot, series, data, datapoints)``` + + Called before Flot copies and normalizes the raw data for the given + series. If the function fills in datapoints.points with normalized + points and sets datapoints.pointsize to the size of the points, + Flot will skip the copying/normalization step for this series. + + In any case, you might be interested in setting datapoints.format, + an array of objects for specifying how a point is normalized and + how it interferes with axis scaling. It accepts the following options: + + ```js + { + x, y: boolean, + number: boolean, + required: boolean, + defaultValue: value, + autoscale: boolean + } + ``` + + "x" and "y" specify whether the value is plotted against the x or y axis, + and is currently used only to calculate axis min-max ranges. The default + format array, for example, looks like this: + + ```js + [ + { x: true, number: true, required: true }, + { y: true, number: true, required: true } + ] + ``` + + This indicates that a point, i.e. [0, 25], consists of two values, with the + first being plotted on the x axis and the second on the y axis. + + If "number" is true, then the value must be numeric, and is set to null if + it cannot be converted to a number. + + "defaultValue" provides a fallback in case the original value is null. This + is for instance handy for bars, where one can omit the third coordinate + (the bottom of the bar), which then defaults to zero. + + If "required" is true, then the value must exist (be non-null) for the + point as a whole to be valid. If no value is provided, then the entire + point is cleared out with nulls, turning it into a gap in the series. + + "autoscale" determines whether the value is considered when calculating an + automatic min-max range for the axes that the value is plotted against. + + - processDatapoints [phase 3] + + ```function(plot, series, datapoints)``` + + Called after normalization of the given series but before finding + min/max of the data points. This hook is useful for implementing data + transformations. "datapoints" contains the normalized data points in + a flat array as datapoints.points with the size of a single point + given in datapoints.pointsize. Here's a simple transform that + multiplies all y coordinates by 2: + + ```js + function multiply(plot, series, datapoints) { + var points = datapoints.points, ps = datapoints.pointsize; + for (var i = 0; i < points.length; i += ps) + points[i + 1] *= 2; + } + ``` + + Note that you must leave datapoints in a good condition as Flot + doesn't check it or do any normalization on it afterwards. + + - processOffset [phase 4] + + ```function(plot, offset)``` + + Called after Flot has initialized the plot's offset, but before it + draws any axes or plot elements. This hook is useful for customizing + the margins between the grid and the edge of the canvas. "offset" is + an object with attributes "top", "bottom", "left" and "right", + corresponding to the margins on the four sides of the plot. + + - drawBackground [phase 5] + + ```function(plot, canvascontext)``` + + Called before all other drawing operations. Used to draw backgrounds + or other custom elements before the plot or axes have been drawn. + + - drawSeries [phase 5] + + ```function(plot, canvascontext, series)``` + + Hook for custom drawing of a single series. Called just before the + standard drawing routine has been called in the loop that draws + each series. + + - draw [phase 5] + + ```function(plot, canvascontext)``` + + Hook for drawing on the canvas. Called after the grid is drawn + (unless it's disabled or grid.aboveData is set) and the series have + been plotted (in case any points, lines or bars have been turned + on). For examples of how to draw things, look at the source code. + + - bindEvents [phase 6] + + ```function(plot, eventHolder)``` + + Called after Flot has setup its event handlers. Should set any + necessary event handlers on eventHolder, a jQuery object with the + canvas, e.g. + + ```js + function (plot, eventHolder) { + eventHolder.mousedown(function (e) { + alert("You pressed the mouse at " + e.pageX + " " + e.pageY); + }); + } + ``` + + Interesting events include click, mousemove, mouseup/down. You can + use all jQuery events. Usually, the event handlers will update the + state by drawing something (add a drawOverlay hook and call + triggerRedrawOverlay) or firing an externally visible event for + user code. See the crosshair plugin for an example. + + Currently, eventHolder actually contains both the static canvas + used for the plot itself and the overlay canvas used for + interactive features because some versions of IE get the stacking + order wrong. The hook only gets one event, though (either for the + overlay or for the static canvas). + + Note that custom plot events generated by Flot are not generated on + eventHolder, but on the div placeholder supplied as the first + argument to the plot call. You can get that with + plot.getPlaceholder() - that's probably also the one you should use + if you need to fire a custom event. + + - drawOverlay [phase 7] + + ```function (plot, canvascontext)``` + + The drawOverlay hook is used for interactive things that need a + canvas to draw on. The model currently used by Flot works the way + that an extra overlay canvas is positioned on top of the static + canvas. This overlay is cleared and then completely redrawn + whenever something interesting happens. This hook is called when + the overlay canvas is to be redrawn. + + "canvascontext" is the 2D context of the overlay canvas. You can + use this to draw things. You'll most likely need some of the + metrics computed by Flot, e.g. plot.width()/plot.height(). See the + crosshair plugin for an example. + + - shutdown [phase 8] + + ```function (plot, eventHolder)``` + + Run when plot.shutdown() is called, which usually only happens in + case a plot is overwritten by a new plot. If you're writing a + plugin that adds extra DOM elements or event handlers, you should + add a callback to clean up after you. Take a look at the section in + the [PLUGINS](PLUGINS.md) document for more info. + + +## Plugins ## + +Plugins extend the functionality of Flot. To use a plugin, simply +include its JavaScript file after Flot in the HTML page. + +If you're worried about download size/latency, you can concatenate all +the plugins you use, and Flot itself for that matter, into one big file +(make sure you get the order right), then optionally run it through a +JavaScript minifier such as YUI Compressor. + +Here's a brief explanation of how the plugin plumbings work: + +Each plugin registers itself in the global array $.plot.plugins. When +you make a new plot object with $.plot, Flot goes through this array +calling the "init" function of each plugin and merging default options +from the "option" attribute of the plugin. The init function gets a +reference to the plot object created and uses this to register hooks +and add new public methods if needed. + +See the [PLUGINS](PLUGINS.md) document for details on how to write a plugin. As the +above description hints, it's actually pretty easy. + + +## Version number ## + +The version number of Flot is available in ```$.plot.version```. diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/index.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/index.js new file mode 100644 index 00000000000000..613939256cfc9f --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/index.js @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +/* @notice + * + * This product includes code that is based on flot-charts, which was available + * under a "MIT" license. + * + * The MIT License (MIT) + * + * Copyright (c) 2007-2014 IOLA and Ole Laursen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import $ from 'jquery'; +if (window) window.jQuery = $; +require('./jquery.flot'); +require('./jquery.flot.time'); +require('./jquery.flot.canvas'); +require('./jquery.flot.symbol'); +require('./jquery.flot.crosshair'); +require('./jquery.flot.selection'); +require('./jquery.flot.pie'); +require('./jquery.flot.stack'); +require('./jquery.flot.threshold'); +require('./jquery.flot.fillbetween'); +require('./jquery.flot.log'); +module.exports = $; diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.colorhelpers.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.colorhelpers.js new file mode 100644 index 00000000000000..b2f6dc4e433a31 --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.colorhelpers.js @@ -0,0 +1,180 @@ +/* Plugin for jQuery for working with colors. + * + * Version 1.1. + * + * Inspiration from jQuery color animation plugin by John Resig. + * + * Released under the MIT license by Ole Laursen, October 2009. + * + * Examples: + * + * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() + * var c = $.color.extract($("#mydiv"), 'background-color'); + * console.log(c.r, c.g, c.b, c.a); + * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" + * + * Note that .scale() and .add() return the same modified object + * instead of making a new one. + * + * V. 1.1: Fix error handling so e.g. parsing an empty string does + * produce a color rather than just crashing. + */ + +(function($) { + $.color = {}; + + // construct color object with some convenient chainable helpers + $.color.make = function (r, g, b, a) { + var o = {}; + o.r = r || 0; + o.g = g || 0; + o.b = b || 0; + o.a = a != null ? a : 1; + + o.add = function (c, d) { + for (var i = 0; i < c.length; ++i) + o[c.charAt(i)] += d; + return o.normalize(); + }; + + o.scale = function (c, f) { + for (var i = 0; i < c.length; ++i) + o[c.charAt(i)] *= f; + return o.normalize(); + }; + + o.toString = function () { + if (o.a >= 1.0) { + return "rgb("+[o.r, o.g, o.b].join(",")+")"; + } else { + return "rgba("+[o.r, o.g, o.b, o.a].join(",")+")"; + } + }; + + o.normalize = function () { + function clamp(min, value, max) { + return value < min ? min: (value > max ? max: value); + } + + o.r = clamp(0, parseInt(o.r), 255); + o.g = clamp(0, parseInt(o.g), 255); + o.b = clamp(0, parseInt(o.b), 255); + o.a = clamp(0, o.a, 1); + return o; + }; + + o.clone = function () { + return $.color.make(o.r, o.b, o.g, o.a); + }; + + return o.normalize(); + } + + // extract CSS color property from element, going up in the DOM + // if it's "transparent" + $.color.extract = function (elem, css) { + var c; + + do { + c = elem.css(css).toLowerCase(); + // keep going until we find an element that has color, or + // we hit the body or root (have no parent) + if (c != '' && c != 'transparent') + break; + elem = elem.parent(); + } while (elem.length && !$.nodeName(elem.get(0), "body")); + + // catch Safari's way of signalling transparent + if (c == "rgba(0, 0, 0, 0)") + c = "transparent"; + + return $.color.parse(c); + } + + // parse CSS color string (like "rgb(10, 32, 43)" or "#fff"), + // returns color object, if parsing failed, you get black (0, 0, + // 0) out + $.color.parse = function (str) { + var res, m = $.color.make; + + // Look for rgb(num,num,num) + if (res = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str)) + return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10)); + + // Look for rgba(num,num,num,num) + if (res = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)) + return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10), parseFloat(res[4])); + + // Look for rgb(num%,num%,num%) + if (res = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str)) + return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55); + + // Look for rgba(num%,num%,num%,num) + if (res = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)) + return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55, parseFloat(res[4])); + + // Look for #a0b1c2 + if (res = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str)) + return m(parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16)); + + // Look for #fff + if (res = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str)) + return m(parseInt(res[1]+res[1], 16), parseInt(res[2]+res[2], 16), parseInt(res[3]+res[3], 16)); + + // Otherwise, we're most likely dealing with a named color + var name = $.trim(str).toLowerCase(); + if (name == "transparent") + return m(255, 255, 255, 0); + else { + // default to black + res = lookupColors[name] || [0, 0, 0]; + return m(res[0], res[1], res[2]); + } + } + + var lookupColors = { + aqua:[0,255,255], + azure:[240,255,255], + beige:[245,245,220], + black:[0,0,0], + blue:[0,0,255], + brown:[165,42,42], + cyan:[0,255,255], + darkblue:[0,0,139], + darkcyan:[0,139,139], + darkgrey:[169,169,169], + darkgreen:[0,100,0], + darkkhaki:[189,183,107], + darkmagenta:[139,0,139], + darkolivegreen:[85,107,47], + darkorange:[255,140,0], + darkorchid:[153,50,204], + darkred:[139,0,0], + darksalmon:[233,150,122], + darkviolet:[148,0,211], + fuchsia:[255,0,255], + gold:[255,215,0], + green:[0,128,0], + indigo:[75,0,130], + khaki:[240,230,140], + lightblue:[173,216,230], + lightcyan:[224,255,255], + lightgreen:[144,238,144], + lightgrey:[211,211,211], + lightpink:[255,182,193], + lightyellow:[255,255,224], + lime:[0,255,0], + magenta:[255,0,255], + maroon:[128,0,0], + navy:[0,0,128], + olive:[128,128,0], + orange:[255,165,0], + pink:[255,192,203], + purple:[128,0,128], + violet:[128,0,128], + red:[255,0,0], + silver:[192,192,192], + white:[255,255,255], + yellow:[255,255,0] + }; +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.canvas.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.canvas.js new file mode 100644 index 00000000000000..29328d58121277 --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.canvas.js @@ -0,0 +1,345 @@ +/* Flot plugin for drawing all elements of a plot on the canvas. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +Flot normally produces certain elements, like axis labels and the legend, using +HTML elements. This permits greater interactivity and customization, and often +looks better, due to cross-browser canvas text inconsistencies and limitations. + +It can also be desirable to render the plot entirely in canvas, particularly +if the goal is to save it as an image, or if Flot is being used in a context +where the HTML DOM does not exist, as is the case within Node.js. This plugin +switches out Flot's standard drawing operations for canvas-only replacements. + +Currently the plugin supports only axis labels, but it will eventually allow +every element of the plot to be rendered directly to canvas. + +The plugin supports these options: + +{ + canvas: boolean +} + +The "canvas" option controls whether full canvas drawing is enabled, making it +possible to toggle on and off. This is useful when a plot uses HTML text in the +browser, but needs to redraw with canvas text when exporting as an image. + +*/ + +(function($) { + + var options = { + canvas: true + }; + + var render, getTextInfo, addText; + + // Cache the prototype hasOwnProperty for faster access + + var hasOwnProperty = Object.prototype.hasOwnProperty; + + function init(plot, classes) { + + var Canvas = classes.Canvas; + + // We only want to replace the functions once; the second time around + // we would just get our new function back. This whole replacing of + // prototype functions is a disaster, and needs to be changed ASAP. + + if (render == null) { + getTextInfo = Canvas.prototype.getTextInfo, + addText = Canvas.prototype.addText, + render = Canvas.prototype.render; + } + + // Finishes rendering the canvas, including overlaid text + + Canvas.prototype.render = function() { + + if (!plot.getOptions().canvas) { + return render.call(this); + } + + var context = this.context, + cache = this._textCache; + + // For each text layer, render elements marked as active + + context.save(); + context.textBaseline = "middle"; + + for (var layerKey in cache) { + if (hasOwnProperty.call(cache, layerKey)) { + var layerCache = cache[layerKey]; + for (var styleKey in layerCache) { + if (hasOwnProperty.call(layerCache, styleKey)) { + var styleCache = layerCache[styleKey], + updateStyles = true; + for (var key in styleCache) { + if (hasOwnProperty.call(styleCache, key)) { + + var info = styleCache[key], + positions = info.positions, + lines = info.lines; + + // Since every element at this level of the cache have the + // same font and fill styles, we can just change them once + // using the values from the first element. + + if (updateStyles) { + context.fillStyle = info.font.color; + context.font = info.font.definition; + updateStyles = false; + } + + for (var i = 0, position; position = positions[i]; i++) { + if (position.active) { + for (var j = 0, line; line = position.lines[j]; j++) { + context.fillText(lines[j].text, line[0], line[1]); + } + } else { + positions.splice(i--, 1); + } + } + + if (positions.length == 0) { + delete styleCache[key]; + } + } + } + } + } + } + } + + context.restore(); + }; + + // Creates (if necessary) and returns a text info object. + // + // When the canvas option is set, the object looks like this: + // + // { + // width: Width of the text's bounding box. + // height: Height of the text's bounding box. + // positions: Array of positions at which this text is drawn. + // lines: [{ + // height: Height of this line. + // widths: Width of this line. + // text: Text on this line. + // }], + // font: { + // definition: Canvas font property string. + // color: Color of the text. + // }, + // } + // + // The positions array contains objects that look like this: + // + // { + // active: Flag indicating whether the text should be visible. + // lines: Array of [x, y] coordinates at which to draw the line. + // x: X coordinate at which to draw the text. + // y: Y coordinate at which to draw the text. + // } + + Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { + + if (!plot.getOptions().canvas) { + return getTextInfo.call(this, layer, text, font, angle, width); + } + + var textStyle, layerCache, styleCache, info; + + // Cast the value to a string, in case we were given a number + + text = "" + text; + + // If the font is a font-spec object, generate a CSS definition + + if (typeof font === "object") { + textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family; + } else { + textStyle = font; + } + + // Retrieve (or create) the cache for the text's layer and styles + + layerCache = this._textCache[layer]; + + if (layerCache == null) { + layerCache = this._textCache[layer] = {}; + } + + styleCache = layerCache[textStyle]; + + if (styleCache == null) { + styleCache = layerCache[textStyle] = {}; + } + + info = styleCache[text]; + + if (info == null) { + + var context = this.context; + + // If the font was provided as CSS, create a div with those + // classes and examine it to generate a canvas font spec. + + if (typeof font !== "object") { + + var element = $("
 
") + .css("position", "absolute") + .addClass(typeof font === "string" ? font : null) + .appendTo(this.getTextLayer(layer)); + + font = { + lineHeight: element.height(), + style: element.css("font-style"), + variant: element.css("font-variant"), + weight: element.css("font-weight"), + family: element.css("font-family"), + color: element.css("color") + }; + + // Setting line-height to 1, without units, sets it equal + // to the font-size, even if the font-size is abstract, + // like 'smaller'. This enables us to read the real size + // via the element's height, working around browsers that + // return the literal 'smaller' value. + + font.size = element.css("line-height", 1).height(); + + element.remove(); + } + + textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family; + + // Create a new info object, initializing the dimensions to + // zero so we can count them up line-by-line. + + info = styleCache[text] = { + width: 0, + height: 0, + positions: [], + lines: [], + font: { + definition: textStyle, + color: font.color + } + }; + + context.save(); + context.font = textStyle; + + // Canvas can't handle multi-line strings; break on various + // newlines, including HTML brs, to build a list of lines. + // Note that we could split directly on regexps, but IE < 9 is + // broken; revisit when we drop IE 7/8 support. + + var lines = (text + "").replace(/
|\r\n|\r/g, "\n").split("\n"); + + for (var i = 0; i < lines.length; ++i) { + + var lineText = lines[i], + measured = context.measureText(lineText); + + info.width = Math.max(measured.width, info.width); + info.height += font.lineHeight; + + info.lines.push({ + text: lineText, + width: measured.width, + height: font.lineHeight + }); + } + + context.restore(); + } + + return info; + }; + + // Adds a text string to the canvas text overlay. + + Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) { + + if (!plot.getOptions().canvas) { + return addText.call(this, layer, x, y, text, font, angle, width, halign, valign); + } + + var info = this.getTextInfo(layer, text, font, angle, width), + positions = info.positions, + lines = info.lines; + + // Text is drawn with baseline 'middle', which we need to account + // for by adding half a line's height to the y position. + + y += info.height / lines.length / 2; + + // Tweak the initial y-position to match vertical alignment + + if (valign == "middle") { + y = Math.round(y - info.height / 2); + } else if (valign == "bottom") { + y = Math.round(y - info.height); + } else { + y = Math.round(y); + } + + // FIXME: LEGACY BROWSER FIX + // AFFECTS: Opera < 12.00 + + // Offset the y coordinate, since Opera is off pretty + // consistently compared to the other browsers. + + if (!!(window.opera && window.opera.version().split(".")[0] < 12)) { + y -= 2; + } + + // Determine whether this text already exists at this position. + // If so, mark it for inclusion in the next render pass. + + for (var i = 0, position; position = positions[i]; i++) { + if (position.x == x && position.y == y) { + position.active = true; + return; + } + } + + // If the text doesn't exist at this position, create a new entry + + position = { + active: true, + lines: [], + x: x, + y: y + }; + + positions.push(position); + + // Fill in the x & y positions of each line, adjusting them + // individually for horizontal alignment. + + for (var i = 0, line; line = lines[i]; i++) { + if (halign == "center") { + position.lines.push([Math.round(x - line.width / 2), y]); + } else if (halign == "right") { + position.lines.push([Math.round(x - line.width), y]); + } else { + position.lines.push([Math.round(x), y]); + } + y += line.height; + } + }; + } + + $.plot.plugins.push({ + init: init, + options: options, + name: "canvas", + version: "1.0" + }); + +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.categories.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.categories.js new file mode 100644 index 00000000000000..2f9b2579714997 --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.categories.js @@ -0,0 +1,190 @@ +/* Flot plugin for plotting textual data or categories. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +Consider a dataset like [["February", 34], ["March", 20], ...]. This plugin +allows you to plot such a dataset directly. + +To enable it, you must specify mode: "categories" on the axis with the textual +labels, e.g. + + $.plot("#placeholder", data, { xaxis: { mode: "categories" } }); + +By default, the labels are ordered as they are met in the data series. If you +need a different ordering, you can specify "categories" on the axis options +and list the categories there: + + xaxis: { + mode: "categories", + categories: ["February", "March", "April"] + } + +If you need to customize the distances between the categories, you can specify +"categories" as an object mapping labels to values + + xaxis: { + mode: "categories", + categories: { "February": 1, "March": 3, "April": 4 } + } + +If you don't specify all categories, the remaining categories will be numbered +from the max value plus 1 (with a spacing of 1 between each). + +Internally, the plugin works by transforming the input data through an auto- +generated mapping where the first category becomes 0, the second 1, etc. +Hence, a point like ["February", 34] becomes [0, 34] internally in Flot (this +is visible in hover and click events that return numbers rather than the +category labels). The plugin also overrides the tick generator to spit out the +categories as ticks instead of the values. + +If you need to map a value back to its label, the mapping is always accessible +as "categories" on the axis object, e.g. plot.getAxes().xaxis.categories. + +*/ + +(function ($) { + var options = { + xaxis: { + categories: null + }, + yaxis: { + categories: null + } + }; + + function processRawData(plot, series, data, datapoints) { + // if categories are enabled, we need to disable + // auto-transformation to numbers so the strings are intact + // for later processing + + var xCategories = series.xaxis.options.mode == "categories", + yCategories = series.yaxis.options.mode == "categories"; + + if (!(xCategories || yCategories)) + return; + + var format = datapoints.format; + + if (!format) { + // FIXME: auto-detection should really not be defined here + var s = series; + format = []; + format.push({ x: true, number: true, required: true }); + format.push({ y: true, number: true, required: true }); + + if (s.bars.show || (s.lines.show && s.lines.fill)) { + var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero)); + format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale }); + if (s.bars.horizontal) { + delete format[format.length - 1].y; + format[format.length - 1].x = true; + } + } + + datapoints.format = format; + } + + for (var m = 0; m < format.length; ++m) { + if (format[m].x && xCategories) + format[m].number = false; + + if (format[m].y && yCategories) + format[m].number = false; + } + } + + function getNextIndex(categories) { + var index = -1; + + for (var v in categories) + if (categories[v] > index) + index = categories[v]; + + return index + 1; + } + + function categoriesTickGenerator(axis) { + var res = []; + for (var label in axis.categories) { + var v = axis.categories[label]; + if (v >= axis.min && v <= axis.max) + res.push([v, label]); + } + + res.sort(function (a, b) { return a[0] - b[0]; }); + + return res; + } + + function setupCategoriesForAxis(series, axis, datapoints) { + if (series[axis].options.mode != "categories") + return; + + if (!series[axis].categories) { + // parse options + var c = {}, o = series[axis].options.categories || {}; + if ($.isArray(o)) { + for (var i = 0; i < o.length; ++i) + c[o[i]] = i; + } + else { + for (var v in o) + c[v] = o[v]; + } + + series[axis].categories = c; + } + + // fix ticks + if (!series[axis].options.ticks) + series[axis].options.ticks = categoriesTickGenerator; + + transformPointsOnAxis(datapoints, axis, series[axis].categories); + } + + function transformPointsOnAxis(datapoints, axis, categories) { + // go through the points, transforming them + var points = datapoints.points, + ps = datapoints.pointsize, + format = datapoints.format, + formatColumn = axis.charAt(0), + index = getNextIndex(categories); + + for (var i = 0; i < points.length; i += ps) { + if (points[i] == null) + continue; + + for (var m = 0; m < ps; ++m) { + var val = points[i + m]; + + if (val == null || !format[m][formatColumn]) + continue; + + if (!(val in categories)) { + categories[val] = index; + ++index; + } + + points[i + m] = categories[val]; + } + } + } + + function processDatapoints(plot, series, datapoints) { + setupCategoriesForAxis(series, "xaxis", datapoints); + setupCategoriesForAxis(series, "yaxis", datapoints); + } + + function init(plot) { + plot.hooks.processRawData.push(processRawData); + plot.hooks.processDatapoints.push(processDatapoints); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'categories', + version: '1.0' + }); +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.crosshair.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.crosshair.js new file mode 100644 index 00000000000000..5111695e3d12ce --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.crosshair.js @@ -0,0 +1,176 @@ +/* Flot plugin for showing crosshairs when the mouse hovers over the plot. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin supports these options: + + crosshair: { + mode: null or "x" or "y" or "xy" + color: color + lineWidth: number + } + +Set the mode to one of "x", "y" or "xy". The "x" mode enables a vertical +crosshair that lets you trace the values on the x axis, "y" enables a +horizontal crosshair and "xy" enables them both. "color" is the color of the +crosshair (default is "rgba(170, 0, 0, 0.80)"), "lineWidth" is the width of +the drawn lines (default is 1). + +The plugin also adds four public methods: + + - setCrosshair( pos ) + + Set the position of the crosshair. Note that this is cleared if the user + moves the mouse. "pos" is in coordinates of the plot and should be on the + form { x: xpos, y: ypos } (you can use x2/x3/... if you're using multiple + axes), which is coincidentally the same format as what you get from a + "plothover" event. If "pos" is null, the crosshair is cleared. + + - clearCrosshair() + + Clear the crosshair. + + - lockCrosshair(pos) + + Cause the crosshair to lock to the current location, no longer updating if + the user moves the mouse. Optionally supply a position (passed on to + setCrosshair()) to move it to. + + Example usage: + + var myFlot = $.plot( $("#graph"), ..., { crosshair: { mode: "x" } } }; + $("#graph").bind( "plothover", function ( evt, position, item ) { + if ( item ) { + // Lock the crosshair to the data point being hovered + myFlot.lockCrosshair({ + x: item.datapoint[ 0 ], + y: item.datapoint[ 1 ] + }); + } else { + // Return normal crosshair operation + myFlot.unlockCrosshair(); + } + }); + + - unlockCrosshair() + + Free the crosshair to move again after locking it. +*/ + +(function ($) { + var options = { + crosshair: { + mode: null, // one of null, "x", "y" or "xy", + color: "rgba(170, 0, 0, 0.80)", + lineWidth: 1 + } + }; + + function init(plot) { + // position of crosshair in pixels + var crosshair = { x: -1, y: -1, locked: false }; + + plot.setCrosshair = function setCrosshair(pos) { + if (!pos) + crosshair.x = -1; + else { + var o = plot.p2c(pos); + crosshair.x = Math.max(0, Math.min(o.left, plot.width())); + crosshair.y = Math.max(0, Math.min(o.top, plot.height())); + } + + plot.triggerRedrawOverlay(); + }; + + plot.clearCrosshair = plot.setCrosshair; // passes null for pos + + plot.lockCrosshair = function lockCrosshair(pos) { + if (pos) + plot.setCrosshair(pos); + crosshair.locked = true; + }; + + plot.unlockCrosshair = function unlockCrosshair() { + crosshair.locked = false; + }; + + function onMouseOut(e) { + if (crosshair.locked) + return; + + if (crosshair.x != -1) { + crosshair.x = -1; + plot.triggerRedrawOverlay(); + } + } + + function onMouseMove(e) { + if (crosshair.locked) + return; + + if (plot.getSelection && plot.getSelection()) { + crosshair.x = -1; // hide the crosshair while selecting + return; + } + + var offset = plot.offset(); + crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width())); + crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height())); + plot.triggerRedrawOverlay(); + } + + plot.hooks.bindEvents.push(function (plot, eventHolder) { + if (!plot.getOptions().crosshair.mode) + return; + + eventHolder.mouseout(onMouseOut); + eventHolder.mousemove(onMouseMove); + }); + + plot.hooks.drawOverlay.push(function (plot, ctx) { + var c = plot.getOptions().crosshair; + if (!c.mode) + return; + + var plotOffset = plot.getPlotOffset(); + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + if (crosshair.x != -1) { + var adj = plot.getOptions().crosshair.lineWidth % 2 ? 0.5 : 0; + + ctx.strokeStyle = c.color; + ctx.lineWidth = c.lineWidth; + ctx.lineJoin = "round"; + + ctx.beginPath(); + if (c.mode.indexOf("x") != -1) { + var drawX = Math.floor(crosshair.x) + adj; + ctx.moveTo(drawX, 0); + ctx.lineTo(drawX, plot.height()); + } + if (c.mode.indexOf("y") != -1) { + var drawY = Math.floor(crosshair.y) + adj; + ctx.moveTo(0, drawY); + ctx.lineTo(plot.width(), drawY); + } + ctx.stroke(); + } + ctx.restore(); + }); + + plot.hooks.shutdown.push(function (plot, eventHolder) { + eventHolder.unbind("mouseout", onMouseOut); + eventHolder.unbind("mousemove", onMouseMove); + }); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'crosshair', + version: '1.0' + }); +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.errorbars.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.errorbars.js new file mode 100644 index 00000000000000..655036e0db846f --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.errorbars.js @@ -0,0 +1,353 @@ +/* Flot plugin for plotting error bars. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +Error bars are used to show standard deviation and other statistical +properties in a plot. + +* Created by Rui Pereira - rui (dot) pereira (at) gmail (dot) com + +This plugin allows you to plot error-bars over points. Set "errorbars" inside +the points series to the axis name over which there will be error values in +your data array (*even* if you do not intend to plot them later, by setting +"show: null" on xerr/yerr). + +The plugin supports these options: + + series: { + points: { + errorbars: "x" or "y" or "xy", + xerr: { + show: null/false or true, + asymmetric: null/false or true, + upperCap: null or "-" or function, + lowerCap: null or "-" or function, + color: null or color, + radius: null or number + }, + yerr: { same options as xerr } + } + } + +Each data point array is expected to be of the type: + + "x" [ x, y, xerr ] + "y" [ x, y, yerr ] + "xy" [ x, y, xerr, yerr ] + +Where xerr becomes xerr_lower,xerr_upper for the asymmetric error case, and +equivalently for yerr. E.g., a datapoint for the "xy" case with symmetric +error-bars on X and asymmetric on Y would be: + + [ x, y, xerr, yerr_lower, yerr_upper ] + +By default no end caps are drawn. Setting upperCap and/or lowerCap to "-" will +draw a small cap perpendicular to the error bar. They can also be set to a +user-defined drawing function, with (ctx, x, y, radius) as parameters, as e.g.: + + function drawSemiCircle( ctx, x, y, radius ) { + ctx.beginPath(); + ctx.arc( x, y, radius, 0, Math.PI, false ); + ctx.moveTo( x - radius, y ); + ctx.lineTo( x + radius, y ); + ctx.stroke(); + } + +Color and radius both default to the same ones of the points series if not +set. The independent radius parameter on xerr/yerr is useful for the case when +we may want to add error-bars to a line, without showing the interconnecting +points (with radius: 0), and still showing end caps on the error-bars. +shadowSize and lineWidth are derived as well from the points series. + +*/ + +(function ($) { + var options = { + series: { + points: { + errorbars: null, //should be 'x', 'y' or 'xy' + xerr: { err: 'x', show: null, asymmetric: null, upperCap: null, lowerCap: null, color: null, radius: null}, + yerr: { err: 'y', show: null, asymmetric: null, upperCap: null, lowerCap: null, color: null, radius: null} + } + } + }; + + function processRawData(plot, series, data, datapoints){ + if (!series.points.errorbars) + return; + + // x,y values + var format = [ + { x: true, number: true, required: true }, + { y: true, number: true, required: true } + ]; + + var errors = series.points.errorbars; + // error bars - first X then Y + if (errors == 'x' || errors == 'xy') { + // lower / upper error + if (series.points.xerr.asymmetric) { + format.push({ x: true, number: true, required: true }); + format.push({ x: true, number: true, required: true }); + } else + format.push({ x: true, number: true, required: true }); + } + if (errors == 'y' || errors == 'xy') { + // lower / upper error + if (series.points.yerr.asymmetric) { + format.push({ y: true, number: true, required: true }); + format.push({ y: true, number: true, required: true }); + } else + format.push({ y: true, number: true, required: true }); + } + datapoints.format = format; + } + + function parseErrors(series, i){ + + var points = series.datapoints.points; + + // read errors from points array + var exl = null, + exu = null, + eyl = null, + eyu = null; + var xerr = series.points.xerr, + yerr = series.points.yerr; + + var eb = series.points.errorbars; + // error bars - first X + if (eb == 'x' || eb == 'xy') { + if (xerr.asymmetric) { + exl = points[i + 2]; + exu = points[i + 3]; + if (eb == 'xy') + if (yerr.asymmetric){ + eyl = points[i + 4]; + eyu = points[i + 5]; + } else eyl = points[i + 4]; + } else { + exl = points[i + 2]; + if (eb == 'xy') + if (yerr.asymmetric) { + eyl = points[i + 3]; + eyu = points[i + 4]; + } else eyl = points[i + 3]; + } + // only Y + } else if (eb == 'y') + if (yerr.asymmetric) { + eyl = points[i + 2]; + eyu = points[i + 3]; + } else eyl = points[i + 2]; + + // symmetric errors? + if (exu == null) exu = exl; + if (eyu == null) eyu = eyl; + + var errRanges = [exl, exu, eyl, eyu]; + // nullify if not showing + if (!xerr.show){ + errRanges[0] = null; + errRanges[1] = null; + } + if (!yerr.show){ + errRanges[2] = null; + errRanges[3] = null; + } + return errRanges; + } + + function drawSeriesErrors(plot, ctx, s){ + + var points = s.datapoints.points, + ps = s.datapoints.pointsize, + ax = [s.xaxis, s.yaxis], + radius = s.points.radius, + err = [s.points.xerr, s.points.yerr]; + + //sanity check, in case some inverted axis hack is applied to flot + var invertX = false; + if (ax[0].p2c(ax[0].max) < ax[0].p2c(ax[0].min)) { + invertX = true; + var tmp = err[0].lowerCap; + err[0].lowerCap = err[0].upperCap; + err[0].upperCap = tmp; + } + + var invertY = false; + if (ax[1].p2c(ax[1].min) < ax[1].p2c(ax[1].max)) { + invertY = true; + var tmp = err[1].lowerCap; + err[1].lowerCap = err[1].upperCap; + err[1].upperCap = tmp; + } + + for (var i = 0; i < s.datapoints.points.length; i += ps) { + + //parse + var errRanges = parseErrors(s, i); + + //cycle xerr & yerr + for (var e = 0; e < err.length; e++){ + + var minmax = [ax[e].min, ax[e].max]; + + //draw this error? + if (errRanges[e * err.length]){ + + //data coordinates + var x = points[i], + y = points[i + 1]; + + //errorbar ranges + var upper = [x, y][e] + errRanges[e * err.length + 1], + lower = [x, y][e] - errRanges[e * err.length]; + + //points outside of the canvas + if (err[e].err == 'x') + if (y > ax[1].max || y < ax[1].min || upper < ax[0].min || lower > ax[0].max) + continue; + if (err[e].err == 'y') + if (x > ax[0].max || x < ax[0].min || upper < ax[1].min || lower > ax[1].max) + continue; + + // prevent errorbars getting out of the canvas + var drawUpper = true, + drawLower = true; + + if (upper > minmax[1]) { + drawUpper = false; + upper = minmax[1]; + } + if (lower < minmax[0]) { + drawLower = false; + lower = minmax[0]; + } + + //sanity check, in case some inverted axis hack is applied to flot + if ((err[e].err == 'x' && invertX) || (err[e].err == 'y' && invertY)) { + //swap coordinates + var tmp = lower; + lower = upper; + upper = tmp; + tmp = drawLower; + drawLower = drawUpper; + drawUpper = tmp; + tmp = minmax[0]; + minmax[0] = minmax[1]; + minmax[1] = tmp; + } + + // convert to pixels + x = ax[0].p2c(x), + y = ax[1].p2c(y), + upper = ax[e].p2c(upper); + lower = ax[e].p2c(lower); + minmax[0] = ax[e].p2c(minmax[0]); + minmax[1] = ax[e].p2c(minmax[1]); + + //same style as points by default + var lw = err[e].lineWidth ? err[e].lineWidth : s.points.lineWidth, + sw = s.points.shadowSize != null ? s.points.shadowSize : s.shadowSize; + + //shadow as for points + if (lw > 0 && sw > 0) { + var w = sw / 2; + ctx.lineWidth = w; + ctx.strokeStyle = "rgba(0,0,0,0.1)"; + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w + w/2, minmax); + + ctx.strokeStyle = "rgba(0,0,0,0.2)"; + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w/2, minmax); + } + + ctx.strokeStyle = err[e].color? err[e].color: s.color; + ctx.lineWidth = lw; + //draw it + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, 0, minmax); + } + } + } + } + + function drawError(ctx,err,x,y,upper,lower,drawUpper,drawLower,radius,offset,minmax){ + + //shadow offset + y += offset; + upper += offset; + lower += offset; + + // error bar - avoid plotting over circles + if (err.err == 'x'){ + if (upper > x + radius) drawPath(ctx, [[upper,y],[Math.max(x + radius,minmax[0]),y]]); + else drawUpper = false; + if (lower < x - radius) drawPath(ctx, [[Math.min(x - radius,minmax[1]),y],[lower,y]] ); + else drawLower = false; + } + else { + if (upper < y - radius) drawPath(ctx, [[x,upper],[x,Math.min(y - radius,minmax[0])]] ); + else drawUpper = false; + if (lower > y + radius) drawPath(ctx, [[x,Math.max(y + radius,minmax[1])],[x,lower]] ); + else drawLower = false; + } + + //internal radius value in errorbar, allows to plot radius 0 points and still keep proper sized caps + //this is a way to get errorbars on lines without visible connecting dots + radius = err.radius != null? err.radius: radius; + + // upper cap + if (drawUpper) { + if (err.upperCap == '-'){ + if (err.err=='x') drawPath(ctx, [[upper,y - radius],[upper,y + radius]] ); + else drawPath(ctx, [[x - radius,upper],[x + radius,upper]] ); + } else if ($.isFunction(err.upperCap)){ + if (err.err=='x') err.upperCap(ctx, upper, y, radius); + else err.upperCap(ctx, x, upper, radius); + } + } + // lower cap + if (drawLower) { + if (err.lowerCap == '-'){ + if (err.err=='x') drawPath(ctx, [[lower,y - radius],[lower,y + radius]] ); + else drawPath(ctx, [[x - radius,lower],[x + radius,lower]] ); + } else if ($.isFunction(err.lowerCap)){ + if (err.err=='x') err.lowerCap(ctx, lower, y, radius); + else err.lowerCap(ctx, x, lower, radius); + } + } + } + + function drawPath(ctx, pts){ + ctx.beginPath(); + ctx.moveTo(pts[0][0], pts[0][1]); + for (var p=1; p < pts.length; p++) + ctx.lineTo(pts[p][0], pts[p][1]); + ctx.stroke(); + } + + function draw(plot, ctx){ + var plotOffset = plot.getPlotOffset(); + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + $.each(plot.getData(), function (i, s) { + if (s.points.errorbars && (s.points.xerr.show || s.points.yerr.show)) + drawSeriesErrors(plot, ctx, s); + }); + ctx.restore(); + } + + function init(plot) { + plot.hooks.processRawData.push(processRawData); + plot.hooks.draw.push(draw); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'errorbars', + version: '1.0' + }); +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.fillbetween.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.fillbetween.js new file mode 100644 index 00000000000000..18b15d26db8c91 --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.fillbetween.js @@ -0,0 +1,226 @@ +/* Flot plugin for computing bottoms for filled line and bar charts. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The case: you've got two series that you want to fill the area between. In Flot +terms, you need to use one as the fill bottom of the other. You can specify the +bottom of each data point as the third coordinate manually, or you can use this +plugin to compute it for you. + +In order to name the other series, you need to give it an id, like this: + + var dataset = [ + { data: [ ... ], id: "foo" } , // use default bottom + { data: [ ... ], fillBetween: "foo" }, // use first dataset as bottom + ]; + + $.plot($("#placeholder"), dataset, { lines: { show: true, fill: true }}); + +As a convenience, if the id given is a number that doesn't appear as an id in +the series, it is interpreted as the index in the array instead (so fillBetween: +0 can also mean the first series). + +Internally, the plugin modifies the datapoints in each series. For line series, +extra data points might be inserted through interpolation. Note that at points +where the bottom line is not defined (due to a null point or start/end of line), +the current line will show a gap too. The algorithm comes from the +jquery.flot.stack.js plugin, possibly some code could be shared. + +*/ + +(function ( $ ) { + + var options = { + series: { + fillBetween: null // or number + } + }; + + function init( plot ) { + + function findBottomSeries( s, allseries ) { + + var i; + + for ( i = 0; i < allseries.length; ++i ) { + if ( allseries[ i ].id === s.fillBetween ) { + return allseries[ i ]; + } + } + + if ( typeof s.fillBetween === "number" ) { + if ( s.fillBetween < 0 || s.fillBetween >= allseries.length ) { + return null; + } + return allseries[ s.fillBetween ]; + } + + return null; + } + + function computeFillBottoms( plot, s, datapoints ) { + + if ( s.fillBetween == null ) { + return; + } + + var other = findBottomSeries( s, plot.getData() ); + + if ( !other ) { + return; + } + + var ps = datapoints.pointsize, + points = datapoints.points, + otherps = other.datapoints.pointsize, + otherpoints = other.datapoints.points, + newpoints = [], + px, py, intery, qx, qy, bottom, + withlines = s.lines.show, + withbottom = ps > 2 && datapoints.format[2].y, + withsteps = withlines && s.lines.steps, + fromgap = true, + i = 0, + j = 0, + l, m; + + while ( true ) { + + if ( i >= points.length ) { + break; + } + + l = newpoints.length; + + if ( points[ i ] == null ) { + + // copy gaps + + for ( m = 0; m < ps; ++m ) { + newpoints.push( points[ i + m ] ); + } + + i += ps; + + } else if ( j >= otherpoints.length ) { + + // for lines, we can't use the rest of the points + + if ( !withlines ) { + for ( m = 0; m < ps; ++m ) { + newpoints.push( points[ i + m ] ); + } + } + + i += ps; + + } else if ( otherpoints[ j ] == null ) { + + // oops, got a gap + + for ( m = 0; m < ps; ++m ) { + newpoints.push( null ); + } + + fromgap = true; + j += otherps; + + } else { + + // cases where we actually got two points + + px = points[ i ]; + py = points[ i + 1 ]; + qx = otherpoints[ j ]; + qy = otherpoints[ j + 1 ]; + bottom = 0; + + if ( px === qx ) { + + for ( m = 0; m < ps; ++m ) { + newpoints.push( points[ i + m ] ); + } + + //newpoints[ l + 1 ] += qy; + bottom = qy; + + i += ps; + j += otherps; + + } else if ( px > qx ) { + + // we got past point below, might need to + // insert interpolated extra point + + if ( withlines && i > 0 && points[ i - ps ] != null ) { + intery = py + ( points[ i - ps + 1 ] - py ) * ( qx - px ) / ( points[ i - ps ] - px ); + newpoints.push( qx ); + newpoints.push( intery ); + for ( m = 2; m < ps; ++m ) { + newpoints.push( points[ i + m ] ); + } + bottom = qy; + } + + j += otherps; + + } else { // px < qx + + // if we come from a gap, we just skip this point + + if ( fromgap && withlines ) { + i += ps; + continue; + } + + for ( m = 0; m < ps; ++m ) { + newpoints.push( points[ i + m ] ); + } + + // we might be able to interpolate a point below, + // this can give us a better y + + if ( withlines && j > 0 && otherpoints[ j - otherps ] != null ) { + bottom = qy + ( otherpoints[ j - otherps + 1 ] - qy ) * ( px - qx ) / ( otherpoints[ j - otherps ] - qx ); + } + + //newpoints[l + 1] += bottom; + + i += ps; + } + + fromgap = false; + + if ( l !== newpoints.length && withbottom ) { + newpoints[ l + 2 ] = bottom; + } + } + + // maintain the line steps invariant + + if ( withsteps && l !== newpoints.length && l > 0 && + newpoints[ l ] !== null && + newpoints[ l ] !== newpoints[ l - ps ] && + newpoints[ l + 1 ] !== newpoints[ l - ps + 1 ] ) { + for (m = 0; m < ps; ++m) { + newpoints[ l + ps + m ] = newpoints[ l + m ]; + } + newpoints[ l + 1 ] = newpoints[ l - ps + 1 ]; + } + } + + datapoints.points = newpoints; + } + + plot.hooks.processDatapoints.push( computeFillBottoms ); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: "fillbetween", + version: "1.0" + }); + +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.image.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.image.js new file mode 100644 index 00000000000000..178f0e69069eff --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.image.js @@ -0,0 +1,241 @@ +/* Flot plugin for plotting images. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The data syntax is [ [ image, x1, y1, x2, y2 ], ... ] where (x1, y1) and +(x2, y2) are where you intend the two opposite corners of the image to end up +in the plot. Image must be a fully loaded JavaScript image (you can make one +with new Image()). If the image is not complete, it's skipped when plotting. + +There are two helpers included for retrieving images. The easiest work the way +that you put in URLs instead of images in the data, like this: + + [ "myimage.png", 0, 0, 10, 10 ] + +Then call $.plot.image.loadData( data, options, callback ) where data and +options are the same as you pass in to $.plot. This loads the images, replaces +the URLs in the data with the corresponding images and calls "callback" when +all images are loaded (or failed loading). In the callback, you can then call +$.plot with the data set. See the included example. + +A more low-level helper, $.plot.image.load(urls, callback) is also included. +Given a list of URLs, it calls callback with an object mapping from URL to +Image object when all images are loaded or have failed loading. + +The plugin supports these options: + + series: { + images: { + show: boolean + anchor: "corner" or "center" + alpha: [ 0, 1 ] + } + } + +They can be specified for a specific series: + + $.plot( $("#placeholder"), [{ + data: [ ... ], + images: { ... } + ]) + +Note that because the data format is different from usual data points, you +can't use images with anything else in a specific data series. + +Setting "anchor" to "center" causes the pixels in the image to be anchored at +the corner pixel centers inside of at the pixel corners, effectively letting +half a pixel stick out to each side in the plot. + +A possible future direction could be support for tiling for large images (like +Google Maps). + +*/ + +(function ($) { + var options = { + series: { + images: { + show: false, + alpha: 1, + anchor: "corner" // or "center" + } + } + }; + + $.plot.image = {}; + + $.plot.image.loadDataImages = function (series, options, callback) { + var urls = [], points = []; + + var defaultShow = options.series.images.show; + + $.each(series, function (i, s) { + if (!(defaultShow || s.images.show)) + return; + + if (s.data) + s = s.data; + + $.each(s, function (i, p) { + if (typeof p[0] == "string") { + urls.push(p[0]); + points.push(p); + } + }); + }); + + $.plot.image.load(urls, function (loadedImages) { + $.each(points, function (i, p) { + var url = p[0]; + if (loadedImages[url]) + p[0] = loadedImages[url]; + }); + + callback(); + }); + } + + $.plot.image.load = function (urls, callback) { + var missing = urls.length, loaded = {}; + if (missing == 0) + callback({}); + + $.each(urls, function (i, url) { + var handler = function () { + --missing; + + loaded[url] = this; + + if (missing == 0) + callback(loaded); + }; + + $('').load(handler).error(handler).attr('src', url); + }); + }; + + function drawSeries(plot, ctx, series) { + var plotOffset = plot.getPlotOffset(); + + if (!series.images || !series.images.show) + return; + + var points = series.datapoints.points, + ps = series.datapoints.pointsize; + + for (var i = 0; i < points.length; i += ps) { + var img = points[i], + x1 = points[i + 1], y1 = points[i + 2], + x2 = points[i + 3], y2 = points[i + 4], + xaxis = series.xaxis, yaxis = series.yaxis, + tmp; + + // actually we should check img.complete, but it + // appears to be a somewhat unreliable indicator in + // IE6 (false even after load event) + if (!img || img.width <= 0 || img.height <= 0) + continue; + + if (x1 > x2) { + tmp = x2; + x2 = x1; + x1 = tmp; + } + if (y1 > y2) { + tmp = y2; + y2 = y1; + y1 = tmp; + } + + // if the anchor is at the center of the pixel, expand the + // image by 1/2 pixel in each direction + if (series.images.anchor == "center") { + tmp = 0.5 * (x2-x1) / (img.width - 1); + x1 -= tmp; + x2 += tmp; + tmp = 0.5 * (y2-y1) / (img.height - 1); + y1 -= tmp; + y2 += tmp; + } + + // clip + if (x1 == x2 || y1 == y2 || + x1 >= xaxis.max || x2 <= xaxis.min || + y1 >= yaxis.max || y2 <= yaxis.min) + continue; + + var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height; + if (x1 < xaxis.min) { + sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1); + x1 = xaxis.min; + } + + if (x2 > xaxis.max) { + sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1); + x2 = xaxis.max; + } + + if (y1 < yaxis.min) { + sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1); + y1 = yaxis.min; + } + + if (y2 > yaxis.max) { + sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1); + y2 = yaxis.max; + } + + x1 = xaxis.p2c(x1); + x2 = xaxis.p2c(x2); + y1 = yaxis.p2c(y1); + y2 = yaxis.p2c(y2); + + // the transformation may have swapped us + if (x1 > x2) { + tmp = x2; + x2 = x1; + x1 = tmp; + } + if (y1 > y2) { + tmp = y2; + y2 = y1; + y1 = tmp; + } + + tmp = ctx.globalAlpha; + ctx.globalAlpha *= series.images.alpha; + ctx.drawImage(img, + sx1, sy1, sx2 - sx1, sy2 - sy1, + x1 + plotOffset.left, y1 + plotOffset.top, + x2 - x1, y2 - y1); + ctx.globalAlpha = tmp; + } + } + + function processRawData(plot, series, data, datapoints) { + if (!series.images.show) + return; + + // format is Image, x1, y1, x2, y2 (opposite corners) + datapoints.format = [ + { required: true }, + { x: true, number: true, required: true }, + { y: true, number: true, required: true }, + { x: true, number: true, required: true }, + { y: true, number: true, required: true } + ]; + } + + function init(plot) { + plot.hooks.processRawData.push(processRawData); + plot.hooks.drawSeries.push(drawSeries); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'image', + version: '1.1' + }); +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.js new file mode 100644 index 00000000000000..43db1cc3d93db1 --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.js @@ -0,0 +1,3168 @@ +/* JavaScript plotting library for jQuery, version 0.8.3. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ + +// first an inline dependency, jquery.colorhelpers.js, we inline it here +// for convenience + +/* Plugin for jQuery for working with colors. + * + * Version 1.1. + * + * Inspiration from jQuery color animation plugin by John Resig. + * + * Released under the MIT license by Ole Laursen, October 2009. + * + * Examples: + * + * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() + * var c = $.color.extract($("#mydiv"), 'background-color'); + * console.log(c.r, c.g, c.b, c.a); + * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" + * + * Note that .scale() and .add() return the same modified object + * instead of making a new one. + * + * V. 1.1: Fix error handling so e.g. parsing an empty string does + * produce a color rather than just crashing. + */ +(function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return valuemax?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery); + +// the actual Flot code +(function($) { + + // Cache the prototype hasOwnProperty for faster access + + var hasOwnProperty = Object.prototype.hasOwnProperty; + + // A shim to provide 'detach' to jQuery versions prior to 1.4. Using a DOM + // operation produces the same effect as detach, i.e. removing the element + // without touching its jQuery data. + + // Do not merge this into Flot 0.9, since it requires jQuery 1.4.4+. + + if (!$.fn.detach) { + $.fn.detach = function() { + return this.each(function() { + if (this.parentNode) { + this.parentNode.removeChild( this ); + } + }); + }; + } + + /////////////////////////////////////////////////////////////////////////// + // The Canvas object is a wrapper around an HTML5 tag. + // + // @constructor + // @param {string} cls List of classes to apply to the canvas. + // @param {element} container Element onto which to append the canvas. + // + // Requiring a container is a little iffy, but unfortunately canvas + // operations don't work unless the canvas is attached to the DOM. + + function Canvas(cls, container) { + + var element = container.children("." + cls)[0]; + + if (element == null) { + + element = document.createElement("canvas"); + element.className = cls; + + $(element).css({ direction: "ltr", position: "absolute", left: 0, top: 0 }) + .appendTo(container); + + // If HTML5 Canvas isn't available, fall back to [Ex|Flash]canvas + + if (!element.getContext) { + if (window.G_vmlCanvasManager) { + element = window.G_vmlCanvasManager.initElement(element); + } else { + throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode."); + } + } + } + + this.element = element; + + var context = this.context = element.getContext("2d"); + + // Determine the screen's ratio of physical to device-independent + // pixels. This is the ratio between the canvas width that the browser + // advertises and the number of pixels actually present in that space. + + // The iPhone 4, for example, has a device-independent width of 320px, + // but its screen is actually 640px wide. It therefore has a pixel + // ratio of 2, while most normal devices have a ratio of 1. + + var devicePixelRatio = window.devicePixelRatio || 1, + backingStoreRatio = + context.webkitBackingStorePixelRatio || + context.mozBackingStorePixelRatio || + context.msBackingStorePixelRatio || + context.oBackingStorePixelRatio || + context.backingStorePixelRatio || 1; + + this.pixelRatio = devicePixelRatio / backingStoreRatio; + + // Size the canvas to match the internal dimensions of its container + + this.resize(container.width(), container.height()); + + // Collection of HTML div layers for text overlaid onto the canvas + + this.textContainer = null; + this.text = {}; + + // Cache of text fragments and metrics, so we can avoid expensively + // re-calculating them when the plot is re-rendered in a loop. + + this._textCache = {}; + } + + // Resizes the canvas to the given dimensions. + // + // @param {number} width New width of the canvas, in pixels. + // @param {number} width New height of the canvas, in pixels. + + Canvas.prototype.resize = function(width, height) { + + if (width <= 0 || height <= 0) { + throw new Error("Invalid dimensions for plot, width = " + width + ", height = " + height); + } + + var element = this.element, + context = this.context, + pixelRatio = this.pixelRatio; + + // Resize the canvas, increasing its density based on the display's + // pixel ratio; basically giving it more pixels without increasing the + // size of its element, to take advantage of the fact that retina + // displays have that many more pixels in the same advertised space. + + // Resizing should reset the state (excanvas seems to be buggy though) + + if (this.width != width) { + element.width = width * pixelRatio; + element.style.width = width + "px"; + this.width = width; + } + + if (this.height != height) { + element.height = height * pixelRatio; + element.style.height = height + "px"; + this.height = height; + } + + // Save the context, so we can reset in case we get replotted. The + // restore ensure that we're really back at the initial state, and + // should be safe even if we haven't saved the initial state yet. + + context.restore(); + context.save(); + + // Scale the coordinate space to match the display density; so even though we + // may have twice as many pixels, we still want lines and other drawing to + // appear at the same size; the extra pixels will just make them crisper. + + context.scale(pixelRatio, pixelRatio); + }; + + // Clears the entire canvas area, not including any overlaid HTML text + + Canvas.prototype.clear = function() { + this.context.clearRect(0, 0, this.width, this.height); + }; + + // Finishes rendering the canvas, including managing the text overlay. + + Canvas.prototype.render = function() { + + var cache = this._textCache; + + // For each text layer, add elements marked as active that haven't + // already been rendered, and remove those that are no longer active. + + for (var layerKey in cache) { + if (hasOwnProperty.call(cache, layerKey)) { + + var layer = this.getTextLayer(layerKey), + layerCache = cache[layerKey]; + + layer.hide(); + + for (var styleKey in layerCache) { + if (hasOwnProperty.call(layerCache, styleKey)) { + var styleCache = layerCache[styleKey]; + for (var key in styleCache) { + if (hasOwnProperty.call(styleCache, key)) { + + var positions = styleCache[key].positions; + + for (var i = 0, position; position = positions[i]; i++) { + if (position.active) { + if (!position.rendered) { + layer.append(position.element); + position.rendered = true; + } + } else { + positions.splice(i--, 1); + if (position.rendered) { + position.element.detach(); + } + } + } + + if (positions.length == 0) { + delete styleCache[key]; + } + } + } + } + } + + layer.show(); + } + } + }; + + // Creates (if necessary) and returns the text overlay container. + // + // @param {string} classes String of space-separated CSS classes used to + // uniquely identify the text layer. + // @return {object} The jQuery-wrapped text-layer div. + + Canvas.prototype.getTextLayer = function(classes) { + + var layer = this.text[classes]; + + // Create the text layer if it doesn't exist + + if (layer == null) { + + // Create the text layer container, if it doesn't exist + + if (this.textContainer == null) { + this.textContainer = $("
") + .css({ + position: "absolute", + top: 0, + left: 0, + bottom: 0, + right: 0, + 'font-size': "smaller", + color: "#545454" + }) + .insertAfter(this.element); + } + + layer = this.text[classes] = $("
") + .addClass(classes) + .css({ + position: "absolute", + top: 0, + left: 0, + bottom: 0, + right: 0 + }) + .appendTo(this.textContainer); + } + + return layer; + }; + + // Creates (if necessary) and returns a text info object. + // + // The object looks like this: + // + // { + // width: Width of the text's wrapper div. + // height: Height of the text's wrapper div. + // element: The jQuery-wrapped HTML div containing the text. + // positions: Array of positions at which this text is drawn. + // } + // + // The positions array contains objects that look like this: + // + // { + // active: Flag indicating whether the text should be visible. + // rendered: Flag indicating whether the text is currently visible. + // element: The jQuery-wrapped HTML div containing the text. + // x: X coordinate at which to draw the text. + // y: Y coordinate at which to draw the text. + // } + // + // Each position after the first receives a clone of the original element. + // + // The idea is that that the width, height, and general 'identity' of the + // text is constant no matter where it is placed; the placements are a + // secondary property. + // + // Canvas maintains a cache of recently-used text info objects; getTextInfo + // either returns the cached element or creates a new entry. + // + // @param {string} layer A string of space-separated CSS classes uniquely + // identifying the layer containing this text. + // @param {string} text Text string to retrieve info for. + // @param {(string|object)=} font Either a string of space-separated CSS + // classes or a font-spec object, defining the text's font and style. + // @param {number=} angle Angle at which to rotate the text, in degrees. + // Angle is currently unused, it will be implemented in the future. + // @param {number=} width Maximum width of the text before it wraps. + // @return {object} a text info object. + + Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { + + var textStyle, layerCache, styleCache, info; + + // Cast the value to a string, in case we were given a number or such + + text = "" + text; + + // If the font is a font-spec object, generate a CSS font definition + + if (typeof font === "object") { + textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "px " + font.family; + } else { + textStyle = font; + } + + // Retrieve (or create) the cache for the text's layer and styles + + layerCache = this._textCache[layer]; + + if (layerCache == null) { + layerCache = this._textCache[layer] = {}; + } + + styleCache = layerCache[textStyle]; + + if (styleCache == null) { + styleCache = layerCache[textStyle] = {}; + } + + info = styleCache[text]; + + // If we can't find a matching element in our cache, create a new one + + if (info == null) { + + var element = $("
").html(text) + .css({ + position: "absolute", + 'max-width': width, + top: -9999 + }) + .appendTo(this.getTextLayer(layer)); + + if (typeof font === "object") { + element.css({ + font: textStyle, + color: font.color + }); + } else if (typeof font === "string") { + element.addClass(font); + } + + info = styleCache[text] = { + width: element.outerWidth(true), + height: element.outerHeight(true), + element: element, + positions: [] + }; + + element.detach(); + } + + return info; + }; + + // Adds a text string to the canvas text overlay. + // + // The text isn't drawn immediately; it is marked as rendering, which will + // result in its addition to the canvas on the next render pass. + // + // @param {string} layer A string of space-separated CSS classes uniquely + // identifying the layer containing this text. + // @param {number} x X coordinate at which to draw the text. + // @param {number} y Y coordinate at which to draw the text. + // @param {string} text Text string to draw. + // @param {(string|object)=} font Either a string of space-separated CSS + // classes or a font-spec object, defining the text's font and style. + // @param {number=} angle Angle at which to rotate the text, in degrees. + // Angle is currently unused, it will be implemented in the future. + // @param {number=} width Maximum width of the text before it wraps. + // @param {string=} halign Horizontal alignment of the text; either "left", + // "center" or "right". + // @param {string=} valign Vertical alignment of the text; either "top", + // "middle" or "bottom". + + Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) { + + var info = this.getTextInfo(layer, text, font, angle, width), + positions = info.positions; + + // Tweak the div's position to match the text's alignment + + if (halign == "center") { + x -= info.width / 2; + } else if (halign == "right") { + x -= info.width; + } + + if (valign == "middle") { + y -= info.height / 2; + } else if (valign == "bottom") { + y -= info.height; + } + + // Determine whether this text already exists at this position. + // If so, mark it for inclusion in the next render pass. + + for (var i = 0, position; position = positions[i]; i++) { + if (position.x == x && position.y == y) { + position.active = true; + return; + } + } + + // If the text doesn't exist at this position, create a new entry + + // For the very first position we'll re-use the original element, + // while for subsequent ones we'll clone it. + + position = { + active: true, + rendered: false, + element: positions.length ? info.element.clone() : info.element, + x: x, + y: y + }; + + positions.push(position); + + // Move the element to its final position within the container + + position.element.css({ + top: Math.round(y), + left: Math.round(x), + 'text-align': halign // In case the text wraps + }); + }; + + // Removes one or more text strings from the canvas text overlay. + // + // If no parameters are given, all text within the layer is removed. + // + // Note that the text is not immediately removed; it is simply marked as + // inactive, which will result in its removal on the next render pass. + // This avoids the performance penalty for 'clear and redraw' behavior, + // where we potentially get rid of all text on a layer, but will likely + // add back most or all of it later, as when redrawing axes, for example. + // + // @param {string} layer A string of space-separated CSS classes uniquely + // identifying the layer containing this text. + // @param {number=} x X coordinate of the text. + // @param {number=} y Y coordinate of the text. + // @param {string=} text Text string to remove. + // @param {(string|object)=} font Either a string of space-separated CSS + // classes or a font-spec object, defining the text's font and style. + // @param {number=} angle Angle at which the text is rotated, in degrees. + // Angle is currently unused, it will be implemented in the future. + + Canvas.prototype.removeText = function(layer, x, y, text, font, angle) { + if (text == null) { + var layerCache = this._textCache[layer]; + if (layerCache != null) { + for (var styleKey in layerCache) { + if (hasOwnProperty.call(layerCache, styleKey)) { + var styleCache = layerCache[styleKey]; + for (var key in styleCache) { + if (hasOwnProperty.call(styleCache, key)) { + var positions = styleCache[key].positions; + for (var i = 0, position; position = positions[i]; i++) { + position.active = false; + } + } + } + } + } + } + } else { + var positions = this.getTextInfo(layer, text, font, angle).positions; + for (var i = 0, position; position = positions[i]; i++) { + if (position.x == x && position.y == y) { + position.active = false; + } + } + } + }; + + /////////////////////////////////////////////////////////////////////////// + // The top-level container for the entire plot. + + function Plot(placeholder, data_, options_, plugins) { + // data is on the form: + // [ series1, series2 ... ] + // where series is either just the data as [ [x1, y1], [x2, y2], ... ] + // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... } + + var series = [], + options = { + // the color theme used for graphs + colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"], + legend: { + show: true, + noColumns: 1, // number of columns in legend table + labelFormatter: null, // fn: string -> string + labelBoxBorderColor: "#ccc", // border color for the little label boxes + container: null, // container (as jQuery object) to put legend in, null means default on top of graph + position: "ne", // position of default legend container within plot + margin: 5, // distance from grid edge to default legend container within plot + backgroundColor: null, // null means auto-detect + backgroundOpacity: 0.85, // set to 0 to avoid background + sorted: null // default to no legend sorting + }, + xaxis: { + show: null, // null = auto-detect, true = always, false = never + position: "bottom", // or "top" + mode: null, // null or "time" + font: null, // null (derived from CSS in placeholder) or object like { size: 11, lineHeight: 13, style: "italic", weight: "bold", family: "sans-serif", variant: "small-caps" } + color: null, // base color, labels, ticks + tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)" + transform: null, // null or f: number -> number to transform axis + inverseTransform: null, // if transform is set, this should be the inverse function + min: null, // min. value to show, null means set automatically + max: null, // max. value to show, null means set automatically + autoscaleMargin: null, // margin in % to add if auto-setting min/max + ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks + tickFormatter: null, // fn: number -> string + labelWidth: null, // size of tick labels in pixels + labelHeight: null, + reserveSpace: null, // whether to reserve space even if axis isn't shown + tickLength: null, // size in pixels of ticks, or "full" for whole line + alignTicksWithAxis: null, // axis number or null for no sync + tickDecimals: null, // no. of decimals, null means auto + tickSize: null, // number or [number, "unit"] + minTickSize: null // number or [number, "unit"] + }, + yaxis: { + autoscaleMargin: 0.02, + position: "left" // or "right" + }, + xaxes: [], + yaxes: [], + series: { + points: { + show: false, + radius: 3, + lineWidth: 2, // in pixels + fill: true, + fillColor: "#ffffff", + symbol: "circle" // or callback + }, + lines: { + // we don't put in show: false so we can see + // whether lines were actively disabled + lineWidth: 2, // in pixels + fill: false, + fillColor: null, + steps: false + // Omit 'zero', so we can later default its value to + // match that of the 'fill' option. + }, + bars: { + show: false, + lineWidth: 2, // in pixels + barWidth: 1, // in units of the x axis + fill: true, + fillColor: null, + align: "left", // "left", "right", or "center" + horizontal: false, + zero: true + }, + shadowSize: 3, + highlightColor: null + }, + grid: { + show: true, + aboveData: false, + color: "#545454", // primary color used for outline and labels + backgroundColor: null, // null for transparent, else color + borderColor: null, // set if different from the grid color + tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)" + margin: 0, // distance from the canvas edge to the grid + labelMargin: 5, // in pixels + axisMargin: 8, // in pixels + borderWidth: 2, // in pixels + minBorderMargin: null, // in pixels, null means taken from points radius + markings: null, // array of ranges or fn: axes -> array of ranges + markingsColor: "#f4f4f4", + markingsLineWidth: 2, + // interactive stuff + clickable: false, + hoverable: false, + autoHighlight: true, // highlight in case mouse is near + mouseActiveRadius: 10 // how far the mouse can be away to activate an item + }, + interaction: { + redrawOverlayInterval: 1000/60 // time between updates, -1 means in same flow + }, + hooks: {} + }, + surface = null, // the canvas for the plot itself + overlay = null, // canvas for interactive stuff on top of plot + eventHolder = null, // jQuery object that events should be bound to + ctx = null, octx = null, + xaxes = [], yaxes = [], + plotOffset = { left: 0, right: 0, top: 0, bottom: 0}, + plotWidth = 0, plotHeight = 0, + hooks = { + processOptions: [], + processRawData: [], + processDatapoints: [], + processOffset: [], + drawBackground: [], + drawSeries: [], + draw: [], + bindEvents: [], + drawOverlay: [], + shutdown: [] + }, + plot = this; + + // public functions + plot.setData = setData; + plot.setupGrid = setupGrid; + plot.draw = draw; + plot.getPlaceholder = function() { return placeholder; }; + plot.getCanvas = function() { return surface.element; }; + plot.getPlotOffset = function() { return plotOffset; }; + plot.width = function () { return plotWidth; }; + plot.height = function () { return plotHeight; }; + plot.offset = function () { + var o = eventHolder.offset(); + o.left += plotOffset.left; + o.top += plotOffset.top; + return o; + }; + plot.getData = function () { return series; }; + plot.getAxes = function () { + var res = {}, i; + $.each(xaxes.concat(yaxes), function (_, axis) { + if (axis) + res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis; + }); + return res; + }; + plot.getXAxes = function () { return xaxes; }; + plot.getYAxes = function () { return yaxes; }; + plot.c2p = canvasToAxisCoords; + plot.p2c = axisToCanvasCoords; + plot.getOptions = function () { return options; }; + plot.highlight = highlight; + plot.unhighlight = unhighlight; + plot.triggerRedrawOverlay = triggerRedrawOverlay; + plot.pointOffset = function(point) { + return { + left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left, 10), + top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top, 10) + }; + }; + plot.shutdown = shutdown; + plot.destroy = function () { + shutdown(); + placeholder.removeData("plot").empty(); + + series = []; + options = null; + surface = null; + overlay = null; + eventHolder = null; + ctx = null; + octx = null; + xaxes = []; + yaxes = []; + hooks = null; + highlights = []; + plot = null; + }; + plot.resize = function () { + var width = placeholder.width(), + height = placeholder.height(); + surface.resize(width, height); + overlay.resize(width, height); + }; + + // public attributes + plot.hooks = hooks; + + // initialize + initPlugins(plot); + parseOptions(options_); + setupCanvases(); + setData(data_); + setupGrid(); + draw(); + bindEvents(); + + + function executeHooks(hook, args) { + args = [plot].concat(args); + for (var i = 0; i < hook.length; ++i) + hook[i].apply(this, args); + } + + function initPlugins() { + + // References to key classes, allowing plugins to modify them + + var classes = { + Canvas: Canvas + }; + + for (var i = 0; i < plugins.length; ++i) { + var p = plugins[i]; + p.init(plot, classes); + if (p.options) + $.extend(true, options, p.options); + } + } + + function parseOptions(opts) { + + $.extend(true, options, opts); + + // $.extend merges arrays, rather than replacing them. When less + // colors are provided than the size of the default palette, we + // end up with those colors plus the remaining defaults, which is + // not expected behavior; avoid it by replacing them here. + + if (opts && opts.colors) { + options.colors = opts.colors; + } + + if (options.xaxis.color == null) + options.xaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + if (options.yaxis.color == null) + options.yaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + + if (options.xaxis.tickColor == null) // grid.tickColor for back-compatibility + options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color; + if (options.yaxis.tickColor == null) // grid.tickColor for back-compatibility + options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color; + + if (options.grid.borderColor == null) + options.grid.borderColor = options.grid.color; + if (options.grid.tickColor == null) + options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + + // Fill in defaults for axis options, including any unspecified + // font-spec fields, if a font-spec was provided. + + // If no x/y axis options were provided, create one of each anyway, + // since the rest of the code assumes that they exist. + + var i, axisOptions, axisCount, + fontSize = placeholder.css("font-size"), + fontSizeDefault = fontSize ? +fontSize.replace("px", "") : 13, + fontDefaults = { + style: placeholder.css("font-style"), + size: Math.round(0.8 * fontSizeDefault), + variant: placeholder.css("font-variant"), + weight: placeholder.css("font-weight"), + family: placeholder.css("font-family") + }; + + axisCount = options.xaxes.length || 1; + for (i = 0; i < axisCount; ++i) { + + axisOptions = options.xaxes[i]; + if (axisOptions && !axisOptions.tickColor) { + axisOptions.tickColor = axisOptions.color; + } + + axisOptions = $.extend(true, {}, options.xaxis, axisOptions); + options.xaxes[i] = axisOptions; + + if (axisOptions.font) { + axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); + if (!axisOptions.font.color) { + axisOptions.font.color = axisOptions.color; + } + if (!axisOptions.font.lineHeight) { + axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15); + } + } + } + + axisCount = options.yaxes.length || 1; + for (i = 0; i < axisCount; ++i) { + + axisOptions = options.yaxes[i]; + if (axisOptions && !axisOptions.tickColor) { + axisOptions.tickColor = axisOptions.color; + } + + axisOptions = $.extend(true, {}, options.yaxis, axisOptions); + options.yaxes[i] = axisOptions; + + if (axisOptions.font) { + axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); + if (!axisOptions.font.color) { + axisOptions.font.color = axisOptions.color; + } + if (!axisOptions.font.lineHeight) { + axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15); + } + } + } + + // backwards compatibility, to be removed in future + if (options.xaxis.noTicks && options.xaxis.ticks == null) + options.xaxis.ticks = options.xaxis.noTicks; + if (options.yaxis.noTicks && options.yaxis.ticks == null) + options.yaxis.ticks = options.yaxis.noTicks; + if (options.x2axis) { + options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis); + options.xaxes[1].position = "top"; + // Override the inherit to allow the axis to auto-scale + if (options.x2axis.min == null) { + options.xaxes[1].min = null; + } + if (options.x2axis.max == null) { + options.xaxes[1].max = null; + } + } + if (options.y2axis) { + options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis); + options.yaxes[1].position = "right"; + // Override the inherit to allow the axis to auto-scale + if (options.y2axis.min == null) { + options.yaxes[1].min = null; + } + if (options.y2axis.max == null) { + options.yaxes[1].max = null; + } + } + if (options.grid.coloredAreas) + options.grid.markings = options.grid.coloredAreas; + if (options.grid.coloredAreasColor) + options.grid.markingsColor = options.grid.coloredAreasColor; + if (options.lines) + $.extend(true, options.series.lines, options.lines); + if (options.points) + $.extend(true, options.series.points, options.points); + if (options.bars) + $.extend(true, options.series.bars, options.bars); + if (options.shadowSize != null) + options.series.shadowSize = options.shadowSize; + if (options.highlightColor != null) + options.series.highlightColor = options.highlightColor; + + // save options on axes for future reference + for (i = 0; i < options.xaxes.length; ++i) + getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i]; + for (i = 0; i < options.yaxes.length; ++i) + getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i]; + + // add hooks from options + for (var n in hooks) + if (options.hooks[n] && options.hooks[n].length) + hooks[n] = hooks[n].concat(options.hooks[n]); + + executeHooks(hooks.processOptions, [options]); + } + + function setData(d) { + series = parseData(d); + fillInSeriesOptions(); + processData(); + } + + function parseData(d) { + var res = []; + for (var i = 0; i < d.length; ++i) { + var s = $.extend(true, {}, options.series); + + if (d[i].data != null) { + s.data = d[i].data; // move the data instead of deep-copy + delete d[i].data; + + $.extend(true, s, d[i]); + + d[i].data = s.data; + } + else + s.data = d[i]; + res.push(s); + } + + return res; + } + + function axisNumber(obj, coord) { + var a = obj[coord + "axis"]; + if (typeof a == "object") // if we got a real axis, extract number + a = a.n; + if (typeof a != "number") + a = 1; // default to first axis + return a; + } + + function allAxes() { + // return flat array without annoying null entries + return $.grep(xaxes.concat(yaxes), function (a) { return a; }); + } + + function canvasToAxisCoords(pos) { + // return an object with x/y corresponding to all used axes + var res = {}, i, axis; + for (i = 0; i < xaxes.length; ++i) { + axis = xaxes[i]; + if (axis && axis.used) + res["x" + axis.n] = axis.c2p(pos.left); + } + + for (i = 0; i < yaxes.length; ++i) { + axis = yaxes[i]; + if (axis && axis.used) + res["y" + axis.n] = axis.c2p(pos.top); + } + + if (res.x1 !== undefined) + res.x = res.x1; + if (res.y1 !== undefined) + res.y = res.y1; + + return res; + } + + function axisToCanvasCoords(pos) { + // get canvas coords from the first pair of x/y found in pos + var res = {}, i, axis, key; + + for (i = 0; i < xaxes.length; ++i) { + axis = xaxes[i]; + if (axis && axis.used) { + key = "x" + axis.n; + if (pos[key] == null && axis.n == 1) + key = "x"; + + if (pos[key] != null) { + res.left = axis.p2c(pos[key]); + break; + } + } + } + + for (i = 0; i < yaxes.length; ++i) { + axis = yaxes[i]; + if (axis && axis.used) { + key = "y" + axis.n; + if (pos[key] == null && axis.n == 1) + key = "y"; + + if (pos[key] != null) { + res.top = axis.p2c(pos[key]); + break; + } + } + } + + return res; + } + + function getOrCreateAxis(axes, number) { + if (!axes[number - 1]) + axes[number - 1] = { + n: number, // save the number for future reference + direction: axes == xaxes ? "x" : "y", + options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis) + }; + + return axes[number - 1]; + } + + function fillInSeriesOptions() { + + var neededColors = series.length, maxIndex = -1, i; + + // Subtract the number of series that already have fixed colors or + // color indexes from the number that we still need to generate. + + for (i = 0; i < series.length; ++i) { + var sc = series[i].color; + if (sc != null) { + neededColors--; + if (typeof sc == "number" && sc > maxIndex) { + maxIndex = sc; + } + } + } + + // If any of the series have fixed color indexes, then we need to + // generate at least as many colors as the highest index. + + if (neededColors <= maxIndex) { + neededColors = maxIndex + 1; + } + + // Generate all the colors, using first the option colors and then + // variations on those colors once they're exhausted. + + var c, colors = [], colorPool = options.colors, + colorPoolSize = colorPool.length, variation = 0; + + for (i = 0; i < neededColors; i++) { + + c = $.color.parse(colorPool[i % colorPoolSize] || "#666"); + + // Each time we exhaust the colors in the pool we adjust + // a scaling factor used to produce more variations on + // those colors. The factor alternates negative/positive + // to produce lighter/darker colors. + + // Reset the variation after every few cycles, or else + // it will end up producing only white or black colors. + + if (i % colorPoolSize == 0 && i) { + if (variation >= 0) { + if (variation < 0.5) { + variation = -variation - 0.2; + } else variation = 0; + } else variation = -variation; + } + + colors[i] = c.scale('rgb', 1 + variation); + } + + // Finalize the series options, filling in their colors + + var colori = 0, s; + for (i = 0; i < series.length; ++i) { + s = series[i]; + + // assign colors + if (s.color == null) { + s.color = colors[colori].toString(); + ++colori; + } + else if (typeof s.color == "number") + s.color = colors[s.color].toString(); + + // turn on lines automatically in case nothing is set + if (s.lines.show == null) { + var v, show = true; + for (v in s) + if (s[v] && s[v].show) { + show = false; + break; + } + if (show) + s.lines.show = true; + } + + // If nothing was provided for lines.zero, default it to match + // lines.fill, since areas by default should extend to zero. + + if (s.lines.zero == null) { + s.lines.zero = !!s.lines.fill; + } + + // setup axes + s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x")); + s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y")); + } + } + + function processData() { + var topSentry = Number.POSITIVE_INFINITY, + bottomSentry = Number.NEGATIVE_INFINITY, + fakeInfinity = Number.MAX_VALUE, + i, j, k, m, length, + s, points, ps, x, y, axis, val, f, p, + data, format; + + function updateAxis(axis, min, max) { + if (min < axis.datamin && min != -fakeInfinity) + axis.datamin = min; + if (max > axis.datamax && max != fakeInfinity) + axis.datamax = max; + } + + $.each(allAxes(), function (_, axis) { + // init axis + axis.datamin = topSentry; + axis.datamax = bottomSentry; + axis.used = false; + }); + + for (i = 0; i < series.length; ++i) { + s = series[i]; + s.datapoints = { points: [] }; + + executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]); + } + + // first pass: clean and copy data + for (i = 0; i < series.length; ++i) { + s = series[i]; + + data = s.data; + format = s.datapoints.format; + + if (!format) { + format = []; + // find out how to copy + format.push({ x: true, number: true, required: true }); + format.push({ y: true, number: true, required: true }); + + if (s.bars.show || (s.lines.show && s.lines.fill)) { + var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero)); + format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale }); + if (s.bars.horizontal) { + delete format[format.length - 1].y; + format[format.length - 1].x = true; + } + } + + s.datapoints.format = format; + } + + if (s.datapoints.pointsize != null) + continue; // already filled in + + s.datapoints.pointsize = format.length; + + ps = s.datapoints.pointsize; + points = s.datapoints.points; + + var insertSteps = s.lines.show && s.lines.steps; + s.xaxis.used = s.yaxis.used = true; + + for (j = k = 0; j < data.length; ++j, k += ps) { + p = data[j]; + + var nullify = p == null; + if (!nullify) { + for (m = 0; m < ps; ++m) { + val = p[m]; + f = format[m]; + + if (f) { + if (f.number && val != null) { + val = +val; // convert to number + if (isNaN(val)) + val = null; + else if (val == Infinity) + val = fakeInfinity; + else if (val == -Infinity) + val = -fakeInfinity; + } + + if (val == null) { + if (f.required) + nullify = true; + + if (f.defaultValue != null) + val = f.defaultValue; + } + } + + points[k + m] = val; + } + } + + if (nullify) { + for (m = 0; m < ps; ++m) { + val = points[k + m]; + if (val != null) { + f = format[m]; + // extract min/max info + if (f.autoscale !== false) { + if (f.x) { + updateAxis(s.xaxis, val, val); + } + if (f.y) { + updateAxis(s.yaxis, val, val); + } + } + } + points[k + m] = null; + } + } + else { + // a little bit of line specific stuff that + // perhaps shouldn't be here, but lacking + // better means... + if (insertSteps && k > 0 + && points[k - ps] != null + && points[k - ps] != points[k] + && points[k - ps + 1] != points[k + 1]) { + // copy the point to make room for a middle point + for (m = 0; m < ps; ++m) + points[k + ps + m] = points[k + m]; + + // middle point has same y + points[k + 1] = points[k - ps + 1]; + + // we've added a point, better reflect that + k += ps; + } + } + } + } + + // give the hooks a chance to run + for (i = 0; i < series.length; ++i) { + s = series[i]; + + executeHooks(hooks.processDatapoints, [ s, s.datapoints]); + } + + // second pass: find datamax/datamin for auto-scaling + for (i = 0; i < series.length; ++i) { + s = series[i]; + points = s.datapoints.points; + ps = s.datapoints.pointsize; + format = s.datapoints.format; + + var xmin = topSentry, ymin = topSentry, + xmax = bottomSentry, ymax = bottomSentry; + + for (j = 0; j < points.length; j += ps) { + if (points[j] == null) + continue; + + for (m = 0; m < ps; ++m) { + val = points[j + m]; + f = format[m]; + if (!f || f.autoscale === false || val == fakeInfinity || val == -fakeInfinity) + continue; + + if (f.x) { + if (val < xmin) + xmin = val; + if (val > xmax) + xmax = val; + } + if (f.y) { + if (val < ymin) + ymin = val; + if (val > ymax) + ymax = val; + } + } + } + + if (s.bars.show) { + // make sure we got room for the bar on the dancing floor + var delta; + + switch (s.bars.align) { + case "left": + delta = 0; + break; + case "right": + delta = -s.bars.barWidth; + break; + default: + delta = -s.bars.barWidth / 2; + } + + if (s.bars.horizontal) { + ymin += delta; + ymax += delta + s.bars.barWidth; + } + else { + xmin += delta; + xmax += delta + s.bars.barWidth; + } + } + + updateAxis(s.xaxis, xmin, xmax); + updateAxis(s.yaxis, ymin, ymax); + } + + $.each(allAxes(), function (_, axis) { + if (axis.datamin == topSentry) + axis.datamin = null; + if (axis.datamax == bottomSentry) + axis.datamax = null; + }); + } + + function setupCanvases() { + + // Make sure the placeholder is clear of everything except canvases + // from a previous plot in this container that we'll try to re-use. + + placeholder.css("padding", 0) // padding messes up the positioning + .children().filter(function(){ + return !$(this).hasClass("flot-overlay") && !$(this).hasClass('flot-base'); + }).remove(); + + if (placeholder.css("position") == 'static') + placeholder.css("position", "relative"); // for positioning labels and overlay + + surface = new Canvas("flot-base", placeholder); + overlay = new Canvas("flot-overlay", placeholder); // overlay canvas for interactive features + + ctx = surface.context; + octx = overlay.context; + + // define which element we're listening for events on + eventHolder = $(overlay.element).unbind(); + + // If we're re-using a plot object, shut down the old one + + var existing = placeholder.data("plot"); + + if (existing) { + existing.shutdown(); + overlay.clear(); + } + + // save in case we get replotted + placeholder.data("plot", plot); + } + + function bindEvents() { + // bind events + if (options.grid.hoverable) { + eventHolder.mousemove(onMouseMove); + + // Use bind, rather than .mouseleave, because we officially + // still support jQuery 1.2.6, which doesn't define a shortcut + // for mouseenter or mouseleave. This was a bug/oversight that + // was fixed somewhere around 1.3.x. We can return to using + // .mouseleave when we drop support for 1.2.6. + + eventHolder.bind("mouseleave", onMouseLeave); + } + + if (options.grid.clickable) + eventHolder.click(onClick); + + executeHooks(hooks.bindEvents, [eventHolder]); + } + + function shutdown() { + if (redrawTimeout) + clearTimeout(redrawTimeout); + + eventHolder.unbind("mousemove", onMouseMove); + eventHolder.unbind("mouseleave", onMouseLeave); + eventHolder.unbind("click", onClick); + + executeHooks(hooks.shutdown, [eventHolder]); + } + + function setTransformationHelpers(axis) { + // set helper functions on the axis, assumes plot area + // has been computed already + + function identity(x) { return x; } + + var s, m, t = axis.options.transform || identity, + it = axis.options.inverseTransform; + + // precompute how much the axis is scaling a point + // in canvas space + if (axis.direction == "x") { + s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min)); + m = Math.min(t(axis.max), t(axis.min)); + } + else { + s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min)); + s = -s; + m = Math.max(t(axis.max), t(axis.min)); + } + + // data point to canvas coordinate + if (t == identity) // slight optimization + axis.p2c = function (p) { return (p - m) * s; }; + else + axis.p2c = function (p) { return (t(p) - m) * s; }; + // canvas coordinate to data point + if (!it) + axis.c2p = function (c) { return m + c / s; }; + else + axis.c2p = function (c) { return it(m + c / s); }; + } + + function measureTickLabels(axis) { + + var opts = axis.options, + ticks = axis.ticks || [], + labelWidth = opts.labelWidth || 0, + labelHeight = opts.labelHeight || 0, + maxWidth = labelWidth || (axis.direction == "x" ? Math.floor(surface.width / (ticks.length || 1)) : null), + legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", + layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, + font = opts.font || "flot-tick-label tickLabel"; + + for (var i = 0; i < ticks.length; ++i) { + + var t = ticks[i]; + + if (!t.label) + continue; + + var info = surface.getTextInfo(layer, t.label, font, null, maxWidth); + + labelWidth = Math.max(labelWidth, info.width); + labelHeight = Math.max(labelHeight, info.height); + } + + axis.labelWidth = opts.labelWidth || labelWidth; + axis.labelHeight = opts.labelHeight || labelHeight; + } + + function allocateAxisBoxFirstPhase(axis) { + // find the bounding box of the axis by looking at label + // widths/heights and ticks, make room by diminishing the + // plotOffset; this first phase only looks at one + // dimension per axis, the other dimension depends on the + // other axes so will have to wait + + var lw = axis.labelWidth, + lh = axis.labelHeight, + pos = axis.options.position, + isXAxis = axis.direction === "x", + tickLength = axis.options.tickLength, + axisMargin = options.grid.axisMargin, + padding = options.grid.labelMargin, + innermost = true, + outermost = true, + first = true, + found = false; + + // Determine the axis's position in its direction and on its side + + $.each(isXAxis ? xaxes : yaxes, function(i, a) { + if (a && (a.show || a.reserveSpace)) { + if (a === axis) { + found = true; + } else if (a.options.position === pos) { + if (found) { + outermost = false; + } else { + innermost = false; + } + } + if (!found) { + first = false; + } + } + }); + + // The outermost axis on each side has no margin + + if (outermost) { + axisMargin = 0; + } + + // The ticks for the first axis in each direction stretch across + + if (tickLength == null) { + tickLength = first ? "full" : 5; + } + + if (!isNaN(+tickLength)) + padding += +tickLength; + + if (isXAxis) { + lh += padding; + + if (pos == "bottom") { + plotOffset.bottom += lh + axisMargin; + axis.box = { top: surface.height - plotOffset.bottom, height: lh }; + } + else { + axis.box = { top: plotOffset.top + axisMargin, height: lh }; + plotOffset.top += lh + axisMargin; + } + } + else { + lw += padding; + + if (pos == "left") { + axis.box = { left: plotOffset.left + axisMargin, width: lw }; + plotOffset.left += lw + axisMargin; + } + else { + plotOffset.right += lw + axisMargin; + axis.box = { left: surface.width - plotOffset.right, width: lw }; + } + } + + // save for future reference + axis.position = pos; + axis.tickLength = tickLength; + axis.box.padding = padding; + axis.innermost = innermost; + } + + function allocateAxisBoxSecondPhase(axis) { + // now that all axis boxes have been placed in one + // dimension, we can set the remaining dimension coordinates + if (axis.direction == "x") { + axis.box.left = plotOffset.left - axis.labelWidth / 2; + axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth; + } + else { + axis.box.top = plotOffset.top - axis.labelHeight / 2; + axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight; + } + } + + function adjustLayoutForThingsStickingOut() { + // possibly adjust plot offset to ensure everything stays + // inside the canvas and isn't clipped off + + var minMargin = options.grid.minBorderMargin, + axis, i; + + // check stuff from the plot (FIXME: this should just read + // a value from the series, otherwise it's impossible to + // customize) + if (minMargin == null) { + minMargin = 0; + for (i = 0; i < series.length; ++i) + minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth/2)); + } + + var margins = { + left: minMargin, + right: minMargin, + top: minMargin, + bottom: minMargin + }; + + // check axis labels, note we don't check the actual + // labels but instead use the overall width/height to not + // jump as much around with replots + $.each(allAxes(), function (_, axis) { + if (axis.reserveSpace && axis.ticks && axis.ticks.length) { + if (axis.direction === "x") { + margins.left = Math.max(margins.left, axis.labelWidth / 2); + margins.right = Math.max(margins.right, axis.labelWidth / 2); + } else { + margins.bottom = Math.max(margins.bottom, axis.labelHeight / 2); + margins.top = Math.max(margins.top, axis.labelHeight / 2); + } + } + }); + + plotOffset.left = Math.ceil(Math.max(margins.left, plotOffset.left)); + plotOffset.right = Math.ceil(Math.max(margins.right, plotOffset.right)); + plotOffset.top = Math.ceil(Math.max(margins.top, plotOffset.top)); + plotOffset.bottom = Math.ceil(Math.max(margins.bottom, plotOffset.bottom)); + } + + function setupGrid() { + var i, axes = allAxes(), showGrid = options.grid.show; + + // Initialize the plot's offset from the edge of the canvas + + for (var a in plotOffset) { + var margin = options.grid.margin || 0; + plotOffset[a] = typeof margin == "number" ? margin : margin[a] || 0; + } + + executeHooks(hooks.processOffset, [plotOffset]); + + // If the grid is visible, add its border width to the offset + + for (var a in plotOffset) { + if(typeof(options.grid.borderWidth) == "object") { + plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0; + } + else { + plotOffset[a] += showGrid ? options.grid.borderWidth : 0; + } + } + + $.each(axes, function (_, axis) { + var axisOpts = axis.options; + axis.show = axisOpts.show == null ? axis.used : axisOpts.show; + axis.reserveSpace = axisOpts.reserveSpace == null ? axis.show : axisOpts.reserveSpace; + setRange(axis); + }); + + if (showGrid) { + + var allocatedAxes = $.grep(axes, function (axis) { + return axis.show || axis.reserveSpace; + }); + + $.each(allocatedAxes, function (_, axis) { + // make the ticks + setupTickGeneration(axis); + setTicks(axis); + snapRangeToTicks(axis, axis.ticks); + // find labelWidth/Height for axis + measureTickLabels(axis); + }); + + // with all dimensions calculated, we can compute the + // axis bounding boxes, start from the outside + // (reverse order) + for (i = allocatedAxes.length - 1; i >= 0; --i) + allocateAxisBoxFirstPhase(allocatedAxes[i]); + + // make sure we've got enough space for things that + // might stick out + adjustLayoutForThingsStickingOut(); + + $.each(allocatedAxes, function (_, axis) { + allocateAxisBoxSecondPhase(axis); + }); + } + + plotWidth = surface.width - plotOffset.left - plotOffset.right; + plotHeight = surface.height - plotOffset.bottom - plotOffset.top; + + // now we got the proper plot dimensions, we can compute the scaling + $.each(axes, function (_, axis) { + setTransformationHelpers(axis); + }); + + if (showGrid) { + drawAxisLabels(); + } + + insertLegend(); + } + + function setRange(axis) { + var opts = axis.options, + min = +(opts.min != null ? opts.min : axis.datamin), + max = +(opts.max != null ? opts.max : axis.datamax), + delta = max - min; + + if (delta == 0.0) { + // degenerate case + var widen = max == 0 ? 1 : 0.01; + + if (opts.min == null) + min -= widen; + // always widen max if we couldn't widen min to ensure we + // don't fall into min == max which doesn't work + if (opts.max == null || opts.min != null) + max += widen; + } + else { + // consider autoscaling + var margin = opts.autoscaleMargin; + if (margin != null) { + if (opts.min == null) { + min -= delta * margin; + // make sure we don't go below zero if all values + // are positive + if (min < 0 && axis.datamin != null && axis.datamin >= 0) + min = 0; + } + if (opts.max == null) { + max += delta * margin; + if (max > 0 && axis.datamax != null && axis.datamax <= 0) + max = 0; + } + } + } + axis.min = min; + axis.max = max; + } + + function setupTickGeneration(axis) { + var opts = axis.options; + + // estimate number of ticks + var noTicks; + if (typeof opts.ticks == "number" && opts.ticks > 0) + noTicks = opts.ticks; + else + // heuristic based on the model a*sqrt(x) fitted to + // some data points that seemed reasonable + noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? surface.width : surface.height); + + var delta = (axis.max - axis.min) / noTicks, + dec = -Math.floor(Math.log(delta) / Math.LN10), + maxDec = opts.tickDecimals; + + if (maxDec != null && dec > maxDec) { + dec = maxDec; + } + + var magn = Math.pow(10, -dec), + norm = delta / magn, // norm is between 1.0 and 10.0 + size; + + if (norm < 1.5) { + size = 1; + } else if (norm < 3) { + size = 2; + // special case for 2.5, requires an extra decimal + if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) { + size = 2.5; + ++dec; + } + } else if (norm < 7.5) { + size = 5; + } else { + size = 10; + } + + size *= magn; + + if (opts.minTickSize != null && size < opts.minTickSize) { + size = opts.minTickSize; + } + + axis.delta = delta; + axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); + axis.tickSize = opts.tickSize || size; + + // Time mode was moved to a plug-in in 0.8, and since so many people use it + // we'll add an especially friendly reminder to make sure they included it. + + if (opts.mode == "time" && !axis.tickGenerator) { + throw new Error("Time mode requires the flot.time plugin."); + } + + // Flot supports base-10 axes; any other mode else is handled by a plug-in, + // like flot.time.js. + + if (!axis.tickGenerator) { + + axis.tickGenerator = function (axis) { + + var ticks = [], + start = floorInBase(axis.min, axis.tickSize), + i = 0, + v = Number.NaN, + prev; + + do { + prev = v; + v = start + i * axis.tickSize; + ticks.push(v); + ++i; + } while (v < axis.max && v != prev); + return ticks; + }; + + axis.tickFormatter = function (value, axis) { + + var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1; + var formatted = "" + Math.round(value * factor) / factor; + + // If tickDecimals was specified, ensure that we have exactly that + // much precision; otherwise default to the value's own precision. + + if (axis.tickDecimals != null) { + var decimal = formatted.indexOf("."); + var precision = decimal == -1 ? 0 : formatted.length - decimal - 1; + if (precision < axis.tickDecimals) { + return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision); + } + } + + return formatted; + }; + } + + if ($.isFunction(opts.tickFormatter)) + axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); }; + + if (opts.alignTicksWithAxis != null) { + var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1]; + if (otherAxis && otherAxis.used && otherAxis != axis) { + // consider snapping min/max to outermost nice ticks + var niceTicks = axis.tickGenerator(axis); + if (niceTicks.length > 0) { + if (opts.min == null) + axis.min = Math.min(axis.min, niceTicks[0]); + if (opts.max == null && niceTicks.length > 1) + axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]); + } + + axis.tickGenerator = function (axis) { + // copy ticks, scaled to this axis + var ticks = [], v, i; + for (i = 0; i < otherAxis.ticks.length; ++i) { + v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min); + v = axis.min + v * (axis.max - axis.min); + ticks.push(v); + } + return ticks; + }; + + // we might need an extra decimal since forced + // ticks don't necessarily fit naturally + if (!axis.mode && opts.tickDecimals == null) { + var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1), + ts = axis.tickGenerator(axis); + + // only proceed if the tick interval rounded + // with an extra decimal doesn't give us a + // zero at end + if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) + axis.tickDecimals = extraDec; + } + } + } + } + + function setTicks(axis) { + var oticks = axis.options.ticks, ticks = []; + if (oticks == null || (typeof oticks == "number" && oticks > 0)) + ticks = axis.tickGenerator(axis); + else if (oticks) { + if ($.isFunction(oticks)) + // generate the ticks + ticks = oticks(axis); + else + ticks = oticks; + } + + // clean up/labelify the supplied ticks, copy them over + var i, v; + axis.ticks = []; + for (i = 0; i < ticks.length; ++i) { + var label = null; + var t = ticks[i]; + if (typeof t == "object") { + v = +t[0]; + if (t.length > 1) + label = t[1]; + } + else + v = +t; + if (label == null) + label = axis.tickFormatter(v, axis); + if (!isNaN(v)) + axis.ticks.push({ v: v, label: label }); + } + } + + function snapRangeToTicks(axis, ticks) { + if (axis.options.autoscaleMargin && ticks.length > 0) { + // snap to ticks + if (axis.options.min == null) + axis.min = Math.min(axis.min, ticks[0].v); + if (axis.options.max == null && ticks.length > 1) + axis.max = Math.max(axis.max, ticks[ticks.length - 1].v); + } + } + + function draw() { + + surface.clear(); + + executeHooks(hooks.drawBackground, [ctx]); + + var grid = options.grid; + + // draw background, if any + if (grid.show && grid.backgroundColor) + drawBackground(); + + if (grid.show && !grid.aboveData) { + drawGrid(); + } + + for (var i = 0; i < series.length; ++i) { + executeHooks(hooks.drawSeries, [ctx, series[i]]); + drawSeries(series[i]); + } + + executeHooks(hooks.draw, [ctx]); + + if (grid.show && grid.aboveData) { + drawGrid(); + } + + surface.render(); + + // A draw implies that either the axes or data have changed, so we + // should probably update the overlay highlights as well. + + triggerRedrawOverlay(); + } + + function extractRange(ranges, coord) { + var axis, from, to, key, axes = allAxes(); + + for (var i = 0; i < axes.length; ++i) { + axis = axes[i]; + if (axis.direction == coord) { + key = coord + axis.n + "axis"; + if (!ranges[key] && axis.n == 1) + key = coord + "axis"; // support x1axis as xaxis + if (ranges[key]) { + from = ranges[key].from; + to = ranges[key].to; + break; + } + } + } + + // backwards-compat stuff - to be removed in future + if (!ranges[key]) { + axis = coord == "x" ? xaxes[0] : yaxes[0]; + from = ranges[coord + "1"]; + to = ranges[coord + "2"]; + } + + // auto-reverse as an added bonus + if (from != null && to != null && from > to) { + var tmp = from; + from = to; + to = tmp; + } + + return { from: from, to: to, axis: axis }; + } + + function drawBackground() { + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)"); + ctx.fillRect(0, 0, plotWidth, plotHeight); + ctx.restore(); + } + + function drawGrid() { + var i, axes, bw, bc; + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + // draw markings + var markings = options.grid.markings; + if (markings) { + if ($.isFunction(markings)) { + axes = plot.getAxes(); + // xmin etc. is backwards compatibility, to be + // removed in the future + axes.xmin = axes.xaxis.min; + axes.xmax = axes.xaxis.max; + axes.ymin = axes.yaxis.min; + axes.ymax = axes.yaxis.max; + + markings = markings(axes); + } + + for (i = 0; i < markings.length; ++i) { + var m = markings[i], + xrange = extractRange(m, "x"), + yrange = extractRange(m, "y"); + + // fill in missing + if (xrange.from == null) + xrange.from = xrange.axis.min; + if (xrange.to == null) + xrange.to = xrange.axis.max; + if (yrange.from == null) + yrange.from = yrange.axis.min; + if (yrange.to == null) + yrange.to = yrange.axis.max; + + // clip + if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max || + yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) + continue; + + xrange.from = Math.max(xrange.from, xrange.axis.min); + xrange.to = Math.min(xrange.to, xrange.axis.max); + yrange.from = Math.max(yrange.from, yrange.axis.min); + yrange.to = Math.min(yrange.to, yrange.axis.max); + + var xequal = xrange.from === xrange.to, + yequal = yrange.from === yrange.to; + + if (xequal && yequal) { + continue; + } + + // then draw + xrange.from = Math.floor(xrange.axis.p2c(xrange.from)); + xrange.to = Math.floor(xrange.axis.p2c(xrange.to)); + yrange.from = Math.floor(yrange.axis.p2c(yrange.from)); + yrange.to = Math.floor(yrange.axis.p2c(yrange.to)); + + if (xequal || yequal) { + var lineWidth = m.lineWidth || options.grid.markingsLineWidth, + subPixel = lineWidth % 2 ? 0.5 : 0; + ctx.beginPath(); + ctx.strokeStyle = m.color || options.grid.markingsColor; + ctx.lineWidth = lineWidth; + if (xequal) { + ctx.moveTo(xrange.to + subPixel, yrange.from); + ctx.lineTo(xrange.to + subPixel, yrange.to); + } else { + ctx.moveTo(xrange.from, yrange.to + subPixel); + ctx.lineTo(xrange.to, yrange.to + subPixel); + } + ctx.stroke(); + } else { + ctx.fillStyle = m.color || options.grid.markingsColor; + ctx.fillRect(xrange.from, yrange.to, + xrange.to - xrange.from, + yrange.from - yrange.to); + } + } + } + + // draw the ticks + axes = allAxes(); + bw = options.grid.borderWidth; + + for (var j = 0; j < axes.length; ++j) { + var axis = axes[j], box = axis.box, + t = axis.tickLength, x, y, xoff, yoff; + if (!axis.show || axis.ticks.length == 0) + continue; + + ctx.lineWidth = 1; + + // find the edges + if (axis.direction == "x") { + x = 0; + if (t == "full") + y = (axis.position == "top" ? 0 : plotHeight); + else + y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0); + } + else { + y = 0; + if (t == "full") + x = (axis.position == "left" ? 0 : plotWidth); + else + x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0); + } + + // draw tick bar + if (!axis.innermost) { + ctx.strokeStyle = axis.options.color; + ctx.beginPath(); + xoff = yoff = 0; + if (axis.direction == "x") + xoff = plotWidth + 1; + else + yoff = plotHeight + 1; + + if (ctx.lineWidth == 1) { + if (axis.direction == "x") { + y = Math.floor(y) + 0.5; + } else { + x = Math.floor(x) + 0.5; + } + } + + ctx.moveTo(x, y); + ctx.lineTo(x + xoff, y + yoff); + ctx.stroke(); + } + + // draw ticks + + ctx.strokeStyle = axis.options.tickColor; + + ctx.beginPath(); + for (i = 0; i < axis.ticks.length; ++i) { + var v = axis.ticks[i].v; + + xoff = yoff = 0; + + if (isNaN(v) || v < axis.min || v > axis.max + // skip those lying on the axes if we got a border + || (t == "full" + && ((typeof bw == "object" && bw[axis.position] > 0) || bw > 0) + && (v == axis.min || v == axis.max))) + continue; + + if (axis.direction == "x") { + x = axis.p2c(v); + yoff = t == "full" ? -plotHeight : t; + + if (axis.position == "top") + yoff = -yoff; + } + else { + y = axis.p2c(v); + xoff = t == "full" ? -plotWidth : t; + + if (axis.position == "left") + xoff = -xoff; + } + + if (ctx.lineWidth == 1) { + if (axis.direction == "x") + x = Math.floor(x) + 0.5; + else + y = Math.floor(y) + 0.5; + } + + ctx.moveTo(x, y); + ctx.lineTo(x + xoff, y + yoff); + } + + ctx.stroke(); + } + + + // draw border + if (bw) { + // If either borderWidth or borderColor is an object, then draw the border + // line by line instead of as one rectangle + bc = options.grid.borderColor; + if(typeof bw == "object" || typeof bc == "object") { + if (typeof bw !== "object") { + bw = {top: bw, right: bw, bottom: bw, left: bw}; + } + if (typeof bc !== "object") { + bc = {top: bc, right: bc, bottom: bc, left: bc}; + } + + if (bw.top > 0) { + ctx.strokeStyle = bc.top; + ctx.lineWidth = bw.top; + ctx.beginPath(); + ctx.moveTo(0 - bw.left, 0 - bw.top/2); + ctx.lineTo(plotWidth, 0 - bw.top/2); + ctx.stroke(); + } + + if (bw.right > 0) { + ctx.strokeStyle = bc.right; + ctx.lineWidth = bw.right; + ctx.beginPath(); + ctx.moveTo(plotWidth + bw.right / 2, 0 - bw.top); + ctx.lineTo(plotWidth + bw.right / 2, plotHeight); + ctx.stroke(); + } + + if (bw.bottom > 0) { + ctx.strokeStyle = bc.bottom; + ctx.lineWidth = bw.bottom; + ctx.beginPath(); + ctx.moveTo(plotWidth + bw.right, plotHeight + bw.bottom / 2); + ctx.lineTo(0, plotHeight + bw.bottom / 2); + ctx.stroke(); + } + + if (bw.left > 0) { + ctx.strokeStyle = bc.left; + ctx.lineWidth = bw.left; + ctx.beginPath(); + ctx.moveTo(0 - bw.left/2, plotHeight + bw.bottom); + ctx.lineTo(0- bw.left/2, 0); + ctx.stroke(); + } + } + else { + ctx.lineWidth = bw; + ctx.strokeStyle = options.grid.borderColor; + ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw); + } + } + + ctx.restore(); + } + + function drawAxisLabels() { + + $.each(allAxes(), function (_, axis) { + var box = axis.box, + legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", + layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, + font = axis.options.font || "flot-tick-label tickLabel", + tick, x, y, halign, valign; + + // Remove text before checking for axis.show and ticks.length; + // otherwise plugins, like flot-tickrotor, that draw their own + // tick labels will end up with both theirs and the defaults. + + surface.removeText(layer); + + if (!axis.show || axis.ticks.length == 0) + return; + + for (var i = 0; i < axis.ticks.length; ++i) { + + tick = axis.ticks[i]; + if (!tick.label || tick.v < axis.min || tick.v > axis.max) + continue; + + if (axis.direction == "x") { + halign = "center"; + x = plotOffset.left + axis.p2c(tick.v); + if (axis.position == "bottom") { + y = box.top + box.padding; + } else { + y = box.top + box.height - box.padding; + valign = "bottom"; + } + } else { + valign = "middle"; + y = plotOffset.top + axis.p2c(tick.v); + if (axis.position == "left") { + x = box.left + box.width - box.padding; + halign = "right"; + } else { + x = box.left + box.padding; + } + } + + surface.addText(layer, x, y, tick.label, font, null, null, halign, valign); + } + }); + } + + function drawSeries(series) { + if (series.lines.show) + drawSeriesLines(series); + if (series.bars.show) + drawSeriesBars(series); + if (series.points.show) + drawSeriesPoints(series); + } + + function drawSeriesLines(series) { + function plotLine(datapoints, xoffset, yoffset, axisx, axisy) { + var points = datapoints.points, + ps = datapoints.pointsize, + prevx = null, prevy = null; + + ctx.beginPath(); + for (var i = ps; i < points.length; i += ps) { + var x1 = points[i - ps], y1 = points[i - ps + 1], + x2 = points[i], y2 = points[i + 1]; + + if (x1 == null || x2 == null) + continue; + + // clip with ymin + if (y1 <= y2 && y1 < axisy.min) { + if (y2 < axisy.min) + continue; // line segment is outside + // compute new intersection point + x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.min; + } + else if (y2 <= y1 && y2 < axisy.min) { + if (y1 < axisy.min) + continue; + x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.min; + } + + // clip with ymax + if (y1 >= y2 && y1 > axisy.max) { + if (y2 > axisy.max) + continue; + x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.max; + } + else if (y2 >= y1 && y2 > axisy.max) { + if (y1 > axisy.max) + continue; + x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.max; + } + + // clip with xmin + if (x1 <= x2 && x1 < axisx.min) { + if (x2 < axisx.min) + continue; + y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.min; + } + else if (x2 <= x1 && x2 < axisx.min) { + if (x1 < axisx.min) + continue; + y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.min; + } + + // clip with xmax + if (x1 >= x2 && x1 > axisx.max) { + if (x2 > axisx.max) + continue; + y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.max; + } + else if (x2 >= x1 && x2 > axisx.max) { + if (x1 > axisx.max) + continue; + y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.max; + } + + if (x1 != prevx || y1 != prevy) + ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); + + prevx = x2; + prevy = y2; + ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset); + } + ctx.stroke(); + } + + function plotLineArea(datapoints, axisx, axisy) { + var points = datapoints.points, + ps = datapoints.pointsize, + bottom = Math.min(Math.max(0, axisy.min), axisy.max), + i = 0, top, areaOpen = false, + ypos = 1, segmentStart = 0, segmentEnd = 0; + + // we process each segment in two turns, first forward + // direction to sketch out top, then once we hit the + // end we go backwards to sketch the bottom + while (true) { + if (ps > 0 && i > points.length + ps) + break; + + i += ps; // ps is negative if going backwards + + var x1 = points[i - ps], + y1 = points[i - ps + ypos], + x2 = points[i], y2 = points[i + ypos]; + + if (areaOpen) { + if (ps > 0 && x1 != null && x2 == null) { + // at turning point + segmentEnd = i; + ps = -ps; + ypos = 2; + continue; + } + + if (ps < 0 && i == segmentStart + ps) { + // done with the reverse sweep + ctx.fill(); + areaOpen = false; + ps = -ps; + ypos = 1; + i = segmentStart = segmentEnd + ps; + continue; + } + } + + if (x1 == null || x2 == null) + continue; + + // clip x values + + // clip with xmin + if (x1 <= x2 && x1 < axisx.min) { + if (x2 < axisx.min) + continue; + y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.min; + } + else if (x2 <= x1 && x2 < axisx.min) { + if (x1 < axisx.min) + continue; + y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.min; + } + + // clip with xmax + if (x1 >= x2 && x1 > axisx.max) { + if (x2 > axisx.max) + continue; + y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.max; + } + else if (x2 >= x1 && x2 > axisx.max) { + if (x1 > axisx.max) + continue; + y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.max; + } + + if (!areaOpen) { + // open area + ctx.beginPath(); + ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); + areaOpen = true; + } + + // now first check the case where both is outside + if (y1 >= axisy.max && y2 >= axisy.max) { + ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max)); + continue; + } + else if (y1 <= axisy.min && y2 <= axisy.min) { + ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min)); + continue; + } + + // else it's a bit more complicated, there might + // be a flat maxed out rectangle first, then a + // triangular cutout or reverse; to find these + // keep track of the current x values + var x1old = x1, x2old = x2; + + // clip the y values, without shortcutting, we + // go through all cases in turn + + // clip with ymin + if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) { + x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.min; + } + else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { + x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.min; + } + + // clip with ymax + if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) { + x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.max; + } + else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { + x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.max; + } + + // if the x value was changed we got a rectangle + // to fill + if (x1 != x1old) { + ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)); + // it goes to (x1, y1), but we fill that below + } + + // fill triangular section, this sometimes result + // in redundant points if (x1, y1) hasn't changed + // from previous line to, but we just ignore that + ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); + + // fill the other rectangle if it's there + if (x2 != x2old) { + ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); + ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)); + } + } + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + ctx.lineJoin = "round"; + + var lw = series.lines.lineWidth, + sw = series.shadowSize; + // FIXME: consider another form of shadow when filling is turned on + if (lw > 0 && sw > 0) { + // draw shadow as a thick and thin line with transparency + ctx.lineWidth = sw; + ctx.strokeStyle = "rgba(0,0,0,0.1)"; + // position shadow at angle from the mid of line + var angle = Math.PI/18; + plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis); + ctx.lineWidth = sw/2; + plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis); + } + + ctx.lineWidth = lw; + ctx.strokeStyle = series.color; + var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight); + if (fillStyle) { + ctx.fillStyle = fillStyle; + plotLineArea(series.datapoints, series.xaxis, series.yaxis); + } + + if (lw > 0) + plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis); + ctx.restore(); + } + + function drawSeriesPoints(series) { + function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) { + var points = datapoints.points, ps = datapoints.pointsize; + + for (var i = 0; i < points.length; i += ps) { + var x = points[i], y = points[i + 1]; + if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) + continue; + + ctx.beginPath(); + x = axisx.p2c(x); + y = axisy.p2c(y) + offset; + if (symbol == "circle") + ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); + else + symbol(ctx, x, y, radius, shadow); + ctx.closePath(); + + if (fillStyle) { + ctx.fillStyle = fillStyle; + ctx.fill(); + } + ctx.stroke(); + } + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + var lw = series.points.lineWidth, + sw = series.shadowSize, + radius = series.points.radius, + symbol = series.points.symbol; + + // If the user sets the line width to 0, we change it to a very + // small value. A line width of 0 seems to force the default of 1. + // Doing the conditional here allows the shadow setting to still be + // optional even with a lineWidth of 0. + + if( lw == 0 ) + lw = 0.0001; + + if (lw > 0 && sw > 0) { + // draw shadow in two steps + var w = sw / 2; + ctx.lineWidth = w; + ctx.strokeStyle = "rgba(0,0,0,0.1)"; + plotPoints(series.datapoints, radius, null, w + w/2, true, + series.xaxis, series.yaxis, symbol); + + ctx.strokeStyle = "rgba(0,0,0,0.2)"; + plotPoints(series.datapoints, radius, null, w/2, true, + series.xaxis, series.yaxis, symbol); + } + + ctx.lineWidth = lw; + ctx.strokeStyle = series.color; + plotPoints(series.datapoints, radius, + getFillStyle(series.points, series.color), 0, false, + series.xaxis, series.yaxis, symbol); + ctx.restore(); + } + + function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { + var left, right, bottom, top, + drawLeft, drawRight, drawTop, drawBottom, + tmp; + + // in horizontal mode, we start the bar from the left + // instead of from the bottom so it appears to be + // horizontal rather than vertical + if (horizontal) { + drawBottom = drawRight = drawTop = true; + drawLeft = false; + left = b; + right = x; + top = y + barLeft; + bottom = y + barRight; + + // account for negative bars + if (right < left) { + tmp = right; + right = left; + left = tmp; + drawLeft = true; + drawRight = false; + } + } + else { + drawLeft = drawRight = drawTop = true; + drawBottom = false; + left = x + barLeft; + right = x + barRight; + bottom = b; + top = y; + + // account for negative bars + if (top < bottom) { + tmp = top; + top = bottom; + bottom = tmp; + drawBottom = true; + drawTop = false; + } + } + + // clip + if (right < axisx.min || left > axisx.max || + top < axisy.min || bottom > axisy.max) + return; + + if (left < axisx.min) { + left = axisx.min; + drawLeft = false; + } + + if (right > axisx.max) { + right = axisx.max; + drawRight = false; + } + + if (bottom < axisy.min) { + bottom = axisy.min; + drawBottom = false; + } + + if (top > axisy.max) { + top = axisy.max; + drawTop = false; + } + + left = axisx.p2c(left); + bottom = axisy.p2c(bottom); + right = axisx.p2c(right); + top = axisy.p2c(top); + + // fill the bar + if (fillStyleCallback) { + c.fillStyle = fillStyleCallback(bottom, top); + c.fillRect(left, top, right - left, bottom - top) + } + + // draw outline + if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { + c.beginPath(); + + // FIXME: inline moveTo is buggy with excanvas + c.moveTo(left, bottom); + if (drawLeft) + c.lineTo(left, top); + else + c.moveTo(left, top); + if (drawTop) + c.lineTo(right, top); + else + c.moveTo(right, top); + if (drawRight) + c.lineTo(right, bottom); + else + c.moveTo(right, bottom); + if (drawBottom) + c.lineTo(left, bottom); + else + c.moveTo(left, bottom); + c.stroke(); + } + } + + function drawSeriesBars(series) { + function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) { + var points = datapoints.points, ps = datapoints.pointsize; + + for (var i = 0; i < points.length; i += ps) { + if (points[i] == null) + continue; + drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth); + } + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + // FIXME: figure out a way to add shadows (for instance along the right edge) + ctx.lineWidth = series.bars.lineWidth; + ctx.strokeStyle = series.color; + + var barLeft; + + switch (series.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -series.bars.barWidth; + break; + default: + barLeft = -series.bars.barWidth / 2; + } + + var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null; + plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis); + ctx.restore(); + } + + function getFillStyle(filloptions, seriesColor, bottom, top) { + var fill = filloptions.fill; + if (!fill) + return null; + + if (filloptions.fillColor) + return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor); + + var c = $.color.parse(seriesColor); + c.a = typeof fill == "number" ? fill : 0.4; + c.normalize(); + return c.toString(); + } + + function insertLegend() { + + if (options.legend.container != null) { + $(options.legend.container).html(""); + } else { + placeholder.find(".legend").remove(); + } + + if (!options.legend.show) { + return; + } + + var fragments = [], entries = [], rowStarted = false, + lf = options.legend.labelFormatter, s, label; + + // Build a list of legend entries, with each having a label and a color + + for (var i = 0; i < series.length; ++i) { + s = series[i]; + if (s.label) { + label = lf ? lf(s.label, s) : s.label; + if (label) { + entries.push({ + label: label, + color: s.color + }); + } + } + } + + // Sort the legend using either the default or a custom comparator + + if (options.legend.sorted) { + if ($.isFunction(options.legend.sorted)) { + entries.sort(options.legend.sorted); + } else if (options.legend.sorted == "reverse") { + entries.reverse(); + } else { + var ascending = options.legend.sorted != "descending"; + entries.sort(function(a, b) { + return a.label == b.label ? 0 : ( + (a.label < b.label) != ascending ? 1 : -1 // Logical XOR + ); + }); + } + } + + // Generate markup for the list of entries, in their final order + + for (var i = 0; i < entries.length; ++i) { + + var entry = entries[i]; + + if (i % options.legend.noColumns == 0) { + if (rowStarted) + fragments.push(''); + fragments.push(''); + rowStarted = true; + } + + fragments.push( + '
' + + '' + entry.label + '' + ); + } + + if (rowStarted) + fragments.push(''); + + if (fragments.length == 0) + return; + + var table = '' + fragments.join("") + '
'; + if (options.legend.container != null) + $(options.legend.container).html(table); + else { + var pos = "", + p = options.legend.position, + m = options.legend.margin; + if (m[0] == null) + m = [m, m]; + if (p.charAt(0) == "n") + pos += 'top:' + (m[1] + plotOffset.top) + 'px;'; + else if (p.charAt(0) == "s") + pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;'; + if (p.charAt(1) == "e") + pos += 'right:' + (m[0] + plotOffset.right) + 'px;'; + else if (p.charAt(1) == "w") + pos += 'left:' + (m[0] + plotOffset.left) + 'px;'; + var legend = $('
' + table.replace('style="', 'style="position:absolute;' + pos +';') + '
').appendTo(placeholder); + if (options.legend.backgroundOpacity != 0.0) { + // put in the transparent background + // separately to avoid blended labels and + // label boxes + var c = options.legend.backgroundColor; + if (c == null) { + c = options.grid.backgroundColor; + if (c && typeof c == "string") + c = $.color.parse(c); + else + c = $.color.extract(legend, 'background-color'); + c.a = 1; + c = c.toString(); + } + var div = legend.children(); + $('
').prependTo(legend).css('opacity', options.legend.backgroundOpacity); + } + } + } + + + // interactive features + + var highlights = [], + redrawTimeout = null; + + // returns the data item the mouse is over, or null if none is found + function findNearbyItem(mouseX, mouseY, seriesFilter) { + var maxDistance = options.grid.mouseActiveRadius, + smallestDistance = maxDistance * maxDistance + 1, + item = null, foundPoint = false, i, j, ps; + + for (i = series.length - 1; i >= 0; --i) { + if (!seriesFilter(series[i])) + continue; + + var s = series[i], + axisx = s.xaxis, + axisy = s.yaxis, + points = s.datapoints.points, + mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster + my = axisy.c2p(mouseY), + maxx = maxDistance / axisx.scale, + maxy = maxDistance / axisy.scale; + + ps = s.datapoints.pointsize; + // with inverse transforms, we can't use the maxx/maxy + // optimization, sadly + if (axisx.options.inverseTransform) + maxx = Number.MAX_VALUE; + if (axisy.options.inverseTransform) + maxy = Number.MAX_VALUE; + + if (s.lines.show || s.points.show) { + for (j = 0; j < points.length; j += ps) { + var x = points[j], y = points[j + 1]; + if (x == null) + continue; + + // For points and lines, the cursor must be within a + // certain distance to the data point + if (x - mx > maxx || x - mx < -maxx || + y - my > maxy || y - my < -maxy) + continue; + + // We have to calculate distances in pixels, not in + // data units, because the scales of the axes may be different + var dx = Math.abs(axisx.p2c(x) - mouseX), + dy = Math.abs(axisy.p2c(y) - mouseY), + dist = dx * dx + dy * dy; // we save the sqrt + + // use <= to ensure last point takes precedence + // (last generally means on top of) + if (dist < smallestDistance) { + smallestDistance = dist; + item = [i, j / ps]; + } + } + } + + if (s.bars.show && !item) { // no other point can be nearby + + var barLeft, barRight; + + switch (s.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -s.bars.barWidth; + break; + default: + barLeft = -s.bars.barWidth / 2; + } + + barRight = barLeft + s.bars.barWidth; + + for (j = 0; j < points.length; j += ps) { + var x = points[j], y = points[j + 1], b = points[j + 2]; + if (x == null) + continue; + + // for a bar graph, the cursor must be inside the bar + if (series[i].bars.horizontal ? + (mx <= Math.max(b, x) && mx >= Math.min(b, x) && + my >= y + barLeft && my <= y + barRight) : + (mx >= x + barLeft && mx <= x + barRight && + my >= Math.min(b, y) && my <= Math.max(b, y))) + item = [i, j / ps]; + } + } + } + + if (item) { + i = item[0]; + j = item[1]; + ps = series[i].datapoints.pointsize; + + return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), + dataIndex: j, + series: series[i], + seriesIndex: i }; + } + + return null; + } + + function onMouseMove(e) { + if (options.grid.hoverable) + triggerClickHoverEvent("plothover", e, + function (s) { return s["hoverable"] != false; }); + } + + function onMouseLeave(e) { + if (options.grid.hoverable) + triggerClickHoverEvent("plothover", e, + function (s) { return false; }); + } + + function onClick(e) { + triggerClickHoverEvent("plotclick", e, + function (s) { return s["clickable"] != false; }); + } + + // trigger click or hover event (they send the same parameters + // so we share their code) + function triggerClickHoverEvent(eventname, event, seriesFilter) { + var offset = eventHolder.offset(), + canvasX = event.pageX - offset.left - plotOffset.left, + canvasY = event.pageY - offset.top - plotOffset.top, + pos = canvasToAxisCoords({ left: canvasX, top: canvasY }); + + pos.pageX = event.pageX; + pos.pageY = event.pageY; + + var item = findNearbyItem(canvasX, canvasY, seriesFilter); + + if (item) { + // fill in mouse pos for any listeners out there + item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left, 10); + item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top, 10); + } + + if (options.grid.autoHighlight) { + // clear auto-highlights + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.auto == eventname && + !(item && h.series == item.series && + h.point[0] == item.datapoint[0] && + h.point[1] == item.datapoint[1])) + unhighlight(h.series, h.point); + } + + if (item) + highlight(item.series, item.datapoint, eventname); + } + + placeholder.trigger(eventname, [ pos, item ]); + } + + function triggerRedrawOverlay() { + var t = options.interaction.redrawOverlayInterval; + if (t == -1) { // skip event queue + drawOverlay(); + return; + } + + if (!redrawTimeout) + redrawTimeout = setTimeout(drawOverlay, t); + } + + function drawOverlay() { + redrawTimeout = null; + + // draw highlights + octx.save(); + overlay.clear(); + octx.translate(plotOffset.left, plotOffset.top); + + var i, hi; + for (i = 0; i < highlights.length; ++i) { + hi = highlights[i]; + + if (hi.series.bars.show) + drawBarHighlight(hi.series, hi.point); + else + drawPointHighlight(hi.series, hi.point); + } + octx.restore(); + + executeHooks(hooks.drawOverlay, [octx]); + } + + function highlight(s, point, auto) { + if (typeof s == "number") + s = series[s]; + + if (typeof point == "number") { + var ps = s.datapoints.pointsize; + point = s.datapoints.points.slice(ps * point, ps * (point + 1)); + } + + var i = indexOfHighlight(s, point); + if (i == -1) { + highlights.push({ series: s, point: point, auto: auto }); + + triggerRedrawOverlay(); + } + else if (!auto) + highlights[i].auto = false; + } + + function unhighlight(s, point) { + if (s == null && point == null) { + highlights = []; + triggerRedrawOverlay(); + return; + } + + if (typeof s == "number") + s = series[s]; + + if (typeof point == "number") { + var ps = s.datapoints.pointsize; + point = s.datapoints.points.slice(ps * point, ps * (point + 1)); + } + + var i = indexOfHighlight(s, point); + if (i != -1) { + highlights.splice(i, 1); + + triggerRedrawOverlay(); + } + } + + function indexOfHighlight(s, p) { + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.series == s && h.point[0] == p[0] + && h.point[1] == p[1]) + return i; + } + return -1; + } + + function drawPointHighlight(series, point) { + var x = point[0], y = point[1], + axisx = series.xaxis, axisy = series.yaxis, + highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(); + + if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) + return; + + var pointRadius = series.points.radius + series.points.lineWidth / 2; + octx.lineWidth = pointRadius; + octx.strokeStyle = highlightColor; + var radius = 1.5 * pointRadius; + x = axisx.p2c(x); + y = axisy.p2c(y); + + octx.beginPath(); + if (series.points.symbol == "circle") + octx.arc(x, y, radius, 0, 2 * Math.PI, false); + else + series.points.symbol(octx, x, y, radius, false); + octx.closePath(); + octx.stroke(); + } + + function drawBarHighlight(series, point) { + var highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(), + fillStyle = highlightColor, + barLeft; + + switch (series.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -series.bars.barWidth; + break; + default: + barLeft = -series.bars.barWidth / 2; + } + + octx.lineWidth = series.bars.lineWidth; + octx.strokeStyle = highlightColor; + + drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth, + function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth); + } + + function getColorOrGradient(spec, bottom, top, defaultColor) { + if (typeof spec == "string") + return spec; + else { + // assume this is a gradient spec; IE currently only + // supports a simple vertical gradient properly, so that's + // what we support too + var gradient = ctx.createLinearGradient(0, top, 0, bottom); + + for (var i = 0, l = spec.colors.length; i < l; ++i) { + var c = spec.colors[i]; + if (typeof c != "string") { + var co = $.color.parse(defaultColor); + if (c.brightness != null) + co = co.scale('rgb', c.brightness); + if (c.opacity != null) + co.a *= c.opacity; + c = co.toString(); + } + gradient.addColorStop(i / (l - 1), c); + } + + return gradient; + } + } + } + + // Add the plot function to the top level of the jQuery object + + $.plot = function(placeholder, data, options) { + //var t0 = new Date(); + var plot = new Plot($(placeholder), data, options, $.plot.plugins); + //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime())); + return plot; + }; + + $.plot.version = "0.8.3"; + + $.plot.plugins = []; + + // Also add the plot function as a chainable property + + $.fn.plot = function(data, options) { + return this.each(function() { + $.plot(this, data, options); + }); + }; + + // round to nearby lower multiple of base + function floorInBase(n, base) { + return base * Math.floor(n / base); + } + +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.log.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.log.js new file mode 100644 index 00000000000000..e32bf5cf7e8173 --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.log.js @@ -0,0 +1,163 @@ +/* @notice + * + * Pretty handling of logarithmic axes. + * Copyright (c) 2007-2014 IOLA and Ole Laursen. + * Licensed under the MIT license. + * Created by Arne de Laat + * Set axis.mode to "log" and make the axis logarithmic using transform: + * axis: { + * mode: 'log', + * transform: function(v) {v <= 0 ? Math.log(v) / Math.LN10 : null}, + * inverseTransform: function(v) {Math.pow(10, v)} + * } + * The transform filters negative and zero values, because those are + * invalid on logarithmic scales. + * This plugin tries to create good looking logarithmic ticks, using + * unicode superscript characters. If all data to be plotted is between two + * powers of ten then the default flot tick generator and renderer are + * used. Logarithmic ticks are places at powers of ten and at half those + * values if there are not to many ticks already (e.g. [1, 5, 10, 50, 100]). + * For details, see https://github.com/flot/flot/pull/1328 +*/ + +(function($) { + + function log10(value) { + /* Get the Log10 of the value + */ + return Math.log(value) / Math.LN10; + } + + function floorAsLog10(value) { + /* Get power of the first power of 10 below the value + */ + return Math.floor(log10(value)); + } + + function ceilAsLog10(value) { + /* Get power of the first power of 10 above the value + */ + return Math.ceil(log10(value)); + } + + + // round to nearby lower multiple of base + function floorInBase(n, base) { + return base * Math.floor(n / base); + } + + function getUnicodePower(power) { + var superscripts = ["⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"], + result = "", + str_power = "" + power; + for (var i = 0; i < str_power.length; i++) { + if (str_power[i] === "+") { + } + else if (str_power[i] === "-") { + result += "⁻"; + } + else { + result += superscripts[str_power[i]]; + } + } + return result; + } + + function init(plot) { + plot.hooks.processOptions.push(function (plot) { + $.each(plot.getAxes(), function(axisName, axis) { + + var opts = axis.options; + + if (opts.mode === "log") { + + axis.tickGenerator = function (axis) { + + var ticks = [], + end = ceilAsLog10(axis.max), + start = floorAsLog10(axis.min), + tick = Number.NaN, + i = 0; + + if (axis.min === null || axis.min <= 0) { + // Bad minimum, make ticks from 1 (10**0) to max + start = 0; + axis.min = 0.6; + } + + if (end <= start) { + // Start less than end?! + ticks = [1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, + 1e7, 1e8, 1e9]; + } + else if (log10(axis.max) - log10(axis.datamin) < 1) { + // Default flot generator incase no powers of 10 + // are between start and end + var prev; + start = floorInBase(axis.min, axis.tickSize); + do { + prev = tick; + tick = start + i * axis.tickSize; + ticks.push(tick); + ++i; + } while (tick < axis.max && tick !== prev); + } + else { + // Make ticks at each power of ten + for (; i <= (end - start); i++) { + tick = Math.pow(10, start + i); + ticks.push(tick); + } + + var length = ticks.length; + + // If not to many ticks also put a tick between + // the powers of ten + if (end - start < 6) { + for (var j = 1; j < length * 2 - 1; j += 2) { + tick = ticks[j - 1] * 5; + ticks.splice(j, 0, tick); + } + } + } + return ticks; + }; + + axis.tickFormatter = function (value, axis) { + var formatted; + if (log10(axis.max) - log10(axis.datamin) < 1) { + // Default flot formatter + var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1; + formatted = "" + Math.round(value * factor) / factor; + if (axis.tickDecimals !== null) { + var decimal = formatted.indexOf("."); + var precision = decimal === -1 ? 0 : formatted.length - decimal - 1; + if (precision < axis.tickDecimals) { + return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision); + } + } + } + else { + var multiplier = "", + exponential = parseFloat(value).toExponential(0), + power = getUnicodePower(exponential.slice(2)); + if (exponential[0] !== "1") { + multiplier = exponential[0] + "x"; + } + formatted = multiplier + "10" + power; + } + return formatted; + }; + } + }); + }); + } + + $.plot.plugins.push({ + init: init, + name: "log", + version: "0.9" + }); + +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.navigate.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.navigate.js new file mode 100644 index 00000000000000..13fb7f17d04b24 --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.navigate.js @@ -0,0 +1,346 @@ +/* Flot plugin for adding the ability to pan and zoom the plot. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The default behaviour is double click and scrollwheel up/down to zoom in, drag +to pan. The plugin defines plot.zoom({ center }), plot.zoomOut() and +plot.pan( offset ) so you easily can add custom controls. It also fires +"plotpan" and "plotzoom" events, useful for synchronizing plots. + +The plugin supports these options: + + zoom: { + interactive: false + trigger: "dblclick" // or "click" for single click + amount: 1.5 // 2 = 200% (zoom in), 0.5 = 50% (zoom out) + } + + pan: { + interactive: false + cursor: "move" // CSS mouse cursor value used when dragging, e.g. "pointer" + frameRate: 20 + } + + xaxis, yaxis, x2axis, y2axis: { + zoomRange: null // or [ number, number ] (min range, max range) or false + panRange: null // or [ number, number ] (min, max) or false + } + +"interactive" enables the built-in drag/click behaviour. If you enable +interactive for pan, then you'll have a basic plot that supports moving +around; the same for zoom. + +"amount" specifies the default amount to zoom in (so 1.5 = 150%) relative to +the current viewport. + +"cursor" is a standard CSS mouse cursor string used for visual feedback to the +user when dragging. + +"frameRate" specifies the maximum number of times per second the plot will +update itself while the user is panning around on it (set to null to disable +intermediate pans, the plot will then not update until the mouse button is +released). + +"zoomRange" is the interval in which zooming can happen, e.g. with zoomRange: +[1, 100] the zoom will never scale the axis so that the difference between min +and max is smaller than 1 or larger than 100. You can set either end to null +to ignore, e.g. [1, null]. If you set zoomRange to false, zooming on that axis +will be disabled. + +"panRange" confines the panning to stay within a range, e.g. with panRange: +[-10, 20] panning stops at -10 in one end and at 20 in the other. Either can +be null, e.g. [-10, null]. If you set panRange to false, panning on that axis +will be disabled. + +Example API usage: + + plot = $.plot(...); + + // zoom default amount in on the pixel ( 10, 20 ) + plot.zoom({ center: { left: 10, top: 20 } }); + + // zoom out again + plot.zoomOut({ center: { left: 10, top: 20 } }); + + // zoom 200% in on the pixel (10, 20) + plot.zoom({ amount: 2, center: { left: 10, top: 20 } }); + + // pan 100 pixels to the left and 20 down + plot.pan({ left: -100, top: 20 }) + +Here, "center" specifies where the center of the zooming should happen. Note +that this is defined in pixel space, not the space of the data points (you can +use the p2c helpers on the axes in Flot to help you convert between these). + +"amount" is the amount to zoom the viewport relative to the current range, so +1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out). You +can set the default in the options. + +*/ + +// First two dependencies, jquery.event.drag.js and +// jquery.mousewheel.js, we put them inline here to save people the +// effort of downloading them. + +/* +jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com) +Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt +*/ +(function(a){function e(h){var k,j=this,l=h.data||{};if(l.elem)j=h.dragTarget=l.elem,h.dragProxy=d.proxy||j,h.cursorOffsetX=l.pageX-l.left,h.cursorOffsetY=l.pageY-l.top,h.offsetX=h.pageX-h.cursorOffsetX,h.offsetY=h.pageY-h.cursorOffsetY;else if(d.dragging||l.which>0&&h.which!=l.which||a(h.target).is(l.not))return;switch(h.type){case"mousedown":return a.extend(l,a(j).offset(),{elem:j,target:h.target,pageX:h.pageX,pageY:h.pageY}),b.add(document,"mousemove mouseup",e,l),i(j,!1),d.dragging=null,!1;case!d.dragging&&"mousemove":if(g(h.pageX-l.pageX)+g(h.pageY-l.pageY) max) { + // make sure min < max + var tmp = min; + min = max; + max = tmp; + } + + //Check that we are in panRange + if (pr) { + if (pr[0] != null && min < pr[0]) { + min = pr[0]; + } + if (pr[1] != null && max > pr[1]) { + max = pr[1]; + } + } + + var range = max - min; + if (zr && + ((zr[0] != null && range < zr[0] && amount >1) || + (zr[1] != null && range > zr[1] && amount <1))) + return; + + opts.min = min; + opts.max = max; + }); + + plot.setupGrid(); + plot.draw(); + + if (!args.preventEvent) + plot.getPlaceholder().trigger("plotzoom", [ plot, args ]); + }; + + plot.pan = function (args) { + var delta = { + x: +args.left, + y: +args.top + }; + + if (isNaN(delta.x)) + delta.x = 0; + if (isNaN(delta.y)) + delta.y = 0; + + $.each(plot.getAxes(), function (_, axis) { + var opts = axis.options, + min, max, d = delta[axis.direction]; + + min = axis.c2p(axis.p2c(axis.min) + d), + max = axis.c2p(axis.p2c(axis.max) + d); + + var pr = opts.panRange; + if (pr === false) // no panning on this axis + return; + + if (pr) { + // check whether we hit the wall + if (pr[0] != null && pr[0] > min) { + d = pr[0] - min; + min += d; + max += d; + } + + if (pr[1] != null && pr[1] < max) { + d = pr[1] - max; + min += d; + max += d; + } + } + + opts.min = min; + opts.max = max; + }); + + plot.setupGrid(); + plot.draw(); + + if (!args.preventEvent) + plot.getPlaceholder().trigger("plotpan", [ plot, args ]); + }; + + function shutdown(plot, eventHolder) { + eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick); + eventHolder.unbind("mousewheel", onMouseWheel); + eventHolder.unbind("dragstart", onDragStart); + eventHolder.unbind("drag", onDrag); + eventHolder.unbind("dragend", onDragEnd); + if (panTimeout) + clearTimeout(panTimeout); + } + + plot.hooks.bindEvents.push(bindEvents); + plot.hooks.shutdown.push(shutdown); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'navigate', + version: '1.3' + }); +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.pie.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.pie.js new file mode 100644 index 00000000000000..24148c0a2e223d --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.pie.js @@ -0,0 +1,824 @@ +/* Flot plugin for rendering pie charts. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin assumes that each series has a single data value, and that each +value is a positive integer or zero. Negative numbers don't make sense for a +pie chart, and have unpredictable results. The values do NOT need to be +passed in as percentages; the plugin will calculate the total and per-slice +percentages internally. + +* Created by Brian Medendorp + +* Updated with contributions from btburnett3, Anthony Aragues and Xavi Ivars + +The plugin supports these options: + + series: { + pie: { + show: true/false + radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto' + innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect + startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result + tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show) + offset: { + top: integer value to move the pie up or down + left: integer value to move the pie left or right, or 'auto' + }, + stroke: { + color: any hexadecimal color value (other formats may or may not work, so best to stick with something like '#FFF') + width: integer pixel width of the stroke + }, + label: { + show: true/false, or 'auto' + formatter: a user-defined function that modifies the text/style of the label text + radius: 0-1 for percentage of fullsize, or a specified pixel length + background: { + color: any hexadecimal color value (other formats may or may not work, so best to stick with something like '#000') + opacity: 0-1 + }, + threshold: 0-1 for the percentage value at which to hide labels (if they're too small) + }, + combine: { + threshold: 0-1 for the percentage value at which to combine slices (if they're too small) + color: any hexadecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined + label: any text value of what the combined slice should be labeled + } + highlight: { + opacity: 0-1 + } + } + } + +More detail and specific examples can be found in the included HTML file. + +*/ + +import { i18n } from '@kbn/i18n'; + +(function($) { + // Maximum redraw attempts when fitting labels within the plot + + var REDRAW_ATTEMPTS = 10; + + // Factor by which to shrink the pie when fitting labels within the plot + + var REDRAW_SHRINK = 0.95; + + function init(plot) { + + var canvas = null, + target = null, + options = null, + maxRadius = null, + centerLeft = null, + centerTop = null, + processed = false, + ctx = null; + + // interactive variables + + var highlights = []; + + // add hook to determine if pie plugin in enabled, and then perform necessary operations + + plot.hooks.processOptions.push(function(plot, options) { + if (options.series.pie.show) { + + options.grid.show = false; + + // set labels.show + + if (options.series.pie.label.show == "auto") { + if (options.legend.show) { + options.series.pie.label.show = false; + } else { + options.series.pie.label.show = true; + } + } + + // set radius + + if (options.series.pie.radius == "auto") { + if (options.series.pie.label.show) { + options.series.pie.radius = 3/4; + } else { + options.series.pie.radius = 1; + } + } + + // ensure sane tilt + + if (options.series.pie.tilt > 1) { + options.series.pie.tilt = 1; + } else if (options.series.pie.tilt < 0) { + options.series.pie.tilt = 0; + } + } + }); + + plot.hooks.bindEvents.push(function(plot, eventHolder) { + var options = plot.getOptions(); + if (options.series.pie.show) { + if (options.grid.hoverable) { + eventHolder.unbind("mousemove").mousemove(onMouseMove); + } + if (options.grid.clickable) { + eventHolder.unbind("click").click(onClick); + } + } + }); + + plot.hooks.processDatapoints.push(function(plot, series, data, datapoints) { + var options = plot.getOptions(); + if (options.series.pie.show) { + processDatapoints(plot, series, data, datapoints); + } + }); + + plot.hooks.drawOverlay.push(function(plot, octx) { + var options = plot.getOptions(); + if (options.series.pie.show) { + drawOverlay(plot, octx); + } + }); + + plot.hooks.draw.push(function(plot, newCtx) { + var options = plot.getOptions(); + if (options.series.pie.show) { + draw(plot, newCtx); + } + }); + + function processDatapoints(plot, series, datapoints) { + if (!processed) { + processed = true; + canvas = plot.getCanvas(); + target = $(canvas).parent(); + options = plot.getOptions(); + plot.setData(combine(plot.getData())); + } + } + + function combine(data) { + + var total = 0, + combined = 0, + numCombined = 0, + color = options.series.pie.combine.color, + newdata = []; + + // Fix up the raw data from Flot, ensuring the data is numeric + + for (var i = 0; i < data.length; ++i) { + + var value = data[i].data; + + // If the data is an array, we'll assume that it's a standard + // Flot x-y pair, and are concerned only with the second value. + + // Note how we use the original array, rather than creating a + // new one; this is more efficient and preserves any extra data + // that the user may have stored in higher indexes. + + if ($.isArray(value) && value.length == 1) { + value = value[0]; + } + + if ($.isArray(value)) { + // Equivalent to $.isNumeric() but compatible with jQuery < 1.7 + if (!isNaN(parseFloat(value[1])) && isFinite(value[1])) { + value[1] = +value[1]; + } else { + value[1] = 0; + } + } else if (!isNaN(parseFloat(value)) && isFinite(value)) { + value = [1, +value]; + } else { + value = [1, 0]; + } + + data[i].data = [value]; + } + + // Sum up all the slices, so we can calculate percentages for each + + for (var i = 0; i < data.length; ++i) { + total += data[i].data[0][1]; + } + + // Count the number of slices with percentages below the combine + // threshold; if it turns out to be just one, we won't combine. + + for (var i = 0; i < data.length; ++i) { + var value = data[i].data[0][1]; + if (value / total <= options.series.pie.combine.threshold) { + combined += value; + numCombined++; + if (!color) { + color = data[i].color; + } + } + } + + for (var i = 0; i < data.length; ++i) { + var value = data[i].data[0][1]; + if (numCombined < 2 || value / total > options.series.pie.combine.threshold) { + newdata.push( + $.extend(data[i], { /* extend to allow keeping all other original data values + and using them e.g. in labelFormatter. */ + data: [[1, value]], + color: data[i].color, + label: data[i].label, + angle: value * Math.PI * 2 / total, + percent: value / (total / 100) + }) + ); + } + } + + if (numCombined > 1) { + newdata.push({ + data: [[1, combined]], + color: color, + label: options.series.pie.combine.label, + angle: combined * Math.PI * 2 / total, + percent: combined / (total / 100) + }); + } + + return newdata; + } + + function draw(plot, newCtx) { + + if (!target) { + return; // if no series were passed + } + + var canvasWidth = plot.getPlaceholder().width(), + canvasHeight = plot.getPlaceholder().height(), + legendWidth = target.children().filter(".legend").children().width() || 0; + + ctx = newCtx; + + // WARNING: HACK! REWRITE THIS CODE AS SOON AS POSSIBLE! + + // When combining smaller slices into an 'other' slice, we need to + // add a new series. Since Flot gives plugins no way to modify the + // list of series, the pie plugin uses a hack where the first call + // to processDatapoints results in a call to setData with the new + // list of series, then subsequent processDatapoints do nothing. + + // The plugin-global 'processed' flag is used to control this hack; + // it starts out false, and is set to true after the first call to + // processDatapoints. + + // Unfortunately this turns future setData calls into no-ops; they + // call processDatapoints, the flag is true, and nothing happens. + + // To fix this we'll set the flag back to false here in draw, when + // all series have been processed, so the next sequence of calls to + // processDatapoints once again starts out with a slice-combine. + // This is really a hack; in 0.9 we need to give plugins a proper + // way to modify series before any processing begins. + + processed = false; + + // calculate maximum radius and center point + + maxRadius = Math.min(canvasWidth, canvasHeight / options.series.pie.tilt) / 2; + centerTop = canvasHeight / 2 + options.series.pie.offset.top; + centerLeft = canvasWidth / 2; + + if (options.series.pie.offset.left == "auto") { + if (options.legend.position.match("w")) { + centerLeft += legendWidth / 2; + } else { + centerLeft -= legendWidth / 2; + } + if (centerLeft < maxRadius) { + centerLeft = maxRadius; + } else if (centerLeft > canvasWidth - maxRadius) { + centerLeft = canvasWidth - maxRadius; + } + } else { + centerLeft += options.series.pie.offset.left; + } + + var slices = plot.getData(), + attempts = 0; + + // Keep shrinking the pie's radius until drawPie returns true, + // indicating that all the labels fit, or we try too many times. + + do { + if (attempts > 0) { + maxRadius *= REDRAW_SHRINK; + } + attempts += 1; + clear(); + if (options.series.pie.tilt <= 0.8) { + drawShadow(); + } + } while (!drawPie() && attempts < REDRAW_ATTEMPTS) + + if (attempts >= REDRAW_ATTEMPTS) { + clear(); + const errorMessage = i18n.translate('xpack.monitoring.pie.unableToDrawLabelsInsideCanvasErrorMessage', { + defaultMessage: 'Could not draw pie with labels contained inside canvas', + }); + target.prepend(`
${errorMessage}
`); + } + + if (plot.setSeries && plot.insertLegend) { + plot.setSeries(slices); + plot.insertLegend(); + } + + // we're actually done at this point, just defining internal functions at this point + + function clear() { + ctx.clearRect(0, 0, canvasWidth, canvasHeight); + target.children().filter(".pieLabel, .pieLabelBackground").remove(); + } + + function drawShadow() { + + var shadowLeft = options.series.pie.shadow.left; + var shadowTop = options.series.pie.shadow.top; + var edge = 10; + var alpha = options.series.pie.shadow.alpha; + var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; + + if (radius >= canvasWidth / 2 - shadowLeft || radius * options.series.pie.tilt >= canvasHeight / 2 - shadowTop || radius <= edge) { + return; // shadow would be outside canvas, so don't draw it + } + + ctx.save(); + ctx.translate(shadowLeft,shadowTop); + ctx.globalAlpha = alpha; + ctx.fillStyle = "#000"; + + // center and rotate to starting position + + ctx.translate(centerLeft,centerTop); + ctx.scale(1, options.series.pie.tilt); + + //radius -= edge; + + for (var i = 1; i <= edge; i++) { + ctx.beginPath(); + ctx.arc(0, 0, radius, 0, Math.PI * 2, false); + ctx.fill(); + radius -= i; + } + + ctx.restore(); + } + + function drawPie() { + + var startAngle = Math.PI * options.series.pie.startAngle; + var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; + + // center and rotate to starting position + + ctx.save(); + ctx.translate(centerLeft,centerTop); + ctx.scale(1, options.series.pie.tilt); + //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera + + // draw slices + + ctx.save(); + var currentAngle = startAngle; + for (var i = 0; i < slices.length; ++i) { + slices[i].startAngle = currentAngle; + drawSlice(slices[i].angle, slices[i].color, true); + } + ctx.restore(); + + // draw slice outlines + + if (options.series.pie.stroke.width > 0) { + ctx.save(); + ctx.lineWidth = options.series.pie.stroke.width; + currentAngle = startAngle; + for (var i = 0; i < slices.length; ++i) { + drawSlice(slices[i].angle, options.series.pie.stroke.color, false); + } + ctx.restore(); + } + + // draw donut hole + + drawDonutHole(ctx); + + ctx.restore(); + + // Draw the labels, returning true if they fit within the plot + + if (options.series.pie.label.show) { + return drawLabels(); + } else return true; + + function drawSlice(angle, color, fill) { + + if (angle <= 0 || isNaN(angle)) { + return; + } + + if (fill) { + ctx.fillStyle = color; + } else { + ctx.strokeStyle = color; + ctx.lineJoin = "round"; + } + + ctx.beginPath(); + if (Math.abs(angle - Math.PI * 2) > 0.000000001) { + ctx.moveTo(0, 0); // Center of the pie + } + + //ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera + ctx.arc(0, 0, radius,currentAngle, currentAngle + angle / 2, false); + ctx.arc(0, 0, radius,currentAngle + angle / 2, currentAngle + angle, false); + ctx.closePath(); + //ctx.rotate(angle); // This doesn't work properly in Opera + currentAngle += angle; + + if (fill) { + ctx.fill(); + } else { + ctx.stroke(); + } + } + + function drawLabels() { + + var currentAngle = startAngle; + var radius = options.series.pie.label.radius > 1 ? options.series.pie.label.radius : maxRadius * options.series.pie.label.radius; + + for (var i = 0; i < slices.length; ++i) { + if (slices[i].percent >= options.series.pie.label.threshold * 100) { + if (!drawLabel(slices[i], currentAngle, i)) { + return false; + } + } + currentAngle += slices[i].angle; + } + + return true; + + function drawLabel(slice, startAngle, index) { + + if (slice.data[0][1] == 0) { + return true; + } + + // format label text + + var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter; + + if (lf) { + text = lf(slice.label, slice); + } else { + text = slice.label; + } + + if (plf) { + text = plf(text, slice); + } + + var halfAngle = ((startAngle + slice.angle) + startAngle) / 2; + var x = centerLeft + Math.round(Math.cos(halfAngle) * radius); + var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt; + + var html = "" + text + ""; + target.append(html); + + var label = target.children("#pieLabel" + index); + var labelTop = (y - label.height() / 2); + var labelLeft = (x - label.width() / 2); + + label.css("top", labelTop); + label.css("left", labelLeft); + + // check to make sure that the label is not outside the canvas + + if (0 - labelTop > 0 || 0 - labelLeft > 0 || canvasHeight - (labelTop + label.height()) < 0 || canvasWidth - (labelLeft + label.width()) < 0) { + return false; + } + + if (options.series.pie.label.background.opacity != 0) { + + // put in the transparent background separately to avoid blended labels and label boxes + + var c = options.series.pie.label.background.color; + + if (c == null) { + c = slice.color; + } + + var pos = "top:" + labelTop + "px;left:" + labelLeft + "px;"; + $("
") + .css("opacity", options.series.pie.label.background.opacity) + .insertBefore(label); + } + + return true; + } // end individual label function + } // end drawLabels function + } // end drawPie function + } // end draw function + + // Placed here because it needs to be accessed from multiple locations + + function drawDonutHole(layer) { + if (options.series.pie.innerRadius > 0) { + + // subtract the center + + layer.save(); + var innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius; + layer.globalCompositeOperation = "destination-out"; // this does not work with excanvas, but it will fall back to using the stroke color + layer.beginPath(); + layer.fillStyle = options.series.pie.stroke.color; + layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false); + layer.fill(); + layer.closePath(); + layer.restore(); + + // add inner stroke + + layer.save(); + layer.beginPath(); + layer.strokeStyle = options.series.pie.stroke.color; + layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false); + layer.stroke(); + layer.closePath(); + layer.restore(); + + // TODO: add extra shadow inside hole (with a mask) if the pie is tilted. + } + } + + //-- Additional Interactive related functions -- + + function isPointInPoly(poly, pt) { + for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) + ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1])) + && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0]) + && (c = !c); + return c; + } + + function findNearbySlice(mouseX, mouseY) { + + var slices = plot.getData(), + options = plot.getOptions(), + radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius, + x, y; + + for (var i = 0; i < slices.length; ++i) { + + var s = slices[i]; + + if (s.pie.show) { + + ctx.save(); + ctx.beginPath(); + ctx.moveTo(0, 0); // Center of the pie + //ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here. + ctx.arc(0, 0, radius, s.startAngle, s.startAngle + s.angle / 2, false); + ctx.arc(0, 0, radius, s.startAngle + s.angle / 2, s.startAngle + s.angle, false); + ctx.closePath(); + x = mouseX - centerLeft; + y = mouseY - centerTop; + + if (ctx.isPointInPath) { + if (ctx.isPointInPath(mouseX - centerLeft, mouseY - centerTop)) { + ctx.restore(); + return { + datapoint: [s.percent, s.data], + dataIndex: 0, + series: s, + seriesIndex: i + }; + } + } else { + + // excanvas for IE doesn;t support isPointInPath, this is a workaround. + + var p1X = radius * Math.cos(s.startAngle), + p1Y = radius * Math.sin(s.startAngle), + p2X = radius * Math.cos(s.startAngle + s.angle / 4), + p2Y = radius * Math.sin(s.startAngle + s.angle / 4), + p3X = radius * Math.cos(s.startAngle + s.angle / 2), + p3Y = radius * Math.sin(s.startAngle + s.angle / 2), + p4X = radius * Math.cos(s.startAngle + s.angle / 1.5), + p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5), + p5X = radius * Math.cos(s.startAngle + s.angle), + p5Y = radius * Math.sin(s.startAngle + s.angle), + arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]], + arrPoint = [x, y]; + + // TODO: perhaps do some mathematical trickery here with the Y-coordinate to compensate for pie tilt? + + if (isPointInPoly(arrPoly, arrPoint)) { + ctx.restore(); + return { + datapoint: [s.percent, s.data], + dataIndex: 0, + series: s, + seriesIndex: i + }; + } + } + + ctx.restore(); + } + } + + return null; + } + + function onMouseMove(e) { + triggerClickHoverEvent("plothover", e); + } + + function onClick(e) { + triggerClickHoverEvent("plotclick", e); + } + + // trigger click or hover event (they send the same parameters so we share their code) + + function triggerClickHoverEvent(eventname, e) { + + var offset = plot.offset(); + var canvasX = parseInt(e.pageX - offset.left); + var canvasY = parseInt(e.pageY - offset.top); + var item = findNearbySlice(canvasX, canvasY); + + if (options.grid.autoHighlight) { + + // clear auto-highlights + + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.auto == eventname && !(item && h.series == item.series)) { + unhighlight(h.series); + } + } + } + + // highlight the slice + + if (item) { + highlight(item.series, eventname); + } + + // trigger any hover bind events + + var pos = { pageX: e.pageX, pageY: e.pageY }; + target.trigger(eventname, [pos, item]); + } + + function highlight(s, auto) { + //if (typeof s == "number") { + // s = series[s]; + //} + + var i = indexOfHighlight(s); + + if (i == -1) { + highlights.push({ series: s, auto: auto }); + plot.triggerRedrawOverlay(); + } else if (!auto) { + highlights[i].auto = false; + } + } + + function unhighlight(s) { + if (s == null) { + highlights = []; + plot.triggerRedrawOverlay(); + } + + //if (typeof s == "number") { + // s = series[s]; + //} + + var i = indexOfHighlight(s); + + if (i != -1) { + highlights.splice(i, 1); + plot.triggerRedrawOverlay(); + } + } + + function indexOfHighlight(s) { + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.series == s) + return i; + } + return -1; + } + + function drawOverlay(plot, octx) { + + var options = plot.getOptions(); + + var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; + + octx.save(); + octx.translate(centerLeft, centerTop); + octx.scale(1, options.series.pie.tilt); + + for (var i = 0; i < highlights.length; ++i) { + drawHighlight(highlights[i].series); + } + + drawDonutHole(octx); + + octx.restore(); + + function drawHighlight(series) { + + if (series.angle <= 0 || isNaN(series.angle)) { + return; + } + + //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString(); + octx.fillStyle = "rgba(255, 255, 255, " + options.series.pie.highlight.opacity + ")"; // this is temporary until we have access to parseColor + octx.beginPath(); + if (Math.abs(series.angle - Math.PI * 2) > 0.000000001) { + octx.moveTo(0, 0); // Center of the pie + } + octx.arc(0, 0, radius, series.startAngle, series.startAngle + series.angle / 2, false); + octx.arc(0, 0, radius, series.startAngle + series.angle / 2, series.startAngle + series.angle, false); + octx.closePath(); + octx.fill(); + } + } + } // end init (plugin body) + + // define pie specific options and their default values + + var options = { + series: { + pie: { + show: false, + radius: "auto", // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value) + innerRadius: 0, /* for donut */ + startAngle: 3/2, + tilt: 1, + shadow: { + left: 5, // shadow left offset + top: 15, // shadow top offset + alpha: 0.02 // shadow alpha + }, + offset: { + top: 0, + left: "auto" + }, + stroke: { + color: "#fff", + width: 1 + }, + label: { + show: "auto", + formatter: function(label, slice) { + return "
" + label + "
" + Math.round(slice.percent) + "%
"; + }, // formatter function + radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value) + background: { + color: null, + opacity: 0 + }, + threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow) + }, + combine: { + threshold: -1, // percentage at which to combine little slices into one larger slice + color: null, // color to give the new slice (auto-generated if null) + label: "Other" // label to give the new slice + }, + highlight: { + //color: "#fff", // will add this functionality once parseColor is available + opacity: 0.5 + } + } + } + }; + + $.plot.plugins.push({ + init: init, + options: options, + name: "pie", + version: "1.1" + }); + +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.resize.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.resize.js new file mode 100644 index 00000000000000..8a626dda0addbc --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.resize.js @@ -0,0 +1,59 @@ +/* Flot plugin for automatically redrawing plots as the placeholder resizes. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +It works by listening for changes on the placeholder div (through the jQuery +resize event plugin) - if the size changes, it will redraw the plot. + +There are no options. If you need to disable the plugin for some plots, you +can just fix the size of their placeholders. + +*/ + +/* Inline dependency: + * jQuery resize event - v1.1 - 3/14/2010 + * http://benalman.com/projects/jquery-resize-plugin/ + * + * Copyright (c) 2010 "Cowboy" Ben Alman + * Dual licensed under the MIT and GPL licenses. + * http://benalman.com/about/license/ + */ +(function($,e,t){"$:nomunge";var i=[],n=$.resize=$.extend($.resize,{}),a,r=false,s="setTimeout",u="resize",m=u+"-special-event",o="pendingDelay",l="activeDelay",f="throttleWindow";n[o]=200;n[l]=20;n[f]=true;$.event.special[u]={setup:function(){if(!n[f]&&this[s]){return false}var e=$(this);i.push(this);e.data(m,{w:e.width(),h:e.height()});if(i.length===1){a=t;h()}},teardown:function(){if(!n[f]&&this[s]){return false}var e=$(this);for(var t=i.length-1;t>=0;t--){if(i[t]==this){i.splice(t,1);break}}e.removeData(m);if(!i.length){if(r){cancelAnimationFrame(a)}else{clearTimeout(a)}a=null}},add:function(e){if(!n[f]&&this[s]){return false}var i;function a(e,n,a){var r=$(this),s=r.data(m)||{};s.w=n!==t?n:r.width();s.h=a!==t?a:r.height();i.apply(this,arguments)}if($.isFunction(e)){i=e;return a}else{i=e.handler;e.handler=a}}};function h(t){if(r===true){r=t||1}for(var s=i.length-1;s>=0;s--){var l=$(i[s]);if(l[0]==e||l.is(":visible")){var f=l.width(),c=l.height(),d=l.data(m);if(d&&(f!==d.w||c!==d.h)){l.trigger(u,[d.w=f,d.h=c]);r=t||true}}else{d=l.data(m);d.w=0;d.h=0}}if(a!==null){if(r&&(t==null||t-r<1e3)){a=e.requestAnimationFrame(h)}else{a=setTimeout(h,n[o]);r=false}}}if(!e.requestAnimationFrame){e.requestAnimationFrame=function(){return e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(t,i){return e.setTimeout(function(){t((new Date).getTime())},n[l])}}()}if(!e.cancelAnimationFrame){e.cancelAnimationFrame=function(){return e.webkitCancelRequestAnimationFrame||e.mozCancelRequestAnimationFrame||e.oCancelRequestAnimationFrame||e.msCancelRequestAnimationFrame||clearTimeout}()}})(jQuery,this); + +(function ($) { + var options = { }; // no options + + function init(plot) { + function onResize() { + var placeholder = plot.getPlaceholder(); + + // somebody might have hidden us and we can't plot + // when we don't have the dimensions + if (placeholder.width() == 0 || placeholder.height() == 0) + return; + + plot.resize(); + plot.setupGrid(); + plot.draw(); + } + + function bindEvents(plot, eventHolder) { + plot.getPlaceholder().resize(onResize); + } + + function shutdown(plot, eventHolder) { + plot.getPlaceholder().unbind("resize", onResize); + } + + plot.hooks.bindEvents.push(bindEvents); + plot.hooks.shutdown.push(shutdown); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'resize', + version: '1.0' + }); +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.selection.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.selection.js new file mode 100644 index 00000000000000..c8707b30f4e6ff --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.selection.js @@ -0,0 +1,360 @@ +/* Flot plugin for selecting regions of a plot. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin supports these options: + +selection: { + mode: null or "x" or "y" or "xy", + color: color, + shape: "round" or "miter" or "bevel", + minSize: number of pixels +} + +Selection support is enabled by setting the mode to one of "x", "y" or "xy". +In "x" mode, the user will only be able to specify the x range, similarly for +"y" mode. For "xy", the selection becomes a rectangle where both ranges can be +specified. "color" is color of the selection (if you need to change the color +later on, you can get to it with plot.getOptions().selection.color). "shape" +is the shape of the corners of the selection. + +"minSize" is the minimum size a selection can be in pixels. This value can +be customized to determine the smallest size a selection can be and still +have the selection rectangle be displayed. When customizing this value, the +fact that it refers to pixels, not axis units must be taken into account. +Thus, for example, if there is a bar graph in time mode with BarWidth set to 1 +minute, setting "minSize" to 1 will not make the minimum selection size 1 +minute, but rather 1 pixel. Note also that setting "minSize" to 0 will prevent +"plotunselected" events from being fired when the user clicks the mouse without +dragging. + +When selection support is enabled, a "plotselected" event will be emitted on +the DOM element you passed into the plot function. The event handler gets a +parameter with the ranges selected on the axes, like this: + + placeholder.bind( "plotselected", function( event, ranges ) { + alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to) + // similar for yaxis - with multiple axes, the extra ones are in + // x2axis, x3axis, ... + }); + +The "plotselected" event is only fired when the user has finished making the +selection. A "plotselecting" event is fired during the process with the same +parameters as the "plotselected" event, in case you want to know what's +happening while it's happening, + +A "plotunselected" event with no arguments is emitted when the user clicks the +mouse to remove the selection. As stated above, setting "minSize" to 0 will +destroy this behavior. + +The plugin also adds the following methods to the plot object: + +- setSelection( ranges, preventEvent ) + + Set the selection rectangle. The passed in ranges is on the same form as + returned in the "plotselected" event. If the selection mode is "x", you + should put in either an xaxis range, if the mode is "y" you need to put in + an yaxis range and both xaxis and yaxis if the selection mode is "xy", like + this: + + setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } }); + + setSelection will trigger the "plotselected" event when called. If you don't + want that to happen, e.g. if you're inside a "plotselected" handler, pass + true as the second parameter. If you are using multiple axes, you can + specify the ranges on any of those, e.g. as x2axis/x3axis/... instead of + xaxis, the plugin picks the first one it sees. + +- clearSelection( preventEvent ) + + Clear the selection rectangle. Pass in true to avoid getting a + "plotunselected" event. + +- getSelection() + + Returns the current selection in the same format as the "plotselected" + event. If there's currently no selection, the function returns null. + +*/ + +(function ($) { + function init(plot) { + var selection = { + first: { x: -1, y: -1}, second: { x: -1, y: -1}, + show: false, + active: false + }; + + // FIXME: The drag handling implemented here should be + // abstracted out, there's some similar code from a library in + // the navigation plugin, this should be massaged a bit to fit + // the Flot cases here better and reused. Doing this would + // make this plugin much slimmer. + var savedhandlers = {}; + + var mouseUpHandler = null; + + function onMouseMove(e) { + if (selection.active) { + updateSelection(e); + + plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]); + } + } + + function onMouseDown(e) { + if (e.which != 1) // only accept left-click + return; + + // cancel out any text selections + document.body.focus(); + + // prevent text selection and drag in old-school browsers + if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) { + savedhandlers.onselectstart = document.onselectstart; + document.onselectstart = function () { return false; }; + } + if (document.ondrag !== undefined && savedhandlers.ondrag == null) { + savedhandlers.ondrag = document.ondrag; + document.ondrag = function () { return false; }; + } + + setSelectionPos(selection.first, e); + + selection.active = true; + + // this is a bit silly, but we have to use a closure to be + // able to whack the same handler again + mouseUpHandler = function (e) { onMouseUp(e); }; + + $(document).one("mouseup", mouseUpHandler); + } + + function onMouseUp(e) { + mouseUpHandler = null; + + // revert drag stuff for old-school browsers + if (document.onselectstart !== undefined) + document.onselectstart = savedhandlers.onselectstart; + if (document.ondrag !== undefined) + document.ondrag = savedhandlers.ondrag; + + // no more dragging + selection.active = false; + updateSelection(e); + + if (selectionIsSane()) + triggerSelectedEvent(); + else { + // this counts as a clear + plot.getPlaceholder().trigger("plotunselected", [ ]); + plot.getPlaceholder().trigger("plotselecting", [ null ]); + } + + return false; + } + + function getSelection() { + if (!selectionIsSane()) + return null; + + if (!selection.show) return null; + + var r = {}, c1 = selection.first, c2 = selection.second; + $.each(plot.getAxes(), function (name, axis) { + if (axis.used) { + var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]); + r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) }; + } + }); + return r; + } + + function triggerSelectedEvent() { + var r = getSelection(); + + plot.getPlaceholder().trigger("plotselected", [ r ]); + + // backwards-compat stuff, to be removed in future + if (r.xaxis && r.yaxis) + plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]); + } + + function clamp(min, value, max) { + return value < min ? min: (value > max ? max: value); + } + + function setSelectionPos(pos, e) { + var o = plot.getOptions(); + var offset = plot.getPlaceholder().offset(); + var plotOffset = plot.getPlotOffset(); + pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width()); + pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height()); + + if (o.selection.mode == "y") + pos.x = pos == selection.first ? 0 : plot.width(); + + if (o.selection.mode == "x") + pos.y = pos == selection.first ? 0 : plot.height(); + } + + function updateSelection(pos) { + if (pos.pageX == null) + return; + + setSelectionPos(selection.second, pos); + if (selectionIsSane()) { + selection.show = true; + plot.triggerRedrawOverlay(); + } + else + clearSelection(true); + } + + function clearSelection(preventEvent) { + if (selection.show) { + selection.show = false; + plot.triggerRedrawOverlay(); + if (!preventEvent) + plot.getPlaceholder().trigger("plotunselected", [ ]); + } + } + + // function taken from markings support in Flot + function extractRange(ranges, coord) { + var axis, from, to, key, axes = plot.getAxes(); + + for (var k in axes) { + axis = axes[k]; + if (axis.direction == coord) { + key = coord + axis.n + "axis"; + if (!ranges[key] && axis.n == 1) + key = coord + "axis"; // support x1axis as xaxis + if (ranges[key]) { + from = ranges[key].from; + to = ranges[key].to; + break; + } + } + } + + // backwards-compat stuff - to be removed in future + if (!ranges[key]) { + axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0]; + from = ranges[coord + "1"]; + to = ranges[coord + "2"]; + } + + // auto-reverse as an added bonus + if (from != null && to != null && from > to) { + var tmp = from; + from = to; + to = tmp; + } + + return { from: from, to: to, axis: axis }; + } + + function setSelection(ranges, preventEvent) { + var axis, range, o = plot.getOptions(); + + if (o.selection.mode == "y") { + selection.first.x = 0; + selection.second.x = plot.width(); + } + else { + range = extractRange(ranges, "x"); + + selection.first.x = range.axis.p2c(range.from); + selection.second.x = range.axis.p2c(range.to); + } + + if (o.selection.mode == "x") { + selection.first.y = 0; + selection.second.y = plot.height(); + } + else { + range = extractRange(ranges, "y"); + + selection.first.y = range.axis.p2c(range.from); + selection.second.y = range.axis.p2c(range.to); + } + + selection.show = true; + plot.triggerRedrawOverlay(); + if (!preventEvent && selectionIsSane()) + triggerSelectedEvent(); + } + + function selectionIsSane() { + var minSize = plot.getOptions().selection.minSize; + return Math.abs(selection.second.x - selection.first.x) >= minSize && + Math.abs(selection.second.y - selection.first.y) >= minSize; + } + + plot.clearSelection = clearSelection; + plot.setSelection = setSelection; + plot.getSelection = getSelection; + + plot.hooks.bindEvents.push(function(plot, eventHolder) { + var o = plot.getOptions(); + if (o.selection.mode != null) { + eventHolder.mousemove(onMouseMove); + eventHolder.mousedown(onMouseDown); + } + }); + + + plot.hooks.drawOverlay.push(function (plot, ctx) { + // draw selection + if (selection.show && selectionIsSane()) { + var plotOffset = plot.getPlotOffset(); + var o = plot.getOptions(); + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + var c = $.color.parse(o.selection.color); + + ctx.strokeStyle = c.scale('a', 0.8).toString(); + ctx.lineWidth = 1; + ctx.lineJoin = o.selection.shape; + ctx.fillStyle = c.scale('a', 0.4).toString(); + + var x = Math.min(selection.first.x, selection.second.x) + 0.5, + y = Math.min(selection.first.y, selection.second.y) + 0.5, + w = Math.abs(selection.second.x - selection.first.x) - 1, + h = Math.abs(selection.second.y - selection.first.y) - 1; + + ctx.fillRect(x, y, w, h); + ctx.strokeRect(x, y, w, h); + + ctx.restore(); + } + }); + + plot.hooks.shutdown.push(function (plot, eventHolder) { + eventHolder.unbind("mousemove", onMouseMove); + eventHolder.unbind("mousedown", onMouseDown); + + if (mouseUpHandler) + $(document).unbind("mouseup", mouseUpHandler); + }); + + } + + $.plot.plugins.push({ + init: init, + options: { + selection: { + mode: null, // one of null, "x", "y" or "xy" + color: "#e8cfac", + shape: "round", // one of "round", "miter", or "bevel" + minSize: 5 // minimum number of pixels + } + }, + name: 'selection', + version: '1.1' + }); +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.stack.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.stack.js new file mode 100644 index 00000000000000..0d91c0f3c01608 --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.stack.js @@ -0,0 +1,188 @@ +/* Flot plugin for stacking data sets rather than overlaying them. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin assumes the data is sorted on x (or y if stacking horizontally). +For line charts, it is assumed that if a line has an undefined gap (from a +null point), then the line above it should have the same gap - insert zeros +instead of "null" if you want another behaviour. This also holds for the start +and end of the chart. Note that stacking a mix of positive and negative values +in most instances doesn't make sense (so it looks weird). + +Two or more series are stacked when their "stack" attribute is set to the same +key (which can be any number or string or just "true"). To specify the default +stack, you can set the stack option like this: + + series: { + stack: null/false, true, or a key (number/string) + } + +You can also specify it for a single series, like this: + + $.plot( $("#placeholder"), [{ + data: [ ... ], + stack: true + }]) + +The stacking order is determined by the order of the data series in the array +(later series end up on top of the previous). + +Internally, the plugin modifies the datapoints in each series, adding an +offset to the y value. For line series, extra data points are inserted through +interpolation. If there's a second y value, it's also adjusted (e.g for bar +charts or filled areas). + +*/ + +(function ($) { + var options = { + series: { stack: null } // or number/string + }; + + function init(plot) { + function findMatchingSeries(s, allseries) { + var res = null; + for (var i = 0; i < allseries.length; ++i) { + if (s == allseries[i]) + break; + + if (allseries[i].stack == s.stack) + res = allseries[i]; + } + + return res; + } + + function stackData(plot, s, datapoints) { + if (s.stack == null || s.stack === false) + return; + + var other = findMatchingSeries(s, plot.getData()); + if (!other) + return; + + var ps = datapoints.pointsize, + points = datapoints.points, + otherps = other.datapoints.pointsize, + otherpoints = other.datapoints.points, + newpoints = [], + px, py, intery, qx, qy, bottom, + withlines = s.lines.show, + horizontal = s.bars.horizontal, + withbottom = ps > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y), + withsteps = withlines && s.lines.steps, + fromgap = true, + keyOffset = horizontal ? 1 : 0, + accumulateOffset = horizontal ? 0 : 1, + i = 0, j = 0, l, m; + + while (true) { + if (i >= points.length) + break; + + l = newpoints.length; + + if (points[i] == null) { + // copy gaps + for (m = 0; m < ps; ++m) + newpoints.push(points[i + m]); + i += ps; + } + else if (j >= otherpoints.length) { + // for lines, we can't use the rest of the points + if (!withlines) { + for (m = 0; m < ps; ++m) + newpoints.push(points[i + m]); + } + i += ps; + } + else if (otherpoints[j] == null) { + // oops, got a gap + for (m = 0; m < ps; ++m) + newpoints.push(null); + fromgap = true; + j += otherps; + } + else { + // cases where we actually got two points + px = points[i + keyOffset]; + py = points[i + accumulateOffset]; + qx = otherpoints[j + keyOffset]; + qy = otherpoints[j + accumulateOffset]; + bottom = 0; + + if (px == qx) { + for (m = 0; m < ps; ++m) + newpoints.push(points[i + m]); + + newpoints[l + accumulateOffset] += qy; + bottom = qy; + + i += ps; + j += otherps; + } + else if (px > qx) { + // we got past point below, might need to + // insert interpolated extra point + if (withlines && i > 0 && points[i - ps] != null) { + intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px); + newpoints.push(qx); + newpoints.push(intery + qy); + for (m = 2; m < ps; ++m) + newpoints.push(points[i + m]); + bottom = qy; + } + + j += otherps; + } + else { // px < qx + if (fromgap && withlines) { + // if we come from a gap, we just skip this point + i += ps; + continue; + } + + for (m = 0; m < ps; ++m) + newpoints.push(points[i + m]); + + // we might be able to interpolate a point below, + // this can give us a better y + if (withlines && j > 0 && otherpoints[j - otherps] != null) + bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx); + + newpoints[l + accumulateOffset] += bottom; + + i += ps; + } + + fromgap = false; + + if (l != newpoints.length && withbottom) + newpoints[l + 2] += bottom; + } + + // maintain the line steps invariant + if (withsteps && l != newpoints.length && l > 0 + && newpoints[l] != null + && newpoints[l] != newpoints[l - ps] + && newpoints[l + 1] != newpoints[l - ps + 1]) { + for (m = 0; m < ps; ++m) + newpoints[l + ps + m] = newpoints[l + m]; + newpoints[l + 1] = newpoints[l - ps + 1]; + } + } + + datapoints.points = newpoints; + } + + plot.hooks.processDatapoints.push(stackData); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'stack', + version: '1.2' + }); +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.symbol.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.symbol.js new file mode 100644 index 00000000000000..79f634971b6fa6 --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.symbol.js @@ -0,0 +1,71 @@ +/* Flot plugin that adds some extra symbols for plotting points. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The symbols are accessed as strings through the standard symbol options: + + series: { + points: { + symbol: "square" // or "diamond", "triangle", "cross" + } + } + +*/ + +(function ($) { + function processRawData(plot, series, datapoints) { + // we normalize the area of each symbol so it is approximately the + // same as a circle of the given radius + + var handlers = { + square: function (ctx, x, y, radius, shadow) { + // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.rect(x - size, y - size, size + size, size + size); + }, + diamond: function (ctx, x, y, radius, shadow) { + // pi * r^2 = 2s^2 => s = r * sqrt(pi/2) + var size = radius * Math.sqrt(Math.PI / 2); + ctx.moveTo(x - size, y); + ctx.lineTo(x, y - size); + ctx.lineTo(x + size, y); + ctx.lineTo(x, y + size); + ctx.lineTo(x - size, y); + }, + triangle: function (ctx, x, y, radius, shadow) { + // pi * r^2 = 1/2 * s^2 * sin (pi / 3) => s = r * sqrt(2 * pi / sin(pi / 3)) + var size = radius * Math.sqrt(2 * Math.PI / Math.sin(Math.PI / 3)); + var height = size * Math.sin(Math.PI / 3); + ctx.moveTo(x - size/2, y + height/2); + ctx.lineTo(x + size/2, y + height/2); + if (!shadow) { + ctx.lineTo(x, y - height/2); + ctx.lineTo(x - size/2, y + height/2); + } + }, + cross: function (ctx, x, y, radius, shadow) { + // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 + var size = radius * Math.sqrt(Math.PI) / 2; + ctx.moveTo(x - size, y - size); + ctx.lineTo(x + size, y + size); + ctx.moveTo(x - size, y + size); + ctx.lineTo(x + size, y - size); + } + }; + + var s = series.points.symbol; + if (handlers[s]) + series.points.symbol = handlers[s]; + } + + function init(plot) { + plot.hooks.processDatapoints.push(processRawData); + } + + $.plot.plugins.push({ + init: init, + name: 'symbols', + version: '1.0' + }); +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.threshold.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.threshold.js new file mode 100644 index 00000000000000..8c99c401d87e5a --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.threshold.js @@ -0,0 +1,142 @@ +/* Flot plugin for thresholding data. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +The plugin supports these options: + + series: { + threshold: { + below: number + color: colorspec + } + } + +It can also be applied to a single series, like this: + + $.plot( $("#placeholder"), [{ + data: [ ... ], + threshold: { ... } + }]) + +An array can be passed for multiple thresholding, like this: + + threshold: [{ + below: number1 + color: color1 + },{ + below: number2 + color: color2 + }] + +These multiple threshold objects can be passed in any order since they are +sorted by the processing function. + +The data points below "below" are drawn with the specified color. This makes +it easy to mark points below 0, e.g. for budget data. + +Internally, the plugin works by splitting the data into two series, above and +below the threshold. The extra series below the threshold will have its label +cleared and the special "originSeries" attribute set to the original series. +You may need to check for this in hover events. + +*/ + +(function ($) { + var options = { + series: { threshold: null } // or { below: number, color: color spec} + }; + + function init(plot) { + function thresholdData(plot, s, datapoints, below, color) { + var ps = datapoints.pointsize, i, x, y, p, prevp, + thresholded = $.extend({}, s); // note: shallow copy + + thresholded.datapoints = { points: [], pointsize: ps, format: datapoints.format }; + thresholded.label = null; + thresholded.color = color; + thresholded.threshold = null; + thresholded.originSeries = s; + thresholded.data = []; + + var origpoints = datapoints.points, + addCrossingPoints = s.lines.show; + + var threspoints = []; + var newpoints = []; + var m; + + for (i = 0; i < origpoints.length; i += ps) { + x = origpoints[i]; + y = origpoints[i + 1]; + + prevp = p; + if (y < below) + p = threspoints; + else + p = newpoints; + + if (addCrossingPoints && prevp != p && x != null + && i > 0 && origpoints[i - ps] != null) { + var interx = x + (below - y) * (x - origpoints[i - ps]) / (y - origpoints[i - ps + 1]); + prevp.push(interx); + prevp.push(below); + for (m = 2; m < ps; ++m) + prevp.push(origpoints[i + m]); + + p.push(null); // start new segment + p.push(null); + for (m = 2; m < ps; ++m) + p.push(origpoints[i + m]); + p.push(interx); + p.push(below); + for (m = 2; m < ps; ++m) + p.push(origpoints[i + m]); + } + + p.push(x); + p.push(y); + for (m = 2; m < ps; ++m) + p.push(origpoints[i + m]); + } + + datapoints.points = newpoints; + thresholded.datapoints.points = threspoints; + + if (thresholded.datapoints.points.length > 0) { + var origIndex = $.inArray(s, plot.getData()); + // Insert newly-generated series right after original one (to prevent it from becoming top-most) + plot.getData().splice(origIndex + 1, 0, thresholded); + } + + // FIXME: there are probably some edge cases left in bars + } + + function processThresholds(plot, s, datapoints) { + if (!s.threshold) + return; + + if (s.threshold instanceof Array) { + s.threshold.sort(function(a, b) { + return a.below - b.below; + }); + + $(s.threshold).each(function(i, th) { + thresholdData(plot, s, datapoints, th.below, th.color); + }); + } + else { + thresholdData(plot, s, datapoints, s.threshold.below, s.threshold.color); + } + } + + plot.hooks.processDatapoints.push(processThresholds); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'threshold', + version: '1.2' + }); +})(jQuery); diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.time.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.time.js new file mode 100644 index 00000000000000..991e87d364e8a5 --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/flot-charts/jquery.flot.time.js @@ -0,0 +1,473 @@ +/* Pretty handling of time axes. + +Copyright (c) 2007-2014 IOLA and Ole Laursen. +Licensed under the MIT license. + +Set axis.mode to "time" to enable. See the section "Time series data" in +API.txt for details. + +*/ + +import { i18n } from '@kbn/i18n'; + +(function($) { + + var options = { + xaxis: { + timezone: null, // "browser" for local to the client or timezone for timezone-js + timeformat: null, // format string to use + twelveHourClock: false, // 12 or 24 time in time mode + monthNames: null // list of names of months + } + }; + + // round to nearby lower multiple of base + + function floorInBase(n, base) { + return base * Math.floor(n / base); + } + + // Returns a string with the date d formatted according to fmt. + // A subset of the Open Group's strftime format is supported. + + function formatDate(d, fmt, monthNames, dayNames) { + + if (typeof d.strftime == "function") { + return d.strftime(fmt); + } + + var leftPad = function(n, pad) { + n = "" + n; + pad = "" + (pad == null ? "0" : pad); + return n.length == 1 ? pad + n : n; + }; + + var r = []; + var escape = false; + var hours = d.getHours(); + var isAM = hours < 12; + + if (monthNames == null) { + monthNames = [ + i18n.translate('xpack.monitoring.janLabel', { + defaultMessage: 'Jan', + }), i18n.translate('xpack.monitoring.febLabel', { + defaultMessage: 'Feb', + }), i18n.translate('xpack.monitoring.marLabel', { + defaultMessage: 'Mar', + }), i18n.translate('xpack.monitoring.aprLabel', { + defaultMessage: 'Apr', + }), i18n.translate('xpack.monitoring.mayLabel', { + defaultMessage: 'May', + }), i18n.translate('xpack.monitoring.junLabel', { + defaultMessage: 'Jun', + }), i18n.translate('xpack.monitoring.julLabel', { + defaultMessage: 'Jul', + }), i18n.translate('xpack.monitoring.augLabel', { + defaultMessage: 'Aug', + }), i18n.translate('xpack.monitoring.sepLabel', { + defaultMessage: 'Sep', + }), i18n.translate('xpack.monitoring.octLabel', { + defaultMessage: 'Oct', + }), i18n.translate('xpack.monitoring.novLabel', { + defaultMessage: 'Nov', + }), i18n.translate('xpack.monitoring.decLabel', { + defaultMessage: 'Dec', + })]; + } + + if (dayNames == null) { + dayNames = [i18n.translate('xpack.monitoring.sunLabel', { + defaultMessage: 'Sun', + }), i18n.translate('xpack.monitoring.monLabel', { + defaultMessage: 'Mon', + }), i18n.translate('xpack.monitoring.tueLabel', { + defaultMessage: 'Tue', + }), i18n.translate('xpack.monitoring.wedLabel', { + defaultMessage: 'Wed', + }), i18n.translate('xpack.monitoring.thuLabel', { + defaultMessage: 'Thu', + }), i18n.translate('xpack.monitoring.friLabel', { + defaultMessage: 'Fri', + }), i18n.translate('xpack.monitoring.satLabel', { + defaultMessage: 'Sat', + })]; + } + + var hours12; + + if (hours > 12) { + hours12 = hours - 12; + } else if (hours == 0) { + hours12 = 12; + } else { + hours12 = hours; + } + + for (var i = 0; i < fmt.length; ++i) { + + var c = fmt.charAt(i); + + if (escape) { + switch (c) { + case 'a': c = "" + dayNames[d.getDay()]; break; + case 'b': c = "" + monthNames[d.getMonth()]; break; + case 'd': c = leftPad(d.getDate()); break; + case 'e': c = leftPad(d.getDate(), " "); break; + case 'h': // For back-compat with 0.7; remove in 1.0 + case 'H': c = leftPad(hours); break; + case 'I': c = leftPad(hours12); break; + case 'l': c = leftPad(hours12, " "); break; + case 'm': c = leftPad(d.getMonth() + 1); break; + case 'M': c = leftPad(d.getMinutes()); break; + // quarters not in Open Group's strftime specification + case 'q': + c = "" + (Math.floor(d.getMonth() / 3) + 1); break; + case 'S': c = leftPad(d.getSeconds()); break; + case 'y': c = leftPad(d.getFullYear() % 100); break; + case 'Y': c = "" + d.getFullYear(); break; + case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break; + case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break; + case 'w': c = "" + d.getDay(); break; + } + r.push(c); + escape = false; + } else { + if (c == "%") { + escape = true; + } else { + r.push(c); + } + } + } + + return r.join(""); + } + + // To have a consistent view of time-based data independent of which time + // zone the client happens to be in we need a date-like object independent + // of time zones. This is done through a wrapper that only calls the UTC + // versions of the accessor methods. + + function makeUtcWrapper(d) { + + function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) { + sourceObj[sourceMethod] = function() { + return targetObj[targetMethod].apply(targetObj, arguments); + }; + }; + + var utc = { + date: d + }; + + // support strftime, if found + + if (d.strftime != undefined) { + addProxyMethod(utc, "strftime", d, "strftime"); + } + + addProxyMethod(utc, "getTime", d, "getTime"); + addProxyMethod(utc, "setTime", d, "setTime"); + + var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"]; + + for (var p = 0; p < props.length; p++) { + addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]); + addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]); + } + + return utc; + }; + + // select time zone strategy. This returns a date-like object tied to the + // desired timezone + + function dateGenerator(ts, opts) { + if (opts.timezone == "browser") { + return new Date(ts); + } else if (!opts.timezone || opts.timezone == "utc") { + return makeUtcWrapper(new Date(ts)); + } else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") { + var d = new timezoneJS.Date(); + // timezone-js is fickle, so be sure to set the time zone before + // setting the time. + d.setTimezone(opts.timezone); + d.setTime(ts); + return d; + } else { + return makeUtcWrapper(new Date(ts)); + } + } + + // map of app. size of time units in milliseconds + + var timeUnitSize = { + "second": 1000, + "minute": 60 * 1000, + "hour": 60 * 60 * 1000, + "day": 24 * 60 * 60 * 1000, + "month": 30 * 24 * 60 * 60 * 1000, + "quarter": 3 * 30 * 24 * 60 * 60 * 1000, + "year": 365.2425 * 24 * 60 * 60 * 1000 + }; + + // the allowed tick sizes, after 1 year we use + // an integer algorithm + + var baseSpec = [ + [1, "second"], [2, "second"], [5, "second"], [10, "second"], + [30, "second"], + [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"], + [30, "minute"], + [1, "hour"], [2, "hour"], [4, "hour"], + [8, "hour"], [12, "hour"], + [1, "day"], [2, "day"], [3, "day"], + [0.25, "month"], [0.5, "month"], [1, "month"], + [2, "month"] + ]; + + // we don't know which variant(s) we'll need yet, but generating both is + // cheap + + var specMonths = baseSpec.concat([[3, "month"], [6, "month"], + [1, "year"]]); + var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"], + [1, "year"]]); + + function init(plot) { + plot.hooks.processOptions.push(function (plot, options) { + $.each(plot.getAxes(), function(axisName, axis) { + + var opts = axis.options; + + if (opts.mode == "time") { + axis.tickGenerator = function(axis) { + + var ticks = []; + var d = dateGenerator(axis.min, opts); + var minSize = 0; + + // make quarter use a possibility if quarters are + // mentioned in either of these options + + var spec = (opts.tickSize && opts.tickSize[1] === + "quarter") || + (opts.minTickSize && opts.minTickSize[1] === + "quarter") ? specQuarters : specMonths; + + if (opts.minTickSize != null) { + if (typeof opts.tickSize == "number") { + minSize = opts.tickSize; + } else { + minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]]; + } + } + + for (var i = 0; i < spec.length - 1; ++i) { + if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]] + + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2 + && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) { + break; + } + } + + var size = spec[i][0]; + var unit = spec[i][1]; + + // special-case the possibility of several years + + if (unit == "year") { + + // if given a minTickSize in years, just use it, + // ensuring that it's an integer + + if (opts.minTickSize != null && opts.minTickSize[1] == "year") { + size = Math.floor(opts.minTickSize[0]); + } else { + + var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10)); + var norm = (axis.delta / timeUnitSize.year) / magn; + + if (norm < 1.5) { + size = 1; + } else if (norm < 3) { + size = 2; + } else if (norm < 7.5) { + size = 5; + } else { + size = 10; + } + + size *= magn; + } + + // minimum size for years is 1 + + if (size < 1) { + size = 1; + } + } + + axis.tickSize = opts.tickSize || [size, unit]; + var tickSize = axis.tickSize[0]; + unit = axis.tickSize[1]; + + var step = tickSize * timeUnitSize[unit]; + + if (unit == "second") { + d.setSeconds(floorInBase(d.getSeconds(), tickSize)); + } else if (unit == "minute") { + d.setMinutes(floorInBase(d.getMinutes(), tickSize)); + } else if (unit == "hour") { + d.setHours(floorInBase(d.getHours(), tickSize)); + } else if (unit == "month") { + d.setMonth(floorInBase(d.getMonth(), tickSize)); + } else if (unit == "quarter") { + d.setMonth(3 * floorInBase(d.getMonth() / 3, + tickSize)); + } else if (unit == "year") { + d.setFullYear(floorInBase(d.getFullYear(), tickSize)); + } + + // reset smaller components + + d.setMilliseconds(0); + + if (step >= timeUnitSize.minute) { + d.setSeconds(0); + } + if (step >= timeUnitSize.hour) { + d.setMinutes(0); + } + if (step >= timeUnitSize.day) { + d.setHours(0); + } + if (step >= timeUnitSize.day * 4) { + d.setDate(1); + } + if (step >= timeUnitSize.month * 2) { + d.setMonth(floorInBase(d.getMonth(), 3)); + } + if (step >= timeUnitSize.quarter * 2) { + d.setMonth(floorInBase(d.getMonth(), 6)); + } + if (step >= timeUnitSize.year) { + d.setMonth(0); + } + + var carry = 0; + var v = Number.NaN; + var prev; + + do { + + prev = v; + v = d.getTime(); + ticks.push(v); + + if (unit == "month" || unit == "quarter") { + if (tickSize < 1) { + + // a bit complicated - we'll divide the + // month/quarter up but we need to take + // care of fractions so we don't end up in + // the middle of a day + + d.setDate(1); + var start = d.getTime(); + d.setMonth(d.getMonth() + + (unit == "quarter" ? 3 : 1)); + var end = d.getTime(); + d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize); + carry = d.getHours(); + d.setHours(0); + } else { + d.setMonth(d.getMonth() + + tickSize * (unit == "quarter" ? 3 : 1)); + } + } else if (unit == "year") { + d.setFullYear(d.getFullYear() + tickSize); + } else { + d.setTime(v + step); + } + } while (v < axis.max && v != prev); + + return ticks; + }; + + axis.tickFormatter = function (v, axis) { + + var d = dateGenerator(v, axis.options); + + // first check global format + + if (opts.timeformat != null) { + return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames); + } + + // possibly use quarters if quarters are mentioned in + // any of these places + + var useQuarters = (axis.options.tickSize && + axis.options.tickSize[1] == "quarter") || + (axis.options.minTickSize && + axis.options.minTickSize[1] == "quarter"); + + var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]]; + var span = axis.max - axis.min; + var suffix = (opts.twelveHourClock) ? " %p" : ""; + var hourCode = (opts.twelveHourClock) ? "%I" : "%H"; + var fmt; + + if (t < timeUnitSize.minute) { + fmt = hourCode + ":%M:%S" + suffix; + } else if (t < timeUnitSize.day) { + if (span < 2 * timeUnitSize.day) { + fmt = hourCode + ":%M" + suffix; + } else { + fmt = "%b %d " + hourCode + ":%M" + suffix; + } + } else if (t < timeUnitSize.month) { + fmt = "%b %d"; + } else if ((useQuarters && t < timeUnitSize.quarter) || + (!useQuarters && t < timeUnitSize.year)) { + if (span < timeUnitSize.year) { + fmt = "%b"; + } else { + fmt = "%b %Y"; + } + } else if (useQuarters && t < timeUnitSize.year) { + if (span < timeUnitSize.year) { + fmt = "Q%q"; + } else { + fmt = "Q%q %Y"; + } + } else { + fmt = "%Y"; + } + + var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames); + + return rt; + }; + } + }); + }); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'time', + version: '1.0' + }); + + // Time-axis support used to be in Flot core, which exposed the + // formatDate function on the plot object. Various plugins depend + // on the function, so we need to re-expose it here. + + $.plot.formatDate = formatDate; + $.plot.dateGenerator = dateGenerator; + +})(jQuery); diff --git a/x-pack/legacy/plugins/monitoring/common/index.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/index.js similarity index 76% rename from x-pack/legacy/plugins/monitoring/common/index.js rename to x-pack/plugins/monitoring/public/lib/jquery_flot/index.js index 183396f8f0d724..abf060aca8c088 100644 --- a/x-pack/legacy/plugins/monitoring/common/index.js +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { formatTimestampToDuration } from './format_timestamp_to_duration'; +export { default } from './jquery_flot'; diff --git a/x-pack/plugins/monitoring/public/lib/jquery_flot/jquery_flot.js b/x-pack/plugins/monitoring/public/lib/jquery_flot/jquery_flot.js new file mode 100644 index 00000000000000..28a4d5f56df158 --- /dev/null +++ b/x-pack/plugins/monitoring/public/lib/jquery_flot/jquery_flot.js @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import $ from 'jquery'; +if (window) { + window.jQuery = $; +} +import './flot-charts/jquery.flot'; + +// load flot plugins +// avoid the `canvas` plugin, it causes blurry fonts +import './flot-charts/jquery.flot.time'; +import './flot-charts/jquery.flot.crosshair'; +import './flot-charts/jquery.flot.selection'; + +export default $; diff --git a/x-pack/legacy/plugins/monitoring/public/lib/logstash/__tests__/pipelines.js b/x-pack/plugins/monitoring/public/lib/logstash/__tests__/pipelines.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/lib/logstash/__tests__/pipelines.js rename to x-pack/plugins/monitoring/public/lib/logstash/__tests__/pipelines.js diff --git a/x-pack/legacy/plugins/monitoring/public/lib/logstash/pipelines.js b/x-pack/plugins/monitoring/public/lib/logstash/pipelines.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/lib/logstash/pipelines.js rename to x-pack/plugins/monitoring/public/lib/logstash/pipelines.js diff --git a/x-pack/legacy/plugins/monitoring/public/lib/route_init.js b/x-pack/plugins/monitoring/public/lib/route_init.js similarity index 95% rename from x-pack/legacy/plugins/monitoring/public/lib/route_init.js rename to x-pack/plugins/monitoring/public/lib/route_init.js index 97a55303dae67d..2c928334ef63e7 100644 --- a/x-pack/legacy/plugins/monitoring/public/lib/route_init.js +++ b/x-pack/plugins/monitoring/public/lib/route_init.js @@ -5,7 +5,7 @@ */ import _ from 'lodash'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; +import { ajaxErrorHandlersProvider } from './ajax_error_handler'; import { isInSetupMode } from './setup_mode'; import { getClusterFromClusters } from './get_cluster_from_clusters'; diff --git a/x-pack/legacy/plugins/monitoring/public/lib/setup_mode.test.js b/x-pack/plugins/monitoring/public/lib/setup_mode.test.js similarity index 72% rename from x-pack/legacy/plugins/monitoring/public/lib/setup_mode.test.js rename to x-pack/plugins/monitoring/public/lib/setup_mode.test.js index 765909f0aa251b..c54c29df09685e 100644 --- a/x-pack/legacy/plugins/monitoring/public/lib/setup_mode.test.js +++ b/x-pack/plugins/monitoring/public/lib/setup_mode.test.js @@ -4,12 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - coreMock, - overlayServiceMock, - notificationServiceMock, -} from '../../../../../../src/core/public/mocks'; - let toggleSetupMode; let initSetupModeState; let getSetupModeState; @@ -26,6 +20,14 @@ jest.mock('react-dom', () => ({ render: jest.fn(), })); +jest.mock('../legacy_shims', () => { + return { + Legacy: { + shims: { getAngularInjector: () => ({ get: () => ({ get: () => 'utc' }) }) }, + }, + }; +}); + let data = {}; const injectorModulesMock = { @@ -61,70 +63,10 @@ function waitForSetupModeData(action) { process.nextTick(action); } -function mockFilterManager() { - let subscriber; - let filters = []; - return { - getUpdates$: () => ({ - subscribe: ({ next }) => { - subscriber = next; - return jest.fn(); - }, - }), - setFilters: newFilters => { - filters = newFilters; - subscriber(); - }, - getFilters: () => filters, - removeAll: () => { - filters = []; - subscriber(); - }, - }; -} - -const pluginData = { - query: { - filterManager: mockFilterManager(), - timefilter: { - timefilter: { - getTime: jest.fn(() => ({ from: 'now-1h', to: 'now' })), - setTime: jest.fn(), - }, - }, - }, -}; - -function setModulesAndMocks(isOnCloud = false) { +function setModulesAndMocks() { jest.clearAllMocks().resetModules(); injectorModulesMock.globalState.inSetupMode = false; - jest.doMock('ui/new_platform', () => ({ - npSetup: { - plugins: { - cloud: isOnCloud ? { cloudId: 'test', isCloudEnabled: true } : {}, - uiActions: { - registerAction: jest.fn(), - attachAction: jest.fn(), - }, - }, - core: { - ...coreMock.createSetup(), - notifications: notificationServiceMock.createStartContract(), - }, - }, - npStart: { - plugins: { - data: pluginData, - navigation: { ui: {} }, - }, - core: { - ...coreMock.createStart(), - overlays: overlayServiceMock.createStartContract(), - }, - }, - })); - const setupMode = require('./setup_mode'); toggleSetupMode = setupMode.toggleSetupMode; initSetupModeState = setupMode.initSetupModeState; @@ -179,37 +121,15 @@ describe('setup_mode', () => { data = {}; }); - it('should not fetch data if on cloud', async done => { - const addDanger = jest.fn(); - data = { - _meta: { - hasPermissions: true, - }, - }; - jest.doMock('ui/notify', () => ({ - toastNotifications: { - addDanger, - }, - })); - setModulesAndMocks(true); - initSetupModeState(angularStateMock.scope, angularStateMock.injector); - await toggleSetupMode(true); - waitForSetupModeData(() => { - const state = getSetupModeState(); - expect(state.enabled).toBe(false); - expect(addDanger).toHaveBeenCalledWith({ - title: 'Setup mode is not available', - text: 'This feature is not available on cloud.', - }); - done(); - }); - }); - it('should not fetch data if the user does not have sufficient permissions', async done => { const addDanger = jest.fn(); - jest.doMock('ui/notify', () => ({ - toastNotifications: { - addDanger, + jest.doMock('../legacy_shims', () => ({ + Legacy: { + shims: { + toastNotifications: { + addDanger, + }, + }, }, })); data = { diff --git a/x-pack/legacy/plugins/monitoring/public/lib/setup_mode.tsx b/x-pack/plugins/monitoring/public/lib/setup_mode.tsx similarity index 82% rename from x-pack/legacy/plugins/monitoring/public/lib/setup_mode.tsx rename to x-pack/plugins/monitoring/public/lib/setup_mode.tsx index 7b081b79d6acd5..5afb382b7cda84 100644 --- a/x-pack/legacy/plugins/monitoring/public/lib/setup_mode.tsx +++ b/x-pack/plugins/monitoring/public/lib/setup_mode.tsx @@ -7,19 +7,11 @@ import React from 'react'; import { render } from 'react-dom'; import { get, contains } from 'lodash'; -import { toastNotifications } from 'ui/notify'; import { i18n } from '@kbn/i18n'; -import { npSetup } from 'ui/new_platform'; -import { PluginsSetup } from 'ui/new_platform/new_platform'; -import chrome from '../np_imports/ui/chrome'; -import { CloudSetup } from '../../../../../plugins/cloud/public'; +import { Legacy } from '../legacy_shims'; import { ajaxErrorHandlersProvider } from './ajax_error_handler'; import { SetupModeEnterButton } from '../components/setup_mode/enter_button'; -interface PluginsSetupWithCloud extends PluginsSetup { - cloud: CloudSetup; -} - function isOnPage(hash: string) { return contains(window.location.hash, hash); } @@ -46,12 +38,12 @@ const checkAngularState = () => { interface ISetupModeState { enabled: boolean; data: any; - callbacks: Function[]; + callback?: (() => void) | null; } const setupModeState: ISetupModeState = { enabled: false, data: null, - callbacks: [], + callback: null, }; export const getSetupModeState = () => setupModeState; @@ -93,18 +85,13 @@ export const fetchCollectionData = async (uuid?: string, fetchWithoutClusterUuid } }; -const notifySetupModeDataChange = (oldData?: any) => { - setupModeState.callbacks.forEach((cb: Function) => cb(oldData)); -}; +const notifySetupModeDataChange = () => setupModeState.callback && setupModeState.callback(); export const updateSetupModeData = async (uuid?: string, fetchWithoutClusterUuid = false) => { - const oldData = setupModeState.data; const data = await fetchCollectionData(uuid, fetchWithoutClusterUuid); setupModeState.data = data; - const { cloud } = npSetup.plugins as PluginsSetupWithCloud; - const isCloudEnabled = !!(cloud && cloud.isCloudEnabled); const hasPermissions = get(data, '_meta.hasPermissions', false); - if (isCloudEnabled || !hasPermissions) { + if (Legacy.shims.isCloud || !hasPermissions) { let text: string = ''; if (!hasPermissions) { text = i18n.translate('xpack.monitoring.setupMode.notAvailablePermissions', { @@ -117,7 +104,7 @@ export const updateSetupModeData = async (uuid?: string, fetchWithoutClusterUuid } angularState.scope.$evalAsync(() => { - toastNotifications.addDanger({ + Legacy.shims.toastNotifications.addDanger({ title: i18n.translate('xpack.monitoring.setupMode.notAvailableTitle', { defaultMessage: 'Setup mode is not available', }), @@ -126,7 +113,7 @@ export const updateSetupModeData = async (uuid?: string, fetchWithoutClusterUuid }); return toggleSetupMode(false); // eslint-disable-line no-use-before-define } - notifySetupModeDataChange(oldData); + notifySetupModeDataChange(); const globalState = angularState.injector.get('globalState'); const clusterUuid = globalState.cluster_uuid; @@ -182,9 +169,7 @@ export const setSetupModeMenuItem = () => { } const globalState = angularState.injector.get('globalState'); - const { cloud } = npSetup.plugins as PluginsSetupWithCloud; - const isCloudEnabled = !!(cloud && cloud.isCloudEnabled); - const enabled = !globalState.inSetupMode && !isCloudEnabled; + const enabled = !globalState.inSetupMode && !Legacy.shims.isCloud; render( , @@ -192,13 +177,13 @@ export const setSetupModeMenuItem = () => { ); }; -export const addSetupModeCallback = (callback: Function) => setupModeState.callbacks.push(callback); +export const addSetupModeCallback = (callback: () => void) => (setupModeState.callback = callback); -export const initSetupModeState = async ($scope: any, $injector: any, callback?: Function) => { +export const initSetupModeState = async ($scope: any, $injector: any, callback?: () => void) => { angularState.scope = $scope; angularState.injector = $injector; if (callback) { - setupModeState.callbacks.push(callback); + setupModeState.callback = callback; } const globalState = $injector.get('globalState'); @@ -212,7 +197,7 @@ export const isInSetupMode = () => { return true; } - const $injector = angularState.injector || chrome.dangerouslyGetActiveInjector(); + const $injector = angularState.injector || Legacy.shims.getAngularInjector(); const globalState = $injector.get('globalState'); return globalState.inSetupMode; }; diff --git a/x-pack/plugins/monitoring/public/plugin.ts b/x-pack/plugins/monitoring/public/plugin.ts new file mode 100644 index 00000000000000..63f0c46c140969 --- /dev/null +++ b/x-pack/plugins/monitoring/public/plugin.ts @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { + App, + AppMountParameters, + CoreSetup, + CoreStart, + Plugin, + PluginInitializerContext, +} from 'kibana/public'; +import { + FeatureCatalogueCategory, + HomePublicPluginSetup, +} from '../../../../src/plugins/home/public'; +import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; +import { MonitoringPluginDependencies, MonitoringConfig } from './types'; +import { + MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS, + KIBANA_ALERTING_ENABLED, +} from '../common/constants'; + +export class MonitoringPlugin + implements Plugin { + constructor(private initializerContext: PluginInitializerContext) {} + + public setup( + core: CoreSetup, + plugins: object & { home?: HomePublicPluginSetup; cloud?: { isCloudEnabled: boolean } } + ) { + const { home } = plugins; + const id = 'monitoring'; + const icon = 'monitoringApp'; + const title = i18n.translate('xpack.monitoring.stackMonitoringTitle', { + defaultMessage: 'Stack Monitoring', + }); + const monitoring = this.initializerContext.config.get(); + + if (!monitoring.ui.enabled || !monitoring.enabled) { + return false; + } + + if (home) { + home.featureCatalogue.register({ + id, + title, + icon, + path: '/app/monitoring', + showOnHomePage: true, + category: FeatureCatalogueCategory.ADMIN, + description: i18n.translate('xpack.monitoring.monitoringDescription', { + defaultMessage: 'Track the real-time health and performance of your Elastic Stack.', + }), + }); + } + + const app: App = { + id, + title, + order: 9002, + euiIconType: icon, + category: DEFAULT_APP_CATEGORIES.management, + mount: async (params: AppMountParameters) => { + const [coreStart, pluginsStart] = await core.getStartServices(); + const { AngularApp } = await import('./angular'); + const deps: MonitoringPluginDependencies = { + navigation: pluginsStart.navigation, + element: params.element, + core: coreStart, + data: pluginsStart.data, + isCloud: Boolean(plugins.cloud?.isCloudEnabled), + pluginInitializerContext: this.initializerContext, + externalConfig: this.getExternalConfig(), + }; + + this.setInitialTimefilter(deps); + this.overrideAlertingEmailDefaults(deps); + + const monitoringApp = new AngularApp(deps); + const removeHistoryListener = params.history.listen(location => { + if (location.pathname === '' && location.hash === '') { + monitoringApp.applyScope(); + } + }); + + return () => { + removeHistoryListener(); + monitoringApp.destroy(); + }; + }, + }; + + core.application.register(app); + return true; + } + + public start(core: CoreStart, plugins: any) {} + + public stop() {} + + private setInitialTimefilter({ core: coreContext, data }: MonitoringPluginDependencies) { + const { timefilter } = data.query.timefilter; + const { uiSettings } = coreContext; + const refreshInterval = { value: 10000, pause: false }; + const time = { from: 'now-1h', to: 'now' }; + timefilter.setRefreshInterval(refreshInterval); + timefilter.setTime(time); + uiSettings.overrideLocalDefault( + 'timepicker:refreshIntervalDefaults', + JSON.stringify(refreshInterval) + ); + uiSettings.overrideLocalDefault('timepicker:timeDefaults', JSON.stringify(time)); + } + + private overrideAlertingEmailDefaults({ core: coreContext }: MonitoringPluginDependencies) { + const { uiSettings } = coreContext; + if (KIBANA_ALERTING_ENABLED && !uiSettings.get(MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS)) { + uiSettings.overrideLocalDefault( + MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS, + JSON.stringify({ + name: i18n.translate('xpack.monitoring.alertingEmailAddress.name', { + defaultMessage: 'Alerting email address', + }), + value: '', + description: i18n.translate('xpack.monitoring.alertingEmailAddress.description', { + defaultMessage: `The default email address to receive alerts from Stack Monitoring`, + }), + category: ['monitoring'], + }) + ); + } + } + + private getExternalConfig() { + const monitoring = this.initializerContext.config.get(); + return [ + ['minIntervalSeconds', monitoring.ui.min_interval_seconds], + ['showLicenseExpiration', monitoring.ui.show_license_expiration], + ['showCgroupMetricsElasticsearch', monitoring.ui.container.elasticsearch.enabled], + ['showCgroupMetricsLogstash', monitoring.ui.container.logstash.enabled], + ]; + } +} diff --git a/x-pack/legacy/plugins/monitoring/public/services/__tests__/breadcrumbs.js b/x-pack/plugins/monitoring/public/services/__tests__/breadcrumbs.js similarity index 96% rename from x-pack/legacy/plugins/monitoring/public/services/__tests__/breadcrumbs.js rename to x-pack/plugins/monitoring/public/services/__tests__/breadcrumbs.js index e5b2e01373340f..d4493d4d39e585 100644 --- a/x-pack/legacy/plugins/monitoring/public/services/__tests__/breadcrumbs.js +++ b/x-pack/plugins/monitoring/public/services/__tests__/breadcrumbs.js @@ -5,8 +5,8 @@ */ import expect from '@kbn/expect'; -import { breadcrumbsProvider } from '../breadcrumbs_provider'; -import { MonitoringMainController } from 'plugins/monitoring/directives/main'; +import { breadcrumbsProvider } from '../breadcrumbs'; +import { MonitoringMainController } from '../../directives/main'; describe('Monitoring Breadcrumbs Service', () => { it('in Cluster Alerts', () => { diff --git a/x-pack/legacy/plugins/monitoring/public/services/__tests__/executor_provider.js b/x-pack/plugins/monitoring/public/services/__tests__/executor.js similarity index 87% rename from x-pack/legacy/plugins/monitoring/public/services/__tests__/executor_provider.js rename to x-pack/plugins/monitoring/public/services/__tests__/executor.js index 2c4d49716406cf..1113f9e32bdc74 100644 --- a/x-pack/legacy/plugins/monitoring/public/services/__tests__/executor_provider.js +++ b/x-pack/plugins/monitoring/public/services/__tests__/executor.js @@ -7,14 +7,22 @@ import ngMock from 'ng_mock'; import expect from '@kbn/expect'; import sinon from 'sinon'; -import { executorProvider } from '../executor_provider'; +import { executorProvider } from '../executor'; import Bluebird from 'bluebird'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { Legacy } from '../../legacy_shims'; +import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; describe('$executor service', () => { let scope; let executor; let $timeout; + let timefilter; + + beforeEach(() => { + const data = dataPluginMock.createStartContract(); + Legacy._shims = { timefilter }; + timefilter = data.query.timefilter.timefilter; + }); beforeEach(ngMock.module('kibana')); diff --git a/x-pack/legacy/plugins/monitoring/public/services/breadcrumbs_provider.js b/x-pack/plugins/monitoring/public/services/breadcrumbs.js similarity index 98% rename from x-pack/legacy/plugins/monitoring/public/services/breadcrumbs_provider.js rename to x-pack/plugins/monitoring/public/services/breadcrumbs.js index 7917606a5bc8e6..f2867180e9c4c7 100644 --- a/x-pack/legacy/plugins/monitoring/public/services/breadcrumbs_provider.js +++ b/x-pack/plugins/monitoring/public/services/breadcrumbs.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import chrome from 'plugins/monitoring/np_imports/ui/chrome'; +import { Legacy } from '../legacy_shims'; import { i18n } from '@kbn/i18n'; // Helper for making objects to use in a link element @@ -195,7 +195,7 @@ export function breadcrumbsProvider() { breadcrumbs = breadcrumbs.concat(getApmBreadcrumbs(mainInstance)); } - chrome.breadcrumbs.set( + Legacy.shims.breadcrumbs.set( breadcrumbs.map(b => ({ text: b.label, href: b.url, diff --git a/x-pack/legacy/plugins/monitoring/public/services/clusters.js b/x-pack/plugins/monitoring/public/services/clusters.js similarity index 77% rename from x-pack/legacy/plugins/monitoring/public/services/clusters.js rename to x-pack/plugins/monitoring/public/services/clusters.js index 40d6fa59228f8a..dfa538b458054f 100644 --- a/x-pack/legacy/plugins/monitoring/public/services/clusters.js +++ b/x-pack/plugins/monitoring/public/services/clusters.js @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { uiModules } from 'plugins/monitoring/np_imports/ui/modules'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { ajaxErrorHandlersProvider } from '../lib/ajax_error_handler'; +import { Legacy } from '../legacy_shims'; import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../common/constants'; function formatClusters(clusters) { @@ -20,10 +19,9 @@ function formatCluster(cluster) { return cluster; } -const uiModule = uiModules.get('monitoring/clusters'); -uiModule.service('monitoringClusters', $injector => { +export function monitoringClustersProvider($injector) { return (clusterUuid, ccs, codePaths) => { - const { min, max } = timefilter.getBounds(); + const { min, max } = Legacy.shims.timefilter.getBounds(); // append clusterUuid if the parameter is given let url = '../api/monitoring/v1/clusters'; @@ -51,4 +49,4 @@ uiModule.service('monitoringClusters', $injector => { return ajaxErrorHandlers(err); }); }; -}); +} diff --git a/x-pack/legacy/plugins/monitoring/public/services/executor_provider.js b/x-pack/plugins/monitoring/public/services/executor.js similarity index 66% rename from x-pack/legacy/plugins/monitoring/public/services/executor_provider.js rename to x-pack/plugins/monitoring/public/services/executor.js index 4a0551fa5af114..7c5e9d652b64e0 100644 --- a/x-pack/legacy/plugins/monitoring/public/services/executor_provider.js +++ b/x-pack/plugins/monitoring/public/services/executor.js @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; -import { subscribeWithScope } from 'plugins/monitoring/np_imports/ui/utils'; +import { Legacy } from '../legacy_shims'; +import { subscribeWithScope } from '../angular/helpers/utils'; import { Subscription } from 'rxjs'; -export function executorProvider(Promise, $timeout) { + +export function executorProvider($timeout, $q) { const queue = []; const subscriptions = new Subscription(); let executionTimer; @@ -61,15 +62,17 @@ export function executorProvider(Promise, $timeout) { * @returns {Promise} a promise of all the services */ function run() { - const noop = () => Promise.resolve(); - return Promise.all( - queue.map(service => { - return service - .execute() - .then(service.handleResponse || noop) - .catch(service.handleError || noop); - }) - ).finally(reset); + const noop = () => $q.resolve(); + return $q + .all( + queue.map(service => { + return service + .execute() + .then(service.handleResponse || noop) + .catch(service.handleError || noop); + }) + ) + .finally(reset); } function reFetch() { @@ -78,7 +81,7 @@ export function executorProvider(Promise, $timeout) { } function killIfPaused() { - if (timefilter.getRefreshInterval().pause) { + if (Legacy.shims.timefilter.getRefreshInterval().pause) { killTimer(); } } @@ -88,6 +91,7 @@ export function executorProvider(Promise, $timeout) { * @returns {void} */ function start() { + const timefilter = Legacy.shims.timefilter; if ( (ignorePaused || timefilter.getRefreshInterval().pause === false) && timefilter.getRefreshInterval().value > 0 @@ -102,17 +106,20 @@ export function executorProvider(Promise, $timeout) { return { register, start($scope) { - subscriptions.add( - subscribeWithScope($scope, timefilter.getFetch$(), { - next: reFetch, - }) - ); - subscriptions.add( - subscribeWithScope($scope, timefilter.getRefreshIntervalUpdate$(), { - next: killIfPaused, - }) - ); - start(); + $scope.$applyAsync(() => { + const timefilter = Legacy.shims.timefilter; + subscriptions.add( + subscribeWithScope($scope, timefilter.getFetch$(), { + next: reFetch, + }) + ); + subscriptions.add( + subscribeWithScope($scope, timefilter.getRefreshIntervalUpdate$(), { + next: killIfPaused, + }) + ); + start(); + }); }, run, destroy, diff --git a/x-pack/legacy/plugins/monitoring/public/services/features.js b/x-pack/plugins/monitoring/public/services/features.js similarity index 86% rename from x-pack/legacy/plugins/monitoring/public/services/features.js rename to x-pack/plugins/monitoring/public/services/features.js index e2357ef08d7df8..f98af10f8dfb49 100644 --- a/x-pack/legacy/plugins/monitoring/public/services/features.js +++ b/x-pack/plugins/monitoring/public/services/features.js @@ -5,10 +5,8 @@ */ import _ from 'lodash'; -import { uiModules } from 'plugins/monitoring/np_imports/ui/modules'; -const uiModule = uiModules.get('monitoring/features', []); -uiModule.service('features', function($window) { +export function featuresProvider($window) { function getData() { let returnData = {}; const monitoringData = $window.localStorage.getItem('xpack.monitoring.data'); @@ -45,4 +43,4 @@ uiModule.service('features', function($window) { isEnabled, update, }; -}); +} diff --git a/x-pack/legacy/plugins/monitoring/public/services/license.js b/x-pack/plugins/monitoring/public/services/license.js similarity index 88% rename from x-pack/legacy/plugins/monitoring/public/services/license.js rename to x-pack/plugins/monitoring/public/services/license.js index 94078b799fdf10..341309004b1106 100644 --- a/x-pack/legacy/plugins/monitoring/public/services/license.js +++ b/x-pack/plugins/monitoring/public/services/license.js @@ -5,11 +5,9 @@ */ import { contains } from 'lodash'; -import { uiModules } from 'plugins/monitoring/np_imports/ui/modules'; import { ML_SUPPORTED_LICENSES } from '../../common/constants'; -const uiModule = uiModules.get('monitoring/license', []); -uiModule.service('license', () => { +export function licenseProvider() { return new (class LicenseService { constructor() { // do not initialize with usable state @@ -50,4 +48,4 @@ uiModule.service('license', () => { return false; } })(); -}); +} diff --git a/x-pack/legacy/plugins/monitoring/public/services/title.js b/x-pack/plugins/monitoring/public/services/title.js similarity index 54% rename from x-pack/legacy/plugins/monitoring/public/services/title.js rename to x-pack/plugins/monitoring/public/services/title.js index 442f4fb5b40291..0715f4dc9e0b6b 100644 --- a/x-pack/legacy/plugins/monitoring/public/services/title.js +++ b/x-pack/plugins/monitoring/public/services/title.js @@ -6,21 +6,20 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import { uiModules } from 'plugins/monitoring/np_imports/ui/modules'; -import { docTitle } from 'ui/doc_title'; +import { Legacy } from '../legacy_shims'; -const uiModule = uiModules.get('monitoring/title', []); -uiModule.service('title', () => { +export function titleProvider($rootScope) { return function changeTitle(cluster, suffix) { let clusterName = _.get(cluster, 'cluster_name'); clusterName = clusterName ? `- ${clusterName}` : ''; suffix = suffix ? `- ${suffix}` : ''; - docTitle.change( - i18n.translate('xpack.monitoring.stackMonitoringDocTitle', { - defaultMessage: 'Stack Monitoring {clusterName} {suffix}', - values: { clusterName, suffix }, - }), - true - ); + $rootScope.$applyAsync(() => { + Legacy.shims.docTitle.change( + i18n.translate('xpack.monitoring.stackMonitoringDocTitle', { + defaultMessage: 'Stack Monitoring {clusterName} {suffix}', + values: { clusterName, suffix }, + }) + ); + }); }; -}); +} diff --git a/x-pack/plugins/monitoring/public/types.ts b/x-pack/plugins/monitoring/public/types.ts new file mode 100644 index 00000000000000..5fcb6b50f5d834 --- /dev/null +++ b/x-pack/plugins/monitoring/public/types.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializerContext, CoreStart } from 'kibana/public'; +import { NavigationPublicPluginStart as NavigationStart } from '../../../../src/plugins/navigation/public'; +import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; + +export { MonitoringConfig } from '../server'; + +export interface MonitoringPluginDependencies { + navigation: NavigationStart; + data: DataPublicPluginStart; + element: HTMLElement; + core: CoreStart; + isCloud: boolean; + pluginInitializerContext: PluginInitializerContext; + externalConfig: Array | Array>; +} diff --git a/x-pack/plugins/monitoring/public/url_state.ts b/x-pack/plugins/monitoring/public/url_state.ts new file mode 100644 index 00000000000000..e66d5462c2bb59 --- /dev/null +++ b/x-pack/plugins/monitoring/public/url_state.ts @@ -0,0 +1,169 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Subscription } from 'rxjs'; +import { History } from 'history'; +import { createHashHistory } from 'history'; +import { MonitoringPluginDependencies } from './types'; + +import { + RefreshInterval, + TimeRange, + syncQueryStateWithUrl, +} from '../../../../src/plugins/data/public'; + +import { + createStateContainer, + createKbnUrlStateStorage, + StateContainer, + INullableBaseStateContainer, + IKbnUrlStateStorage, + ISyncStateRef, + syncState, +} from '../../../../src/plugins/kibana_utils/public'; + +interface Route { + params: { _g: unknown }; +} + +interface RawObject { + [key: string]: unknown; +} + +export interface MonitoringAppState { + [key: string]: unknown; + cluster_uuid?: string; + ccs?: boolean; + inSetupMode?: boolean; + refreshInterval?: RefreshInterval; + time?: TimeRange; + filters?: any[]; +} + +export interface MonitoringAppStateTransitions { + set: ( + state: MonitoringAppState + ) => ( + prop: T, + value: MonitoringAppState[T] + ) => MonitoringAppState; +} + +const GLOBAL_STATE_KEY = '_g'; +const objectEquals = (objA: any, objB: any) => JSON.stringify(objA) === JSON.stringify(objB); + +export class GlobalState { + private readonly stateSyncRef: ISyncStateRef; + private readonly stateContainer: StateContainer< + MonitoringAppState, + MonitoringAppStateTransitions + >; + private readonly stateStorage: IKbnUrlStateStorage; + private readonly stateContainerChangeSub: Subscription; + private readonly syncQueryStateWithUrlManager: { stop: () => void }; + private readonly timefilterRef: MonitoringPluginDependencies['data']['query']['timefilter']['timefilter']; + + private lastAssignedState: MonitoringAppState = {}; + private lastKnownGlobalState?: string; + + constructor( + queryService: MonitoringPluginDependencies['data']['query'], + rootScope: ng.IRootScopeService, + ngLocation: ng.ILocationService, + externalState: RawObject + ) { + this.timefilterRef = queryService.timefilter.timefilter; + + const history: History = createHashHistory(); + this.stateStorage = createKbnUrlStateStorage({ useHash: false, history }); + + const initialStateFromUrl = this.stateStorage.get(GLOBAL_STATE_KEY) as MonitoringAppState; + + this.stateContainer = createStateContainer(initialStateFromUrl, { + set: state => (prop, value) => ({ ...state, [prop]: value }), + }); + + this.stateSyncRef = syncState({ + storageKey: GLOBAL_STATE_KEY, + stateContainer: this.stateContainer as INullableBaseStateContainer, + stateStorage: this.stateStorage, + }); + + this.stateContainerChangeSub = this.stateContainer.state$.subscribe(() => { + this.lastAssignedState = this.getState(); + if (!this.stateContainer.get() && this.lastKnownGlobalState) { + rootScope.$applyAsync(() => + ngLocation.search(`${GLOBAL_STATE_KEY}=${this.lastKnownGlobalState}`).replace() + ); + } + this.syncExternalState(externalState); + }); + + this.syncQueryStateWithUrlManager = syncQueryStateWithUrl(queryService, this.stateStorage); + this.stateSyncRef.start(); + this.startHashSync(rootScope, ngLocation); + this.lastAssignedState = this.getState(); + + rootScope.$on('$destroy', () => this.destroy()); + } + + private syncExternalState(externalState: { [key: string]: unknown }) { + const currentState = this.stateContainer.get(); + for (const key in currentState) { + if ( + ({ save: 1, time: 1, refreshInterval: 1, filters: 1 } as { [key: string]: number })[key] + ) { + continue; + } + if (currentState[key] !== externalState[key]) { + externalState[key] = currentState[key]; + } + } + } + + private startHashSync(rootScope: ng.IRootScopeService, ngLocation: ng.ILocationService) { + rootScope.$on( + '$routeChangeStart', + (_: { preventDefault: () => void }, newState: Route, oldState: Route) => { + const currentGlobalState = oldState?.params?._g; + const nextGlobalState = newState?.params?._g; + if (!nextGlobalState && currentGlobalState && typeof currentGlobalState === 'string') { + newState.params._g = currentGlobalState; + ngLocation.search(`${GLOBAL_STATE_KEY}=${currentGlobalState}`).replace(); + } + this.lastKnownGlobalState = (nextGlobalState || currentGlobalState) as string; + } + ); + } + + public setState(state?: { [key: string]: unknown }) { + const currentAppState = this.getState(); + const newAppState = { ...currentAppState, ...state }; + if (state && objectEquals(newAppState, currentAppState)) { + return; + } + const newState = { + ...newAppState, + refreshInterval: this.timefilterRef.getRefreshInterval(), + time: this.timefilterRef.getTime(), + }; + this.lastAssignedState = newState; + this.stateContainer.set(newState); + } + + public getState(): MonitoringAppState { + const currentState = { ...this.lastAssignedState, ...this.stateContainer.get() }; + delete currentState.filters; + const { refreshInterval: _nullA, time: _nullB, ...currentAppState } = currentState; + return currentAppState || {}; + } + + public destroy() { + this.syncQueryStateWithUrlManager.stop(); + this.stateContainerChangeSub.unsubscribe(); + this.stateSyncRef.stop(); + } +} diff --git a/x-pack/legacy/plugins/monitoring/public/views/__tests__/base_controller.js b/x-pack/plugins/monitoring/public/views/__tests__/base_controller.js similarity index 95% rename from x-pack/legacy/plugins/monitoring/public/views/__tests__/base_controller.js rename to x-pack/plugins/monitoring/public/views/__tests__/base_controller.js index 6c3c73a35601c2..4f7180b138aa5f 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/__tests__/base_controller.js +++ b/x-pack/plugins/monitoring/public/views/__tests__/base_controller.js @@ -7,8 +7,9 @@ import { spy, stub } from 'sinon'; import expect from '@kbn/expect'; import { MonitoringViewBaseController } from '../'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { Legacy } from '../../legacy_shims'; import { PromiseWithCancel, Status } from '../../../common/cancel_promise'; +import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; /* * Mostly copied from base_table_controller test, with modifications @@ -21,9 +22,13 @@ describe('MonitoringViewBaseController', function() { let titleService; let executorService; let configService; + let timefilter; const httpCall = ms => new Promise(resolve => setTimeout(() => resolve(), ms)); before(() => { + const data = dataPluginMock.createStartContract(); + Legacy._shims = { timefilter }; + timefilter = data.query.timefilter.timefilter; titleService = spy(); executorService = { register: spy(), diff --git a/x-pack/legacy/plugins/monitoring/public/views/__tests__/base_table_controller.js b/x-pack/plugins/monitoring/public/views/__tests__/base_table_controller.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/__tests__/base_table_controller.js rename to x-pack/plugins/monitoring/public/views/__tests__/base_table_controller.js diff --git a/x-pack/legacy/plugins/monitoring/public/views/access_denied/index.html b/x-pack/plugins/monitoring/public/views/access_denied/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/access_denied/index.html rename to x-pack/plugins/monitoring/public/views/access_denied/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/access_denied/index.js b/x-pack/plugins/monitoring/public/views/access_denied/index.js similarity index 89% rename from x-pack/legacy/plugins/monitoring/public/views/access_denied/index.js rename to x-pack/plugins/monitoring/public/views/access_denied/index.js index a0cfc79f001ca4..856e59702963a1 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/access_denied/index.js +++ b/x-pack/plugins/monitoring/public/views/access_denied/index.js @@ -5,8 +5,8 @@ */ import { noop } from 'lodash'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import uiChrome from 'plugins/monitoring/np_imports/ui/chrome'; +import { uiRoutes } from '../../angular/helpers/routes'; +import { Legacy } from '../../legacy_shims'; import template from './index.html'; const tryPrivilege = ($http, kbnUrl) => { @@ -40,7 +40,7 @@ uiRoutes.when('/access-denied', { // The template's "Back to Kibana" button click handler this.goToKibana = () => { - $window.location.href = uiChrome.getBasePath() + kbnBaseUrl; + $window.location.href = Legacy.shims.getBasePath() + kbnBaseUrl; }; // keep trying to load data in the background diff --git a/x-pack/legacy/plugins/monitoring/public/views/alerts/index.html b/x-pack/plugins/monitoring/public/views/alerts/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/alerts/index.html rename to x-pack/plugins/monitoring/public/views/alerts/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/alerts/index.js b/x-pack/plugins/monitoring/public/views/alerts/index.js similarity index 77% rename from x-pack/legacy/plugins/monitoring/public/views/alerts/index.js rename to x-pack/plugins/monitoring/public/views/alerts/index.js index 62cc985887e9f5..2e7a34c0aef291 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/alerts/index.js +++ b/x-pack/plugins/monitoring/public/views/alerts/index.js @@ -8,12 +8,11 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { render } from 'react-dom'; import { find, get } from 'lodash'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; +import { uiRoutes } from '../../angular/helpers/routes'; import template from './index.html'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { I18nContext } from 'ui/i18n'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { routeInitProvider } from '../../lib/route_init'; +import { ajaxErrorHandlersProvider } from '../../lib/ajax_error_handler'; +import { Legacy } from '../../legacy_shims'; import { Alerts } from '../../components/alerts'; import { MonitoringViewBaseEuiTableController } from '../base_eui_table_controller'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -28,7 +27,7 @@ function getPageData($injector) { ? `../api/monitoring/v1/alert_status` : `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/legacy_alerts`; - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); const data = { timeRange: { min: timeBounds.min.toISOString(), @@ -103,22 +102,20 @@ uiRoutes.when('/alerts', { ); render( - - - - - {app} - - - - - - - - , + + + + {app} + + + + + + + , document.getElementById('monitoringAlertsApp') ); }; diff --git a/x-pack/legacy/plugins/monitoring/public/views/all.js b/x-pack/plugins/monitoring/public/views/all.js similarity index 98% rename from x-pack/legacy/plugins/monitoring/public/views/all.js rename to x-pack/plugins/monitoring/public/views/all.js index ded378b050c2df..51dcce751863c2 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/all.js +++ b/x-pack/plugins/monitoring/public/views/all.js @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import './loading'; import './no_data'; import './access_denied'; import './alerts'; diff --git a/x-pack/legacy/plugins/monitoring/public/views/apm/instance/index.html b/x-pack/plugins/monitoring/public/views/apm/instance/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/apm/instance/index.html rename to x-pack/plugins/monitoring/public/views/apm/instance/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/apm/instance/index.js b/x-pack/plugins/monitoring/public/views/apm/instance/index.js similarity index 83% rename from x-pack/legacy/plugins/monitoring/public/views/apm/instance/index.js rename to x-pack/plugins/monitoring/public/views/apm/instance/index.js index 4d0f858d281175..982857ab5aea46 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/apm/instance/index.js +++ b/x-pack/plugins/monitoring/public/views/apm/instance/index.js @@ -13,12 +13,11 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { find, get } from 'lodash'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import template from './index.html'; import { MonitoringViewBaseController } from '../../base_controller'; import { ApmServerInstance } from '../../../components/apm/instance'; -import { I18nContext } from 'ui/i18n'; import { CODE_PATH_APM } from '../../../../common/constants'; uiRoutes.when('/apm/instances/:uuid', { @@ -64,14 +63,12 @@ uiRoutes.when('/apm/instances/:uuid', { renderReact(data) { const component = ( - - - + ); super.renderReact(component); } diff --git a/x-pack/legacy/plugins/monitoring/public/views/apm/instances/index.html b/x-pack/plugins/monitoring/public/views/apm/instances/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/apm/instances/index.html rename to x-pack/plugins/monitoring/public/views/apm/instances/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/apm/instances/index.js b/x-pack/plugins/monitoring/public/views/apm/instances/index.js similarity index 70% rename from x-pack/legacy/plugins/monitoring/public/views/apm/instances/index.js rename to x-pack/plugins/monitoring/public/views/apm/instances/index.js index 317879063b6e5f..8cd0b03e89e046 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/apm/instances/index.js +++ b/x-pack/plugins/monitoring/public/views/apm/instances/index.js @@ -7,12 +7,11 @@ import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import template from './index.html'; import { ApmServerInstances } from '../../../components/apm/instances'; import { MonitoringViewBaseEuiTableController } from '../..'; -import { I18nContext } from 'ui/i18n'; import { SetupModeRenderer } from '../../../components/renderers'; import { APM_SYSTEM_ID, CODE_PATH_APM } from '../../../../common/constants'; @@ -62,28 +61,26 @@ uiRoutes.when('/apm/instances', { const { pagination, sorting, onTableChange } = this; const component = ( - - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - + ( + + {flyoutComponent} + + {bottomBarComponent} + + )} + /> ); super.renderReact(component); } diff --git a/x-pack/legacy/plugins/monitoring/public/views/apm/overview/index.html b/x-pack/plugins/monitoring/public/views/apm/overview/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/apm/overview/index.html rename to x-pack/plugins/monitoring/public/views/apm/overview/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/apm/overview/index.js b/x-pack/plugins/monitoring/public/views/apm/overview/index.js similarity index 81% rename from x-pack/legacy/plugins/monitoring/public/views/apm/overview/index.js rename to x-pack/plugins/monitoring/public/views/apm/overview/index.js index e6562f428d2a06..1fdd441e62e220 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/apm/overview/index.js +++ b/x-pack/plugins/monitoring/public/views/apm/overview/index.js @@ -6,12 +6,11 @@ import React from 'react'; import { find } from 'lodash'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import template from './index.html'; import { MonitoringViewBaseController } from '../../base_controller'; import { ApmOverview } from '../../../components/apm/overview'; -import { I18nContext } from 'ui/i18n'; import { CODE_PATH_APM } from '../../../../common/constants'; uiRoutes.when('/apm', { @@ -48,11 +47,7 @@ uiRoutes.when('/apm', { } renderReact(data) { - const component = ( - - - - ); + const component = ; super.renderReact(component); } }, diff --git a/x-pack/legacy/plugins/monitoring/public/views/base_controller.js b/x-pack/plugins/monitoring/public/views/base_controller.js similarity index 77% rename from x-pack/legacy/plugins/monitoring/public/views/base_controller.js rename to x-pack/plugins/monitoring/public/views/base_controller.js index 25b4d97177a988..e5e59f2f8e826d 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/base_controller.js +++ b/x-pack/plugins/monitoring/public/views/base_controller.js @@ -8,9 +8,8 @@ import React from 'react'; import moment from 'moment'; import { render, unmountComponentAtNode } from 'react-dom'; import { getPageData } from '../lib/get_page_data'; -import { PageLoading } from 'plugins/monitoring/components'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; -import { I18nContext } from 'ui/i18n'; +import { PageLoading } from '../components'; +import { Legacy } from '../legacy_shims'; import { PromiseWithCancel } from '../../common/cancel_promise'; import { updateSetupModeData, getSetupModeState } from '../lib/setup_mode'; @@ -113,18 +112,6 @@ export class MonitoringViewBaseController { const { enableTimeFilter = true, enableAutoRefresh = true } = options; - if (enableTimeFilter === false) { - timefilter.disableTimeRangeSelector(); - } else { - timefilter.enableTimeRangeSelector(); - } - - if (enableAutoRefresh === false) { - timefilter.disableAutoRefreshSelector(); - } else { - timefilter.enableAutoRefreshSelector(); - } - this.updateData = () => { if (this.updateDataPromise) { // Do not sent another request if one is inflight @@ -146,7 +133,46 @@ export class MonitoringViewBaseController { }); }); }; - fetchDataImmediately && this.updateData(); + + $scope.$applyAsync(() => { + const timefilter = Legacy.shims.timefilter; + + if (enableTimeFilter === false) { + timefilter.disableTimeRangeSelector(); + } else { + timefilter.enableTimeRangeSelector(); + } + + if (enableAutoRefresh === false) { + timefilter.disableAutoRefreshSelector(); + } else { + timefilter.enableAutoRefreshSelector(); + } + + // needed for chart pages + this.onBrush = ({ xaxis }) => { + removePopstateHandler(); + const { to, from } = xaxis; + const timezone = config.get('dateFormat:tz'); + const offset = getOffsetInMS(timezone); + timefilter.setTime({ + from: moment(from - offset), + to: moment(to - offset), + mode: 'absolute', + }); + $executor.cancel(); + $executor.run(); + ++zoomInLevel; + clearTimeout(deferTimer); + /* + Needed to defer 'popstate' event, so it does not fire immediately after it's added. + 10ms is to make sure the event is not added with the same code digest + */ + deferTimer = setTimeout(() => addPopstateHandler(), 10); + }; + + fetchDataImmediately && this.updateData(); + }); $executor.register({ execute: () => this.updateData(), @@ -155,35 +181,14 @@ export class MonitoringViewBaseController { $scope.$on('$destroy', () => { clearTimeout(deferTimer); removePopstateHandler(); - if (this.reactNodeId) { + const targetElement = document.getElementById(this.reactNodeId); + if (targetElement) { // WIP https://github.com/elastic/x-pack-kibana/issues/5198 - unmountComponentAtNode(document.getElementById(this.reactNodeId)); + unmountComponentAtNode(targetElement); } $executor.destroy(); }); - // needed for chart pages - this.onBrush = ({ xaxis }) => { - removePopstateHandler(); - const { to, from } = xaxis; - const timezone = config.get('dateFormat:tz'); - const offset = getOffsetInMS(timezone); - timefilter.setTime({ - from: moment(from - offset), - to: moment(to - offset), - mode: 'absolute', - }); - $executor.cancel(); - $executor.run(); - ++zoomInLevel; - clearTimeout(deferTimer); - /* - Needed to defer 'popstate' event, so it does not fire immediately after it's added. - 10ms is to make sure the event is not added with the same code digest - */ - deferTimer = setTimeout(() => addPopstateHandler(), 10); - }; - this.setTitle = title => titleService($scope.cluster, title); } @@ -193,16 +198,11 @@ export class MonitoringViewBaseController { console.warn(`"#${this.reactNodeId}" element has not been added to the DOM yet`); return; } - if (this._isDataInitialized === false) { - render( - - - , - renderElement - ); - } else { - render(component, renderElement); - } + const I18nContext = Legacy.shims.I18nContext; + const wrappedComponent = ( + {!this._isDataInitialized ? : component} + ); + render(wrappedComponent, renderElement); } getPaginationRouteOptions() { diff --git a/x-pack/legacy/plugins/monitoring/public/views/base_eui_table_controller.js b/x-pack/plugins/monitoring/public/views/base_eui_table_controller.js similarity index 97% rename from x-pack/legacy/plugins/monitoring/public/views/base_eui_table_controller.js rename to x-pack/plugins/monitoring/public/views/base_eui_table_controller.js index 0460adaeecd104..ac5ba45af8614a 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/base_eui_table_controller.js +++ b/x-pack/plugins/monitoring/public/views/base_eui_table_controller.js @@ -5,7 +5,7 @@ */ import { MonitoringViewBaseController } from './'; -import { euiTableStorageGetter, euiTableStorageSetter } from 'plugins/monitoring/components/table'; +import { euiTableStorageGetter, euiTableStorageSetter } from '../components/table'; import { EUI_SORT_ASCENDING } from '../../common/constants'; const PAGE_SIZE_OPTIONS = [5, 10, 20, 50]; diff --git a/x-pack/legacy/plugins/monitoring/public/views/base_table_controller.js b/x-pack/plugins/monitoring/public/views/base_table_controller.js similarity index 95% rename from x-pack/legacy/plugins/monitoring/public/views/base_table_controller.js rename to x-pack/plugins/monitoring/public/views/base_table_controller.js index 6ae486eae96fc1..2275608c473dd9 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/base_table_controller.js +++ b/x-pack/plugins/monitoring/public/views/base_table_controller.js @@ -5,7 +5,7 @@ */ import { MonitoringViewBaseController } from './'; -import { tableStorageGetter, tableStorageSetter } from 'plugins/monitoring/components/table'; +import { tableStorageGetter, tableStorageSetter } from '../components/table'; /** * Class to manage common instantiation behaviors in a view controller diff --git a/x-pack/legacy/plugins/monitoring/public/views/beats/beat/get_page_data.js b/x-pack/plugins/monitoring/public/views/beats/beat/get_page_data.js similarity index 82% rename from x-pack/legacy/plugins/monitoring/public/views/beats/beat/get_page_data.js rename to x-pack/plugins/monitoring/public/views/beats/beat/get_page_data.js index 7e77e93d52fe8d..7c9c459218529d 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/beats/beat/get_page_data.js +++ b/x-pack/plugins/monitoring/public/views/beats/beat/get_page_data.js @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { Legacy } from '../../../legacy_shims'; export function getPageData($injector) { const $http = $injector.get('$http'); const $route = $injector.get('$route'); const globalState = $injector.get('globalState'); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/beats/beat/${$route.current.params.beatUuid}`; - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { diff --git a/x-pack/legacy/plugins/monitoring/public/views/beats/beat/index.html b/x-pack/plugins/monitoring/public/views/beats/beat/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/beats/beat/index.html rename to x-pack/plugins/monitoring/public/views/beats/beat/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/beats/beat/index.js b/x-pack/plugins/monitoring/public/views/beats/beat/index.js similarity index 92% rename from x-pack/legacy/plugins/monitoring/public/views/beats/beat/index.js rename to x-pack/plugins/monitoring/public/views/beats/beat/index.js index b3fad1b4cc3cb6..9d5f9b4d562a65 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/beats/beat/index.js +++ b/x-pack/plugins/monitoring/public/views/beats/beat/index.js @@ -6,8 +6,8 @@ import { find } from 'lodash'; import { i18n } from '@kbn/i18n'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import { MonitoringViewBaseController } from '../../'; import { getPageData } from './get_page_data'; import template from './index.html'; diff --git a/x-pack/legacy/plugins/monitoring/public/views/beats/listing/get_page_data.js b/x-pack/plugins/monitoring/public/views/beats/listing/get_page_data.js similarity index 80% rename from x-pack/legacy/plugins/monitoring/public/views/beats/listing/get_page_data.js rename to x-pack/plugins/monitoring/public/views/beats/listing/get_page_data.js index 1838011dee6520..77942303afcc21 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/beats/listing/get_page_data.js +++ b/x-pack/plugins/monitoring/public/views/beats/listing/get_page_data.js @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { Legacy } from '../../../legacy_shims'; export function getPageData($injector) { const $http = $injector.get('$http'); const globalState = $injector.get('globalState'); - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/beats/beats`; return $http diff --git a/x-pack/legacy/plugins/monitoring/public/views/beats/listing/index.html b/x-pack/plugins/monitoring/public/views/beats/listing/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/beats/listing/index.html rename to x-pack/plugins/monitoring/public/views/beats/listing/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/beats/listing/index.js b/x-pack/plugins/monitoring/public/views/beats/listing/index.js similarity index 66% rename from x-pack/legacy/plugins/monitoring/public/views/beats/listing/index.js rename to x-pack/plugins/monitoring/public/views/beats/listing/index.js index 48848007c9c27c..7d011089fdd7d5 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/beats/listing/index.js +++ b/x-pack/plugins/monitoring/public/views/beats/listing/index.js @@ -6,13 +6,12 @@ import { find } from 'lodash'; import { i18n } from '@kbn/i18n'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import { MonitoringViewBaseEuiTableController } from '../../'; import { getPageData } from './get_page_data'; import template from './index.html'; import React, { Fragment } from 'react'; -import { I18nContext } from 'ui/i18n'; import { Listing } from '../../../components/beats/listing/listing'; import { SetupModeRenderer } from '../../../components/renderers'; import { CODE_PATH_BEATS, BEATS_SYSTEM_ID } from '../../../../common/constants'; @@ -62,31 +61,29 @@ uiRoutes.when('/beats/beats', { renderComponent() { const { sorting, pagination, onTableChange } = this.scope.beats; this.renderReact( - - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - + ( + + {flyoutComponent} + + {bottomBarComponent} + + )} + /> ); } }, diff --git a/x-pack/legacy/plugins/monitoring/public/views/beats/overview/get_page_data.js b/x-pack/plugins/monitoring/public/views/beats/overview/get_page_data.js similarity index 80% rename from x-pack/legacy/plugins/monitoring/public/views/beats/overview/get_page_data.js rename to x-pack/plugins/monitoring/public/views/beats/overview/get_page_data.js index a3b120b277b941..e5d576961b797b 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/beats/overview/get_page_data.js +++ b/x-pack/plugins/monitoring/public/views/beats/overview/get_page_data.js @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { Legacy } from '../../../legacy_shims'; export function getPageData($injector) { const $http = $injector.get('$http'); const globalState = $injector.get('globalState'); - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/beats`; return $http diff --git a/x-pack/legacy/plugins/monitoring/public/views/beats/overview/index.html b/x-pack/plugins/monitoring/public/views/beats/overview/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/beats/overview/index.html rename to x-pack/plugins/monitoring/public/views/beats/overview/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/beats/overview/index.js b/x-pack/plugins/monitoring/public/views/beats/overview/index.js similarity index 91% rename from x-pack/legacy/plugins/monitoring/public/views/beats/overview/index.js rename to x-pack/plugins/monitoring/public/views/beats/overview/index.js index aea62d5c7f78f0..0eb39ef3722638 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/beats/overview/index.js +++ b/x-pack/plugins/monitoring/public/views/beats/overview/index.js @@ -6,8 +6,8 @@ import { find } from 'lodash'; import { i18n } from '@kbn/i18n'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import { MonitoringViewBaseController } from '../../'; import { getPageData } from './get_page_data'; import template from './index.html'; diff --git a/x-pack/legacy/plugins/monitoring/public/views/cluster/listing/index.html b/x-pack/plugins/monitoring/public/views/cluster/listing/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/cluster/listing/index.html rename to x-pack/plugins/monitoring/public/views/cluster/listing/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/cluster/listing/index.js b/x-pack/plugins/monitoring/public/views/cluster/listing/index.js similarity index 75% rename from x-pack/legacy/plugins/monitoring/public/views/cluster/listing/index.js rename to x-pack/plugins/monitoring/public/views/cluster/listing/index.js index 958226514b1466..42be4f02f5c94d 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/cluster/listing/index.js +++ b/x-pack/plugins/monitoring/public/views/cluster/listing/index.js @@ -5,10 +5,9 @@ */ import React from 'react'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import { MonitoringViewBaseEuiTableController } from '../../'; -import { I18nContext } from 'ui/i18n'; import template from './index.html'; import { Listing } from '../../../components/cluster/listing'; import { CODE_PATH_ALL } from '../../../../common/constants'; @@ -33,7 +32,7 @@ uiRoutes } if (clusters.length === 1) { // Bypass the cluster listing if there is just 1 cluster - kbnUrl.changePath('/overview'); + kbnUrl.redirect('/overview'); return Promise.reject(); } return clusters; @@ -63,21 +62,19 @@ uiRoutes () => this.data, data => { this.renderReact( - - - + ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/cluster/overview/index.html b/x-pack/plugins/monitoring/public/views/cluster/overview/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/cluster/overview/index.html rename to x-pack/plugins/monitoring/public/views/cluster/overview/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/cluster/overview/index.js b/x-pack/plugins/monitoring/public/views/cluster/overview/index.js similarity index 68% rename from x-pack/legacy/plugins/monitoring/public/views/cluster/overview/index.js rename to x-pack/plugins/monitoring/public/views/cluster/overview/index.js index e1777b8ed7b49a..3f6fb77f02288c 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/cluster/overview/index.js +++ b/x-pack/plugins/monitoring/public/views/cluster/overview/index.js @@ -5,14 +5,13 @@ */ import React, { Fragment } from 'react'; import { isEmpty } from 'lodash'; -import chrome from 'ui/chrome'; +import { Legacy } from '../../../legacy_shims'; import { i18n } from '@kbn/i18n'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import template from './index.html'; import { MonitoringViewBaseController } from '../../'; -import { Overview } from 'plugins/monitoring/components/cluster/overview'; -import { I18nContext } from 'ui/i18n'; +import { Overview } from '../../../components/cluster/overview'; import { SetupModeRenderer } from '../../../components/renderers'; import { CODE_PATH_ALL, @@ -70,31 +69,29 @@ uiRoutes.when('/overview', { return; } - let emailAddress = chrome.getInjected('monitoringLegacyEmailAddress') || ''; + let emailAddress = Legacy.shims.getInjected('monitoringLegacyEmailAddress') || ''; if (KIBANA_ALERTING_ENABLED) { emailAddress = config.get(MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS) || emailAddress; } this.renderReact( - - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - + ( + + {flyoutComponent} + + {bottomBarComponent} + + )} + /> ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/get_page_data.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/get_page_data.js similarity index 80% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/get_page_data.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/ccr/get_page_data.js index 83dd24209dfe3b..d4a86b00a7505f 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/get_page_data.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/get_page_data.js @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { Legacy } from '../../../legacy_shims'; export function getPageData($injector) { const $http = $injector.get('$http'); const globalState = $injector.get('globalState'); - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/ccr`; return $http diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/index.html rename to x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.js similarity index 83% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/index.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.js index cf51347842f4ab..88d3a3614243f5 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/index.js @@ -6,13 +6,12 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; +import { uiRoutes } from '../../../angular/helpers/routes'; import { getPageData } from './get_page_data'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { routeInitProvider } from '../../../lib/route_init'; import template from './index.html'; import { Ccr } from '../../../components/elasticsearch/ccr'; import { MonitoringViewBaseController } from '../../base_controller'; -import { I18nContext } from 'ui/i18n'; import { CODE_PATH_ELASTICSEARCH } from '../../../../common/constants'; uiRoutes.when('/elasticsearch/ccr', { @@ -45,11 +44,7 @@ uiRoutes.when('/elasticsearch/ccr', { ); this.renderReact = ({ data }) => { - super.renderReact( - - - - ); + super.renderReact(); }; } }, diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/shard/get_page_data.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/get_page_data.js similarity index 82% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/shard/get_page_data.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/get_page_data.js index 22ca094d28b077..20f39edbb6a759 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/shard/get_page_data.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/get_page_data.js @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { ajaxErrorHandlersProvider } from '../../../../lib/ajax_error_handler'; +import { Legacy } from '../../../../legacy_shims'; export function getPageData($injector) { const $http = $injector.get('$http'); const $route = $injector.get('$route'); const globalState = $injector.get('globalState'); - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/ccr/${$route.current.params.index}/shard/${$route.current.params.shardId}`; return $http diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.html rename to x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js similarity index 86% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js index ff35f7f743f663..260422d322a2d2 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/ccr/shard/index.js @@ -7,13 +7,12 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; +import { uiRoutes } from '../../../../angular/helpers/routes'; import { getPageData } from './get_page_data'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { routeInitProvider } from '../../../../lib/route_init'; import template from './index.html'; import { MonitoringViewBaseController } from '../../../base_controller'; import { CcrShard } from '../../../../components/elasticsearch/ccr_shard'; -import { I18nContext } from 'ui/i18n'; import { CODE_PATH_ELASTICSEARCH } from '../../../../../common/constants'; uiRoutes.when('/elasticsearch/ccr/:index/shard/:shardId', { @@ -54,11 +53,7 @@ uiRoutes.when('/elasticsearch/ccr/:index/shard/:shardId', { ); this.renderReact = props => { - super.renderReact( - - - - ); + super.renderReact(); }; } }, diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/index/advanced/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/index/advanced/index.html rename to x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/index/advanced/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.js similarity index 79% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/index/advanced/index.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.js index 4fc439b4e0123b..9fbaf6c00725dd 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/index/advanced/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/index/advanced/index.js @@ -9,13 +9,12 @@ */ import React from 'react'; import { i18n } from '@kbn/i18n'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../../angular/helpers/routes'; +import { ajaxErrorHandlersProvider } from '../../../../lib/ajax_error_handler'; +import { routeInitProvider } from '../../../../lib/route_init'; import template from './index.html'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { Legacy } from '../../../../legacy_shims'; import { AdvancedIndex } from '../../../../components/elasticsearch/index/advanced'; -import { I18nContext } from 'ui/i18n'; import { MonitoringViewBaseController } from '../../../base_controller'; import { CODE_PATH_ELASTICSEARCH } from '../../../../../common/constants'; @@ -24,7 +23,7 @@ function getPageData($injector) { const $route = $injector.get('$route'); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/indices/${$route.current.params.index}`; const $http = $injector.get('$http'); - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { @@ -78,14 +77,12 @@ uiRoutes.when('/elasticsearch/indices/:index/advanced', { () => this.data, data => { this.renderReact( - - - + ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/index/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/index/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/index/index.html rename to x-pack/plugins/monitoring/public/views/elasticsearch/index/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/index/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/index/index.js similarity index 80% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/index/index.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/index/index.js index bbeef8294a897b..d2f49d2280e152 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/index/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/index/index.js @@ -9,12 +9,11 @@ */ import React from 'react'; import { i18n } from '@kbn/i18n'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; import template from './index.html'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; -import { I18nContext } from 'ui/i18n'; +import { Legacy } from '../../../legacy_shims'; import { labels } from '../../../components/elasticsearch/shard_allocation/lib/labels'; import { indicesByNodes } from '../../../components/elasticsearch/shard_allocation/transformers/indices_by_nodes'; import { Index } from '../../../components/elasticsearch/index/index'; @@ -26,7 +25,7 @@ function getPageData($injector) { const $route = $injector.get('$route'); const globalState = $injector.get('globalState'); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/indices/${$route.current.params.index}`; - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { @@ -96,17 +95,15 @@ uiRoutes.when('/elasticsearch/indices/:index', { } this.renderReact( - - - + ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/indices/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/indices/index.html rename to x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/indices/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.js similarity index 80% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/indices/index.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.js index f1d96557b0c1c8..ee177c3e8ed042 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/indices/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/indices/index.js @@ -7,12 +7,11 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import { MonitoringViewBaseEuiTableController } from '../../'; import { ElasticsearchIndices } from '../../../components'; import template from './index.html'; -import { I18nContext } from 'ui/i18n'; import { CODE_PATH_ELASTICSEARCH } from '../../../../common/constants'; uiRoutes.when('/elasticsearch/indices', { @@ -71,17 +70,15 @@ uiRoutes.when('/elasticsearch/indices', { this.renderReact = ({ clusterStatus, indices }) => { super.renderReact( - - - + ); }; } diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ml_jobs/get_page_data.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/get_page_data.js similarity index 80% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ml_jobs/get_page_data.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/get_page_data.js index 1943b580f7a75e..0b50a04d530369 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ml_jobs/get_page_data.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/get_page_data.js @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { Legacy } from '../../../legacy_shims'; export function getPageData($injector) { const $http = $injector.get('$http'); const globalState = $injector.get('globalState'); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/ml_jobs`; - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { ccs: globalState.ccs, diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.html rename to x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.js similarity index 92% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.js index 5e66a4147ab708..d1dd81223ad5e2 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/ml_jobs/index.js @@ -6,8 +6,8 @@ import { find } from 'lodash'; import { i18n } from '@kbn/i18n'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import { MonitoringViewBaseEuiTableController } from '../../'; import { getPageData } from './get_page_data'; import template from './index.html'; diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/advanced/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/advanced/index.html rename to x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js similarity index 78% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js index 2bbdf604d00ce0..5c45509fce37c6 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js @@ -9,12 +9,11 @@ */ import React from 'react'; import { i18n } from '@kbn/i18n'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../../angular/helpers/routes'; +import { ajaxErrorHandlersProvider } from '../../../../lib/ajax_error_handler'; +import { routeInitProvider } from '../../../../lib/route_init'; import template from './index.html'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; -import { I18nContext } from 'ui/i18n'; +import { Legacy } from '../../../../legacy_shims'; import { AdvancedNode } from '../../../../components/elasticsearch/node/advanced'; import { MonitoringViewBaseController } from '../../../base_controller'; import { CODE_PATH_ELASTICSEARCH } from '../../../../../common/constants'; @@ -23,7 +22,7 @@ function getPageData($injector) { const $http = $injector.get('$http'); const globalState = $injector.get('globalState'); const $route = $injector.get('$route'); - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/nodes/${$route.current.params.node}`; return $http @@ -79,14 +78,12 @@ uiRoutes.when('/elasticsearch/nodes/:node/advanced', { ); this.renderReact( - - - + ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/get_page_data.js b/x-pack/plugins/monitoring/public/views/elasticsearch/node/get_page_data.js similarity index 84% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/get_page_data.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/node/get_page_data.js index 0d9e0b25eacd02..6aaa8aa452f68d 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/get_page_data.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/node/get_page_data.js @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { Legacy } from '../../../legacy_shims'; export function getPageData($injector) { const $http = $injector.get('$http'); @@ -14,7 +14,7 @@ export function getPageData($injector) { const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/elasticsearch/nodes/${$route.current.params.node}`; const features = $injector.get('features'); const showSystemIndices = features.isEnabled('showSystemIndices', false); - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/index.html rename to x-pack/plugins/monitoring/public/views/elasticsearch/node/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js similarity index 84% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/index.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js index fa76222d78e2d5..c34be490d17113 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/node/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js @@ -10,12 +10,11 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { partial } from 'lodash'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import { getPageData } from './get_page_data'; import template from './index.html'; import { Node } from '../../../components/elasticsearch/node/node'; -import { I18nContext } from 'ui/i18n'; import { labels } from '../../../components/elasticsearch/shard_allocation/lib/labels'; import { nodesByIndices } from '../../../components/elasticsearch/shard_allocation/transformers/nodes_by_indices'; import { MonitoringViewBaseController } from '../../base_controller'; @@ -79,17 +78,15 @@ uiRoutes.when('/elasticsearch/nodes/:node', { $scope.labels = labels.node; this.renderReact( - - - + ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/nodes/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/nodes/index.html rename to x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/nodes/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js similarity index 74% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/nodes/index.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js index a9a6774d4c8837..db4aaca15c00e2 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/nodes/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js @@ -7,13 +7,12 @@ import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { Legacy } from '../../../legacy_shims'; import template from './index.html'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { routeInitProvider } from '../../../lib/route_init'; import { MonitoringViewBaseEuiTableController } from '../../'; import { ElasticsearchNodes } from '../../../components'; -import { I18nContext } from 'ui/i18n'; import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; import { SetupModeRenderer } from '../../../components/renderers'; import { ELASTICSEARCH_SYSTEM_ID, CODE_PATH_ELASTICSEARCH } from '../../../../common/constants'; @@ -42,7 +41,7 @@ uiRoutes.when('/elasticsearch/nodes', { _api; // to fix eslint const $http = $injector.get('$http'); const globalState = $injector.get('globalState'); - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); const getNodes = (clusterUuid = globalState.cluster_uuid) => $http.post(`../api/monitoring/v1/clusters/${clusterUuid}/elasticsearch/nodes`, { @@ -91,27 +90,25 @@ uiRoutes.when('/elasticsearch/nodes', { }; super.renderReact( - - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - + ( + + {flyoutComponent} + + {bottomBarComponent} + + )} + /> ); }; } diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/overview/controller.js b/x-pack/plugins/monitoring/public/views/elasticsearch/overview/controller.js similarity index 83% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/overview/controller.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/overview/controller.js index 9f59b4d6322222..7cdd7dfae0af07 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/overview/controller.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/overview/controller.js @@ -7,8 +7,7 @@ import React from 'react'; import { find } from 'lodash'; import { MonitoringViewBaseController } from '../../'; -import { ElasticsearchOverview } from 'plugins/monitoring/components'; -import { I18nContext } from 'ui/i18n'; +import { ElasticsearchOverview } from '../../../components'; export class ElasticsearchOverviewController extends MonitoringViewBaseController { constructor($injector, $scope) { @@ -78,19 +77,17 @@ export class ElasticsearchOverviewController extends MonitoringViewBaseControlle const { clusterStatus, metrics, shardActivity, logs } = data; const shardActivityData = shardActivity && this.filterShardActivityData(shardActivity); // no filter on data = null const component = ( - - - + ); super.renderReact(component); diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/overview/index.html b/x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/overview/index.html rename to x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/overview/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.js similarity index 84% rename from x-pack/legacy/plugins/monitoring/public/views/elasticsearch/overview/index.js rename to x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.js index 475c0fc4948576..1c27a4d004abe9 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/elasticsearch/overview/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/overview/index.js @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import template from './index.html'; import { ElasticsearchOverviewController } from './controller'; import { CODE_PATH_ELASTICSEARCH } from '../../../../common/constants'; diff --git a/x-pack/legacy/plugins/monitoring/public/views/index.js b/x-pack/plugins/monitoring/public/views/index.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/index.js rename to x-pack/plugins/monitoring/public/views/index.js diff --git a/x-pack/legacy/plugins/monitoring/public/views/kibana/instance/index.html b/x-pack/plugins/monitoring/public/views/kibana/instance/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/kibana/instance/index.html rename to x-pack/plugins/monitoring/public/views/kibana/instance/index.html diff --git a/x-pack/plugins/monitoring/public/views/kibana/instance/index.js b/x-pack/plugins/monitoring/public/views/kibana/instance/index.js new file mode 100644 index 00000000000000..b743b4e49f0962 --- /dev/null +++ b/x-pack/plugins/monitoring/public/views/kibana/instance/index.js @@ -0,0 +1,150 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* + * Kibana Instance + */ +import React from 'react'; +import { get } from 'lodash'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { routeInitProvider } from '../../../lib/route_init'; +import template from './index.html'; +import { Legacy } from '../../../legacy_shims'; +import { + EuiPage, + EuiPageBody, + EuiPageContent, + EuiSpacer, + EuiFlexGrid, + EuiFlexItem, + EuiPanel, +} from '@elastic/eui'; +import { MonitoringTimeseriesContainer } from '../../../components/chart'; +import { DetailStatus } from '../../../components/kibana/detail_status'; +import { MonitoringViewBaseController } from '../../base_controller'; +import { CODE_PATH_KIBANA } from '../../../../common/constants'; + +function getPageData($injector) { + const $http = $injector.get('$http'); + const globalState = $injector.get('globalState'); + const $route = $injector.get('$route'); + const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/kibana/${$route.current.params.uuid}`; + const timeBounds = Legacy.shims.timefilter.getBounds(); + + return $http + .post(url, { + ccs: globalState.ccs, + timeRange: { + min: timeBounds.min.toISOString(), + max: timeBounds.max.toISOString(), + }, + }) + .then(response => response.data) + .catch(err => { + const Private = $injector.get('Private'); + const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); + return ajaxErrorHandlers(err); + }); +} + +uiRoutes.when('/kibana/instances/:uuid', { + template, + resolve: { + clusters(Private) { + const routeInit = Private(routeInitProvider); + return routeInit({ codePaths: [CODE_PATH_KIBANA] }); + }, + pageData: getPageData, + }, + controllerAs: 'monitoringKibanaInstanceApp', + controller: class extends MonitoringViewBaseController { + constructor($injector, $scope) { + super({ + title: `Kibana - ${get($scope.pageData, 'kibanaSummary.name')}`, + defaultData: {}, + getPageData, + reactNodeId: 'monitoringKibanaInstanceApp', + $scope, + $injector, + }); + + $scope.$watch( + () => this.data, + data => { + if (!data || !data.metrics) { + return; + } + + this.setTitle(`Kibana - ${get(data, 'kibanaSummary.name')}`); + + this.renderReact( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + } + ); + } + }, +}); diff --git a/x-pack/legacy/plugins/monitoring/public/views/kibana/instances/get_page_data.js b/x-pack/plugins/monitoring/public/views/kibana/instances/get_page_data.js similarity index 80% rename from x-pack/legacy/plugins/monitoring/public/views/kibana/instances/get_page_data.js rename to x-pack/plugins/monitoring/public/views/kibana/instances/get_page_data.js index 4f8d7fa20d3320..9f78bd07ecbf81 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/kibana/instances/get_page_data.js +++ b/x-pack/plugins/monitoring/public/views/kibana/instances/get_page_data.js @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { Legacy } from '../../../legacy_shims'; export function getPageData($injector) { const $http = $injector.get('$http'); const globalState = $injector.get('globalState'); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/kibana/instances`; - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { diff --git a/x-pack/legacy/plugins/monitoring/public/views/kibana/instances/index.html b/x-pack/plugins/monitoring/public/views/kibana/instances/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/kibana/instances/index.html rename to x-pack/plugins/monitoring/public/views/kibana/instances/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/kibana/instances/index.js b/x-pack/plugins/monitoring/public/views/kibana/instances/index.js similarity index 56% rename from x-pack/legacy/plugins/monitoring/public/views/kibana/instances/index.js rename to x-pack/plugins/monitoring/public/views/kibana/instances/index.js index 51a7e033bd0d6e..d179928ded693a 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/kibana/instances/index.js +++ b/x-pack/plugins/monitoring/public/views/kibana/instances/index.js @@ -5,14 +5,13 @@ */ import React, { Fragment } from 'react'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import { MonitoringViewBaseEuiTableController } from '../../'; import { getPageData } from './get_page_data'; import template from './index.html'; -import { KibanaInstances } from 'plugins/monitoring/components/kibana/instances'; +import { KibanaInstances } from '../../../components/kibana/instances'; import { SetupModeRenderer } from '../../../components/renderers'; -import { I18nContext } from 'ui/i18n'; import { KIBANA_SYSTEM_ID, CODE_PATH_KIBANA } from '../../../../common/constants'; uiRoutes.when('/kibana/instances', { @@ -40,31 +39,29 @@ uiRoutes.when('/kibana/instances', { const renderReact = () => { this.renderReact( - - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - + ( + + {flyoutComponent} + + {bottomBarComponent} + + )} + /> ); }; diff --git a/x-pack/legacy/plugins/monitoring/public/views/kibana/overview/index.html b/x-pack/plugins/monitoring/public/views/kibana/overview/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/kibana/overview/index.html rename to x-pack/plugins/monitoring/public/views/kibana/overview/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/kibana/overview/index.js b/x-pack/plugins/monitoring/public/views/kibana/overview/index.js similarity index 58% rename from x-pack/legacy/plugins/monitoring/public/views/kibana/overview/index.js rename to x-pack/plugins/monitoring/public/views/kibana/overview/index.js index 0705e3b7f270ba..b0be4f7862a91c 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/kibana/overview/index.js +++ b/x-pack/plugins/monitoring/public/views/kibana/overview/index.js @@ -8,12 +8,12 @@ * Kibana Overview */ import React from 'react'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; +import { uiRoutes } from '../../../angular/helpers/routes'; import { MonitoringTimeseriesContainer } from '../../../components/chart'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { routeInitProvider } from '../../../lib/route_init'; import template from './index.html'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { Legacy } from '../../../legacy_shims'; import { EuiPage, EuiPageBody, @@ -24,7 +24,6 @@ import { EuiFlexItem, } from '@elastic/eui'; import { ClusterStatus } from '../../../components/kibana/cluster_status'; -import { I18nContext } from 'ui/i18n'; import { MonitoringViewBaseController } from '../../base_controller'; import { CODE_PATH_KIBANA } from '../../../../common/constants'; @@ -32,7 +31,7 @@ function getPageData($injector) { const $http = $injector.get('$http'); const globalState = $injector.get('globalState'); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/kibana`; - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { @@ -79,34 +78,32 @@ uiRoutes.when('/kibana', { } this.renderReact( - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/license/controller.js b/x-pack/plugins/monitoring/public/views/license/controller.js similarity index 71% rename from x-pack/legacy/plugins/monitoring/public/views/license/controller.js rename to x-pack/plugins/monitoring/public/views/license/controller.js index ce6e9c8fb74cd7..1d6a179db98c48 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/license/controller.js +++ b/x-pack/plugins/monitoring/public/views/license/controller.js @@ -8,19 +8,17 @@ import { get, find } from 'lodash'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import chrome from 'plugins/monitoring/np_imports/ui/chrome'; +import { Legacy } from '../../legacy_shims'; import { formatDateTimeLocal } from '../../../common/formatting'; -import { MANAGEMENT_BASE_PATH } from 'plugins/xpack_main/components'; -import { License } from 'plugins/monitoring/components'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; -import { I18nContext } from 'ui/i18n'; +import { BASE_PATH as MANAGEMENT_BASE_PATH } from '../../../../../plugins/license_management/common/constants'; +import { License } from '../../components'; const REACT_NODE_ID = 'licenseReact'; export class LicenseViewController { constructor($injector, $scope) { - timefilter.disableTimeRangeSelector(); - timefilter.disableAutoRefreshSelector(); + Legacy.shims.timefilter.disableTimeRangeSelector(); + Legacy.shims.timefilter.disableAutoRefreshSelector(); $scope.$on('$destroy', () => { unmountComponentAtNode(document.getElementById(REACT_NODE_ID)); @@ -47,14 +45,14 @@ export class LicenseViewController { this.isExpired = Date.now() > get(cluster, 'license.expiry_date_in_millis'); this.isPrimaryCluster = cluster.isPrimary; - const basePath = chrome.getBasePath(); + const basePath = Legacy.shims.getBasePath(); this.uploadLicensePath = basePath + '/app/kibana#' + MANAGEMENT_BASE_PATH + 'upload_license'; this.renderReact($scope); } renderReact($scope) { - const injector = chrome.dangerouslyGetActiveInjector(); + const injector = Legacy.shims.getAngularInjector(); const timezone = injector.get('config').get('dateFormat:tz'); $scope.$evalAsync(() => { const { isPrimaryCluster, license, isExpired, uploadLicensePath } = this; @@ -65,16 +63,14 @@ export class LicenseViewController { // Mount the React component to the template render( - - - , + , document.getElementById(REACT_NODE_ID) ); }); diff --git a/x-pack/legacy/plugins/monitoring/public/views/license/index.html b/x-pack/plugins/monitoring/public/views/license/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/license/index.html rename to x-pack/plugins/monitoring/public/views/license/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/license/index.js b/x-pack/plugins/monitoring/public/views/license/index.js similarity index 83% rename from x-pack/legacy/plugins/monitoring/public/views/license/index.js rename to x-pack/plugins/monitoring/public/views/license/index.js index e0796c85d8f854..46e93a8f01f457 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/license/index.js +++ b/x-pack/plugins/monitoring/public/views/license/index.js @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../angular/helpers/routes'; +import { routeInitProvider } from '../../lib/route_init'; import template from './index.html'; import { LicenseViewController } from './controller'; import { CODE_PATH_LICENSE } from '../../../common/constants'; diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/node/advanced/index.html b/x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/node/advanced/index.html rename to x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/node/advanced/index.js b/x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.js similarity index 65% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/node/advanced/index.js rename to x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.js index 29cf4839eff947..4099a99f122f2b 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/logstash/node/advanced/index.js +++ b/x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.js @@ -9,13 +9,13 @@ */ import React from 'react'; import { i18n } from '@kbn/i18n'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../../angular/helpers/routes'; +import { ajaxErrorHandlersProvider } from '../../../../lib/ajax_error_handler'; +import { routeInitProvider } from '../../../../lib/route_init'; import template from './index.html'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { Legacy } from '../../../../legacy_shims'; import { MonitoringViewBaseController } from '../../../base_controller'; -import { DetailStatus } from 'plugins/monitoring/components/logstash/detail_status'; +import { DetailStatus } from '../../../../components/logstash/detail_status'; import { EuiPage, EuiPageBody, @@ -26,7 +26,6 @@ import { EuiFlexItem, } from '@elastic/eui'; import { MonitoringTimeseriesContainer } from '../../../../components/chart'; -import { I18nContext } from 'ui/i18n'; import { CODE_PATH_LOGSTASH } from '../../../../../common/constants'; function getPageData($injector) { @@ -34,7 +33,7 @@ function getPageData($injector) { const globalState = $injector.get('globalState'); const $route = $injector.get('$route'); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/logstash/node/${$route.current.params.uuid}`; - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { @@ -97,31 +96,29 @@ uiRoutes.when('/logstash/node/:uuid/advanced', { ]; this.renderReact( - - - - - - - - - - {metricsToShow.map((metric, index) => ( - - - - - ))} - - - - - + + + + + + + + + {metricsToShow.map((metric, index) => ( + + + + + ))} + + + + ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/node/index.html b/x-pack/plugins/monitoring/public/views/logstash/node/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/node/index.html rename to x-pack/plugins/monitoring/public/views/logstash/node/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/node/index.js b/x-pack/plugins/monitoring/public/views/logstash/node/index.js similarity index 65% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/node/index.js rename to x-pack/plugins/monitoring/public/views/logstash/node/index.js index f1777d1e46ef01..141761d8cc11a9 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/logstash/node/index.js +++ b/x-pack/plugins/monitoring/public/views/logstash/node/index.js @@ -9,12 +9,12 @@ */ import React from 'react'; import { i18n } from '@kbn/i18n'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { routeInitProvider } from '../../../lib/route_init'; import template from './index.html'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; -import { DetailStatus } from 'plugins/monitoring/components/logstash/detail_status'; +import { Legacy } from '../../../legacy_shims'; +import { DetailStatus } from '../../../components/logstash/detail_status'; import { EuiPage, EuiPageBody, @@ -25,7 +25,6 @@ import { EuiFlexItem, } from '@elastic/eui'; import { MonitoringTimeseriesContainer } from '../../../components/chart'; -import { I18nContext } from 'ui/i18n'; import { MonitoringViewBaseController } from '../../base_controller'; import { CODE_PATH_LOGSTASH } from '../../../../common/constants'; @@ -34,7 +33,7 @@ function getPageData($injector) { const $route = $injector.get('$route'); const globalState = $injector.get('globalState'); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/logstash/node/${$route.current.params.uuid}`; - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { @@ -98,31 +97,29 @@ uiRoutes.when('/logstash/node/:uuid', { ]; this.renderReact( - - - - - - - - - - {metricsToShow.map((metric, index) => ( - - - - - ))} - - - - - + + + + + + + + + {metricsToShow.map((metric, index) => ( + + + + + ))} + + + + ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/node/pipelines/index.html b/x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/node/pipelines/index.html rename to x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/node/pipelines/index.js b/x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.js similarity index 74% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/node/pipelines/index.js rename to x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.js index 017988b70bdd41..442e8533c18f60 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/logstash/node/pipelines/index.js +++ b/x-pack/plugins/monitoring/public/views/logstash/node/pipelines/index.js @@ -10,14 +10,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; -import { isPipelineMonitoringSupportedInVersion } from 'plugins/monitoring/lib/logstash/pipelines'; +import { uiRoutes } from '../../../../angular/helpers/routes'; +import { ajaxErrorHandlersProvider } from '../../../../lib/ajax_error_handler'; +import { routeInitProvider } from '../../../../lib/route_init'; +import { isPipelineMonitoringSupportedInVersion } from '../../../../lib/logstash/pipelines'; import template from './index.html'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { Legacy } from '../../../../legacy_shims'; import { MonitoringViewBaseEuiTableController } from '../../../'; -import { I18nContext } from 'ui/i18n'; import { PipelineListing } from '../../../../components/logstash/pipeline_listing/pipeline_listing'; import { DetailStatus } from '../../../../components/logstash/detail_status'; import { CODE_PATH_LOGSTASH } from '../../../../../common/constants'; @@ -31,7 +30,7 @@ const getPageData = ($injector, _api = undefined, routeOptions = {}) => { const logstashUuid = $route.current.params.uuid; const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/logstash/node/${logstashUuid}/pipelines`; - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { @@ -107,23 +106,21 @@ uiRoutes.when('/logstash/node/:uuid/pipelines', { }; this.renderReact( - - - + ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/nodes/get_page_data.js b/x-pack/plugins/monitoring/public/views/logstash/nodes/get_page_data.js similarity index 80% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/nodes/get_page_data.js rename to x-pack/plugins/monitoring/public/views/logstash/nodes/get_page_data.js index d476f6ba5143e1..1d5d6007814f4d 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/logstash/nodes/get_page_data.js +++ b/x-pack/plugins/monitoring/public/views/logstash/nodes/get_page_data.js @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { Legacy } from '../../../legacy_shims'; export function getPageData($injector) { const $http = $injector.get('$http'); const globalState = $injector.get('globalState'); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/logstash/nodes`; - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/nodes/index.html b/x-pack/plugins/monitoring/public/views/logstash/nodes/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/nodes/index.html rename to x-pack/plugins/monitoring/public/views/logstash/nodes/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/nodes/index.js b/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js similarity index 57% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/nodes/index.js rename to x-pack/plugins/monitoring/public/views/logstash/nodes/index.js index 30f851b2a75340..49b2a20f11ea20 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/logstash/nodes/index.js +++ b/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { Fragment } from 'react'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { routeInitProvider } from '../../../lib/route_init'; import { MonitoringViewBaseEuiTableController } from '../../'; import { getPageData } from './get_page_data'; import template from './index.html'; -import { I18nContext } from 'ui/i18n'; import { Listing } from '../../../components/logstash/listing'; import { SetupModeRenderer } from '../../../components/renderers'; import { CODE_PATH_LOGSTASH, LOGSTASH_SYSTEM_ID } from '../../../../common/constants'; @@ -41,28 +40,26 @@ uiRoutes.when('/logstash/nodes', { () => this.data, data => { this.renderReact( - - ( - - {flyoutComponent} - - {bottomBarComponent} - - )} - /> - + ( + + {flyoutComponent} + + {bottomBarComponent} + + )} + /> ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/overview/index.html b/x-pack/plugins/monitoring/public/views/logstash/overview/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/overview/index.html rename to x-pack/plugins/monitoring/public/views/logstash/overview/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/overview/index.js b/x-pack/plugins/monitoring/public/views/logstash/overview/index.js similarity index 73% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/overview/index.js rename to x-pack/plugins/monitoring/public/views/logstash/overview/index.js index f41f54555952ec..05b1747f4cfff0 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/logstash/overview/index.js +++ b/x-pack/plugins/monitoring/public/views/logstash/overview/index.js @@ -8,12 +8,11 @@ * Logstash Overview */ import React from 'react'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { routeInitProvider } from '../../../lib/route_init'; import template from './index.html'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; -import { I18nContext } from 'ui/i18n'; +import { Legacy } from '../../../legacy_shims'; import { Overview } from '../../../components/logstash/overview'; import { MonitoringViewBaseController } from '../../base_controller'; import { CODE_PATH_LOGSTASH } from '../../../../common/constants'; @@ -22,7 +21,7 @@ function getPageData($injector) { const $http = $injector.get('$http'); const globalState = $injector.get('globalState'); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/logstash`; - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { @@ -63,14 +62,12 @@ uiRoutes.when('/logstash', { () => this.data, data => { this.renderReact( - - - + ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/pipeline/index.html b/x-pack/plugins/monitoring/public/views/logstash/pipeline/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/pipeline/index.html rename to x-pack/plugins/monitoring/public/views/logstash/pipeline/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/pipeline/index.js b/x-pack/plugins/monitoring/public/views/logstash/pipeline/index.js similarity index 77% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/pipeline/index.js rename to x-pack/plugins/monitoring/public/views/logstash/pipeline/index.js index 11cb8516847c87..0dd24d68540ce9 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/logstash/pipeline/index.js +++ b/x-pack/plugins/monitoring/public/views/logstash/pipeline/index.js @@ -8,21 +8,20 @@ * Logstash Node Pipeline View */ import React from 'react'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; +import { uiRoutes } from '../../../angular/helpers/routes'; import moment from 'moment'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { routeInitProvider } from '../../../lib/route_init'; import { CALCULATE_DURATION_SINCE, CODE_PATH_LOGSTASH } from '../../../../common/constants'; import { formatTimestampToDuration } from '../../../../common/format_timestamp_to_duration'; import template from './index.html'; import { i18n } from '@kbn/i18n'; -import { List } from 'plugins/monitoring/components/logstash/pipeline_viewer/models/list'; -import { PipelineState } from 'plugins/monitoring/components/logstash/pipeline_viewer/models/pipeline_state'; -import { PipelineViewer } from 'plugins/monitoring/components/logstash/pipeline_viewer'; -import { Pipeline } from 'plugins/monitoring/components/logstash/pipeline_viewer/models/pipeline'; -import { vertexFactory } from 'plugins/monitoring/components/logstash/pipeline_viewer/models/graph/vertex_factory'; +import { List } from '../../../components/logstash/pipeline_viewer/models/list'; +import { PipelineState } from '../../../components/logstash/pipeline_viewer/models/pipeline_state'; +import { PipelineViewer } from '../../../components/logstash/pipeline_viewer'; +import { Pipeline } from '../../../components/logstash/pipeline_viewer/models/pipeline'; +import { vertexFactory } from '../../../components/logstash/pipeline_viewer/models/graph/vertex_factory'; import { MonitoringViewBaseController } from '../../base_controller'; -import { I18nContext } from 'ui/i18n'; import { EuiPageBody, EuiPage, EuiPageContent } from '@elastic/eui'; let previousPipelineHash = undefined; @@ -146,22 +145,20 @@ uiRoutes.when('/logstash/pipelines/:id/:hash?', { this.pipelineState = new PipelineState(data.pipeline); this.detailVertex = data.vertex ? vertexFactory(null, data.vertex) : null; this.renderReact( - - - - - - - - - + + + + + + + ); } ); diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/pipelines/index.html b/x-pack/plugins/monitoring/public/views/logstash/pipelines/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/pipelines/index.html rename to x-pack/plugins/monitoring/public/views/logstash/pipelines/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/logstash/pipelines/index.js b/x-pack/plugins/monitoring/public/views/logstash/pipelines/index.js similarity index 75% rename from x-pack/legacy/plugins/monitoring/public/views/logstash/pipelines/index.js rename to x-pack/plugins/monitoring/public/views/logstash/pipelines/index.js index 75a18000c14dd3..4ddaba1e0a7c9c 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/logstash/pipelines/index.js +++ b/x-pack/plugins/monitoring/public/views/logstash/pipelines/index.js @@ -7,13 +7,12 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { find } from 'lodash'; -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; -import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler'; -import { routeInitProvider } from 'plugins/monitoring/lib/route_init'; -import { isPipelineMonitoringSupportedInVersion } from 'plugins/monitoring/lib/logstash/pipelines'; +import { uiRoutes } from '../../../angular/helpers/routes'; +import { ajaxErrorHandlersProvider } from '../../../lib/ajax_error_handler'; +import { routeInitProvider } from '../../../lib/route_init'; +import { isPipelineMonitoringSupportedInVersion } from '../../../lib/logstash/pipelines'; import template from './index.html'; -import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter'; -import { I18nContext } from 'ui/i18n'; +import { Legacy } from '../../../legacy_shims'; import { PipelineListing } from '../../../components/logstash/pipeline_listing/pipeline_listing'; import { MonitoringViewBaseEuiTableController } from '../..'; import { CODE_PATH_LOGSTASH } from '../../../../common/constants'; @@ -29,7 +28,7 @@ const getPageData = ($injector, _api = undefined, routeOptions = {}) => { const Private = $injector.get('Private'); const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/logstash/pipelines`; - const timeBounds = timefilter.getBounds(); + const timeBounds = Legacy.shims.timefilter.getBounds(); return $http .post(url, { @@ -103,21 +102,19 @@ uiRoutes.when('/logstash/pipelines', { }; super.renderReact( - - this.onBrush({ xaxis })} - stats={pageData.clusterStatus} - data={pageData.pipelines} - {...this.getPaginationTableProps(pagination)} - upgradeMessage={upgradeMessage} - dateFormat={config.get('dateFormat')} - angular={{ - kbnUrl, - scope: $scope, - }} - /> - + this.onBrush({ xaxis })} + stats={pageData.clusterStatus} + data={pageData.pipelines} + {...this.getPaginationTableProps(pagination)} + upgradeMessage={upgradeMessage} + dateFormat={config.get('dateFormat')} + angular={{ + kbnUrl, + scope: $scope, + }} + /> ); }; diff --git a/x-pack/legacy/plugins/monitoring/public/views/no_data/__tests__/model_updater.test.js b/x-pack/plugins/monitoring/public/views/no_data/__tests__/model_updater.test.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/no_data/__tests__/model_updater.test.js rename to x-pack/plugins/monitoring/public/views/no_data/__tests__/model_updater.test.js diff --git a/x-pack/legacy/plugins/monitoring/public/views/no_data/controller.js b/x-pack/plugins/monitoring/public/views/no_data/controller.js similarity index 87% rename from x-pack/legacy/plugins/monitoring/public/views/no_data/controller.js rename to x-pack/plugins/monitoring/public/views/no_data/controller.js index a914aa0155e905..14c30da2ce9993 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/no_data/controller.js +++ b/x-pack/plugins/monitoring/public/views/no_data/controller.js @@ -10,17 +10,17 @@ import { NodeSettingsChecker, Enabler, startChecks, -} from 'plugins/monitoring/lib/elasticsearch_settings'; +} from '../../lib/elasticsearch_settings'; import { ModelUpdater } from './model_updater'; -import { NoData } from 'plugins/monitoring/components'; -import { I18nContext } from 'ui/i18n'; +import { NoData } from '../../components'; import { CODE_PATH_LICENSE } from '../../../common/constants'; import { MonitoringViewBaseController } from '../base_controller'; import { i18n } from '@kbn/i18n'; -import { npSetup } from 'ui/new_platform'; +import { Legacy } from '../../legacy_shims'; export class NoDataController extends MonitoringViewBaseController { constructor($injector, $scope) { + window.injectorThree = $injector; const monitoringClusters = $injector.get('monitoringClusters'); const kbnUrl = $injector.get('kbnUrl'); const $http = $injector.get('$http'); @@ -99,18 +99,13 @@ export class NoDataController extends MonitoringViewBaseController { render(enabler) { const props = this; - const { cloud } = npSetup.plugins; - const isCloudEnabled = !!(cloud && cloud.isCloudEnabled); - this.renderReact( - - - + ); } } diff --git a/x-pack/legacy/plugins/monitoring/public/views/no_data/index.html b/x-pack/plugins/monitoring/public/views/no_data/index.html similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/no_data/index.html rename to x-pack/plugins/monitoring/public/views/no_data/index.html diff --git a/x-pack/legacy/plugins/monitoring/public/views/no_data/index.js b/x-pack/plugins/monitoring/public/views/no_data/index.js similarity index 63% rename from x-pack/legacy/plugins/monitoring/public/views/no_data/index.js rename to x-pack/plugins/monitoring/public/views/no_data/index.js index edade513e5ab21..9876739dfcbbe6 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/no_data/index.js +++ b/x-pack/plugins/monitoring/public/views/no_data/index.js @@ -4,13 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import uiRoutes from 'plugins/monitoring/np_imports/ui/routes'; +import { uiRoutes } from '../../angular/helpers/routes'; import template from './index.html'; import { NoDataController } from './controller'; -uiRoutes - .when('/no-data', { - template, - controller: NoDataController, - }) - .otherwise({ redirectTo: '/home' }); +uiRoutes.when('/no-data', { + template, + controller: NoDataController, +}); diff --git a/x-pack/legacy/plugins/monitoring/public/views/no_data/model_updater.js b/x-pack/plugins/monitoring/public/views/no_data/model_updater.js similarity index 100% rename from x-pack/legacy/plugins/monitoring/public/views/no_data/model_updater.js rename to x-pack/plugins/monitoring/public/views/no_data/model_updater.js diff --git a/x-pack/plugins/monitoring/server/index.ts b/x-pack/plugins/monitoring/server/index.ts index a992037fc6087e..60f04c535ebf1a 100644 --- a/x-pack/plugins/monitoring/server/index.ts +++ b/x-pack/plugins/monitoring/server/index.ts @@ -10,8 +10,14 @@ import { Plugin } from './plugin'; import { configSchema } from './config'; import { deprecations } from './deprecations'; +export { MonitoringConfig } from './config'; export const plugin = (initContext: PluginInitializerContext) => new Plugin(initContext); export const config: PluginConfigDescriptor> = { schema: configSchema, deprecations, + exposeToBrowser: { + enabled: true, + ui: true, + kibana: true, + }, }; diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts index 90187b7853185a..3df2b015aa0343 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts @@ -8,7 +8,7 @@ import { CallAPIOptions } from 'src/core/server'; import { take } from 'rxjs/operators'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { Observable } from 'rxjs'; -import { KIBANA_STATS_TYPE_MONITORING } from '../../../../legacy/plugins/monitoring/common/constants'; +import { KIBANA_STATS_TYPE_MONITORING } from '../../../monitoring/common/constants'; import { KIBANA_SPACES_STATS_TYPE } from '../../common/constants'; import { PluginsSetup } from '../plugin'; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index bf257d66a59bc2..4cd152f2d297d7 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -11921,7 +11921,6 @@ "xpack.monitoring.metrics.logstashInstance.systemLoad.last5MinutesDescription": "過去 5 分間の平均負荷です。", "xpack.monitoring.metrics.logstashInstance.systemLoad.last5MinutesLabel": "5m", "xpack.monitoring.monitoringDescription": "Elastic Stack のリアルタイムのヘルスとパフォーマンスをトラッキングします。", - "xpack.monitoring.monitoringTitle": "Monitoring", "xpack.monitoring.noData.blurbs.changesNeededDescription": "監視を実行するには、次の手順に従います", "xpack.monitoring.noData.blurbs.changesNeededTitle": "調整が必要です", "xpack.monitoring.noData.blurbs.cloudDeploymentDescription": "次の場所に戻ってください: ", @@ -12001,7 +12000,6 @@ "xpack.monitoring.summaryStatus.statusDescription": "ステータス", "xpack.monitoring.summaryStatus.statusIconLabel": "ステータス: {status}", "xpack.monitoring.summaryStatus.statusIconTitle": "ステータス: {statusIcon}", - "xpack.monitoring.uiExportsDescription": "Elastic Stack の監視です", "xpack.painlessLab.apiReferenceButtonLabel": "API リファレンス", "xpack.painlessLab.context.defaultLabel": "スクリプト結果は文字列に変換されます", "xpack.painlessLab.context.filterLabel": "フィルターのスクリプトクエリのコンテキストを使用する", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 323ddfda32d703..619ea0646eb44a 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -11925,7 +11925,6 @@ "xpack.monitoring.metrics.logstashInstance.systemLoad.last5MinutesDescription": "过去 5 分钟的负载平均值。", "xpack.monitoring.metrics.logstashInstance.systemLoad.last5MinutesLabel": "5 分钟", "xpack.monitoring.monitoringDescription": "跟踪 Elastic Stack 的实时运行状况和性能。", - "xpack.monitoring.monitoringTitle": "Monitoring", "xpack.monitoring.noData.blurbs.changesNeededDescription": "要运行 Monitoring,请执行以下步骤", "xpack.monitoring.noData.blurbs.changesNeededTitle": "您需要做些调整", "xpack.monitoring.noData.blurbs.cloudDeploymentDescription": "请返回到您的 ", @@ -12005,7 +12004,6 @@ "xpack.monitoring.summaryStatus.statusDescription": "状态", "xpack.monitoring.summaryStatus.statusIconLabel": "状态:{status}", "xpack.monitoring.summaryStatus.statusIconTitle": "状态:{statusIcon}", - "xpack.monitoring.uiExportsDescription": "Elastic Stack 的 Monitoring 组件", "xpack.painlessLab.apiReferenceButtonLabel": "API 参考", "xpack.painlessLab.context.defaultLabel": "脚本结果将转换成字符串", "xpack.painlessLab.context.filterLabel": "使用筛选脚本查询的上下文", From 16ba937bae63c9925b80285ad076325ee1a63569 Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Thu, 30 Apr 2020 13:08:50 -0700 Subject: [PATCH 30/47] [APM] Client new platform migration (#64046) * migrate files from legacy path to new plugin path * update file paths to reflect migrated files * move minimal legacy client files back to legacy path in order to run kibana * Completes the full cutover to the new kibana platform removing all shims and legacy adapters. * Adds APM to ignored list for casing check. * - moves public/utils/pickKeys.ts to common/utils/pick_keys.ts - exposes getTraceUrl as a plugin static export of apm/public and updates import in infra - fixes FeaturesPluginSetup import in apm/public app - renames get_apm_index_pattern_titles -> get_apm_index_pattern_title - getApmIndexPatternTitle is now a synchronous getter function - removes unused comments and xpack.apm.apmForESDescription i18n translations * Moves automatic index pattern saved object creation from plugin start to when the Home screen first renders * removed unnecessary legacy css imports * fixed ci issues by: - moving readOnly badge, and help extension setup to occure only when apm app is mounted - registering saved object types - also moved createStaticIndexPattern from a react useEffect on the APM home screen to when the app is mounted --- src/cli/cluster/cluster_manager.ts | 4 +- src/dev/precommit_hook/casing_check_config.js | 6 +- src/dev/run_check_lockfile_symlinks.js | 4 +- src/dev/storybook/aliases.ts | 2 +- src/dev/typescript/projects.ts | 2 +- x-pack/index.js | 2 - x-pack/legacy/plugins/apm/.prettierrc | 4 - .../apm/e2e/cypress/integration/snapshots.js | 10 - x-pack/legacy/plugins/apm/index.ts | 166 ---- x-pack/legacy/plugins/apm/mappings.json | 933 ------------------ x-pack/legacy/plugins/apm/public/index.scss | 16 - x-pack/legacy/plugins/apm/public/index.tsx | 36 - .../apm/public/legacy_register_feature.ts | 24 - .../getConfigFromInjectedMetadata.ts | 24 - .../plugins/apm/public/new-platform/index.tsx | 13 - .../apm/public/new-platform/plugin.tsx | 203 ---- .../apm/public/style/global_overrides.css | 34 - .../plugins/apm/public/templates/index.html | 1 - .../{legacy => }/plugins/apm/CONTRIBUTING.md | 0 .../apm/common/apm_saved_object_constants.ts | 2 +- .../apm/common/utils/pick_keys.ts} | 0 .../plugins/apm/dev_docs/github_commands.md | 0 .../plugins/apm/dev_docs/typescript.md | 4 +- .../plugins/apm/dev_docs/vscode_setup.md | 6 +- .../{legacy => }/plugins/apm/e2e/.gitignore | 0 x-pack/{legacy => }/plugins/apm/e2e/README.md | 6 +- .../plugins/apm/e2e/ci/Dockerfile | 0 .../plugins/apm/e2e/ci/entrypoint.sh | 4 +- .../plugins/apm/e2e/ci/kibana.e2e.yml | 0 .../plugins/apm/e2e/ci/prepare-kibana.sh | 2 +- .../{legacy => }/plugins/apm/e2e/cypress.json | 0 .../apm/e2e/cypress/fixtures/example.json | 0 .../apm/e2e/cypress/integration/apm.feature | 0 .../apm/e2e/cypress/integration/helpers.ts | 0 .../apm/e2e/cypress/integration/snapshots.js | 16 + .../plugins/apm/e2e/cypress/plugins/index.js | 0 .../apm/e2e/cypress/support/commands.js | 0 .../plugins/apm/e2e/cypress/support/index.ts | 0 .../cypress/support/step_definitions/apm.ts | 0 .../apm/e2e/cypress/typings/index.d.ts | 0 .../plugins/apm/e2e/cypress/webpack.config.js | 0 .../plugins/apm/e2e/ingest-data/replay.js | 0 .../{legacy => }/plugins/apm/e2e/package.json | 0 .../{legacy => }/plugins/apm/e2e/run-e2e.sh | 2 +- .../plugins/apm/e2e/tsconfig.json | 0 x-pack/{legacy => }/plugins/apm/e2e/yarn.lock | 0 x-pack/plugins/apm/kibana.json | 24 +- .../plugins/apm/public/application/index.tsx | 123 +++ .../__test__/APMIndicesPermission.test.tsx | 0 .../app/APMIndicesPermission/index.tsx | 0 .../DetailView/ErrorTabs.tsx | 2 +- .../DetailView/ExceptionStacktrace.test.tsx | 0 .../DetailView/ExceptionStacktrace.tsx | 2 +- .../__snapshots__/index.test.tsx.snap | 0 .../DetailView/index.test.tsx | 0 .../ErrorGroupDetails/DetailView/index.tsx | 4 +- .../ErrorGroupDetails/Distribution/index.tsx | 0 .../app/ErrorGroupDetails/index.tsx | 4 +- .../List/__test__/List.test.tsx | 0 .../__test__/__snapshots__/List.test.tsx.snap | 0 .../List/__test__/props.json | 0 .../app/ErrorGroupOverview/List/index.tsx | 4 +- .../app/ErrorGroupOverview/index.tsx | 4 +- .../public/components/app/Home/Home.test.tsx | 0 .../app/Home/__snapshots__/Home.test.tsx.snap | 2 - .../apm/public/components/app/Home/index.tsx | 0 .../app/Main/ProvideBreadcrumbs.tsx | 0 .../app/Main/ScrollToTopOnPathChange.tsx | 0 .../app/Main/UpdateBreadcrumbs.test.tsx | 0 .../components/app/Main/UpdateBreadcrumbs.tsx | 0 .../UpdateBreadcrumbs.test.tsx.snap | 0 .../Main/__test__/ProvideBreadcrumbs.test.tsx | 0 .../app/Main/route_config/index.tsx | 4 +- .../route_handlers/agent_configuration.tsx | 0 .../app/Main/route_config/route_names.tsx | 0 .../AlertingFlyout/index.tsx | 4 +- .../AlertIntegrations/index.tsx | 2 +- .../app/ServiceDetails/ServiceDetailTabs.tsx | 5 +- .../TransactionSelect.tsx | 0 .../MachineLearningFlyout/index.tsx | 2 +- .../MachineLearningFlyout/view.tsx | 0 .../ServiceIntegrations/WatcherFlyout.tsx | 71 +- .../createErrorGroupWatch.test.ts.snap | 0 .../__test__/createErrorGroupWatch.test.ts | 0 .../__test__/esResponse.ts | 0 .../createErrorGroupWatch.ts | 2 +- .../ServiceIntegrations/index.tsx | 0 .../components/app/ServiceDetails/index.tsx | 0 .../components/app/ServiceMap/BetaBadge.tsx | 0 .../components/app/ServiceMap/Controls.tsx | 0 .../app/ServiceMap/Cytoscape.stories.tsx | 0 .../components/app/ServiceMap/Cytoscape.tsx | 2 +- .../app/ServiceMap/EmptyBanner.test.tsx | 0 .../components/app/ServiceMap/EmptyBanner.tsx | 0 .../app/ServiceMap/LoadingOverlay.tsx | 0 .../app/ServiceMap/Popover/Buttons.tsx | 0 .../app/ServiceMap/Popover/Contents.tsx | 2 +- .../app/ServiceMap/Popover/Info.tsx | 2 +- .../ServiceMap/Popover/Popover.stories.tsx | 0 .../Popover/ServiceMetricFetcher.tsx | 2 +- .../ServiceMap/Popover/ServiceMetricList.tsx | 2 +- .../app/ServiceMap/Popover/index.tsx | 2 +- .../cytoscape-layout-test-response.json | 0 .../app/ServiceMap/cytoscapeOptions.ts | 2 +- .../public/components/app/ServiceMap/icons.ts | 4 +- .../components/app/ServiceMap/icons/aws.svg | 0 .../app/ServiceMap/icons/cassandra.svg | 0 .../components/app/ServiceMap/icons/dark.svg | 0 .../app/ServiceMap/icons/database.svg | 0 .../app/ServiceMap/icons/default.svg | 0 .../app/ServiceMap/icons/documents.svg | 0 .../app/ServiceMap/icons/dot-net.svg | 0 .../app/ServiceMap/icons/elasticsearch.svg | 0 .../components/app/ServiceMap/icons/globe.svg | 0 .../components/app/ServiceMap/icons/go.svg | 0 .../app/ServiceMap/icons/graphql.svg | 0 .../components/app/ServiceMap/icons/grpc.svg | 0 .../app/ServiceMap/icons/handlebars.svg | 0 .../components/app/ServiceMap/icons/java.svg | 0 .../components/app/ServiceMap/icons/kafka.svg | 0 .../app/ServiceMap/icons/mongodb.svg | 0 .../components/app/ServiceMap/icons/mysql.svg | 0 .../app/ServiceMap/icons/nodejs.svg | 0 .../components/app/ServiceMap/icons/php.svg | 0 .../app/ServiceMap/icons/postgresql.svg | 0 .../app/ServiceMap/icons/python.svg | 0 .../components/app/ServiceMap/icons/redis.svg | 0 .../components/app/ServiceMap/icons/ruby.svg | 0 .../components/app/ServiceMap/icons/rumjs.svg | 0 .../app/ServiceMap/icons/websocket.svg | 0 .../components/app/ServiceMap/index.test.tsx | 2 +- .../components/app/ServiceMap/index.tsx | 4 +- .../app/ServiceMap/useRefDimensions.ts | 0 .../components/app/ServiceMetrics/index.tsx | 2 +- .../app/ServiceNodeMetrics/index.test.tsx | 0 .../app/ServiceNodeMetrics/index.tsx | 2 +- .../app/ServiceNodeOverview/index.tsx | 6 +- .../app/ServiceOverview/NoServicesMessage.tsx | 0 .../ServiceList/__test__/List.test.js | 0 .../__test__/__snapshots__/List.test.js.snap | 0 .../ServiceList/__test__/props.json | 0 .../app/ServiceOverview/ServiceList/index.tsx | 4 +- .../__test__/NoServicesMessage.test.tsx | 0 .../__test__/ServiceOverview.test.tsx | 0 .../NoServicesMessage.test.tsx.snap | 0 .../ServiceOverview.test.tsx.snap | 0 .../components/app/ServiceOverview/index.tsx | 6 +- .../ServicePage/FormRowSelect.tsx | 0 .../ServicePage/ServicePage.tsx | 4 +- .../SettingsPage/SettingFormRow.tsx | 6 +- .../SettingsPage/SettingsPage.tsx | 10 +- .../SettingsPage/saveConfig.ts | 4 +- .../index.stories.tsx | 2 +- .../AgentConfigurationCreateEdit/index.tsx | 2 +- .../List/ConfirmDeleteModal.tsx | 4 +- .../AgentConfigurations/List/index.tsx | 4 +- .../Settings/AgentConfigurations/index.tsx | 2 +- .../app/Settings/ApmIndices/index.test.tsx | 0 .../app/Settings/ApmIndices/index.tsx | 0 .../CustomLink/CreateCustomLinkButton.tsx | 0 .../CustomLinkFlyout/DeleteButton.tsx | 0 .../CustomLinkFlyout/Documentation.tsx | 0 .../CustomLinkFlyout/FiltersSection.tsx | 2 +- .../CustomLinkFlyout/FlyoutFooter.tsx | 0 .../CustomLinkFlyout/LinkPreview.test.tsx | 0 .../CustomLinkFlyout/LinkPreview.tsx | 4 +- .../CustomLinkFlyout/LinkSection.tsx | 2 +- .../CustomLinkFlyout/helper.test.ts | 2 +- .../CustomLink/CustomLinkFlyout/helper.ts | 6 +- .../CustomLink/CustomLinkFlyout/index.tsx | 2 +- .../CustomLinkFlyout/saveCustomLink.ts | 2 +- .../CustomLink/CustomLinkTable.tsx | 2 +- .../CustomizeUI/CustomLink/EmptyPrompt.tsx | 0 .../Settings/CustomizeUI/CustomLink/Title.tsx | 0 .../CustomizeUI/CustomLink/index.test.tsx | 2 +- .../Settings/CustomizeUI/CustomLink/index.tsx | 2 +- .../app/Settings/CustomizeUI/index.tsx | 0 .../public/components/app/Settings/index.tsx | 0 .../app/TraceLink/__test__/TraceLink.test.tsx | 0 .../public/components/app/TraceLink/index.tsx | 4 +- .../app/TraceOverview/TraceList.tsx | 2 +- .../components/app/TraceOverview/index.tsx | 4 +- .../__test__/distribution.test.ts | 2 +- .../TransactionDetails/Distribution/index.tsx | 4 +- .../WaterfallWithSummmary/ErrorCount.tsx | 0 .../MaybeViewTraceLink.tsx | 2 +- .../WaterfallWithSummmary/PercentOfParent.tsx | 0 .../WaterfallWithSummmary/TransactionTabs.tsx | 2 +- .../Marks/__test__/get_agent_marks.test.ts | 2 +- .../Marks/__test__/get_error_marks.test.ts | 0 .../Marks/get_agent_marks.ts | 2 +- .../Marks/get_error_marks.ts | 2 +- .../WaterfallContainer/Marks/index.ts | 0 .../WaterfallContainer/ServiceLegends.tsx | 0 .../Waterfall/FlyoutTopLevelProperties.tsx | 4 +- .../Waterfall/ResponsiveFlyout.tsx | 0 .../Waterfall/SpanFlyout/DatabaseContext.tsx | 2 +- .../Waterfall/SpanFlyout/HttpContext.tsx | 2 +- .../SpanFlyout/StickySpanProperties.tsx | 8 +- .../SpanFlyout/TruncateHeightSection.tsx | 0 .../Waterfall/SpanFlyout/index.tsx | 4 +- .../Waterfall/SyncBadge.stories.tsx | 0 .../Waterfall/SyncBadge.tsx | 0 .../TransactionFlyout/DroppedSpansWarning.tsx | 2 +- .../Waterfall/TransactionFlyout/index.tsx | 2 +- .../Waterfall/WaterfallFlyout.tsx | 0 .../Waterfall/WaterfallItem.tsx | 4 +- .../WaterfallContainer/Waterfall/index.tsx | 0 .../waterfall_helpers.test.ts.snap | 0 .../mock_responses/spans.json | 0 .../mock_responses/transaction.json | 0 .../waterfall_helpers.test.ts | 6 +- .../waterfall_helpers/waterfall_helpers.ts | 8 +- .../WaterfallContainer.stories.tsx | 2 +- .../WaterfallContainer/index.tsx | 0 .../waterfallContainer.stories.data.ts | 0 .../__tests__/ErrorCount.test.tsx | 0 .../WaterfallWithSummmary/index.tsx | 2 +- .../app/TransactionDetails/index.tsx | 4 +- .../app/TransactionOverview/List/index.tsx | 4 +- .../__jest__/TransactionOverview.test.tsx | 0 .../app/TransactionOverview/index.tsx | 4 +- .../app/TransactionOverview/useRedirect.ts | 0 .../components/shared/ApmHeader/index.tsx | 0 .../DatePicker/__test__/DatePicker.test.tsx | 0 .../components/shared/DatePicker/index.tsx | 0 .../public/components/shared/EmptyMessage.tsx | 0 .../shared/EnvironmentBadge/index.tsx | 0 .../shared/EnvironmentFilter/index.tsx | 2 +- .../ErrorRateAlertTrigger/index.stories.tsx | 0 .../shared/ErrorRateAlertTrigger/index.tsx | 4 +- .../components/shared/ErrorStatePrompt.tsx | 0 .../public/components/shared/EuiTabLink.tsx | 0 .../shared/HeightRetainer/index.tsx | 0 .../ImpactBar/__test__/ImpactBar.test.js | 0 .../__snapshots__/ImpactBar.test.js.snap | 0 .../components/shared/ImpactBar/index.tsx | 0 .../shared/KeyValueTable/FormattedValue.tsx | 2 +- .../__test__/KeyValueTable.test.tsx | 0 .../components/shared/KeyValueTable/index.tsx | 0 .../shared/KueryBar/Typeahead/ClickOutside.js | 1 + .../shared/KueryBar/Typeahead/Suggestion.js | 0 .../shared/KueryBar/Typeahead/Suggestions.js | 0 .../shared/KueryBar/Typeahead/index.js | 0 .../shared/KueryBar/get_bool_filter.ts | 4 +- .../components/shared/KueryBar/index.tsx | 2 +- .../LicensePrompt/LicensePrompt.stories.tsx | 0 .../components/shared/LicensePrompt/index.tsx | 0 .../Links/DiscoverLinks/DiscoverErrorLink.tsx | 4 +- .../Links/DiscoverLinks/DiscoverLink.tsx | 2 +- .../Links/DiscoverLinks/DiscoverSpanLink.tsx | 4 +- .../DiscoverLinks/DiscoverTransactionLink.tsx | 4 +- .../__test__/DiscoverErrorButton.test.tsx | 2 +- .../__test__/DiscoverErrorLink.test.tsx | 2 +- .../DiscoverLinks.integration.test.tsx | 6 +- .../DiscoverTransactionButton.test.tsx | 2 +- .../__test__/DiscoverTransactionLink.test.tsx | 2 +- .../DiscoverErrorButton.test.tsx.snap | 0 .../DiscoverErrorLink.test.tsx.snap | 0 .../DiscoverTransactionButton.test.tsx.snap | 0 .../DiscoverTransactionLink.test.tsx.snap | 0 .../__test__/mockTransaction.json | 0 .../shared/Links/ElasticDocsLink.tsx | 0 .../shared/Links/InfraLink.test.tsx | 0 .../components/shared/Links/InfraLink.tsx | 2 +- .../shared/Links/KibanaLink.test.tsx | 0 .../components/shared/Links/KibanaLink.tsx | 0 .../MachineLearningLinks/MLJobLink.test.tsx | 0 .../Links/MachineLearningLinks/MLJobLink.tsx | 2 +- .../MachineLearningLinks/MLLink.test.tsx | 0 .../Links/MachineLearningLinks/MLLink.tsx | 0 .../shared/Links/SetupInstructionsLink.tsx | 0 .../shared/Links/apm/APMLink.test.tsx | 0 .../components/shared/Links/apm/APMLink.tsx | 0 .../shared/Links/apm/ErrorDetailLink.tsx | 0 .../shared/Links/apm/ErrorOverviewLink.tsx | 2 +- .../shared/Links/apm/ExternalLinks.test.ts | 0 .../shared/Links/apm/ExternalLinks.ts | 0 .../components/shared/Links/apm/HomeLink.tsx | 0 .../shared/Links/apm/MetricOverviewLink.tsx | 2 +- .../shared/Links/apm/ServiceMapLink.tsx | 0 .../apm/ServiceNodeMetricOverviewLink.tsx | 2 +- .../Links/apm/ServiceNodeOverviewLink.tsx | 2 +- .../shared/Links/apm/ServiceOverviewLink.tsx | 2 +- .../shared/Links/apm/SettingsLink.tsx | 0 .../shared/Links/apm/TraceOverviewLink.tsx | 2 +- .../Links/apm/TransactionDetailLink.tsx | 2 +- .../Links/apm/TransactionOverviewLink.tsx | 2 +- .../Links/apm/agentConfigurationLinks.tsx | 2 +- .../components/shared/Links/rison_helpers.ts | 0 .../shared/Links/url_helpers.test.tsx | 0 .../components/shared/Links/url_helpers.ts | 4 +- .../components/shared/LoadingStatePrompt.tsx | 0 .../LocalUIFilters/Filter/FilterBadgeList.tsx | 0 .../Filter/FilterTitleButton.tsx | 0 .../shared/LocalUIFilters/Filter/index.tsx | 0 .../TransactionTypeFilter/index.tsx | 0 .../shared/LocalUIFilters/index.tsx | 4 +- .../__test__/ManagedTable.test.js | 0 .../__snapshots__/ManagedTable.test.js.snap | 0 .../components/shared/ManagedTable/index.tsx | 0 .../__test__/ErrorMetadata.test.tsx | 2 +- .../MetadataTable/ErrorMetadata/index.tsx | 2 +- .../MetadataTable/ErrorMetadata/sections.ts | 0 .../shared/MetadataTable/Section.tsx | 0 .../__test__/SpanMetadata.test.tsx | 2 +- .../MetadataTable/SpanMetadata/index.tsx | 2 +- .../MetadataTable/SpanMetadata/sections.ts | 0 .../__test__/TransactionMetadata.test.tsx | 2 +- .../TransactionMetadata/index.tsx | 2 +- .../TransactionMetadata/sections.ts | 0 .../__test__/MetadataTable.test.tsx | 0 .../MetadataTable/__test__/Section.test.tsx | 0 .../MetadataTable/__test__/helper.test.ts | 2 +- .../components/shared/MetadataTable/helper.ts | 6 +- .../components/shared/MetadataTable/index.tsx | 0 .../shared/MetadataTable/sections.ts | 0 .../shared/SelectWithPlaceholder/index.tsx | 0 .../PopoverExpression/index.tsx | 0 .../shared/ServiceAlertTrigger/index.tsx | 0 .../Stacktrace/CauseStacktrace.test.tsx | 0 .../shared/Stacktrace/CauseStacktrace.tsx | 2 +- .../components/shared/Stacktrace/Context.tsx | 2 +- .../shared/Stacktrace/FrameHeading.tsx | 2 +- .../Stacktrace/LibraryStacktrace.test.tsx | 0 .../shared/Stacktrace/LibraryStacktrace.tsx | 2 +- .../shared/Stacktrace/Stackframe.tsx | 2 +- .../shared/Stacktrace/Variables.tsx | 2 +- .../Stacktrace/__test__/Stackframe.test.tsx | 2 +- .../__snapshots__/Stackframe.test.tsx.snap | 0 .../__test__/__snapshots__/index.test.ts.snap | 0 .../shared/Stacktrace/__test__/index.test.ts | 2 +- .../Stacktrace/__test__/stacktraces.json | 0 .../components/shared/Stacktrace/index.tsx | 2 +- .../StickyProperties/StickyProperties.test.js | 5 +- .../StickyProperties.test.js.snap | 0 .../shared/StickyProperties/index.tsx | 0 .../shared/Summary/DurationSummaryItem.tsx | 0 .../Summary/ErrorCountSummaryItemBadge.tsx | 0 .../__test__/HttpInfoSummaryItem.test.tsx | 0 .../Summary/HttpInfoSummaryItem/index.tsx | 0 .../__test__/HttpStatusBadge.test.tsx | 0 .../shared/Summary/HttpStatusBadge/index.tsx | 0 .../Summary/HttpStatusBadge/statusCodes.ts | 0 .../Summary/TransactionResultSummaryItem.tsx | 0 .../Summary/TransactionSummary.test.tsx | 0 .../shared/Summary/TransactionSummary.tsx | 4 +- .../Summary/UserAgentSummaryItem.test.tsx | 0 .../shared/Summary/UserAgentSummaryItem.tsx | 2 +- .../Summary/__fixtures__/transactions.ts | 2 +- .../ErrorCountSummaryItemBadge.test.tsx | 0 .../components/shared/Summary/index.tsx | 2 +- .../TimestampTooltip/__test__/index.test.tsx | 0 .../shared/TimestampTooltip/index.tsx | 0 .../CustomLink/CustomLinkPopover.test.tsx | 4 +- .../CustomLink/CustomLinkPopover.tsx | 4 +- .../CustomLink/CustomLinkSection.test.tsx | 4 +- .../CustomLink/CustomLinkSection.tsx | 4 +- .../CustomLink/ManageCustomLink.test.tsx | 0 .../CustomLink/ManageCustomLink.tsx | 0 .../CustomLink/index.test.tsx | 4 +- .../CustomLink/index.tsx | 6 +- .../TransactionActionMenu.tsx | 6 +- .../__test__/TransactionActionMenu.test.tsx | 4 +- .../TransactionActionMenu.test.tsx.snap | 0 .../__test__/mockData.ts | 0 .../__test__/sections.test.ts | 2 +- .../shared/TransactionActionMenu/sections.ts | 2 +- .../TransactionBreakdownGraph/index.tsx | 11 +- .../TransactionBreakdownHeader.tsx | 0 .../TransactionBreakdownKpiList.tsx | 5 +- .../shared/TransactionBreakdown/index.tsx | 2 +- .../index.stories.tsx | 0 .../TransactionDurationAlertTrigger/index.tsx | 4 +- .../charts/CustomPlot/AnnotationsPlot.tsx | 4 +- .../charts/CustomPlot/InteractivePlot.js | 0 .../shared/charts/CustomPlot/Legends.js | 0 .../charts/CustomPlot/SelectionMarker.js | 0 .../shared/charts/CustomPlot/StaticPlot.js | 0 .../shared/charts/CustomPlot/StatusText.js | 0 .../shared/charts/CustomPlot/VoronoiPlot.js | 0 .../charts/CustomPlot/getEmptySeries.ts | 0 .../CustomPlot/getTimezoneOffsetInMs.test.ts | 0 .../CustomPlot/getTimezoneOffsetInMs.ts | 0 .../shared/charts/CustomPlot/index.js | 0 .../charts/CustomPlot/plotUtils.test.ts | 5 +- .../shared/charts/CustomPlot/plotUtils.tsx | 5 +- .../charts/CustomPlot/test/CustomPlot.test.js | 0 .../__snapshots__/CustomPlot.test.js.snap | 0 .../CustomPlot/test/responseWithData.json | 0 .../shared/charts/Histogram/SingleRect.js | 0 .../Histogram/__test__/Histogram.test.js | 0 .../__snapshots__/Histogram.test.js.snap | 0 .../charts/Histogram/__test__/response.json | 0 .../shared/charts/Histogram/index.js | 0 .../components/shared/charts/Legend/index.tsx | 0 .../shared/charts/MetricsChart/index.tsx | 6 +- .../shared/charts/Timeline/LastTickValue.tsx | 0 .../charts/Timeline/Marker/AgentMarker.tsx | 0 .../charts/Timeline/Marker/ErrorMarker.tsx | 2 +- .../Marker/__test__/AgentMarker.test.tsx | 0 .../Marker/__test__/ErrorMarker.test.tsx | 0 .../Timeline/Marker/__test__/Marker.test.tsx | 0 .../__snapshots__/AgentMarker.test.tsx.snap | 0 .../__snapshots__/ErrorMarker.test.tsx.snap | 0 .../__snapshots__/Marker.test.tsx.snap | 0 .../shared/charts/Timeline/Marker/index.tsx | 0 .../shared/charts/Timeline/Timeline.test.tsx | 0 .../shared/charts/Timeline/TimelineAxis.tsx | 0 .../shared/charts/Timeline/VerticalLines.tsx | 0 .../__snapshots__/Timeline.test.tsx.snap | 0 .../shared/charts/Timeline/index.tsx | 0 .../shared/charts/Timeline/plotUtils.ts | 0 .../components/shared/charts/Tooltip/index.js | 0 .../BrowserLineChart.test.tsx | 0 .../TransactionCharts/BrowserLineChart.tsx | 0 .../ChoroplethMap/ChoroplethToolTip.tsx | 0 .../TransactionCharts/ChoroplethMap/index.tsx | 0 .../DurationByCountryMap/index.tsx | 0 .../TransactionLineChart/index.tsx | 2 +- .../shared/charts/TransactionCharts/index.tsx | 9 +- .../charts/helper/__test__/timezone.test.ts | 0 .../shared/charts/helper/timezone.ts | 0 .../Delayed/index.test.tsx | 0 .../useDelayedVisibility/Delayed/index.ts | 0 .../useDelayedVisibility/index.test.tsx | 0 .../shared/useDelayedVisibility/index.ts | 0 .../ApmPluginContext/MockApmPluginContext.tsx | 3 +- .../public/context/ApmPluginContext/index.tsx | 3 +- .../apm/public/context/ChartsSyncContext.tsx | 0 .../InvalidLicenseNotification.tsx | 0 .../public/context/LicenseContext/index.tsx | 2 +- .../context/LoadingIndicatorContext.tsx | 0 .../apm/public/context/LocationContext.tsx | 0 .../public/context/MatchedRouteContext.tsx | 0 .../MockUrlParamsContextProvider.tsx | 0 .../__tests__/UrlParamsContext.test.tsx | 0 .../context/UrlParamsContext/constants.ts | 0 .../context/UrlParamsContext/helpers.ts | 2 +- .../public/context/UrlParamsContext/index.tsx | 6 +- .../UrlParamsContext/resolveUrlParams.ts | 4 +- .../public/context/UrlParamsContext/types.ts | 4 +- .../apm/public}/featureCatalogueEntry.ts | 2 +- .../plugins/apm/public/hooks/useAgentName.ts | 0 .../apm/public/hooks/useApmPluginContext.ts | 0 .../hooks/useAvgDurationByBrowser.test.ts | 0 .../public/hooks/useAvgDurationByBrowser.ts | 6 +- .../public/hooks/useAvgDurationByCountry.ts | 0 .../plugins/apm/public/hooks/useCallApi.ts | 0 .../apm/public/hooks/useChartsSync.tsx | 0 .../apm/public/hooks/useComponentId.tsx | 0 .../apm/public/hooks/useDeepObjectIdentity.ts | 0 .../public/hooks/useDynamicIndexPattern.ts | 2 +- .../hooks/useFetcher.integration.test.tsx | 0 .../apm/public/hooks/useFetcher.test.tsx | 0 .../plugins/apm/public/hooks/useFetcher.tsx | 2 +- .../plugins/apm/public/hooks/useKibanaUrl.ts | 0 .../plugins/apm/public/hooks/useLicense.ts | 0 .../apm/public/hooks/useLoadingIndicator.ts | 0 .../apm/public/hooks/useLocalUIFilters.ts | 8 +- .../plugins/apm/public/hooks/useLocation.tsx | 0 .../apm/public/hooks/useMatchedRoutes.tsx | 0 .../public/hooks/useServiceMetricCharts.ts | 2 +- .../hooks/useServiceTransactionTypes.tsx | 0 .../public/hooks/useTransactionBreakdown.ts | 0 .../apm/public/hooks/useTransactionCharts.ts | 0 .../hooks/useTransactionDistribution.ts | 2 +- .../apm/public/hooks/useTransactionList.ts | 2 +- .../plugins/apm/public/hooks/useUrlParams.tsx | 0 .../plugins/apm/public/hooks/useWaterfall.ts | 0 .../{legacy => }/plugins/apm/public/icon.svg | 0 .../apm-ml-anomaly-detection-example.png | Bin x-pack/plugins/apm/public/index.ts | 25 + x-pack/plugins/apm/public/plugin.ts | 133 +++ .../__tests__/chartSelectors.test.ts | 0 .../__tests__/mockData/anomalyData.ts | 0 .../apm/public/selectors/chartSelectors.ts | 6 +- .../services/__test__/SessionStorageMock.ts | 0 .../public/services/__test__/callApi.test.ts | 0 .../services/__test__/callApmApi.test.ts | 0 .../apm/public/services/rest/callApi.ts | 0 .../public/services/rest/createCallApmApi.ts | 4 +- .../apm/public/services/rest/index_pattern.ts | 6 + .../plugins/apm/public/services/rest/ml.ts | 6 +- .../apm/public/services/rest/watcher.ts | 0 .../apm/public}/setHelpExtension.ts | 0 .../plugins/apm/public/style/variables.ts | 0 .../apm/public}/toggleAppLinkInNav.ts | 2 +- .../apm/public}/updateBadge.ts | 1 - .../utils/__test__/flattenObject.test.ts | 0 .../plugins/apm/public/utils/flattenObject.ts | 2 +- .../formatters/__test__/datetime.test.ts | 0 .../formatters/__test__/duration.test.ts | 0 .../formatters/__test__/formatters.test.ts | 0 .../utils/formatters/__test__/size.test.ts | 0 .../apm/public/utils/formatters/datetime.ts | 0 .../apm/public/utils/formatters/duration.ts | 4 +- .../apm/public/utils/formatters/formatters.ts | 0 .../apm/public/utils/formatters/index.ts | 0 .../apm/public/utils/formatters/size.ts | 2 +- .../public/utils/getRangeFromTimeSeries.ts | 2 +- .../plugins/apm/public/utils/history.ts | 0 .../apm/public/utils/httpStatusCodeToColor.ts | 0 .../public/utils/isValidCoordinateValue.ts | 2 +- .../plugins/apm/public/utils/testHelpers.tsx | 6 +- x-pack/{legacy => }/plugins/apm/readme.md | 8 +- .../plugins/apm/scripts/.gitignore | 0 .../setup-custom-kibana-user-role.ts | 0 .../plugins/apm/scripts/optimize-tsconfig.js | 0 .../apm/scripts/optimize-tsconfig/optimize.js | 0 .../apm/scripts/optimize-tsconfig/paths.js | 0 .../scripts/optimize-tsconfig/tsconfig.json | 3 +- .../scripts/optimize-tsconfig/unoptimize.js | 0 .../plugins/apm/scripts/package.json | 0 .../apm/scripts/setup-kibana-security.js | 0 .../plugins/apm/scripts/storybook.js | 0 .../apm/scripts/unoptimize-tsconfig.js | 0 .../apm/scripts/upload-telemetry-data.js | 0 .../download-telemetry-template.ts | 0 .../generate-sample-documents.ts | 2 +- .../scripts/upload-telemetry-data/index.ts | 2 +- x-pack/plugins/apm/server/feature.ts | 72 ++ x-pack/plugins/apm/server/index.ts | 2 +- .../lib/errors/distribution/queries.test.ts | 2 +- .../apm/server/lib/errors/queries.test.ts | 2 +- .../apm/server/lib/helpers/es_client.ts | 2 +- .../server/lib/helpers/setup_request.test.ts | 13 - .../get_apm_index_pattern_title.ts | 10 + .../apm/server/lib/metrics/queries.test.ts | 2 +- .../server/lib/service_nodes/queries.test.ts | 2 +- .../lib/services/annotations/index.test.ts | 2 +- .../apm/server/lib/services/queries.test.ts | 2 +- .../agent_configuration/queries.test.ts | 2 +- .../create_or_update_custom_link.test.ts | 2 +- .../custom_link/get_transaction.test.ts | 2 +- .../custom_link/list_custom_links.test.ts | 2 +- .../apm/server/lib/traces/queries.test.ts | 2 +- .../lib/transaction_groups/queries.test.ts | 2 +- .../server/lib/transactions/queries.test.ts | 2 +- .../local_ui_filters/queries.test.ts | 2 +- .../apm/server/lib/ui_filters/queries.test.ts | 2 +- x-pack/plugins/apm/server/plugin.ts | 40 +- .../server/routes/create_api/index.test.ts | 4 +- .../apm/server/routes/create_api/index.ts | 3 +- .../apm/server/routes/create_apm_api.ts | 4 +- .../apm/server/routes/index_pattern.ts | 8 + x-pack/plugins/apm/server/routes/typings.ts | 7 +- .../apm/server/saved_objects/apm_indices.ts | 34 + .../apm/server/saved_objects/apm_telemetry.ts | 921 +++++++++++++++++ .../plugins/apm/server/saved_objects/index.ts | 8 + x-pack/plugins/apm/typings/common.d.ts | 8 +- .../log_entry_actions_menu.tsx | 2 +- .../lib/adapters/framework/adapter_types.ts | 4 +- .../siem/scripts/optimize_tsconfig/README.md | 2 +- .../calculate_timeseries_interval.ts | 2 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../kuery_bar/typeahead/suggestion.js | 3 +- .../kuery_bar/typeahead/suggestions.js | 3 +- x-pack/tsconfig.json | 2 +- 560 files changed, 1748 insertions(+), 1902 deletions(-) delete mode 100644 x-pack/legacy/plugins/apm/.prettierrc delete mode 100644 x-pack/legacy/plugins/apm/e2e/cypress/integration/snapshots.js delete mode 100644 x-pack/legacy/plugins/apm/index.ts delete mode 100644 x-pack/legacy/plugins/apm/mappings.json delete mode 100644 x-pack/legacy/plugins/apm/public/index.scss delete mode 100644 x-pack/legacy/plugins/apm/public/index.tsx delete mode 100644 x-pack/legacy/plugins/apm/public/legacy_register_feature.ts delete mode 100644 x-pack/legacy/plugins/apm/public/new-platform/getConfigFromInjectedMetadata.ts delete mode 100644 x-pack/legacy/plugins/apm/public/new-platform/index.tsx delete mode 100644 x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx delete mode 100644 x-pack/legacy/plugins/apm/public/style/global_overrides.css delete mode 100644 x-pack/legacy/plugins/apm/public/templates/index.html rename x-pack/{legacy => }/plugins/apm/CONTRIBUTING.md (100%) rename x-pack/{legacy/plugins/apm/public/utils/pickKeys.ts => plugins/apm/common/utils/pick_keys.ts} (100%) rename x-pack/{legacy => }/plugins/apm/dev_docs/github_commands.md (100%) rename x-pack/{legacy => }/plugins/apm/dev_docs/typescript.md (83%) rename x-pack/{legacy => }/plugins/apm/dev_docs/vscode_setup.md (83%) rename x-pack/{legacy => }/plugins/apm/e2e/.gitignore (100%) rename x-pack/{legacy => }/plugins/apm/e2e/README.md (84%) rename x-pack/{legacy => }/plugins/apm/e2e/ci/Dockerfile (100%) rename x-pack/{legacy => }/plugins/apm/e2e/ci/entrypoint.sh (90%) rename x-pack/{legacy => }/plugins/apm/e2e/ci/kibana.e2e.yml (100%) rename x-pack/{legacy => }/plugins/apm/e2e/ci/prepare-kibana.sh (95%) rename x-pack/{legacy => }/plugins/apm/e2e/cypress.json (100%) rename x-pack/{legacy => }/plugins/apm/e2e/cypress/fixtures/example.json (100%) rename x-pack/{legacy => }/plugins/apm/e2e/cypress/integration/apm.feature (100%) rename x-pack/{legacy => }/plugins/apm/e2e/cypress/integration/helpers.ts (100%) create mode 100644 x-pack/plugins/apm/e2e/cypress/integration/snapshots.js rename x-pack/{legacy => }/plugins/apm/e2e/cypress/plugins/index.js (100%) rename x-pack/{legacy => }/plugins/apm/e2e/cypress/support/commands.js (100%) rename x-pack/{legacy => }/plugins/apm/e2e/cypress/support/index.ts (100%) rename x-pack/{legacy => }/plugins/apm/e2e/cypress/support/step_definitions/apm.ts (100%) rename x-pack/{legacy => }/plugins/apm/e2e/cypress/typings/index.d.ts (100%) rename x-pack/{legacy => }/plugins/apm/e2e/cypress/webpack.config.js (100%) rename x-pack/{legacy => }/plugins/apm/e2e/ingest-data/replay.js (100%) rename x-pack/{legacy => }/plugins/apm/e2e/package.json (100%) rename x-pack/{legacy => }/plugins/apm/e2e/run-e2e.sh (98%) rename x-pack/{legacy => }/plugins/apm/e2e/tsconfig.json (100%) rename x-pack/{legacy => }/plugins/apm/e2e/yarn.lock (100%) create mode 100644 x-pack/plugins/apm/public/application/index.tsx rename x-pack/{legacy => }/plugins/apm/public/components/app/APMIndicesPermission/__test__/APMIndicesPermission.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/APMIndicesPermission/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ErrorTabs.tsx (92%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.tsx (92%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/__snapshots__/index.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/List.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/props.json (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Home/Home.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Home/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Main/ScrollToTopOnPathChange.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Main/__snapshots__/UpdateBreadcrumbs.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Main/__test__/ProvideBreadcrumbs.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Main/route_config/index.tsx (98%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Main/route_config/route_handlers/agent_configuration.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Main/route_config/route_names.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/AlertingFlyout/index.tsx (82%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/TransactionSelect.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/__snapshots__/createErrorGroupWatch.test.ts.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/createErrorGroupWatch.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/esResponse.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts (98%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceDetails/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/BetaBadge.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/Controls.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx (98%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/EmptyBanner.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/EmptyBanner.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/LoadingOverlay.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/Popover/Info.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/cytoscape-layout-test-response.json (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts (98%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons.ts (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/aws.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/cassandra.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/dark.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/database.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/default.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/documents.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/dot-net.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/elasticsearch.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/globe.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/go.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/graphql.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/grpc.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/handlebars.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/java.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/kafka.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/mongodb.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/mysql.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/nodejs.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/php.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/postgresql.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/python.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/redis.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/ruby.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/rumjs.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/icons/websocket.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/index.test.tsx (94%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/index.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMap/useRefDimensions.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceMetrics/index.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceNodeMetrics/index.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx (98%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/props.json (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx (94%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceOverview/__test__/NoServicesMessage.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/ServiceOverview/index.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/FormRowSelect.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/ServicePage.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx (92%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingsPage.tsx (94%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/saveConfig.ts (90%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/AgentConfigurations/List/ConfirmDeleteModal.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/AgentConfigurations/List/index.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/ApmIndices/index.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/DeleteButton.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/Documentation.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx (98%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FlyoutFooter.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.tsx (94%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkSection.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.test.ts (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.ts (91%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/index.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/saveCustomLink.ts (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/EmptyPrompt.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/Title.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx (99%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/CustomizeUI/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/Settings/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TraceLink/__test__/TraceLink.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TraceLink/index.tsx (92%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TraceOverview/TraceList.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TraceOverview/index.tsx (91%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts (92%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/ErrorCount.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/PercentOfParent.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts (92%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_error_marks.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts (87%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts (89%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/index.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/ServiceLegends.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx (90%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/ResponsiveFlyout.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/DatabaseContext.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/HttpContext.tsx (92%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx (86%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/TruncateHeightSection.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.stories.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/DroppedSpansWarning.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallFlyout.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/mock_responses/spans.json (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/mock_responses/transaction.json (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts (98%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/__tests__/ErrorCount.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionDetails/index.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionOverview/List/index.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionOverview/__jest__/TransactionOverview.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionOverview/index.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/app/TransactionOverview/useRedirect.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/ApmHeader/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/DatePicker/__test__/DatePicker.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/DatePicker/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/EmptyMessage.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/EnvironmentBadge/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.stories.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.tsx (92%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/ErrorStatePrompt.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/EuiTabLink.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/HeightRetainer/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/ImpactBar/__test__/ImpactBar.test.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/ImpactBar/__test__/__snapshots__/ImpactBar.test.js.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/ImpactBar/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/KeyValueTable/FormattedValue.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/KeyValueTable/__test__/KeyValueTable.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/KeyValueTable/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/KueryBar/Typeahead/ClickOutside.js (95%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestion.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestions.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/KueryBar/Typeahead/index.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts (92%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/KueryBar/index.tsx (98%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/LicensePrompt/LicensePrompt.stories.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/LicensePrompt/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.tsx (85%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverSpanLink.tsx (79%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.tsx (85%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx (94%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx (94%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx (92%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx (90%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx (88%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorButton.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorLink.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverTransactionButton.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverTransactionLink.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/mockTransaction.json (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/InfraLink.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/InfraLink.tsx (94%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/KibanaLink.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/KibanaLink.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx (88%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/SetupInstructionsLink.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/APMLink.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/APMLink.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/ErrorDetailLink.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/ExternalLinks.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/ExternalLinks.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/HomeLink.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx (92%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/ServiceNodeMetricOverviewLink.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx (92%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/ServiceOverviewLink.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/SettingsLink.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/TransactionDetailLink.tsx (94%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/apm/agentConfigurationLinks.tsx (87%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/rison_helpers.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/url_helpers.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Links/url_helpers.ts (87%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/LoadingStatePrompt.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterBadgeList.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterTitleButton.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/LocalUIFilters/Filter/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/LocalUIFilters/TransactionTypeFilter/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/LocalUIFilters/index.tsx (91%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/ManagedTable/__test__/ManagedTable.test.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/ManagedTable/__test__/__snapshots__/ManagedTable.test.js.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/ManagedTable/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx (87%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/sections.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/Section.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx (88%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx (87%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/__test__/Section.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts (96%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/helper.ts (85%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/MetadataTable/sections.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/SelectWithPlaceholder/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/ServiceAlertTrigger/PopoverExpression/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/ServiceAlertTrigger/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/Context.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/FrameHeading.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/Stackframe.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/Variables.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/__test__/Stackframe.test.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/index.test.ts.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/__test__/index.test.ts (97%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/__test__/stacktraces.json (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Stacktrace/index.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/StickyProperties/StickyProperties.test.js (95%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/StickyProperties/__snapshots__/StickyProperties.test.js.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/StickyProperties/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/DurationSummaryItem.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/__test__/HttpInfoSummaryItem.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/HttpStatusBadge/__test__/HttpStatusBadge.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/HttpStatusBadge/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/HttpStatusBadge/statusCodes.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/TransactionResultSummaryItem.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/TransactionSummary.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx (91%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.tsx (90%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts (92%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/Summary/index.tsx (94%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TimestampTooltip/__test__/index.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TimestampTooltip/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.test.tsx (91%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.tsx (90%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.test.tsx (85%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx (86%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx (94%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx (92%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx (98%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/__test__/__snapshots__/TransactionActionMenu.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/__test__/mockData.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts (98%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts (98%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx (81%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownHeader.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownKpiList.tsx (95%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionBreakdown/index.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionDurationAlertTrigger/index.stories.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/TransactionDurationAlertTrigger/index.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx (92%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/InteractivePlot.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/SelectionMarker.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/StaticPlot.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/StatusText.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/VoronoiPlot.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/getEmptySeries.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/index.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts (94%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/test/CustomPlot.test.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/CustomPlot/test/responseWithData.json (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Histogram/SingleRect.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Histogram/__test__/Histogram.test.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Histogram/__test__/__snapshots__/Histogram.test.js.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Histogram/__test__/response.json (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Histogram/index.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Legend/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx (90%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/LastTickValue.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx (97%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/AgentMarker.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/ErrorMarker.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/Marker.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/AgentMarker.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/ErrorMarker.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/Marker.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/Marker/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/Timeline.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/VerticalLines.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Timeline/plotUtils.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/Tooltip/index.js (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/ChoroplethToolTip.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx (96%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/helper/__test__/timezone.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/charts/helper/timezone.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/components/shared/useDelayedVisibility/index.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx (93%) rename x-pack/{legacy => }/plugins/apm/public/context/ApmPluginContext/index.tsx (87%) rename x-pack/{legacy => }/plugins/apm/public/context/ChartsSyncContext.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/context/LicenseContext/index.tsx (94%) rename x-pack/{legacy => }/plugins/apm/public/context/LoadingIndicatorContext.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/context/LocationContext.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/context/MatchedRouteContext.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/context/UrlParamsContext/MockUrlParamsContextProvider.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/context/UrlParamsContext/__tests__/UrlParamsContext.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/context/UrlParamsContext/constants.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/context/UrlParamsContext/helpers.ts (97%) rename x-pack/{legacy => }/plugins/apm/public/context/UrlParamsContext/index.tsx (92%) rename x-pack/{legacy => }/plugins/apm/public/context/UrlParamsContext/resolveUrlParams.ts (93%) rename x-pack/{legacy => }/plugins/apm/public/context/UrlParamsContext/types.ts (83%) rename x-pack/{legacy/plugins/apm/public/new-platform => plugins/apm/public}/featureCatalogueEntry.ts (88%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useAgentName.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useApmPluginContext.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useAvgDurationByBrowser.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useAvgDurationByBrowser.ts (83%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useAvgDurationByCountry.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useCallApi.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useChartsSync.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useComponentId.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useDeepObjectIdentity.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useDynamicIndexPattern.ts (89%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useFetcher.integration.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useFetcher.test.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useFetcher.tsx (98%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useKibanaUrl.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useLicense.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useLoadingIndicator.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useLocalUIFilters.ts (88%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useLocation.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useMatchedRoutes.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useServiceMetricCharts.ts (91%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useServiceTransactionTypes.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useTransactionBreakdown.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useTransactionCharts.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useTransactionDistribution.ts (93%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useTransactionList.ts (94%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useUrlParams.tsx (100%) rename x-pack/{legacy => }/plugins/apm/public/hooks/useWaterfall.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/icon.svg (100%) rename x-pack/{legacy => }/plugins/apm/public/images/apm-ml-anomaly-detection-example.png (100%) create mode 100644 x-pack/plugins/apm/public/index.ts create mode 100644 x-pack/plugins/apm/public/plugin.ts rename x-pack/{legacy => }/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/selectors/__tests__/mockData/anomalyData.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/selectors/chartSelectors.ts (94%) rename x-pack/{legacy => }/plugins/apm/public/services/__test__/SessionStorageMock.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/services/__test__/callApi.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/services/__test__/callApmApi.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/services/rest/callApi.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/services/rest/createCallApmApi.ts (89%) rename x-pack/{legacy => }/plugins/apm/public/services/rest/index_pattern.ts (76%) rename x-pack/{legacy => }/plugins/apm/public/services/rest/ml.ts (91%) rename x-pack/{legacy => }/plugins/apm/public/services/rest/watcher.ts (100%) rename x-pack/{legacy/plugins/apm/public/new-platform => plugins/apm/public}/setHelpExtension.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/style/variables.ts (100%) rename x-pack/{legacy/plugins/apm/public/new-platform => plugins/apm/public}/toggleAppLinkInNav.ts (91%) rename x-pack/{legacy/plugins/apm/public/new-platform => plugins/apm/public}/updateBadge.ts (99%) rename x-pack/{legacy => }/plugins/apm/public/utils/__test__/flattenObject.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/utils/flattenObject.ts (94%) rename x-pack/{legacy => }/plugins/apm/public/utils/formatters/__test__/datetime.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/utils/formatters/__test__/duration.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/utils/formatters/__test__/formatters.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/utils/formatters/__test__/size.test.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/utils/formatters/datetime.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/utils/formatters/duration.ts (96%) rename x-pack/{legacy => }/plugins/apm/public/utils/formatters/formatters.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/utils/formatters/index.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/utils/formatters/size.ts (95%) rename x-pack/{legacy => }/plugins/apm/public/utils/getRangeFromTimeSeries.ts (88%) rename x-pack/{legacy => }/plugins/apm/public/utils/history.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/utils/httpStatusCodeToColor.ts (100%) rename x-pack/{legacy => }/plugins/apm/public/utils/isValidCoordinateValue.ts (84%) rename x-pack/{legacy => }/plugins/apm/public/utils/testHelpers.tsx (96%) rename x-pack/{legacy => }/plugins/apm/readme.md (92%) rename x-pack/{legacy => }/plugins/apm/scripts/.gitignore (100%) rename x-pack/{legacy => }/plugins/apm/scripts/kibana-security/setup-custom-kibana-user-role.ts (100%) rename x-pack/{legacy => }/plugins/apm/scripts/optimize-tsconfig.js (100%) rename x-pack/{legacy => }/plugins/apm/scripts/optimize-tsconfig/optimize.js (100%) rename x-pack/{legacy => }/plugins/apm/scripts/optimize-tsconfig/paths.js (100%) rename x-pack/{legacy => }/plugins/apm/scripts/optimize-tsconfig/tsconfig.json (60%) rename x-pack/{legacy => }/plugins/apm/scripts/optimize-tsconfig/unoptimize.js (100%) rename x-pack/{legacy => }/plugins/apm/scripts/package.json (100%) rename x-pack/{legacy => }/plugins/apm/scripts/setup-kibana-security.js (100%) rename x-pack/{legacy => }/plugins/apm/scripts/storybook.js (100%) rename x-pack/{legacy => }/plugins/apm/scripts/unoptimize-tsconfig.js (100%) rename x-pack/{legacy => }/plugins/apm/scripts/upload-telemetry-data.js (100%) rename x-pack/{legacy => }/plugins/apm/scripts/upload-telemetry-data/download-telemetry-template.ts (100%) rename x-pack/{legacy => }/plugins/apm/scripts/upload-telemetry-data/generate-sample-documents.ts (97%) rename x-pack/{legacy => }/plugins/apm/scripts/upload-telemetry-data/index.ts (98%) create mode 100644 x-pack/plugins/apm/server/feature.ts create mode 100644 x-pack/plugins/apm/server/lib/index_pattern/get_apm_index_pattern_title.ts create mode 100644 x-pack/plugins/apm/server/saved_objects/apm_indices.ts create mode 100644 x-pack/plugins/apm/server/saved_objects/apm_telemetry.ts create mode 100644 x-pack/plugins/apm/server/saved_objects/index.ts diff --git a/src/cli/cluster/cluster_manager.ts b/src/cli/cluster/cluster_manager.ts index f16ba61234a950..97dec3eead303c 100644 --- a/src/cli/cluster/cluster_manager.ts +++ b/src/cli/cluster/cluster_manager.ts @@ -266,8 +266,8 @@ export class ClusterManager { fromRoot('src/legacy/server/sass/__tmp__'), fromRoot('x-pack/legacy/plugins/reporting/.chromium'), fromRoot('x-pack/plugins/siem/cypress'), - fromRoot('x-pack/legacy/plugins/apm/e2e'), - fromRoot('x-pack/legacy/plugins/apm/scripts'), + fromRoot('x-pack/plugins/apm/e2e'), + fromRoot('x-pack/plugins/apm/scripts'), fromRoot('x-pack/legacy/plugins/canvas/canvas_plugin_src'), // prevents server from restarting twice for Canvas plugin changes, 'plugins/java_languageserver', ]; diff --git a/src/dev/precommit_hook/casing_check_config.js b/src/dev/precommit_hook/casing_check_config.js index 14e25ab863dbcd..8630221b3e94f3 100644 --- a/src/dev/precommit_hook/casing_check_config.js +++ b/src/dev/precommit_hook/casing_check_config.js @@ -35,7 +35,6 @@ export const IGNORE_FILE_GLOBS = [ '**/Gruntfile.js', 'tasks/config/**/*', '**/{Dockerfile,docker-compose.yml}', - 'x-pack/legacy/plugins/apm/**/*', 'x-pack/legacy/plugins/canvas/tasks/**/*', 'x-pack/legacy/plugins/canvas/canvas_plugin_src/**/*', 'x-pack/plugins/monitoring/public/lib/jquery_flot/**/*', @@ -59,6 +58,11 @@ export const IGNORE_FILE_GLOBS = [ // filename required by api-extractor 'api-documenter.json', + + // TODO fix file names in APM to remove these + 'x-pack/plugins/apm/public/**/*', + 'x-pack/plugins/apm/scripts/**/*', + 'x-pack/plugins/apm/e2e/**/*', ]; /** diff --git a/src/dev/run_check_lockfile_symlinks.js b/src/dev/run_check_lockfile_symlinks.js index 6c6fc54638ee82..b912ea9ddb87ea 100644 --- a/src/dev/run_check_lockfile_symlinks.js +++ b/src/dev/run_check_lockfile_symlinks.js @@ -35,9 +35,9 @@ const IGNORE_FILE_GLOBS = [ // fixtures aren't used in production, ignore them '**/*fixtures*/**/*', // cypress isn't used in production, ignore it - 'x-pack/legacy/plugins/apm/e2e/*', + 'x-pack/plugins/apm/e2e/*', // apm scripts aren't used in production, ignore them - 'x-pack/legacy/plugins/apm/scripts/*', + 'x-pack/plugins/apm/scripts/*', ]; run(async ({ log }) => { diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 43114b2edccfc8..4dc930dae3e251 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -18,7 +18,7 @@ */ export const storybookAliases = { - apm: 'x-pack/legacy/plugins/apm/scripts/storybook.js', + apm: 'x-pack/plugins/apm/scripts/storybook.js', canvas: 'x-pack/legacy/plugins/canvas/scripts/storybook_new.js', codeeditor: 'src/plugins/kibana_react/public/code_editor/scripts/storybook.ts', drilldowns: 'x-pack/plugins/drilldowns/scripts/storybook.js', diff --git a/src/dev/typescript/projects.ts b/src/dev/typescript/projects.ts index 01d8a30b598c19..a13f61af601733 100644 --- a/src/dev/typescript/projects.ts +++ b/src/dev/typescript/projects.ts @@ -30,7 +30,7 @@ export const PROJECTS = [ new Project(resolve(REPO_ROOT, 'x-pack/plugins/siem/cypress/tsconfig.json'), { name: 'siem/cypress', }), - new Project(resolve(REPO_ROOT, 'x-pack/legacy/plugins/apm/e2e/tsconfig.json'), { + new Project(resolve(REPO_ROOT, 'x-pack/plugins/apm/e2e/tsconfig.json'), { name: 'apm/cypress', disableTypeCheck: true, }), diff --git a/x-pack/index.js b/x-pack/index.js index cfadddac3994ac..89cbb03f084eb4 100644 --- a/x-pack/index.js +++ b/x-pack/index.js @@ -10,7 +10,6 @@ import { reporting } from './legacy/plugins/reporting'; import { security } from './legacy/plugins/security'; import { dashboardMode } from './legacy/plugins/dashboard_mode'; import { beats } from './legacy/plugins/beats_management'; -import { apm } from './legacy/plugins/apm'; import { maps } from './legacy/plugins/maps'; import { spaces } from './legacy/plugins/spaces'; import { canvas } from './legacy/plugins/canvas'; @@ -28,7 +27,6 @@ module.exports = function(kibana) { security(kibana), dashboardMode(kibana), beats(kibana), - apm(kibana), maps(kibana), canvas(kibana), infra(kibana), diff --git a/x-pack/legacy/plugins/apm/.prettierrc b/x-pack/legacy/plugins/apm/.prettierrc deleted file mode 100644 index 650cb880f6f5ab..00000000000000 --- a/x-pack/legacy/plugins/apm/.prettierrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "singleQuote": true, - "semi": true -} diff --git a/x-pack/legacy/plugins/apm/e2e/cypress/integration/snapshots.js b/x-pack/legacy/plugins/apm/e2e/cypress/integration/snapshots.js deleted file mode 100644 index 968c2675a62e76..00000000000000 --- a/x-pack/legacy/plugins/apm/e2e/cypress/integration/snapshots.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - "APM": { - "Transaction duration charts": { - "1": "500 ms", - "2": "250 ms", - "3": "0 ms" - } - }, - "__version": "4.2.0" -} diff --git a/x-pack/legacy/plugins/apm/index.ts b/x-pack/legacy/plugins/apm/index.ts deleted file mode 100644 index d2383acd45ebad..00000000000000 --- a/x-pack/legacy/plugins/apm/index.ts +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; -import { Server } from 'hapi'; -import { resolve } from 'path'; -import { APMPluginContract } from '../../../plugins/apm/server'; -import { LegacyPluginInitializer } from '../../../../src/legacy/types'; -import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; -import mappings from './mappings.json'; - -export const apm: LegacyPluginInitializer = kibana => { - return new kibana.Plugin({ - require: [ - 'kibana', - 'elasticsearch', - 'xpack_main', - 'apm_oss', - 'task_manager' - ], - id: 'apm', - configPrefix: 'xpack.apm', - publicDir: resolve(__dirname, 'public'), - uiExports: { - app: { - title: 'APM', - description: i18n.translate('xpack.apm.apmForESDescription', { - defaultMessage: 'APM for the Elastic Stack' - }), - main: 'plugins/apm/index', - icon: 'plugins/apm/icon.svg', - euiIconType: 'apmApp', - order: 8100, - category: DEFAULT_APP_CATEGORIES.observability - }, - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - home: ['plugins/apm/legacy_register_feature'], - - // TODO: get proper types - injectDefaultVars(server: Server) { - const config = server.config(); - return { - apmUiEnabled: config.get('xpack.apm.ui.enabled'), - // TODO: rename to apm_oss.indexPatternTitle in 7.0 (breaking change) - apmIndexPatternTitle: config.get('apm_oss.indexPattern'), - apmServiceMapEnabled: config.get('xpack.apm.serviceMapEnabled') - }; - }, - savedObjectSchemas: { - 'apm-services-telemetry': { - isNamespaceAgnostic: true - }, - 'apm-indices': { - isNamespaceAgnostic: true - } - }, - mappings - }, - - // TODO: get proper types - config(Joi: any) { - return Joi.object({ - // display menu item - ui: Joi.object({ - enabled: Joi.boolean().default(true), - transactionGroupBucketSize: Joi.number().default(100), - maxTraceItems: Joi.number().default(1000) - }).default(), - - // enable plugin - enabled: Joi.boolean().default(true), - - // index patterns - autocreateApmIndexPattern: Joi.boolean().default(true), - - // service map - serviceMapEnabled: Joi.boolean().default(true), - serviceMapFingerprintBucketSize: Joi.number().default(100), - serviceMapTraceIdBucketSize: Joi.number().default(65), - serviceMapFingerprintGlobalBucketSize: Joi.number().default(1000), - serviceMapTraceIdGlobalBucketSize: Joi.number().default(6), - serviceMapMaxTracesPerRequest: Joi.number().default(50), - - // telemetry - telemetryCollectionEnabled: Joi.boolean().default(true) - }).default(); - }, - - // TODO: get proper types - init(server: Server) { - server.plugins.xpack_main.registerFeature({ - id: 'apm', - name: i18n.translate('xpack.apm.featureRegistry.apmFeatureName', { - defaultMessage: 'APM' - }), - order: 900, - icon: 'apmApp', - navLinkId: 'apm', - app: ['apm', 'kibana'], - catalogue: ['apm'], - // see x-pack/plugins/features/common/feature_kibana_privileges.ts - privileges: { - all: { - app: ['apm', 'kibana'], - api: [ - 'apm', - 'apm_write', - 'actions-read', - 'actions-all', - 'alerting-read', - 'alerting-all' - ], - catalogue: ['apm'], - savedObject: { - all: ['alert', 'action', 'action_task_params'], - read: [] - }, - ui: [ - 'show', - 'save', - 'alerting:show', - 'actions:show', - 'alerting:save', - 'actions:save', - 'alerting:delete', - 'actions:delete' - ] - }, - read: { - app: ['apm', 'kibana'], - api: [ - 'apm', - 'actions-read', - 'actions-all', - 'alerting-read', - 'alerting-all' - ], - catalogue: ['apm'], - savedObject: { - all: ['alert', 'action', 'action_task_params'], - read: [] - }, - ui: [ - 'show', - 'alerting:show', - 'actions:show', - 'alerting:save', - 'actions:save', - 'alerting:delete', - 'actions:delete' - ] - } - } - }); - const apmPlugin = server.newPlatform.setup.plugins - .apm as APMPluginContract; - - apmPlugin.registerLegacyAPI({ - server - }); - } - }); -}; diff --git a/x-pack/legacy/plugins/apm/mappings.json b/x-pack/legacy/plugins/apm/mappings.json deleted file mode 100644 index 6ca9f137920853..00000000000000 --- a/x-pack/legacy/plugins/apm/mappings.json +++ /dev/null @@ -1,933 +0,0 @@ -{ - "apm-telemetry": { - "properties": { - "agents": { - "properties": { - "dotnet": { - "properties": { - "agent": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "language": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "runtime": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "go": { - "properties": { - "agent": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "language": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "runtime": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "java": { - "properties": { - "agent": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "language": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "runtime": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "js-base": { - "properties": { - "agent": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "language": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "runtime": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "nodejs": { - "properties": { - "agent": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "language": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "runtime": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "python": { - "properties": { - "agent": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "language": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "runtime": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "ruby": { - "properties": { - "agent": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "language": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "runtime": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - }, - "rum-js": { - "properties": { - "agent": { - "properties": { - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "service": { - "properties": { - "framework": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "language": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - }, - "runtime": { - "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, - "name": { - "type": "keyword", - "ignore_above": 1024 - }, - "version": { - "type": "keyword", - "ignore_above": 1024 - } - } - } - } - } - } - } - } - }, - "counts": { - "properties": { - "agent_configuration": { - "properties": { - "all": { - "type": "long" - } - } - }, - "error": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "max_error_groups_per_service": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "max_transaction_groups_per_service": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "onboarding": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "services": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "sourcemap": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "span": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - }, - "traces": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "1d": { - "type": "long" - }, - "all": { - "type": "long" - } - } - } - } - }, - "cardinality": { - "properties": { - "user_agent": { - "properties": { - "original": { - "properties": { - "all_agents": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "rum": { - "properties": { - "1d": { - "type": "long" - } - } - } - } - } - } - }, - "transaction": { - "properties": { - "name": { - "properties": { - "all_agents": { - "properties": { - "1d": { - "type": "long" - } - } - }, - "rum": { - "properties": { - "1d": { - "type": "long" - } - } - } - } - } - } - } - } - }, - "has_any_services": { - "type": "boolean" - }, - "indices": { - "properties": { - "all": { - "properties": { - "total": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "type": "long" - } - } - } - } - } - } - }, - "shards": { - "properties": { - "total": { - "type": "long" - } - } - } - } - }, - "integrations": { - "properties": { - "ml": { - "properties": { - "all_jobs_count": { - "type": "long" - } - } - } - } - }, - "retainment": { - "properties": { - "error": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "metric": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "onboarding": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "span": { - "properties": { - "ms": { - "type": "long" - } - } - }, - "transaction": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "services_per_agent": { - "properties": { - "dotnet": { - "type": "long", - "null_value": 0 - }, - "go": { - "type": "long", - "null_value": 0 - }, - "java": { - "type": "long", - "null_value": 0 - }, - "js-base": { - "type": "long", - "null_value": 0 - }, - "nodejs": { - "type": "long", - "null_value": 0 - }, - "python": { - "type": "long", - "null_value": 0 - }, - "ruby": { - "type": "long", - "null_value": 0 - }, - "rum-js": { - "type": "long", - "null_value": 0 - } - } - }, - "tasks": { - "properties": { - "agent_configuration": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "agents": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "cardinality": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "groupings": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "indices_stats": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "integrations": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "processor_events": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "services": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, - "versions": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - } - } - }, - "version": { - "properties": { - "apm_server": { - "properties": { - "major": { - "type": "long" - }, - "minor": { - "type": "long" - }, - "patch": { - "type": "long" - } - } - } - } - } - } - }, - "apm-indices": { - "properties": { - "apm_oss.sourcemapIndices": { - "type": "keyword" - }, - "apm_oss.errorIndices": { - "type": "keyword" - }, - "apm_oss.onboardingIndices": { - "type": "keyword" - }, - "apm_oss.spanIndices": { - "type": "keyword" - }, - "apm_oss.transactionIndices": { - "type": "keyword" - }, - "apm_oss.metricsIndices": { - "type": "keyword" - } - } - } -} diff --git a/x-pack/legacy/plugins/apm/public/index.scss b/x-pack/legacy/plugins/apm/public/index.scss deleted file mode 100644 index 04a070c304d6fa..00000000000000 --- a/x-pack/legacy/plugins/apm/public/index.scss +++ /dev/null @@ -1,16 +0,0 @@ -// Import the EUI global scope so we can use EUI constants -@import 'src/legacy/ui/public/styles/_styling_constants'; - -/* APM plugin styles */ - -// Prefix all styles with "apm" to avoid conflicts. -// Examples -// apmChart -// apmChart__legend -// apmChart__legend--small -// apmChart__legend-isLoading - -.apmReactRoot { - overflow-x: auto; - height: 100%; -} diff --git a/x-pack/legacy/plugins/apm/public/index.tsx b/x-pack/legacy/plugins/apm/public/index.tsx deleted file mode 100644 index 59b2fedaafba6c..00000000000000 --- a/x-pack/legacy/plugins/apm/public/index.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { npSetup, npStart } from 'ui/new_platform'; -import 'react-vis/dist/style.css'; -import { PluginInitializerContext } from 'kibana/public'; -import 'ui/autoload/all'; -import chrome from 'ui/chrome'; -import { plugin } from './new-platform'; -import { REACT_APP_ROOT_ID } from './new-platform/plugin'; -import './style/global_overrides.css'; -import template from './templates/index.html'; - -// This will be moved to core.application.register when the new platform -// migration is complete. -// @ts-ignore -chrome.setRootTemplate(template); - -const checkForRoot = () => { - return new Promise(resolve => { - const ready = !!document.getElementById(REACT_APP_ROOT_ID); - if (ready) { - resolve(); - } else { - setTimeout(() => resolve(checkForRoot()), 10); - } - }); -}; -checkForRoot().then(() => { - const pluginInstance = plugin({} as PluginInitializerContext); - pluginInstance.setup(npSetup.core, npSetup.plugins); - pluginInstance.start(npStart.core, npStart.plugins); -}); diff --git a/x-pack/legacy/plugins/apm/public/legacy_register_feature.ts b/x-pack/legacy/plugins/apm/public/legacy_register_feature.ts deleted file mode 100644 index f12865399054e8..00000000000000 --- a/x-pack/legacy/plugins/apm/public/legacy_register_feature.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { npSetup } from 'ui/new_platform'; -import { featureCatalogueEntry } from './new-platform/featureCatalogueEntry'; - -const { - core, - plugins: { home } -} = npSetup; -const apmUiEnabled = core.injectedMetadata.getInjectedVar( - 'apmUiEnabled' -) as boolean; - -if (apmUiEnabled) { - home.featureCatalogue.register(featureCatalogueEntry); -} - -home.environment.update({ - apmUi: apmUiEnabled -}); diff --git a/x-pack/legacy/plugins/apm/public/new-platform/getConfigFromInjectedMetadata.ts b/x-pack/legacy/plugins/apm/public/new-platform/getConfigFromInjectedMetadata.ts deleted file mode 100644 index 6dc77f7733b2db..00000000000000 --- a/x-pack/legacy/plugins/apm/public/new-platform/getConfigFromInjectedMetadata.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { npStart } from 'ui/new_platform'; -import { ConfigSchema } from './plugin'; - -const { core } = npStart; - -export function getConfigFromInjectedMetadata(): ConfigSchema { - const { - apmIndexPatternTitle, - apmServiceMapEnabled, - apmUiEnabled - } = core.injectedMetadata.getInjectedVars(); - - return { - indexPatternTitle: `${apmIndexPatternTitle}`, - serviceMapEnabled: !!apmServiceMapEnabled, - ui: { enabled: !!apmUiEnabled } - }; -} diff --git a/x-pack/legacy/plugins/apm/public/new-platform/index.tsx b/x-pack/legacy/plugins/apm/public/new-platform/index.tsx deleted file mode 100644 index 0674dc48316f43..00000000000000 --- a/x-pack/legacy/plugins/apm/public/new-platform/index.tsx +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { PluginInitializer } from '../../../../../../src/core/public'; -import { ApmPlugin, ApmPluginSetup, ApmPluginStart } from './plugin'; - -export const plugin: PluginInitializer< - ApmPluginSetup, - ApmPluginStart -> = pluginInitializerContext => new ApmPlugin(pluginInitializerContext); diff --git a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx b/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx deleted file mode 100644 index 80a45ba66c4fa4..00000000000000 --- a/x-pack/legacy/plugins/apm/public/new-platform/plugin.tsx +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { ApmRoute } from '@elastic/apm-rum-react'; -import { i18n } from '@kbn/i18n'; -import React from 'react'; -import ReactDOM from 'react-dom'; -import { Route, Router, Switch } from 'react-router-dom'; -import styled from 'styled-components'; -import { - CoreSetup, - CoreStart, - Plugin, - PluginInitializerContext -} from '../../../../../../src/core/public'; -import { DataPublicPluginSetup } from '../../../../../../src/plugins/data/public'; -import { HomePublicPluginSetup } from '../../../../../../src/plugins/home/public'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; -import { PluginSetupContract as AlertingPluginPublicSetup } from '../../../../../plugins/alerting/public'; -import { AlertType } from '../../../../../plugins/apm/common/alert_types'; -import { LicensingPluginSetup } from '../../../../../plugins/licensing/public'; -import { - AlertsContextProvider, - TriggersAndActionsUIPublicPluginSetup -} from '../../../../../plugins/triggers_actions_ui/public'; -import { APMIndicesPermission } from '../components/app/APMIndicesPermission'; -import { routes } from '../components/app/Main/route_config'; -import { ScrollToTopOnPathChange } from '../components/app/Main/ScrollToTopOnPathChange'; -import { UpdateBreadcrumbs } from '../components/app/Main/UpdateBreadcrumbs'; -import { ErrorRateAlertTrigger } from '../components/shared/ErrorRateAlertTrigger'; -import { TransactionDurationAlertTrigger } from '../components/shared/TransactionDurationAlertTrigger'; -import { ApmPluginContext } from '../context/ApmPluginContext'; -import { LicenseProvider } from '../context/LicenseContext'; -import { LoadingIndicatorProvider } from '../context/LoadingIndicatorContext'; -import { LocationProvider } from '../context/LocationContext'; -import { MatchedRouteProvider } from '../context/MatchedRouteContext'; -import { UrlParamsProvider } from '../context/UrlParamsContext'; -import { createCallApmApi } from '../services/rest/createCallApmApi'; -import { createStaticIndexPattern } from '../services/rest/index_pattern'; -import { px, unit, units } from '../style/variables'; -import { history } from '../utils/history'; -import { featureCatalogueEntry } from './featureCatalogueEntry'; -import { getConfigFromInjectedMetadata } from './getConfigFromInjectedMetadata'; -import { setHelpExtension } from './setHelpExtension'; -import { toggleAppLinkInNav } from './toggleAppLinkInNav'; -import { setReadonlyBadge } from './updateBadge'; - -export const REACT_APP_ROOT_ID = 'react-apm-root'; - -const MainContainer = styled.div` - min-width: ${px(unit * 50)}; - padding: ${px(units.plus)}; - height: 100%; -`; - -const App = () => { - return ( - - - - - - {routes.map((route, i) => ( - - ))} - - - - ); -}; - -export type ApmPluginSetup = void; -export type ApmPluginStart = void; - -export interface ApmPluginSetupDeps { - alerting?: AlertingPluginPublicSetup; - data: DataPublicPluginSetup; - home: HomePublicPluginSetup; - licensing: LicensingPluginSetup; - triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup; -} - -export interface ConfigSchema { - indexPatternTitle: string; - serviceMapEnabled: boolean; - ui: { - enabled: boolean; - }; -} - -export class ApmPlugin - implements Plugin { - // When we switch over from the old platform to new platform the plugins will - // be coming from setup instead of start, since that's where we do - // `core.application.register`. During the transitions we put plugins on an - // instance property so we can use it in start. - setupPlugins: ApmPluginSetupDeps = {} as ApmPluginSetupDeps; - - constructor( - // @ts-ignore Not using initializerContext now, but will be once NP - // migration is complete. - private readonly initializerContext: PluginInitializerContext - ) {} - - // Take the DOM element as the constructor, so we can mount the app. - public setup(_core: CoreSetup, plugins: ApmPluginSetupDeps) { - plugins.home.featureCatalogue.register(featureCatalogueEntry); - this.setupPlugins = plugins; - } - - public start(core: CoreStart) { - const i18nCore = core.i18n; - const plugins = this.setupPlugins; - createCallApmApi(core.http); - - // Once we're actually an NP plugin we'll get the config from the - // initializerContext like: - // - // const config = this.initializerContext.config.get(); - // - // Until then we use a shim to get it from legacy injectedMetadata: - const config = getConfigFromInjectedMetadata(); - - // render APM feedback link in global help menu - setHelpExtension(core); - setReadonlyBadge(core); - toggleAppLinkInNav(core, config); - - const apmPluginContextValue = { - config, - core, - plugins - }; - - plugins.triggers_actions_ui.alertTypeRegistry.register({ - id: AlertType.ErrorRate, - name: i18n.translate('xpack.apm.alertTypes.errorRate', { - defaultMessage: 'Error rate' - }), - iconClass: 'bell', - alertParamsExpression: ErrorRateAlertTrigger, - validate: () => ({ - errors: [] - }) - }); - - plugins.triggers_actions_ui.alertTypeRegistry.register({ - id: AlertType.TransactionDuration, - name: i18n.translate('xpack.apm.alertTypes.transactionDuration', { - defaultMessage: 'Transaction duration' - }), - iconClass: 'bell', - alertParamsExpression: TransactionDurationAlertTrigger, - validate: () => ({ - errors: [] - }) - }); - - ReactDOM.render( - - - - - - - - - - - - - - - - - - - - - , - document.getElementById(REACT_APP_ROOT_ID) - ); - - // create static index pattern and store as saved object. Not needed by APM UI but for legacy reasons in Discover, Dashboard etc. - createStaticIndexPattern().catch(e => { - // eslint-disable-next-line no-console - console.log('Error fetching static index pattern', e); - }); - } - - public stop() {} -} diff --git a/x-pack/legacy/plugins/apm/public/style/global_overrides.css b/x-pack/legacy/plugins/apm/public/style/global_overrides.css deleted file mode 100644 index 75b4532f7c9a1b..00000000000000 --- a/x-pack/legacy/plugins/apm/public/style/global_overrides.css +++ /dev/null @@ -1,34 +0,0 @@ -/* -Hide unused secondary Kibana navigation -*/ -.kuiLocalNav { - min-height: initial; -} - -.kuiLocalNavRow.kuiLocalNavRow--secondary { - display: none; -} - -/* -Remove unnecessary space below the navigation dropdown -*/ -.kuiLocalDropdown { - margin-bottom: 0; - border-bottom: none; -} - -/* -Hide the "0-10 of 100" text in KUIPager component for all KUIControlledTable -*/ -.kuiControlledTable .kuiPagerText { - display: none; -} - -/* -Hide default dashed gridlines in EUI chart component for all APM graphs -*/ - -.rv-xy-plot__grid-lines__line { - stroke-opacity: 1; - stroke-dasharray: 1; -} diff --git a/x-pack/legacy/plugins/apm/public/templates/index.html b/x-pack/legacy/plugins/apm/public/templates/index.html deleted file mode 100644 index 78e0ade3ad6243..00000000000000 --- a/x-pack/legacy/plugins/apm/public/templates/index.html +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/x-pack/legacy/plugins/apm/CONTRIBUTING.md b/x-pack/plugins/apm/CONTRIBUTING.md similarity index 100% rename from x-pack/legacy/plugins/apm/CONTRIBUTING.md rename to x-pack/plugins/apm/CONTRIBUTING.md diff --git a/x-pack/plugins/apm/common/apm_saved_object_constants.ts b/x-pack/plugins/apm/common/apm_saved_object_constants.ts index 0529d90fe940a4..eb16db7715fedc 100644 --- a/x-pack/plugins/apm/common/apm_saved_object_constants.ts +++ b/x-pack/plugins/apm/common/apm_saved_object_constants.ts @@ -5,7 +5,7 @@ */ // the types have to match the names of the saved object mappings -// in /x-pack/legacy/plugins/apm/mappings.json +// in /x-pack/plugins/apm/mappings.json // APM indices export const APM_INDICES_SAVED_OBJECT_TYPE = 'apm-indices'; diff --git a/x-pack/legacy/plugins/apm/public/utils/pickKeys.ts b/x-pack/plugins/apm/common/utils/pick_keys.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/utils/pickKeys.ts rename to x-pack/plugins/apm/common/utils/pick_keys.ts diff --git a/x-pack/legacy/plugins/apm/dev_docs/github_commands.md b/x-pack/plugins/apm/dev_docs/github_commands.md similarity index 100% rename from x-pack/legacy/plugins/apm/dev_docs/github_commands.md rename to x-pack/plugins/apm/dev_docs/github_commands.md diff --git a/x-pack/legacy/plugins/apm/dev_docs/typescript.md b/x-pack/plugins/apm/dev_docs/typescript.md similarity index 83% rename from x-pack/legacy/plugins/apm/dev_docs/typescript.md rename to x-pack/plugins/apm/dev_docs/typescript.md index 6858e93ec09e00..6de61b665a1b17 100644 --- a/x-pack/legacy/plugins/apm/dev_docs/typescript.md +++ b/x-pack/plugins/apm/dev_docs/typescript.md @@ -4,8 +4,8 @@ Kibana and X-Pack are very large TypeScript projects, and it comes at a cost. Ed To run the optimization: -`$ node x-pack/legacy/plugins/apm/scripts/optimize-tsconfig` +`$ node x-pack/plugins/apm/scripts/optimize-tsconfig` To undo the optimization: -`$ node x-pack/legacy/plugins/apm/scripts/unoptimize-tsconfig` +`$ node x-pack/plugins/apm/scripts/unoptimize-tsconfig` diff --git a/x-pack/legacy/plugins/apm/dev_docs/vscode_setup.md b/x-pack/plugins/apm/dev_docs/vscode_setup.md similarity index 83% rename from x-pack/legacy/plugins/apm/dev_docs/vscode_setup.md rename to x-pack/plugins/apm/dev_docs/vscode_setup.md index e1901b3855f735..1c80d1476520d1 100644 --- a/x-pack/legacy/plugins/apm/dev_docs/vscode_setup.md +++ b/x-pack/plugins/apm/dev_docs/vscode_setup.md @@ -1,6 +1,6 @@ ### Visual Studio Code -When using [Visual Studio Code](https://code.visualstudio.com/) with APM it's best to set up a [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) and add the `x-pack/legacy/plugins/apm` directory, the `x-pack` directory, and the root of the Kibana repository to the workspace. This makes it so you can navigate and search within APM and use the wider workspace roots when you need to widen your search. +When using [Visual Studio Code](https://code.visualstudio.com/) with APM it's best to set up a [multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces) and add the `x-pack/plugins/apm` directory, the `x-pack` directory, and the root of the Kibana repository to the workspace. This makes it so you can navigate and search within APM and use the wider workspace roots when you need to widen your search. #### Using the Jest extension @@ -25,7 +25,7 @@ If you have a workspace configured as described above you should have: in your Workspace settings, and: ```json -"jest.pathToJest": "node scripts/jest.js --testPathPattern=legacy/plugins/apm", +"jest.pathToJest": "node scripts/jest.js --testPathPattern=plugins/apm", "jest.rootPath": "../../.." ``` @@ -40,7 +40,7 @@ To make the [VSCode debugger](https://vscode.readthedocs.io/en/latest/editor/deb "type": "node", "name": "APM Jest", "request": "launch", - "args": ["--runInBand", "--testPathPattern=legacy/plugins/apm"], + "args": ["--runInBand", "--testPathPattern=plugins/apm"], "cwd": "${workspaceFolder}/../../..", "console": "internalConsole", "internalConsoleOptions": "openOnSessionStart", diff --git a/x-pack/legacy/plugins/apm/e2e/.gitignore b/x-pack/plugins/apm/e2e/.gitignore similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/.gitignore rename to x-pack/plugins/apm/e2e/.gitignore diff --git a/x-pack/legacy/plugins/apm/e2e/README.md b/x-pack/plugins/apm/e2e/README.md similarity index 84% rename from x-pack/legacy/plugins/apm/e2e/README.md rename to x-pack/plugins/apm/e2e/README.md index a891d64539a3f2..b630747ac2d3ea 100644 --- a/x-pack/legacy/plugins/apm/e2e/README.md +++ b/x-pack/plugins/apm/e2e/README.md @@ -3,7 +3,7 @@ **Run E2E tests** ```sh -x-pack/legacy/plugins/apm/e2e/run-e2e.sh +x-pack/plugins/apm/e2e/run-e2e.sh ``` _Starts Kibana, APM Server, Elasticsearch (with sample data) and runs the tests_ @@ -16,9 +16,9 @@ The Jenkins CI uses a shell script to prepare Kibana: ```shell # Prepare and run Kibana locally -$ x-pack/legacy/plugins/apm/e2e/ci/prepare-kibana.sh +$ x-pack/plugins/apm/e2e/ci/prepare-kibana.sh # Build Docker image for Kibana -$ docker build --tag cypress --build-arg NODE_VERSION=$(cat .node-version) x-pack/legacy/plugins/apm/e2e/ci +$ docker build --tag cypress --build-arg NODE_VERSION=$(cat .node-version) x-pack/plugins/apm/e2e/ci # Run Docker image $ docker run --rm -t --user "$(id -u):$(id -g)" \ -v `pwd`:/app --network="host" \ diff --git a/x-pack/legacy/plugins/apm/e2e/ci/Dockerfile b/x-pack/plugins/apm/e2e/ci/Dockerfile similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/ci/Dockerfile rename to x-pack/plugins/apm/e2e/ci/Dockerfile diff --git a/x-pack/legacy/plugins/apm/e2e/ci/entrypoint.sh b/x-pack/plugins/apm/e2e/ci/entrypoint.sh similarity index 90% rename from x-pack/legacy/plugins/apm/e2e/ci/entrypoint.sh rename to x-pack/plugins/apm/e2e/ci/entrypoint.sh index ae5155d966e58a..3349aa74dadb9b 100755 --- a/x-pack/legacy/plugins/apm/e2e/ci/entrypoint.sh +++ b/x-pack/plugins/apm/e2e/ci/entrypoint.sh @@ -21,9 +21,9 @@ npm config set cache ${HOME} # --exclude=packages/ \ # --exclude=built_assets --exclude=target \ # --exclude=data /app ${HOME}/ -#cd ${HOME}/app/x-pack/legacy/plugins/apm/e2e/cypress +#cd ${HOME}/app/x-pack/plugins/apm/e2e/cypress -cd /app/x-pack/legacy/plugins/apm/e2e +cd /app/x-pack/plugins/apm/e2e ## Install dependencies for cypress CI=true npm install yarn install diff --git a/x-pack/legacy/plugins/apm/e2e/ci/kibana.e2e.yml b/x-pack/plugins/apm/e2e/ci/kibana.e2e.yml similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/ci/kibana.e2e.yml rename to x-pack/plugins/apm/e2e/ci/kibana.e2e.yml diff --git a/x-pack/legacy/plugins/apm/e2e/ci/prepare-kibana.sh b/x-pack/plugins/apm/e2e/ci/prepare-kibana.sh similarity index 95% rename from x-pack/legacy/plugins/apm/e2e/ci/prepare-kibana.sh rename to x-pack/plugins/apm/e2e/ci/prepare-kibana.sh index 6df17bd51e0e86..637f8fa9b4c749 100755 --- a/x-pack/legacy/plugins/apm/e2e/ci/prepare-kibana.sh +++ b/x-pack/plugins/apm/e2e/ci/prepare-kibana.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -e -E2E_DIR="x-pack/legacy/plugins/apm/e2e" +E2E_DIR="x-pack/plugins/apm/e2e" echo "1/3 Install dependencies ..." # shellcheck disable=SC1091 diff --git a/x-pack/legacy/plugins/apm/e2e/cypress.json b/x-pack/plugins/apm/e2e/cypress.json similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/cypress.json rename to x-pack/plugins/apm/e2e/cypress.json diff --git a/x-pack/legacy/plugins/apm/e2e/cypress/fixtures/example.json b/x-pack/plugins/apm/e2e/cypress/fixtures/example.json similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/cypress/fixtures/example.json rename to x-pack/plugins/apm/e2e/cypress/fixtures/example.json diff --git a/x-pack/legacy/plugins/apm/e2e/cypress/integration/apm.feature b/x-pack/plugins/apm/e2e/cypress/integration/apm.feature similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/cypress/integration/apm.feature rename to x-pack/plugins/apm/e2e/cypress/integration/apm.feature diff --git a/x-pack/legacy/plugins/apm/e2e/cypress/integration/helpers.ts b/x-pack/plugins/apm/e2e/cypress/integration/helpers.ts similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/cypress/integration/helpers.ts rename to x-pack/plugins/apm/e2e/cypress/integration/helpers.ts diff --git a/x-pack/plugins/apm/e2e/cypress/integration/snapshots.js b/x-pack/plugins/apm/e2e/cypress/integration/snapshots.js new file mode 100644 index 00000000000000..a462f4a5041451 --- /dev/null +++ b/x-pack/plugins/apm/e2e/cypress/integration/snapshots.js @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +module.exports = { + APM: { + 'Transaction duration charts': { + '1': '500 ms', + '2': '250 ms', + '3': '0 ms' + } + }, + __version: '4.2.0' +}; diff --git a/x-pack/legacy/plugins/apm/e2e/cypress/plugins/index.js b/x-pack/plugins/apm/e2e/cypress/plugins/index.js similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/cypress/plugins/index.js rename to x-pack/plugins/apm/e2e/cypress/plugins/index.js diff --git a/x-pack/legacy/plugins/apm/e2e/cypress/support/commands.js b/x-pack/plugins/apm/e2e/cypress/support/commands.js similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/cypress/support/commands.js rename to x-pack/plugins/apm/e2e/cypress/support/commands.js diff --git a/x-pack/legacy/plugins/apm/e2e/cypress/support/index.ts b/x-pack/plugins/apm/e2e/cypress/support/index.ts similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/cypress/support/index.ts rename to x-pack/plugins/apm/e2e/cypress/support/index.ts diff --git a/x-pack/legacy/plugins/apm/e2e/cypress/support/step_definitions/apm.ts b/x-pack/plugins/apm/e2e/cypress/support/step_definitions/apm.ts similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/cypress/support/step_definitions/apm.ts rename to x-pack/plugins/apm/e2e/cypress/support/step_definitions/apm.ts diff --git a/x-pack/legacy/plugins/apm/e2e/cypress/typings/index.d.ts b/x-pack/plugins/apm/e2e/cypress/typings/index.d.ts similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/cypress/typings/index.d.ts rename to x-pack/plugins/apm/e2e/cypress/typings/index.d.ts diff --git a/x-pack/legacy/plugins/apm/e2e/cypress/webpack.config.js b/x-pack/plugins/apm/e2e/cypress/webpack.config.js similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/cypress/webpack.config.js rename to x-pack/plugins/apm/e2e/cypress/webpack.config.js diff --git a/x-pack/legacy/plugins/apm/e2e/ingest-data/replay.js b/x-pack/plugins/apm/e2e/ingest-data/replay.js similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/ingest-data/replay.js rename to x-pack/plugins/apm/e2e/ingest-data/replay.js diff --git a/x-pack/legacy/plugins/apm/e2e/package.json b/x-pack/plugins/apm/e2e/package.json similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/package.json rename to x-pack/plugins/apm/e2e/package.json diff --git a/x-pack/legacy/plugins/apm/e2e/run-e2e.sh b/x-pack/plugins/apm/e2e/run-e2e.sh similarity index 98% rename from x-pack/legacy/plugins/apm/e2e/run-e2e.sh rename to x-pack/plugins/apm/e2e/run-e2e.sh index 7c17c14dc96018..818d45abb0e652 100755 --- a/x-pack/legacy/plugins/apm/e2e/run-e2e.sh +++ b/x-pack/plugins/apm/e2e/run-e2e.sh @@ -27,7 +27,7 @@ cd ${E2E_DIR} # Ask user to start Kibana ################################################## echo "\n${bold}To start Kibana please run the following command:${normal} -node ./scripts/kibana --no-base-path --dev --no-dev-config --config x-pack/legacy/plugins/apm/e2e/ci/kibana.e2e.yml" +node ./scripts/kibana --no-base-path --dev --no-dev-config --config x-pack/plugins/apm/e2e/ci/kibana.e2e.yml" # # Create tmp folder diff --git a/x-pack/legacy/plugins/apm/e2e/tsconfig.json b/x-pack/plugins/apm/e2e/tsconfig.json similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/tsconfig.json rename to x-pack/plugins/apm/e2e/tsconfig.json diff --git a/x-pack/legacy/plugins/apm/e2e/yarn.lock b/x-pack/plugins/apm/e2e/yarn.lock similarity index 100% rename from x-pack/legacy/plugins/apm/e2e/yarn.lock rename to x-pack/plugins/apm/e2e/yarn.lock diff --git a/x-pack/plugins/apm/kibana.json b/x-pack/plugins/apm/kibana.json index 7ffdb676c740fe..1a0ad67c7b6969 100644 --- a/x-pack/plugins/apm/kibana.json +++ b/x-pack/plugins/apm/kibana.json @@ -1,13 +1,23 @@ { "id": "apm", - "server": true, "version": "8.0.0", "kibanaVersion": "kibana", - "configPath": [ - "xpack", - "apm" + "requiredPlugins": [ + "features", + "apm_oss", + "data", + "home", + "licensing", + "triggers_actions_ui" + ], + "optionalPlugins": [ + "cloud", + "usageCollection", + "taskManager", + "actions", + "alerting" ], - "ui": false, - "requiredPlugins": ["apm_oss", "data", "home", "licensing"], - "optionalPlugins": ["cloud", "usageCollection", "taskManager","actions", "alerting"] + "server": true, + "ui": true, + "configPath": ["xpack", "apm"] } diff --git a/x-pack/plugins/apm/public/application/index.tsx b/x-pack/plugins/apm/public/application/index.tsx new file mode 100644 index 00000000000000..c3738329219a81 --- /dev/null +++ b/x-pack/plugins/apm/public/application/index.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ApmRoute } from '@elastic/apm-rum-react'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Route, Router, Switch } from 'react-router-dom'; +import styled from 'styled-components'; +import { CoreStart, AppMountParameters } from '../../../../../src/core/public'; +import { ApmPluginSetupDeps } from '../plugin'; +import { ApmPluginContext } from '../context/ApmPluginContext'; +import { LicenseProvider } from '../context/LicenseContext'; +import { LoadingIndicatorProvider } from '../context/LoadingIndicatorContext'; +import { LocationProvider } from '../context/LocationContext'; +import { MatchedRouteProvider } from '../context/MatchedRouteContext'; +import { UrlParamsProvider } from '../context/UrlParamsContext'; +import { AlertsContextProvider } from '../../../triggers_actions_ui/public'; +import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/public'; +import { px, unit, units } from '../style/variables'; +import { UpdateBreadcrumbs } from '../components/app/Main/UpdateBreadcrumbs'; +import { APMIndicesPermission } from '../components/app/APMIndicesPermission'; +import { ScrollToTopOnPathChange } from '../components/app/Main/ScrollToTopOnPathChange'; +import { routes } from '../components/app/Main/route_config'; +import { history } from '../utils/history'; +import { ConfigSchema } from '..'; +import 'react-vis/dist/style.css'; + +const MainContainer = styled.div` + min-width: ${px(unit * 50)}; + padding: ${px(units.plus)}; + height: 100%; +`; + +const App = () => { + return ( + + + + + + {routes.map((route, i) => ( + + ))} + + + + ); +}; + +const ApmAppRoot = ({ + core, + deps, + routerHistory, + config +}: { + core: CoreStart; + deps: ApmPluginSetupDeps; + routerHistory: typeof history; + config: ConfigSchema; +}) => { + const i18nCore = core.i18n; + const plugins = deps; + const apmPluginContextValue = { + config, + core, + plugins + }; + return ( + + + + + + + + + + + + + + + + + + + + + + ); +}; + +/** + * This module is rendered asynchronously in the Kibana platform. + */ +export const renderApp = ( + core: CoreStart, + deps: ApmPluginSetupDeps, + { element }: AppMountParameters, + config: ConfigSchema +) => { + ReactDOM.render( + , + element + ); + return () => ReactDOM.unmountComponentAtNode(element); +}; diff --git a/x-pack/legacy/plugins/apm/public/components/app/APMIndicesPermission/__test__/APMIndicesPermission.test.tsx b/x-pack/plugins/apm/public/components/app/APMIndicesPermission/__test__/APMIndicesPermission.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/APMIndicesPermission/__test__/APMIndicesPermission.test.tsx rename to x-pack/plugins/apm/public/components/app/APMIndicesPermission/__test__/APMIndicesPermission.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/APMIndicesPermission/index.tsx b/x-pack/plugins/apm/public/components/app/APMIndicesPermission/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/APMIndicesPermission/index.tsx rename to x-pack/plugins/apm/public/components/app/APMIndicesPermission/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ErrorTabs.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ErrorTabs.tsx similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ErrorTabs.tsx rename to x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ErrorTabs.tsx index 33774c941ffd61..5982346d97b890 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ErrorTabs.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ErrorTabs.tsx @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import { isEmpty } from 'lodash'; -import { APMError } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error'; +import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; export interface ErrorTab { key: 'log_stacktrace' | 'exception_stacktrace' | 'metadata'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.test.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.test.tsx rename to x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.tsx similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.tsx rename to x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.tsx index 75e518a278aea5..faec93013886cd 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/ExceptionStacktrace.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { EuiTitle } from '@elastic/eui'; -import { Exception } from '../../../../../../../../plugins/apm/typings/es_schemas/raw/error_raw'; +import { Exception } from '../../../../../typings/es_schemas/raw/error_raw'; import { Stacktrace } from '../../../shared/Stacktrace'; import { CauseStacktrace } from '../../../shared/Stacktrace/CauseStacktrace'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/__snapshots__/index.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/__snapshots__/index.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/__snapshots__/index.test.tsx.snap rename to x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/__snapshots__/index.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.test.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.test.tsx rename to x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx rename to x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx index 490bf472065e3e..9e2fd776e67a39 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx @@ -20,8 +20,8 @@ import React from 'react'; import styled from 'styled-components'; import { first } from 'lodash'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ErrorGroupAPIResponse } from '../../../../../../../../plugins/apm/server/lib/errors/get_error_group'; -import { APMError } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error'; +import { ErrorGroupAPIResponse } from '../../../../../server/lib/errors/get_error_group'; +import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; import { IUrlParams } from '../../../../context/UrlParamsContext/types'; import { px, unit, units } from '../../../../style/variables'; import { DiscoverErrorLink } from '../../../shared/Links/DiscoverLinks/DiscoverErrorLink'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx rename to x-pack/plugins/apm/public/components/app/ErrorGroupDetails/Distribution/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx rename to x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx index ccd720ceee0758..c40c711a590be0 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx @@ -17,7 +17,7 @@ import theme from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; import React, { Fragment } from 'react'; import styled from 'styled-components'; -import { NOT_AVAILABLE_LABEL } from '../../../../../../../plugins/apm/common/i18n'; +import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; import { useFetcher } from '../../../hooks/useFetcher'; import { fontFamilyCode, fontSizes, px, units } from '../../../style/variables'; import { ApmHeader } from '../../shared/ApmHeader'; @@ -25,7 +25,7 @@ import { DetailView } from './DetailView'; import { ErrorDistribution } from './Distribution'; import { useLocation } from '../../../hooks/useLocation'; import { useUrlParams } from '../../../hooks/useUrlParams'; -import { useTrackPageview } from '../../../../../../../plugins/observability/public'; +import { useTrackPageview } from '../../../../../observability/public'; const Titles = styled.div` margin-bottom: ${px(units.plus)}; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/List.test.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/List.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/List.test.tsx rename to x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/List.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap rename to x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/props.json b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/props.json similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/props.json rename to x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/props.json diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx rename to x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx index 250b9a5d188d0e..695d3463d3b3da 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx @@ -9,9 +9,9 @@ import numeral from '@elastic/numeral'; import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; import styled from 'styled-components'; -import { NOT_AVAILABLE_LABEL } from '../../../../../../../../plugins/apm/common/i18n'; +import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ErrorGroupListAPIResponse } from '../../../../../../../../plugins/apm/server/lib/errors/get_error_groups'; +import { ErrorGroupListAPIResponse } from '../../../../../server/lib/errors/get_error_groups'; import { fontFamilyCode, fontSizes, diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx rename to x-pack/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx index 8c5a4545f1043f..604893952d9d6c 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupOverview/index.tsx @@ -17,8 +17,8 @@ import { useFetcher } from '../../../hooks/useFetcher'; import { ErrorDistribution } from '../ErrorGroupDetails/Distribution'; import { ErrorGroupList } from './List'; import { useUrlParams } from '../../../hooks/useUrlParams'; -import { useTrackPageview } from '../../../../../../../plugins/observability/public'; -import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings'; +import { useTrackPageview } from '../../../../../observability/public'; +import { PROJECTION } from '../../../../common/projections/typings'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; const ErrorGroupOverview: React.FC = () => { diff --git a/x-pack/legacy/plugins/apm/public/components/app/Home/Home.test.tsx b/x-pack/plugins/apm/public/components/app/Home/Home.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Home/Home.test.tsx rename to x-pack/plugins/apm/public/components/app/Home/Home.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap b/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap rename to x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap index 2b1f835a14f4a3..9f461eeb5b6fcb 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/Home/__snapshots__/Home.test.tsx.snap @@ -5,7 +5,6 @@ exports[`Home component should render services 1`] = ` value={ Object { "config": Object { - "indexPatternTitle": "apm-*", "serviceMapEnabled": true, "ui": Object { "enabled": false, @@ -46,7 +45,6 @@ exports[`Home component should render traces 1`] = ` value={ Object { "config": Object { - "indexPatternTitle": "apm-*", "serviceMapEnabled": true, "ui": Object { "enabled": false, diff --git a/x-pack/legacy/plugins/apm/public/components/app/Home/index.tsx b/x-pack/plugins/apm/public/components/app/Home/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Home/index.tsx rename to x-pack/plugins/apm/public/components/app/Home/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.tsx b/x-pack/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.tsx rename to x-pack/plugins/apm/public/components/app/Main/ProvideBreadcrumbs.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/ScrollToTopOnPathChange.tsx b/x-pack/plugins/apm/public/components/app/Main/ScrollToTopOnPathChange.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Main/ScrollToTopOnPathChange.tsx rename to x-pack/plugins/apm/public/components/app/Main/ScrollToTopOnPathChange.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.test.tsx b/x-pack/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.test.tsx rename to x-pack/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx b/x-pack/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx rename to x-pack/plugins/apm/public/components/app/Main/UpdateBreadcrumbs.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/__snapshots__/UpdateBreadcrumbs.test.tsx.snap b/x-pack/plugins/apm/public/components/app/Main/__snapshots__/UpdateBreadcrumbs.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Main/__snapshots__/UpdateBreadcrumbs.test.tsx.snap rename to x-pack/plugins/apm/public/components/app/Main/__snapshots__/UpdateBreadcrumbs.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/__test__/ProvideBreadcrumbs.test.tsx b/x-pack/plugins/apm/public/components/app/Main/__test__/ProvideBreadcrumbs.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Main/__test__/ProvideBreadcrumbs.test.tsx rename to x-pack/plugins/apm/public/components/app/Main/__test__/ProvideBreadcrumbs.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/route_config/index.tsx b/x-pack/plugins/apm/public/components/app/Main/route_config/index.tsx similarity index 98% rename from x-pack/legacy/plugins/apm/public/components/app/Main/route_config/index.tsx rename to x-pack/plugins/apm/public/components/app/Main/route_config/index.tsx index c87e56fe9eff65..6d1db8c5dc6d43 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Main/route_config/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Main/route_config/index.tsx @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { Redirect, RouteComponentProps } from 'react-router-dom'; -import { SERVICE_NODE_NAME_MISSING } from '../../../../../../../../plugins/apm/common/service_nodes'; +import { SERVICE_NODE_NAME_MISSING } from '../../../../../common/service_nodes'; import { ErrorGroupDetails } from '../../ErrorGroupDetails'; import { ServiceDetails } from '../../ServiceDetails'; import { TransactionDetails } from '../../TransactionDetails'; @@ -20,7 +20,7 @@ import { ApmIndices } from '../../Settings/ApmIndices'; import { toQuery } from '../../../shared/Links/url_helpers'; import { ServiceNodeMetrics } from '../../ServiceNodeMetrics'; import { resolveUrlParams } from '../../../../context/UrlParamsContext/resolveUrlParams'; -import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../../../../../plugins/apm/common/i18n'; +import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../../common/i18n'; import { TraceLink } from '../../TraceLink'; import { CustomizeUI } from '../../Settings/CustomizeUI'; import { diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/route_config/route_handlers/agent_configuration.tsx b/x-pack/plugins/apm/public/components/app/Main/route_config/route_handlers/agent_configuration.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Main/route_config/route_handlers/agent_configuration.tsx rename to x-pack/plugins/apm/public/components/app/Main/route_config/route_handlers/agent_configuration.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Main/route_config/route_names.tsx b/x-pack/plugins/apm/public/components/app/Main/route_config/route_names.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Main/route_config/route_names.tsx rename to x-pack/plugins/apm/public/components/app/Main/route_config/route_names.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/AlertingFlyout/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/AlertingFlyout/index.tsx similarity index 82% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/AlertingFlyout/index.tsx rename to x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/AlertingFlyout/index.tsx index 7e8d057a7be6c8..a1ccb04e3c42a7 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/AlertingFlyout/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/AlertingFlyout/index.tsx @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { AlertType } from '../../../../../../../../../plugins/apm/common/alert_types'; -import { AlertAdd } from '../../../../../../../../../plugins/triggers_actions_ui/public'; +import { AlertType } from '../../../../../../common/alert_types'; +import { AlertAdd } from '../../../../../../../triggers_actions_ui/public'; type AlertAddProps = React.ComponentProps; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx rename to x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx index 92b325ab00d353..75c6c79bc804a0 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/AlertIntegrations/index.tsx @@ -12,7 +12,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; -import { AlertType } from '../../../../../../../../plugins/apm/common/alert_types'; +import { AlertType } from '../../../../../common/alert_types'; import { AlertingFlyout } from './AlertingFlyout'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx rename to x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx index 131bb7f65d4b31..7ab2f7bac8ae29 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceDetailTabs.tsx @@ -7,10 +7,7 @@ import { EuiTabs } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { - isJavaAgentName, - isRumAgentName -} from '../../../../../../../plugins/apm/common/agent_name'; +import { isJavaAgentName, isRumAgentName } from '../../../../common/agent_name'; import { useAgentName } from '../../../hooks/useAgentName'; import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; import { useUrlParams } from '../../../hooks/useUrlParams'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/TransactionSelect.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/TransactionSelect.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/TransactionSelect.tsx rename to x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/TransactionSelect.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx rename to x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx index cc5c62e25b4911..b7480a42ba94b5 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import React, { Component } from 'react'; -import { toMountPoint } from '../../../../../../../../../../src/plugins/kibana_react/public'; +import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public'; import { startMLJob } from '../../../../../services/rest/ml'; import { IUrlParams } from '../../../../../context/UrlParamsContext/types'; import { MLJobLink } from '../../../../shared/Links/MachineLearningLinks/MLJobLink'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx rename to x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/view.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx rename to x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx index 85254bee12e134..3bbd8a01d05494 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/WatcherFlyout.tsx @@ -30,12 +30,13 @@ import { padLeft, range } from 'lodash'; import moment from 'moment-timezone'; import React, { Component } from 'react'; import styled from 'styled-components'; -import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public'; +import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public'; import { IUrlParams } from '../../../../context/UrlParamsContext/types'; import { KibanaLink } from '../../../shared/Links/KibanaLink'; import { createErrorGroupWatch, Schedule } from './createErrorGroupWatch'; import { ElasticDocsLink } from '../../../shared/Links/ElasticDocsLink'; import { ApmPluginContext } from '../../../../context/ApmPluginContext'; +import { getApmIndexPatternTitle } from '../../../../services/rest/index_pattern'; type ScheduleKey = keyof Schedule; @@ -149,11 +150,7 @@ export class WatcherFlyout extends Component< this.setState({ slackUrl: event.target.value }); }; - public createWatch = ({ - indexPatternTitle - }: { - indexPatternTitle: string; - }) => () => { + public createWatch = () => { const { serviceName } = this.props.urlParams; const { core } = this.context; @@ -190,19 +187,21 @@ export class WatcherFlyout extends Component< unit: 'h' }; - return createErrorGroupWatch({ - http: core.http, - emails, - schedule, - serviceName, - slackUrl, - threshold: this.state.threshold, - timeRange, - apmIndexPatternTitle: indexPatternTitle - }) - .then((id: string) => { - this.props.onClose(); - this.addSuccessToast(id); + return getApmIndexPatternTitle() + .then(indexPatternTitle => { + return createErrorGroupWatch({ + http: core.http, + emails, + schedule, + serviceName, + slackUrl, + threshold: this.state.threshold, + timeRange, + apmIndexPatternTitle: indexPatternTitle + }).then((id: string) => { + this.props.onClose(); + this.addSuccessToast(id); + }); }) .catch(e => { // eslint-disable-next-line @@ -613,26 +612,20 @@ export class WatcherFlyout extends Component< - - {({ config }) => { - return ( - - {i18n.translate( - 'xpack.apm.serviceDetails.enableErrorReportsPanel.createWatchButtonLabel', - { - defaultMessage: 'Create watch' - } - )} - - ); - }} - + this.createWatch()} + fill + disabled={ + !this.state.actions.email && !this.state.actions.slack + } + > + {i18n.translate( + 'xpack.apm.serviceDetails.enableErrorReportsPanel.createWatchButtonLabel', + { + defaultMessage: 'Create watch' + } + )} + diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/__snapshots__/createErrorGroupWatch.test.ts.snap b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/__snapshots__/createErrorGroupWatch.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/__snapshots__/createErrorGroupWatch.test.ts.snap rename to x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/__snapshots__/createErrorGroupWatch.test.ts.snap diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/createErrorGroupWatch.test.ts b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/createErrorGroupWatch.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/createErrorGroupWatch.test.ts rename to x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/createErrorGroupWatch.test.ts diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/esResponse.ts b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/esResponse.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/esResponse.ts rename to x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/__test__/esResponse.ts diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts similarity index 98% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts rename to x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts index 690db9fcdd8d6e..d45453e24f1c96 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts +++ b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/createErrorGroupWatch.ts @@ -17,7 +17,7 @@ import { ERROR_LOG_MESSAGE, PROCESSOR_EVENT, SERVICE_NAME -} from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; +} from '../../../../../common/elasticsearch_fieldnames'; import { createWatch } from '../../../../services/rest/watcher'; function getSlackPathUrl(slackUrl?: string) { diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx rename to x-pack/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceDetails/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/index.tsx rename to x-pack/plugins/apm/public/components/app/ServiceDetails/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/BetaBadge.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/BetaBadge.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/BetaBadge.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/BetaBadge.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Controls.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Controls.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Controls.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/Controls.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx similarity index 98% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx index 53c86f92ee5578..ad77434bca9f43 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx @@ -19,7 +19,7 @@ import { cytoscapeOptions, nodeHeight } from './cytoscapeOptions'; -import { useUiTracker } from '../../../../../../../plugins/observability/public'; +import { useUiTracker } from '../../../../../observability/public'; export const CytoscapeContext = createContext( undefined diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/EmptyBanner.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/EmptyBanner.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/EmptyBanner.test.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/EmptyBanner.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/EmptyBanner.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/EmptyBanner.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/EmptyBanner.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/EmptyBanner.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/LoadingOverlay.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/LoadingOverlay.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/LoadingOverlay.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/LoadingOverlay.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Buttons.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx index 491ebdc5aad158..bc3434f277d1c0 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Contents.tsx @@ -12,7 +12,7 @@ import { } from '@elastic/eui'; import cytoscape from 'cytoscape'; import React from 'react'; -import { SERVICE_FRAMEWORK_NAME } from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; +import { SERVICE_FRAMEWORK_NAME } from '../../../../../common/elasticsearch_fieldnames'; import { Buttons } from './Buttons'; import { Info } from './Info'; import { ServiceMetricFetcher } from './ServiceMetricFetcher'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Info.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Info.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Info.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Info.tsx index e1df3b474e9de7..541f4f6a1e775f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Info.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Info.tsx @@ -12,7 +12,7 @@ import styled from 'styled-components'; import { SPAN_SUBTYPE, SPAN_TYPE -} from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; +} from '../../../../../common/elasticsearch_fieldnames'; const ItemRow = styled.div` line-height: 2; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Popover.stories.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx index 697aa6a1b652b8..5e6412333a2e18 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricFetcher.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { ServiceNodeMetrics } from '../../../../../../../../plugins/apm/common/service_map'; +import { ServiceNodeMetrics } from '../../../../../common/service_map'; import { useFetcher } from '../../../../hooks/useFetcher'; import { useUrlParams } from '../../../../hooks/useUrlParams'; import { ServiceMetricList } from './ServiceMetricList'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx index 056af68cc8173c..3cee986261a68b 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/ServiceMetricList.tsx @@ -15,7 +15,7 @@ import { i18n } from '@kbn/i18n'; import { isNumber } from 'lodash'; import React from 'react'; import styled from 'styled-components'; -import { ServiceNodeMetrics } from '../../../../../../../../plugins/apm/common/service_map'; +import { ServiceNodeMetrics } from '../../../../../common/service_map'; import { asDuration, asPercent, tpmUnit } from '../../../../utils/formatters'; function LoadingSpinner() { diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx index 102b135f3cd1f8..1c9d5092bfcf5d 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/index.tsx @@ -14,7 +14,7 @@ import React, { useRef, useState } from 'react'; -import { SERVICE_NAME } from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; +import { SERVICE_NAME } from '../../../../../common/elasticsearch_fieldnames'; import { CytoscapeContext } from '../Cytoscape'; import { Contents } from './Contents'; import { animationOptions } from '../cytoscapeOptions'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscape-layout-test-response.json b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape-layout-test-response.json similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscape-layout-test-response.json rename to x-pack/plugins/apm/public/components/app/ServiceMap/cytoscape-layout-test-response.json diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts similarity index 98% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts rename to x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts index e9942a327b69ed..554f84f0ad2368 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts @@ -9,7 +9,7 @@ import { CSSProperties } from 'react'; import { SERVICE_NAME, SPAN_DESTINATION_SERVICE_RESOURCE -} from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; +} from '../../../../common/elasticsearch_fieldnames'; import { defaultIcon, iconForNode } from './icons'; // IE 11 does not properly load some SVGs or draw certain shapes. This causes diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons.ts rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts index 321b39dabbbd51..9fe5cbd23b07cd 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons.ts +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/icons.ts @@ -5,12 +5,12 @@ */ import cytoscape from 'cytoscape'; -import { isRumAgentName } from '../../../../../../../plugins/apm/common/agent_name'; +import { isRumAgentName } from '../../../../common/agent_name'; import { AGENT_NAME, SPAN_SUBTYPE, SPAN_TYPE -} from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; +} from '../../../../common/elasticsearch_fieldnames'; import awsIcon from './icons/aws.svg'; import cassandraIcon from './icons/cassandra.svg'; import darkIcon from './icons/dark.svg'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/aws.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/aws.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/aws.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/aws.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/cassandra.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/cassandra.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/cassandra.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/cassandra.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/dark.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/dark.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/dark.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/dark.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/database.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/database.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/database.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/database.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/default.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/default.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/default.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/default.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/documents.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/documents.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/documents.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/documents.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/dot-net.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/dot-net.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/dot-net.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/dot-net.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/elasticsearch.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/elasticsearch.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/elasticsearch.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/elasticsearch.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/globe.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/globe.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/globe.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/globe.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/go.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/go.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/go.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/go.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/graphql.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/graphql.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/graphql.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/graphql.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/grpc.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/grpc.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/grpc.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/grpc.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/handlebars.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/handlebars.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/handlebars.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/handlebars.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/java.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/java.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/java.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/java.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/kafka.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/kafka.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/kafka.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/kafka.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/mongodb.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/mongodb.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/mongodb.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/mongodb.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/mysql.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/mysql.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/mysql.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/mysql.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/nodejs.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/nodejs.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/nodejs.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/nodejs.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/php.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/php.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/php.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/php.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/postgresql.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/postgresql.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/postgresql.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/postgresql.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/python.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/python.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/python.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/python.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/redis.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/redis.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/redis.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/redis.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/ruby.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/ruby.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/ruby.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/ruby.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/rumjs.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/rumjs.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/rumjs.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/rumjs.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/websocket.svg b/x-pack/plugins/apm/public/components/app/ServiceMap/icons/websocket.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons/websocket.svg rename to x-pack/plugins/apm/public/components/app/ServiceMap/icons/websocket.svg diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/index.test.tsx similarity index 94% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.test.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/index.test.tsx index d93caa601f0b6b..c7e25269511bfa 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/index.test.tsx @@ -6,7 +6,7 @@ import { render } from '@testing-library/react'; import React, { FunctionComponent } from 'react'; -import { License } from '../../../../../../../plugins/licensing/common/license'; +import { License } from '../../../../../licensing/common/license'; import { LicenseContext } from '../../../context/LicenseContext'; import { ServiceMap } from './'; import { MockApmPluginContextWrapper } from '../../../context/ApmPluginContext/MockApmPluginContext'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx index 94e42f1b911605..b57f0b047c6134 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/index.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { invalidLicenseMessage, isValidPlatinumLicense -} from '../../../../../../../plugins/apm/common/service_map'; +} from '../../../../common/service_map'; import { useFetcher } from '../../../hooks/useFetcher'; import { useLicense } from '../../../hooks/useLicense'; import { useUrlParams } from '../../../hooks/useUrlParams'; @@ -23,7 +23,7 @@ import { EmptyBanner } from './EmptyBanner'; import { Popover } from './Popover'; import { useRefDimensions } from './useRefDimensions'; import { BetaBadge } from './BetaBadge'; -import { useTrackPageview } from '../../../../../../../plugins/observability/public'; +import { useTrackPageview } from '../../../../../observability/public'; interface ServiceMapProps { serviceName?: string; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/useRefDimensions.ts b/x-pack/plugins/apm/public/components/app/ServiceMap/useRefDimensions.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMap/useRefDimensions.ts rename to x-pack/plugins/apm/public/components/app/ServiceMap/useRefDimensions.ts diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceMetrics/index.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx rename to x-pack/plugins/apm/public/components/app/ServiceMetrics/index.tsx index 060e635e835496..0fb8c00a2b1620 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMetrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMetrics/index.tsx @@ -16,7 +16,7 @@ import { useServiceMetricCharts } from '../../../hooks/useServiceMetricCharts'; import { MetricsChart } from '../../shared/charts/MetricsChart'; import { useUrlParams } from '../../../hooks/useUrlParams'; import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext'; -import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings'; +import { PROJECTION } from '../../../../common/projections/typings'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; interface ServiceMetricsProps { diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.test.tsx rename to x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx similarity index 98% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx rename to x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx index 2bf26946932ea6..3929c153ae4191 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceNodeMetrics/index.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; -import { SERVICE_NODE_NAME_MISSING } from '../../../../../../../plugins/apm/common/service_nodes'; +import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes'; import { ApmHeader } from '../../shared/ApmHeader'; import { useUrlParams } from '../../../hooks/useUrlParams'; import { useAgentName } from '../../../hooks/useAgentName'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx rename to x-pack/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx index 3af1a70ef3fdc7..4e57cb47691bee 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceNodeOverview/index.tsx @@ -13,9 +13,9 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; -import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../../../../plugins/apm/common/i18n'; -import { SERVICE_NODE_NAME_MISSING } from '../../../../../../../plugins/apm/common/service_nodes'; -import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings'; +import { UNIDENTIFIED_SERVICE_NODES_LABEL } from '../../../../common/i18n'; +import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes'; +import { PROJECTION } from '../../../../common/projections/typings'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; import { useUrlParams } from '../../../hooks/useUrlParams'; import { ManagedTable, ITableColumn } from '../../shared/ManagedTable'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx rename to x-pack/plugins/apm/public/components/app/ServiceOverview/NoServicesMessage.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js rename to x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/List.test.js diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap rename to x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/__snapshots__/List.test.js.snap diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/props.json b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/props.json similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/props.json rename to x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/__test__/props.json diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx similarity index 94% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx rename to x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx index 1ac29c5626e3a1..7e2d03ad358991 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx @@ -9,8 +9,8 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import styled from 'styled-components'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ServiceListAPIResponse } from '../../../../../../../../plugins/apm/server/lib/services/get_services'; -import { NOT_AVAILABLE_LABEL } from '../../../../../../../../plugins/apm/common/i18n'; +import { ServiceListAPIResponse } from '../../../../../server/lib/services/get_services'; +import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; import { fontSizes, truncate } from '../../../../style/variables'; import { asDecimal, convertTo } from '../../../../utils/formatters'; import { ManagedTable } from '../../../shared/ManagedTable'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/NoServicesMessage.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/NoServicesMessage.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/NoServicesMessage.test.tsx rename to x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/NoServicesMessage.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx rename to x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/ServiceOverview.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap rename to x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/NoServicesMessage.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap rename to x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx rename to x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx index 52bc414a93a238..99b169e3ec3619 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/index.tsx @@ -9,13 +9,13 @@ import { EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useMemo } from 'react'; import url from 'url'; -import { toMountPoint } from '../../../../../../../../src/plugins/kibana_react/public'; +import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public'; import { useFetcher } from '../../../hooks/useFetcher'; import { NoServicesMessage } from './NoServicesMessage'; import { ServiceList } from './ServiceList'; import { useUrlParams } from '../../../hooks/useUrlParams'; -import { useTrackPageview } from '../../../../../../../plugins/observability/public'; -import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings'; +import { useTrackPageview } from '../../../../../observability/public'; +import { PROJECTION } from '../../../../common/projections/typings'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/FormRowSelect.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/FormRowSelect.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/FormRowSelect.tsx rename to x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/FormRowSelect.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/ServicePage.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/ServicePage.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/ServicePage.tsx rename to x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/ServicePage.tsx index 43002c79aa2b47..f4b942c7f46eb2 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/ServicePage.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/ServicePage.tsx @@ -16,11 +16,11 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { isString } from 'lodash'; import { EuiButtonEmpty } from '@elastic/eui'; -import { AgentConfigurationIntake } from '../../../../../../../../../../plugins/apm/common/agent_configuration/configuration_types'; +import { AgentConfigurationIntake } from '../../../../../../../common/agent_configuration/configuration_types'; import { omitAllOption, getOptionLabel -} from '../../../../../../../../../../plugins/apm/common/agent_configuration/all_option'; +} from '../../../../../../../common/agent_configuration/all_option'; import { useFetcher, FETCH_STATUS } from '../../../../../../hooks/useFetcher'; import { FormRowSelect } from './FormRowSelect'; import { APMLink } from '../../../../../shared/Links/apm/APMLink'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx rename to x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx index baab600145b812..fcd75a05b01d9e 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx @@ -17,12 +17,12 @@ import { EuiIconTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { SettingDefinition } from '../../../../../../../../../../plugins/apm/common/agent_configuration/setting_definitions/types'; -import { isValid } from '../../../../../../../../../../plugins/apm/common/agent_configuration/setting_definitions'; +import { SettingDefinition } from '../../../../../../../common/agent_configuration/setting_definitions/types'; +import { isValid } from '../../../../../../../common/agent_configuration/setting_definitions'; import { amountAndUnitToString, amountAndUnitToObject -} from '../../../../../../../../../../plugins/apm/common/agent_configuration/amount_and_unit'; +} from '../../../../../../../common/agent_configuration/amount_and_unit'; import { SelectWithPlaceholder } from '../../../../../shared/SelectWithPlaceholder'; function FormRow({ diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingsPage.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingsPage.tsx similarity index 94% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingsPage.tsx rename to x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingsPage.tsx index 6d76b696003330..e41bdaf0c9c09f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingsPage.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingsPage.tsx @@ -23,19 +23,19 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty } from '@elastic/eui'; import { EuiCallOut } from '@elastic/eui'; import { FETCH_STATUS } from '../../../../../../hooks/useFetcher'; -import { AgentName } from '../../../../../../../../../../plugins/apm/typings/es_schemas/ui/fields/agent'; +import { AgentName } from '../../../../../../../typings/es_schemas/ui/fields/agent'; import { history } from '../../../../../../utils/history'; -import { AgentConfigurationIntake } from '../../../../../../../../../../plugins/apm/common/agent_configuration/configuration_types'; +import { AgentConfigurationIntake } from '../../../../../../../common/agent_configuration/configuration_types'; import { filterByAgent, settingDefinitions, isValid -} from '../../../../../../../../../../plugins/apm/common/agent_configuration/setting_definitions'; +} from '../../../../../../../common/agent_configuration/setting_definitions'; import { saveConfig } from './saveConfig'; import { useApmPluginContext } from '../../../../../../hooks/useApmPluginContext'; -import { useUiTracker } from '../../../../../../../../../../plugins/observability/public'; +import { useUiTracker } from '../../../../../../../../observability/public'; import { SettingFormRow } from './SettingFormRow'; -import { getOptionLabel } from '../../../../../../../../../../plugins/apm/common/agent_configuration/all_option'; +import { getOptionLabel } from '../../../../../../../common/agent_configuration/all_option'; function removeEmpty(obj: T): T { return Object.fromEntries( diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/saveConfig.ts b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/saveConfig.ts similarity index 90% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/saveConfig.ts rename to x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/saveConfig.ts index 7e3bcd68699bee..5f7354bf6f7137 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/saveConfig.ts +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/saveConfig.ts @@ -6,11 +6,11 @@ import { i18n } from '@kbn/i18n'; import { NotificationsStart } from 'kibana/public'; -import { AgentConfigurationIntake } from '../../../../../../../../../../plugins/apm/common/agent_configuration/configuration_types'; +import { AgentConfigurationIntake } from '../../../../../../../common/agent_configuration/configuration_types'; import { getOptionLabel, omitAllOption -} from '../../../../../../../../../../plugins/apm/common/agent_configuration/all_option'; +} from '../../../../../../../common/agent_configuration/all_option'; import { callApmApi } from '../../../../../../services/rest/createCallApmApi'; export async function saveConfig({ diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx rename to x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx index 531e557b6ef864..089bc58f50a88e 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.stories.tsx @@ -13,7 +13,7 @@ import { storiesOf } from '@storybook/react'; import React from 'react'; import { HttpSetup } from 'kibana/public'; -import { AgentConfiguration } from '../../../../../../../../../plugins/apm/common/agent_configuration/configuration_types'; +import { AgentConfiguration } from '../../../../../../common/agent_configuration/configuration_types'; import { FETCH_STATUS } from '../../../../../hooks/useFetcher'; import { createCallApmApi } from '../../../../../services/rest/createCallApmApi'; import { AgentConfigurationCreateEdit } from './index'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.tsx rename to x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.tsx index 638e518563f8c9..3a6f94b9758002 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/index.tsx @@ -13,7 +13,7 @@ import { history } from '../../../../../utils/history'; import { AgentConfigurationIntake, AgentConfiguration -} from '../../../../../../../../../plugins/apm/common/agent_configuration/configuration_types'; +} from '../../../../../../common/agent_configuration/configuration_types'; import { ServicePage } from './ServicePage/ServicePage'; import { SettingsPage } from './SettingsPage/SettingsPage'; import { fromQuery, toQuery } from '../../../../shared/Links/url_helpers'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/List/ConfirmDeleteModal.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/ConfirmDeleteModal.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/List/ConfirmDeleteModal.tsx rename to x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/ConfirmDeleteModal.tsx index 267aaddc93f768..6a1a4725623055 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/List/ConfirmDeleteModal.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/ConfirmDeleteModal.tsx @@ -9,8 +9,8 @@ import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui'; import { NotificationsStart } from 'kibana/public'; import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AgentConfigurationListAPIResponse } from '../../../../../../../../../plugins/apm/server/lib/settings/agent_configuration/list_configurations'; -import { getOptionLabel } from '../../../../../../../../../plugins/apm/common/agent_configuration/all_option'; +import { AgentConfigurationListAPIResponse } from '../../../../../../server/lib/settings/agent_configuration/list_configurations'; +import { getOptionLabel } from '../../../../../../common/agent_configuration/all_option'; import { callApmApi } from '../../../../../services/rest/createCallApmApi'; import { useApmPluginContext } from '../../../../../hooks/useApmPluginContext'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/List/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/index.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/List/index.tsx rename to x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/index.tsx index 6d5f65121d8fd1..9eaa7786baca08 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/List/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/List/index.tsx @@ -20,10 +20,10 @@ import { FETCH_STATUS } from '../../../../../hooks/useFetcher'; import { ITableColumn, ManagedTable } from '../../../../shared/ManagedTable'; import { LoadingStatePrompt } from '../../../../shared/LoadingStatePrompt'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AgentConfigurationListAPIResponse } from '../../../../../../../../../plugins/apm/server/lib/settings/agent_configuration/list_configurations'; +import { AgentConfigurationListAPIResponse } from '../../../../../../server/lib/settings/agent_configuration/list_configurations'; import { TimestampTooltip } from '../../../../shared/TimestampTooltip'; import { px, units } from '../../../../../style/variables'; -import { getOptionLabel } from '../../../../../../../../../plugins/apm/common/agent_configuration/all_option'; +import { getOptionLabel } from '../../../../../../common/agent_configuration/all_option'; import { createAgentConfigurationHref, editAgentConfigurationHref diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx rename to x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx index 8171e339adc82b..4349e542449ccb 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/AgentConfigurations/index.tsx @@ -17,7 +17,7 @@ import { import { isEmpty } from 'lodash'; import { useFetcher } from '../../../../hooks/useFetcher'; import { AgentConfigurationList } from './List'; -import { useTrackPageview } from '../../../../../../../../plugins/observability/public'; +import { useTrackPageview } from '../../../../../../observability/public'; import { createAgentConfigurationHref } from '../../../shared/Links/apm/agentConfigurationLinks'; export function AgentConfigurations() { diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/ApmIndices/index.test.tsx b/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/ApmIndices/index.test.tsx rename to x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx rename to x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CreateCustomLinkButton.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/DeleteButton.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/DeleteButton.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/DeleteButton.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/DeleteButton.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/Documentation.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/Documentation.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/Documentation.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/Documentation.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx similarity index 98% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx index fb8ffe6722c87d..9c244e3cde411e 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx @@ -19,7 +19,7 @@ import React from 'react'; import { Filter, FilterKey -} from '../../../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; +} from '../../../../../../../common/custom_link/custom_link_types'; import { DEFAULT_OPTION, FILTER_SELECT_OPTIONS, diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FlyoutFooter.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FlyoutFooter.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FlyoutFooter.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FlyoutFooter.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.test.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.test.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.tsx similarity index 94% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.tsx index 8edfb176a1af8d..8fed838a48261f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkPreview.tsx @@ -17,8 +17,8 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { debounce } from 'lodash'; -import { Filter } from '../../../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; -import { Transaction } from '../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Filter } from '../../../../../../../common/custom_link/custom_link_types'; +import { Transaction } from '../../../../../../../typings/es_schemas/ui/transaction'; import { callApmApi } from '../../../../../../services/rest/createCallApmApi'; import { replaceTemplateVariables, convertFiltersToQuery } from './helper'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkSection.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkSection.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkSection.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkSection.tsx index 630f7148ad4083..210033888d90c5 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkSection.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/LinkSection.tsx @@ -12,7 +12,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { CustomLink } from '../../../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; +import { CustomLink } from '../../../../../../../common/custom_link/custom_link_types'; import { Documentation } from './Documentation'; interface InputField { diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.test.ts b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.test.ts similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.test.ts rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.test.ts index 0a63cfcff9aa53..49e381aab675d6 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.test.ts +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.test.ts @@ -7,7 +7,7 @@ import { getSelectOptions, replaceTemplateVariables } from '../CustomLinkFlyout/helper'; -import { Transaction } from '../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../../../typings/es_schemas/ui/transaction'; describe('Custom link helper', () => { describe('getSelectOptions', () => { diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.ts b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.ts similarity index 91% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.ts rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.ts index 7bfdbf1655e0d8..8c35b8fe775065 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.ts +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/helper.ts @@ -6,12 +6,12 @@ import { i18n } from '@kbn/i18n'; import Mustache from 'mustache'; import { isEmpty, get } from 'lodash'; -import { FILTER_OPTIONS } from '../../../../../../../../../../plugins/apm/common/custom_link/custom_link_filter_options'; +import { FILTER_OPTIONS } from '../../../../../../../common/custom_link/custom_link_filter_options'; import { Filter, FilterKey -} from '../../../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; -import { Transaction } from '../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +} from '../../../../../../../common/custom_link/custom_link_types'; +import { Transaction } from '../../../../../../../typings/es_schemas/ui/transaction'; interface FilterSelectOption { value: 'DEFAULT' | FilterKey; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/index.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/index.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/index.tsx index 0b25a0a79edd91..150147d9af405e 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/index.tsx @@ -14,7 +14,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useState } from 'react'; -import { Filter } from '../../../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; +import { Filter } from '../../../../../../../common/custom_link/custom_link_types'; import { useApmPluginContext } from '../../../../../../hooks/useApmPluginContext'; import { FiltersSection } from './FiltersSection'; import { FlyoutFooter } from './FlyoutFooter'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/saveCustomLink.ts b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/saveCustomLink.ts similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/saveCustomLink.ts rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/saveCustomLink.ts index 9cbaf16320a6bb..685b3ab0229507 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/saveCustomLink.ts +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/saveCustomLink.ts @@ -9,7 +9,7 @@ import { NotificationsStart } from 'kibana/public'; import { Filter, CustomLink -} from '../../../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; +} from '../../../../../../../common/custom_link/custom_link_types'; import { callApmApi } from '../../../../../../services/rest/createCallApmApi'; export async function saveCustomLink({ diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx index 68e6ee52af0b06..d68fb757e53d17 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkTable.tsx @@ -13,7 +13,7 @@ import { EuiSpacer } from '@elastic/eui'; import { isEmpty } from 'lodash'; -import { CustomLink } from '../../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; +import { CustomLink } from '../../../../../../common/custom_link/custom_link_types'; import { units, px } from '../../../../../style/variables'; import { ManagedTable } from '../../../../shared/ManagedTable'; import { TimestampTooltip } from '../../../../shared/TimestampTooltip'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/EmptyPrompt.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/EmptyPrompt.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/EmptyPrompt.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/EmptyPrompt.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/Title.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/Title.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/Title.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/Title.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx similarity index 99% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx index e5c20b260e0972..32a08f5ffaf7ca 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.test.tsx @@ -8,7 +8,7 @@ import { fireEvent, render, wait, RenderResult } from '@testing-library/react'; import React from 'react'; import { act } from 'react-dom/test-utils'; import * as apmApi from '../../../../../services/rest/createCallApmApi'; -import { License } from '../../../../../../../../../plugins/licensing/common/license'; +import { License } from '../../../../../../../licensing/common/license'; import * as hooks from '../../../../../hooks/useFetcher'; import { LicenseContext } from '../../../../../context/LicenseContext'; import { CustomLinkOverview } from '.'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx index e9a915e0f59bce..b94ce513bc2109 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx @@ -8,7 +8,7 @@ import { EuiPanel, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { isEmpty } from 'lodash'; import React, { useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { CustomLink } from '../../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; +import { CustomLink } from '../../../../../../common/custom_link/custom_link_types'; import { useLicense } from '../../../../../hooks/useLicense'; import { useFetcher, FETCH_STATUS } from '../../../../../hooks/useFetcher'; import { CustomLinkFlyout } from './CustomLinkFlyout'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/index.tsx rename to x-pack/plugins/apm/public/components/app/Settings/CustomizeUI/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/index.tsx b/x-pack/plugins/apm/public/components/app/Settings/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/Settings/index.tsx rename to x-pack/plugins/apm/public/components/app/Settings/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TraceLink/__test__/TraceLink.test.tsx b/x-pack/plugins/apm/public/components/app/TraceLink/__test__/TraceLink.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TraceLink/__test__/TraceLink.test.tsx rename to x-pack/plugins/apm/public/components/app/TraceLink/__test__/TraceLink.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TraceLink/index.tsx b/x-pack/plugins/apm/public/components/app/TraceLink/index.tsx similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/app/TraceLink/index.tsx rename to x-pack/plugins/apm/public/components/app/TraceLink/index.tsx index f0301f8917d10b..04d830f4649d4f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TraceLink/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TraceLink/index.tsx @@ -9,8 +9,8 @@ import React from 'react'; import { Redirect } from 'react-router-dom'; import styled from 'styled-components'; import url from 'url'; -import { TRACE_ID } from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; -import { Transaction } from '../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { TRACE_ID } from '../../../../common/elasticsearch_fieldnames'; +import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; import { FETCH_STATUS, useFetcher } from '../../../hooks/useFetcher'; import { useUrlParams } from '../../../hooks/useUrlParams'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/TraceList.tsx b/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/TraceOverview/TraceList.tsx rename to x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx index 91f3051acf0770..92d5a38cc11ca3 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/TraceList.tsx +++ b/x-pack/plugins/apm/public/components/app/TraceOverview/TraceList.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import styled from 'styled-components'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ITransactionGroup } from '../../../../../../../plugins/apm/server/lib/transaction_groups/transform'; +import { ITransactionGroup } from '../../../../server/lib/transaction_groups/transform'; import { fontSizes, truncate } from '../../../style/variables'; import { convertTo } from '../../../utils/formatters'; import { EmptyMessage } from '../../shared/EmptyMessage'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx b/x-pack/plugins/apm/public/components/app/TraceOverview/index.tsx similarity index 91% rename from x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx rename to x-pack/plugins/apm/public/components/app/TraceOverview/index.tsx index bfbad78a5c0269..a7fa927f9e9b15 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TraceOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TraceOverview/index.tsx @@ -9,9 +9,9 @@ import React, { useMemo } from 'react'; import { FETCH_STATUS, useFetcher } from '../../../hooks/useFetcher'; import { TraceList } from './TraceList'; import { useUrlParams } from '../../../hooks/useUrlParams'; -import { useTrackPageview } from '../../../../../../../plugins/observability/public'; +import { useTrackPageview } from '../../../../../observability/public'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; -import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings'; +import { PROJECTION } from '../../../../common/projections/typings'; export function TraceOverview() { const { urlParams, uiFilters } = useUrlParams(); diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts rename to x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts index 08682fb3be8428..7ad0a77505b9dd 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/__test__/distribution.test.ts @@ -6,7 +6,7 @@ import { getFormattedBuckets } from '../index'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { IBucket } from '../../../../../../../../../plugins/apm/server/lib/transactions/distribution/get_buckets/transform'; +import { IBucket } from '../../../../../../server/lib/transactions/distribution/get_buckets/transform'; describe('Distribution', () => { it('getFormattedBuckets', () => { diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx index e70133aabb679f..b7dbfbdbd7d7ee 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/Distribution/index.tsx @@ -10,9 +10,9 @@ import d3 from 'd3'; import React, { FunctionComponent, useCallback } from 'react'; import { isEmpty } from 'lodash'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { TransactionDistributionAPIResponse } from '../../../../../../../../plugins/apm/server/lib/transactions/distribution'; +import { TransactionDistributionAPIResponse } from '../../../../../server/lib/transactions/distribution'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { IBucket } from '../../../../../../../../plugins/apm/server/lib/transactions/distribution/get_buckets/transform'; +import { IBucket } from '../../../../../server/lib/transactions/distribution/get_buckets/transform'; import { IUrlParams } from '../../../../context/UrlParamsContext/types'; import { getDurationFormatter } from '../../../../utils/formatters'; // @ts-ignore diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/ErrorCount.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/ErrorCount.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/ErrorCount.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/ErrorCount.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx index 4e105957f5f9d8..1db8e02e386927 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/MaybeViewTraceLink.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { EuiButton, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Transaction as ITransaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction as ITransaction } from '../../../../../typings/es_schemas/ui/transaction'; import { TransactionDetailLink } from '../../../shared/Links/apm/TransactionDetailLink'; import { IWaterfall } from './WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/PercentOfParent.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/PercentOfParent.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/PercentOfParent.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/PercentOfParent.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx index 9026dd90ddceb5..27e0584c696c1a 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TransactionTabs.tsx @@ -8,7 +8,7 @@ import { EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Location } from 'history'; import React from 'react'; -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { IUrlParams } from '../../../../context/UrlParamsContext/types'; import { fromQuery, toQuery } from '../../../shared/Links/url_helpers'; import { history } from '../../../../utils/history'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts index 030729522f35e4..ae908b25cc6155 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_agent_marks.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction'; import { getAgentMarks } from '../get_agent_marks'; describe('getAgentMarks', () => { diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_error_marks.test.ts b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_error_marks.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_error_marks.test.ts rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/__test__/get_error_marks.test.ts diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts similarity index 87% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts index 1dcb1598662c96..2bc64e30b4f7e5 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_agent_marks.ts @@ -5,7 +5,7 @@ */ import { sortBy } from 'lodash'; -import { Transaction } from '../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../../../typings/es_schemas/ui/transaction'; import { Mark } from '.'; // Extends Mark without adding new properties to it. diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts similarity index 89% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts index a9694efcbcae74..ad54cec5c26a74 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/get_error_marks.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { isEmpty } from 'lodash'; -import { ErrorRaw } from '../../../../../../../../../../plugins/apm/typings/es_schemas/raw/error_raw'; +import { ErrorRaw } from '../../../../../../../typings/es_schemas/raw/error_raw'; import { IWaterfallError, IServiceColors diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/index.ts b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/index.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/index.ts rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Marks/index.ts diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/ServiceLegends.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/ServiceLegends.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/ServiceLegends.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/ServiceLegends.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx similarity index 90% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx index 6e58dbc5b6ea33..bbc457450e475b 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx @@ -9,8 +9,8 @@ import React from 'react'; import { SERVICE_NAME, TRANSACTION_NAME -} from '../../../../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; -import { Transaction } from '../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +} from '../../../../../../../common/elasticsearch_fieldnames'; +import { Transaction } from '../../../../../../../typings/es_schemas/ui/transaction'; import { TransactionDetailLink } from '../../../../../shared/Links/apm/TransactionDetailLink'; import { StickyProperties } from '../../../../../shared/StickyProperties'; import { TransactionOverviewLink } from '../../../../../shared/Links/apm/TransactionOverviewLink'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/ResponsiveFlyout.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/ResponsiveFlyout.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/ResponsiveFlyout.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/ResponsiveFlyout.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/DatabaseContext.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/DatabaseContext.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/DatabaseContext.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/DatabaseContext.tsx index 6200d5f098ad59..7a08a84bf30bac 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/DatabaseContext.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/DatabaseContext.tsx @@ -18,7 +18,7 @@ import SyntaxHighlighter, { // @ts-ignore import { xcode } from 'react-syntax-highlighter/dist/styles'; import styled from 'styled-components'; -import { Span } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/span'; +import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; import { borderRadius, fontFamilyCode, diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/HttpContext.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/HttpContext.tsx similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/HttpContext.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/HttpContext.tsx index 438e88df3351d7..28564481074fa4 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/HttpContext.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/HttpContext.tsx @@ -17,7 +17,7 @@ import { unit, units } from '../../../../../../../style/variables'; -import { Span } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/span'; +import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; const ContextUrl = styled.div` padding: ${px(units.half)} ${px(unit)}; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx similarity index 86% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx index 621497a0b22e01..d49959c5cbffb8 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx @@ -6,14 +6,14 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; -import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction'; import { SPAN_NAME, TRANSACTION_NAME, SERVICE_NAME -} from '../../../../../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; -import { NOT_AVAILABLE_LABEL } from '../../../../../../../../../../../plugins/apm/common/i18n'; -import { Span } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/span'; +} from '../../../../../../../../common/elasticsearch_fieldnames'; +import { NOT_AVAILABLE_LABEL } from '../../../../../../../../common/i18n'; +import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; import { StickyProperties } from '../../../../../../shared/StickyProperties'; import { TransactionOverviewLink } from '../../../../../../shared/Links/apm/TransactionOverviewLink'; import { TransactionDetailLink } from '../../../../../../shared/Links/apm/TransactionDetailLink'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/TruncateHeightSection.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/TruncateHeightSection.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/TruncateHeightSection.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/TruncateHeightSection.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx index f57ddb5cf69a23..1da22516629f26 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx @@ -25,8 +25,8 @@ import { px, units } from '../../../../../../../style/variables'; import { Summary } from '../../../../../../shared/Summary'; import { TimestampTooltip } from '../../../../../../shared/TimestampTooltip'; import { DurationSummaryItem } from '../../../../../../shared/Summary/DurationSummaryItem'; -import { Span } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/span'; -import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; +import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction'; import { DiscoverSpanLink } from '../../../../../../shared/Links/DiscoverLinks/DiscoverSpanLink'; import { Stacktrace } from '../../../../../../shared/Stacktrace'; import { ResponsiveFlyout } from '../ResponsiveFlyout'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.stories.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.stories.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.stories.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.stories.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SyncBadge.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/DroppedSpansWarning.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/DroppedSpansWarning.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/DroppedSpansWarning.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/DroppedSpansWarning.tsx index 85cf0b642530fc..87ecb96f74735f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/DroppedSpansWarning.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/DroppedSpansWarning.tsx @@ -7,7 +7,7 @@ import { EuiCallOut, EuiHorizontalRule } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction'; import { ElasticDocsLink } from '../../../../../../shared/Links/ElasticDocsLink'; export function DroppedSpansWarning({ diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx index e24414bb28d52b..5fb679818f0a7e 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx @@ -16,7 +16,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction'; import { TransactionActionMenu } from '../../../../../../shared/TransactionActionMenu/TransactionActionMenu'; import { TransactionSummary } from '../../../../../../shared/Summary/TransactionSummary'; import { FlyoutTopLevelProperties } from '../FlyoutTopLevelProperties'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallFlyout.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallFlyout.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallFlyout.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallFlyout.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx index 5c6e0cc5ce4350..d8edcce46c2d76 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/WaterfallItem.tsx @@ -10,13 +10,13 @@ import styled from 'styled-components'; import { EuiIcon, EuiText, EuiTitle, EuiToolTip } from '@elastic/eui'; import theme from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; -import { isRumAgentName } from '../../../../../../../../../../plugins/apm/common/agent_name'; +import { isRumAgentName } from '../../../../../../../common/agent_name'; import { px, unit, units } from '../../../../../../style/variables'; import { asDuration } from '../../../../../../utils/formatters'; import { ErrorCount } from '../../ErrorCount'; import { IWaterfallItem } from './waterfall_helpers/waterfall_helpers'; import { ErrorOverviewLink } from '../../../../../shared/Links/apm/ErrorOverviewLink'; -import { TRACE_ID } from '../../../../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; +import { TRACE_ID } from '../../../../../../../common/elasticsearch_fieldnames'; import { SyncBadge } from './SyncBadge'; import { Margins } from '../../../../../shared/charts/Timeline'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/__snapshots__/waterfall_helpers.test.ts.snap diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/mock_responses/spans.json b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/mock_responses/spans.json similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/mock_responses/spans.json rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/mock_responses/spans.json diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/mock_responses/transaction.json b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/mock_responses/transaction.json similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/mock_responses/transaction.json rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/mock_responses/transaction.json diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts similarity index 98% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts index e0a01e9422c85b..75304932ed2bad 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts @@ -5,8 +5,8 @@ */ import { groupBy } from 'lodash'; -import { Span } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/span'; -import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; +import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction'; import { getClockSkew, getOrderedWaterfallItems, @@ -15,7 +15,7 @@ import { IWaterfallTransaction, IWaterfallError } from './waterfall_helpers'; -import { APMError } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error'; +import { APMError } from '../../../../../../../../typings/es_schemas/ui/apm_error'; describe('waterfall_helpers', () => { describe('getWaterfall', () => { diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts index 73193cc7c9dbbf..8ddce66f0b853d 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts @@ -16,10 +16,10 @@ import { zipObject } from 'lodash'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { TraceAPIResponse } from '../../../../../../../../../../../plugins/apm/server/lib/traces/get_trace'; -import { APMError } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error'; -import { Span } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/span'; -import { Transaction } from '../../../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { TraceAPIResponse } from '../../../../../../../../server/lib/traces/get_trace'; +import { APMError } from '../../../../../../../../typings/es_schemas/ui/apm_error'; +import { Span } from '../../../../../../../../typings/es_schemas/ui/span'; +import { Transaction } from '../../../../../../../../typings/es_schemas/ui/transaction'; interface IWaterfallGroup { [key: string]: IWaterfallItem[]; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx index f681f4dfc675ad..87710fb9b8d961 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { storiesOf } from '@storybook/react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { TraceAPIResponse } from '../../../../../../../../../plugins/apm/server/lib/traces/get_trace'; +import { TraceAPIResponse } from '../../../../../../server/lib/traces/get_trace'; import { WaterfallContainer } from './index'; import { location, diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/index.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/__tests__/ErrorCount.test.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/__tests__/ErrorCount.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/__tests__/ErrorCount.test.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/__tests__/ErrorCount.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx index 1082052c6929dc..056e9cdb751485 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx @@ -17,7 +17,7 @@ import { i18n } from '@kbn/i18n'; import { Location } from 'history'; import React, { useEffect, useState } from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { IBucket } from '../../../../../../../../plugins/apm/server/lib/transactions/distribution/get_buckets/transform'; +import { IBucket } from '../../../../../server/lib/transactions/distribution/get_buckets/transform'; import { IUrlParams } from '../../../../context/UrlParamsContext/types'; import { history } from '../../../../utils/history'; import { fromQuery, toQuery } from '../../../shared/Links/url_helpers'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx rename to x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx index e2634be0e0be8d..2544dc2a1a77cd 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionDetails/index.tsx @@ -26,8 +26,8 @@ import { useUrlParams } from '../../../hooks/useUrlParams'; import { FETCH_STATUS } from '../../../hooks/useFetcher'; import { TransactionBreakdown } from '../../shared/TransactionBreakdown'; import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext'; -import { useTrackPageview } from '../../../../../../../plugins/observability/public'; -import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings'; +import { useTrackPageview } from '../../../../../observability/public'; +import { PROJECTION } from '../../../../common/projections/typings'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; import { HeightRetainer } from '../../shared/HeightRetainer'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/List/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionOverview/List/index.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/List/index.tsx rename to x-pack/plugins/apm/public/components/app/TransactionOverview/List/index.tsx index 16fda7c6009068..e3b33f11d0805f 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/List/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionOverview/List/index.tsx @@ -8,9 +8,9 @@ import { EuiIcon, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; import styled from 'styled-components'; -import { NOT_AVAILABLE_LABEL } from '../../../../../../../../plugins/apm/common/i18n'; +import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ITransactionGroup } from '../../../../../../../../plugins/apm/server/lib/transaction_groups/transform'; +import { ITransactionGroup } from '../../../../../server/lib/transaction_groups/transform'; import { fontFamilyCode, truncate } from '../../../../style/variables'; import { asDecimal, convertTo } from '../../../../utils/formatters'; import { ImpactBar } from '../../../shared/ImpactBar'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/__jest__/TransactionOverview.test.tsx b/x-pack/plugins/apm/public/components/app/TransactionOverview/__jest__/TransactionOverview.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/__jest__/TransactionOverview.test.tsx rename to x-pack/plugins/apm/public/components/app/TransactionOverview/__jest__/TransactionOverview.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx b/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx rename to x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx index b008c984178673..60aac3fcdfeef0 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/TransactionOverview/index.tsx @@ -27,10 +27,10 @@ import { getHasMLJob } from '../../../services/rest/ml'; import { history } from '../../../utils/history'; import { useLocation } from '../../../hooks/useLocation'; import { ChartsSyncContextProvider } from '../../../context/ChartsSyncContext'; -import { useTrackPageview } from '../../../../../../../plugins/observability/public'; +import { useTrackPageview } from '../../../../../observability/public'; import { fromQuery, toQuery } from '../../shared/Links/url_helpers'; import { LocalUIFilters } from '../../shared/LocalUIFilters'; -import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings'; +import { PROJECTION } from '../../../../common/projections/typings'; import { useUrlParams } from '../../../hooks/useUrlParams'; import { useServiceTransactionTypes } from '../../../hooks/useServiceTransactionTypes'; import { TransactionTypeFilter } from '../../shared/LocalUIFilters/TransactionTypeFilter'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/useRedirect.ts b/x-pack/plugins/apm/public/components/app/TransactionOverview/useRedirect.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionOverview/useRedirect.ts rename to x-pack/plugins/apm/public/components/app/TransactionOverview/useRedirect.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ApmHeader/index.tsx b/x-pack/plugins/apm/public/components/shared/ApmHeader/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/ApmHeader/index.tsx rename to x-pack/plugins/apm/public/components/shared/ApmHeader/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/DatePicker/__test__/DatePicker.test.tsx b/x-pack/plugins/apm/public/components/shared/DatePicker/__test__/DatePicker.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/DatePicker/__test__/DatePicker.test.tsx rename to x-pack/plugins/apm/public/components/shared/DatePicker/__test__/DatePicker.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/DatePicker/index.tsx b/x-pack/plugins/apm/public/components/shared/DatePicker/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/DatePicker/index.tsx rename to x-pack/plugins/apm/public/components/shared/DatePicker/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/EmptyMessage.tsx b/x-pack/plugins/apm/public/components/shared/EmptyMessage.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/EmptyMessage.tsx rename to x-pack/plugins/apm/public/components/shared/EmptyMessage.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/EnvironmentBadge/index.tsx b/x-pack/plugins/apm/public/components/shared/EnvironmentBadge/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/EnvironmentBadge/index.tsx rename to x-pack/plugins/apm/public/components/shared/EnvironmentBadge/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx b/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx rename to x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx index e911011f0979ce..d17b3b7689b195 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/EnvironmentFilter/index.tsx @@ -15,7 +15,7 @@ import { fromQuery, toQuery } from '../Links/url_helpers'; import { ENVIRONMENT_ALL, ENVIRONMENT_NOT_DEFINED -} from '../../../../../../../plugins/apm/common/environment_filter_values'; +} from '../../../../common/environment_filter_values'; function updateEnvironmentUrl( location: ReturnType, diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.stories.tsx b/x-pack/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.stories.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.stories.tsx rename to x-pack/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.stories.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.tsx b/x-pack/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.tsx similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.tsx rename to x-pack/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.tsx index b7e23c2979cb89..658def7ddbb575 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.tsx @@ -7,8 +7,8 @@ import React from 'react'; import { EuiFieldNumber } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { isFinite } from 'lodash'; -import { ForLastExpression } from '../../../../../../../plugins/triggers_actions_ui/public'; -import { ALERT_TYPES_CONFIG } from '../../../../../../../plugins/apm/common/alert_types'; +import { ForLastExpression } from '../../../../../triggers_actions_ui/public'; +import { ALERT_TYPES_CONFIG } from '../../../../common/alert_types'; import { ServiceAlertTrigger } from '../ServiceAlertTrigger'; import { PopoverExpression } from '../ServiceAlertTrigger/PopoverExpression'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ErrorStatePrompt.tsx b/x-pack/plugins/apm/public/components/shared/ErrorStatePrompt.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/ErrorStatePrompt.tsx rename to x-pack/plugins/apm/public/components/shared/ErrorStatePrompt.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/EuiTabLink.tsx b/x-pack/plugins/apm/public/components/shared/EuiTabLink.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/EuiTabLink.tsx rename to x-pack/plugins/apm/public/components/shared/EuiTabLink.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/HeightRetainer/index.tsx b/x-pack/plugins/apm/public/components/shared/HeightRetainer/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/HeightRetainer/index.tsx rename to x-pack/plugins/apm/public/components/shared/HeightRetainer/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ImpactBar/__test__/ImpactBar.test.js b/x-pack/plugins/apm/public/components/shared/ImpactBar/__test__/ImpactBar.test.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/ImpactBar/__test__/ImpactBar.test.js rename to x-pack/plugins/apm/public/components/shared/ImpactBar/__test__/ImpactBar.test.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ImpactBar/__test__/__snapshots__/ImpactBar.test.js.snap b/x-pack/plugins/apm/public/components/shared/ImpactBar/__test__/__snapshots__/ImpactBar.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/ImpactBar/__test__/__snapshots__/ImpactBar.test.js.snap rename to x-pack/plugins/apm/public/components/shared/ImpactBar/__test__/__snapshots__/ImpactBar.test.js.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ImpactBar/index.tsx b/x-pack/plugins/apm/public/components/shared/ImpactBar/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/ImpactBar/index.tsx rename to x-pack/plugins/apm/public/components/shared/ImpactBar/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/KeyValueTable/FormattedValue.tsx b/x-pack/plugins/apm/public/components/shared/KeyValueTable/FormattedValue.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/shared/KeyValueTable/FormattedValue.tsx rename to x-pack/plugins/apm/public/components/shared/KeyValueTable/FormattedValue.tsx index 4de07f75ff84f8..d33960fe5196bd 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/KeyValueTable/FormattedValue.tsx +++ b/x-pack/plugins/apm/public/components/shared/KeyValueTable/FormattedValue.tsx @@ -8,7 +8,7 @@ import theme from '@elastic/eui/dist/eui_theme_light.json'; import { isBoolean, isNumber, isObject } from 'lodash'; import React from 'react'; import styled from 'styled-components'; -import { NOT_AVAILABLE_LABEL } from '../../../../../../../plugins/apm/common/i18n'; +import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; const EmptyValue = styled.span` color: ${theme.euiColorMediumShade}; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/KeyValueTable/__test__/KeyValueTable.test.tsx b/x-pack/plugins/apm/public/components/shared/KeyValueTable/__test__/KeyValueTable.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/KeyValueTable/__test__/KeyValueTable.test.tsx rename to x-pack/plugins/apm/public/components/shared/KeyValueTable/__test__/KeyValueTable.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/KeyValueTable/index.tsx b/x-pack/plugins/apm/public/components/shared/KeyValueTable/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/KeyValueTable/index.tsx rename to x-pack/plugins/apm/public/components/shared/KeyValueTable/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/Typeahead/ClickOutside.js b/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/ClickOutside.js similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/shared/KueryBar/Typeahead/ClickOutside.js rename to x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/ClickOutside.js index 5ad256efe0945d..93a95c844a9755 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/Typeahead/ClickOutside.js +++ b/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/ClickOutside.js @@ -27,6 +27,7 @@ export default class ClickOutside extends Component { }; render() { + // eslint-disable-next-line no-unused-vars const { onClickOutside, ...restProps } = this.props; return (
diff --git a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestion.js b/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestion.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestion.js rename to x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestion.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestions.js b/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestions.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestions.js rename to x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/Suggestions.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/Typeahead/index.js b/x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/index.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/KueryBar/Typeahead/index.js rename to x-pack/plugins/apm/public/components/shared/KueryBar/Typeahead/index.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts b/x-pack/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts rename to x-pack/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts index 19a9ae9538ad6f..f4628524cced58 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts +++ b/x-pack/plugins/apm/public/components/shared/KueryBar/get_bool_filter.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ESFilter } from '../../../../../../../plugins/apm/typings/elasticsearch'; +import { ESFilter } from '../../../../typings/elasticsearch'; import { TRANSACTION_TYPE, ERROR_GROUP_ID, PROCESSOR_EVENT, TRANSACTION_NAME, SERVICE_NAME -} from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; +} from '../../../../common/elasticsearch_fieldnames'; import { IUrlParams } from '../../../context/UrlParamsContext/types'; export function getBoolFilter(urlParams: IUrlParams) { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/index.tsx b/x-pack/plugins/apm/public/components/shared/KueryBar/index.tsx similarity index 98% rename from x-pack/legacy/plugins/apm/public/components/shared/KueryBar/index.tsx rename to x-pack/plugins/apm/public/components/shared/KueryBar/index.tsx index dba31822dd23e8..2622d08d4779d3 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/KueryBar/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/KueryBar/index.tsx @@ -21,7 +21,7 @@ import { QuerySuggestion, esKuery, IIndexPattern -} from '../../../../../../../../src/plugins/data/public'; +} from '../../../../../../../src/plugins/data/public'; const Container = styled.div` margin-bottom: 10px; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/LicensePrompt/LicensePrompt.stories.tsx b/x-pack/plugins/apm/public/components/shared/LicensePrompt/LicensePrompt.stories.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/LicensePrompt/LicensePrompt.stories.tsx rename to x-pack/plugins/apm/public/components/shared/LicensePrompt/LicensePrompt.stories.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/LicensePrompt/index.tsx b/x-pack/plugins/apm/public/components/shared/LicensePrompt/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/LicensePrompt/index.tsx rename to x-pack/plugins/apm/public/components/shared/LicensePrompt/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.tsx similarity index 85% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.tsx index 806d9f73369c34..05e4080d5d0b7d 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverErrorLink.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { ERROR_GROUP_ID, SERVICE_NAME -} from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; -import { APMError } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error'; +} from '../../../../../common/elasticsearch_fieldnames'; +import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; import { DiscoverLink } from './DiscoverLink'; function getDiscoverQuery(error: APMError, kuery?: string) { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx index 6dc93292956fa2..b58a450d26644b 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx @@ -11,7 +11,7 @@ import url from 'url'; import rison, { RisonValue } from 'rison-node'; import { useLocation } from '../../../../hooks/useLocation'; import { getTimepickerRisonData } from '../rison_helpers'; -import { APM_STATIC_INDEX_PATTERN_ID } from '../../../../../../../../plugins/apm/common/index_pattern_constants'; +import { APM_STATIC_INDEX_PATTERN_ID } from '../../../../../common/index_pattern_constants'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; import { AppMountContextBasePath } from '../../../../context/ApmPluginContext'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverSpanLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverSpanLink.tsx similarity index 79% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverSpanLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverSpanLink.tsx index 8fe5be28def229..ac9e33b3acd694 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverSpanLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverSpanLink.tsx @@ -5,8 +5,8 @@ */ import React from 'react'; -import { SPAN_ID } from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; -import { Span } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/span'; +import { SPAN_ID } from '../../../../../common/elasticsearch_fieldnames'; +import { Span } from '../../../../../typings/es_schemas/ui/span'; import { DiscoverLink } from './DiscoverLink'; function getDiscoverQuery(span: Span) { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.tsx similarity index 85% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.tsx index b0af33fd7d7f75..a5f4df7dbac1b0 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverTransactionLink.tsx @@ -9,8 +9,8 @@ import { PROCESSOR_EVENT, TRACE_ID, TRANSACTION_ID -} from '../../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +} from '../../../../../common/elasticsearch_fieldnames'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { DiscoverLink } from './DiscoverLink'; export function getDiscoverQuery(transaction: Transaction) { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx similarity index 94% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx index eeb9fd20a4bcb0..acf8d89432b230 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorButton.test.tsx @@ -6,7 +6,7 @@ import { shallow, ShallowWrapper } from 'enzyme'; import React from 'react'; -import { APMError } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error'; +import { APMError } from '../../../../../../typings/es_schemas/ui/apm_error'; import { DiscoverErrorLink } from '../DiscoverErrorLink'; describe('DiscoverErrorLink without kuery', () => { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx similarity index 94% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx index eeb9fd20a4bcb0..acf8d89432b230 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverErrorLink.test.tsx @@ -6,7 +6,7 @@ import { shallow, ShallowWrapper } from 'enzyme'; import React from 'react'; -import { APMError } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error'; +import { APMError } from '../../../../../../typings/es_schemas/ui/apm_error'; import { DiscoverErrorLink } from '../DiscoverErrorLink'; describe('DiscoverErrorLink without kuery', () => { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx index 759caa785c1af3..ea79fe12ff0bd7 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverLinks.integration.test.tsx @@ -6,9 +6,9 @@ import { Location } from 'history'; import React from 'react'; -import { APMError } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error'; -import { Span } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/span'; -import { Transaction } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { APMError } from '../../../../../../typings/es_schemas/ui/apm_error'; +import { Span } from '../../../../../../typings/es_schemas/ui/span'; +import { Transaction } from '../../../../../../typings/es_schemas/ui/transaction'; import { getRenderedHref } from '../../../../../utils/testHelpers'; import { DiscoverErrorLink } from '../DiscoverErrorLink'; import { DiscoverSpanLink } from '../DiscoverSpanLink'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx similarity index 90% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx index d72925c1956a41..5769ca34a9a87c 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionButton.test.tsx @@ -6,7 +6,7 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { Transaction } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../../typings/es_schemas/ui/transaction'; import { DiscoverTransactionLink, getDiscoverQuery diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx similarity index 88% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx index 15a92474fcc6d5..2f65cf77346314 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/DiscoverTransactionLink.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Transaction } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../../typings/es_schemas/ui/transaction'; // @ts-ignore import configureStore from '../../../../../store/config/configureStore'; import { getDiscoverQuery } from '../DiscoverTransactionLink'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorButton.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorButton.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorButton.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorButton.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorLink.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorLink.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorLink.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverErrorLink.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverTransactionButton.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverTransactionButton.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverTransactionButton.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverTransactionButton.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverTransactionLink.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverTransactionLink.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverTransactionLink.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/__snapshots__/DiscoverTransactionLink.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/mockTransaction.json b/x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/mockTransaction.json similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/mockTransaction.json rename to x-pack/plugins/apm/public/components/shared/Links/DiscoverLinks/__test__/mockTransaction.json diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/ElasticDocsLink.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/InfraLink.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/InfraLink.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/InfraLink.tsx similarity index 94% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/InfraLink.tsx index 7efe5cb96cfbdd..0ae9f64dc24efe 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/InfraLink.tsx @@ -10,7 +10,7 @@ import url from 'url'; import { fromQuery } from './url_helpers'; import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; import { AppMountContextBasePath } from '../../../context/ApmPluginContext'; -import { InfraAppId } from '../../../../../../../plugins/infra/public'; +import { InfraAppId } from '../../../../../infra/public'; interface InfraQueryParams { time?: number; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/KibanaLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/KibanaLink.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/KibanaLink.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/KibanaLink.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/KibanaLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/KibanaLink.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/KibanaLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/KibanaLink.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx similarity index 88% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx index ecf788ddd2e69a..81c5d17d491c06 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLJobLink.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { getMlJobId } from '../../../../../../../../plugins/apm/common/ml_job_constants'; +import { getMlJobId } from '../../../../../common/ml_job_constants'; import { MLLink } from './MLLink'; interface Props { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/MachineLearningLinks/MLLink.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/SetupInstructionsLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/SetupInstructionsLink.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/SetupInstructionsLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/SetupInstructionsLink.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/APMLink.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/APMLink.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/APMLink.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/APMLink.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/APMLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/APMLink.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/APMLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/APMLink.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ErrorDetailLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ErrorDetailLink.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ErrorDetailLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/ErrorDetailLink.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx index fcc0dc7d266957..ebcf220994cda4 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/ErrorOverviewLink.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { APMLink, APMLinkExtendProps } from './APMLink'; import { useUrlParams } from '../../../../hooks/useUrlParams'; -import { pickKeys } from '../../../../utils/pickKeys'; +import { pickKeys } from '../../../../../common/utils/pick_keys'; import { APMQueryParams } from '../url_helpers'; interface Props extends APMLinkExtendProps { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ExternalLinks.test.ts b/x-pack/plugins/apm/public/components/shared/Links/apm/ExternalLinks.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ExternalLinks.test.ts rename to x-pack/plugins/apm/public/components/shared/Links/apm/ExternalLinks.test.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ExternalLinks.ts b/x-pack/plugins/apm/public/components/shared/Links/apm/ExternalLinks.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ExternalLinks.ts rename to x-pack/plugins/apm/public/components/shared/Links/apm/ExternalLinks.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/HomeLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/HomeLink.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/HomeLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/HomeLink.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx index 7d21e1efa44f23..bd3e3b36a86013 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/MetricOverviewLink.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { APMLink, APMLinkExtendProps } from './APMLink'; import { useUrlParams } from '../../../../hooks/useUrlParams'; -import { pickKeys } from '../../../../utils/pickKeys'; +import { pickKeys } from '../../../../../common/utils/pick_keys'; interface Props extends APMLinkExtendProps { serviceName: string; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/ServiceMapLink.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeMetricOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeMetricOverviewLink.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeMetricOverviewLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeMetricOverviewLink.tsx index 527c3da9e7e1ce..1473221cca2be6 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeMetricOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeMetricOverviewLink.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { APMLink, APMLinkExtendProps } from './APMLink'; import { useUrlParams } from '../../../../hooks/useUrlParams'; -import { pickKeys } from '../../../../utils/pickKeys'; +import { pickKeys } from '../../../../../common/utils/pick_keys'; interface Props extends APMLinkExtendProps { serviceName: string; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx index db1b6ec117bf43..b479ab77e1127f 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceNodeOverviewLink.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { APMLink, APMLinkExtendProps } from './APMLink'; import { useUrlParams } from '../../../../hooks/useUrlParams'; -import { pickKeys } from '../../../../utils/pickKeys'; +import { pickKeys } from '../../../../../common/utils/pick_keys'; interface Props extends APMLinkExtendProps { serviceName: string; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceOverviewLink.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceOverviewLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/ServiceOverviewLink.tsx index 101f1602506aa1..577209a26e46b4 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/ServiceOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/ServiceOverviewLink.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { APMLink, APMLinkExtendProps } from './APMLink'; import { useUrlParams } from '../../../../hooks/useUrlParams'; -import { pickKeys } from '../../../../utils/pickKeys'; +import { pickKeys } from '../../../../../common/utils/pick_keys'; const ServiceOverviewLink = (props: APMLinkExtendProps) => { const { urlParams } = useUrlParams(); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/SettingsLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/SettingsLink.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/SettingsLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/SettingsLink.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx index 371544c142a2d7..dc4519365cbc25 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/TraceOverviewLink.tsx @@ -12,7 +12,7 @@ import React from 'react'; import { APMLink, APMLinkExtendProps } from './APMLink'; import { useUrlParams } from '../../../../hooks/useUrlParams'; -import { pickKeys } from '../../../../utils/pickKeys'; +import { pickKeys } from '../../../../../common/utils/pick_keys'; const TraceOverviewLink = (props: APMLinkExtendProps) => { const { urlParams } = useUrlParams(); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TransactionDetailLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/TransactionDetailLink.tsx similarity index 94% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TransactionDetailLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/TransactionDetailLink.tsx index 784f9b36ff621d..62783367518518 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TransactionDetailLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/TransactionDetailLink.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { APMLink, APMLinkExtendProps } from './APMLink'; import { useUrlParams } from '../../../../hooks/useUrlParams'; -import { pickKeys } from '../../../../utils/pickKeys'; +import { pickKeys } from '../../../../../common/utils/pick_keys'; interface Props extends APMLinkExtendProps { serviceName: string; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx index af60a0a7484458..ccef83ee73fb89 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/TransactionOverviewLink.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { APMLink, APMLinkExtendProps } from './APMLink'; import { useUrlParams } from '../../../../hooks/useUrlParams'; -import { pickKeys } from '../../../../utils/pickKeys'; +import { pickKeys } from '../../../../../common/utils/pick_keys'; interface Props extends APMLinkExtendProps { serviceName: string; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/agentConfigurationLinks.tsx b/x-pack/plugins/apm/public/components/shared/Links/apm/agentConfigurationLinks.tsx similarity index 87% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/apm/agentConfigurationLinks.tsx rename to x-pack/plugins/apm/public/components/shared/Links/apm/agentConfigurationLinks.tsx index 0c747e0773a69d..6885e44f1ad1fa 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/apm/agentConfigurationLinks.tsx +++ b/x-pack/plugins/apm/public/components/shared/Links/apm/agentConfigurationLinks.tsx @@ -5,7 +5,7 @@ */ import { getAPMHref } from './APMLink'; -import { AgentConfigurationIntake } from '../../../../../../../../plugins/apm/common/agent_configuration/configuration_types'; +import { AgentConfigurationIntake } from '../../../../../common/agent_configuration/configuration_types'; import { history } from '../../../../utils/history'; export function editAgentConfigurationHref( diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/rison_helpers.ts b/x-pack/plugins/apm/public/components/shared/Links/rison_helpers.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/rison_helpers.ts rename to x-pack/plugins/apm/public/components/shared/Links/rison_helpers.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx b/x-pack/plugins/apm/public/components/shared/Links/url_helpers.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.test.tsx rename to x-pack/plugins/apm/public/components/shared/Links/url_helpers.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts b/x-pack/plugins/apm/public/components/shared/Links/url_helpers.ts similarity index 87% rename from x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts rename to x-pack/plugins/apm/public/components/shared/Links/url_helpers.ts index c7d71d0b6dac50..b296302c47edf3 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/url_helpers.ts +++ b/x-pack/plugins/apm/public/components/shared/Links/url_helpers.ts @@ -6,8 +6,8 @@ import { parse, stringify } from 'query-string'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LocalUIFilterName } from '../../../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters/config'; -import { url } from '../../../../../../../../src/plugins/kibana_utils/public'; +import { LocalUIFilterName } from '../../../../server/lib/ui_filters/local_ui_filters/config'; +import { url } from '../../../../../../../src/plugins/kibana_utils/public'; export function toQuery(search?: string): APMQueryParamsRaw { return search ? parse(search.slice(1), { sort: false }) : {}; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/LoadingStatePrompt.tsx b/x-pack/plugins/apm/public/components/shared/LoadingStatePrompt.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/LoadingStatePrompt.tsx rename to x-pack/plugins/apm/public/components/shared/LoadingStatePrompt.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterBadgeList.tsx b/x-pack/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterBadgeList.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterBadgeList.tsx rename to x-pack/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterBadgeList.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterTitleButton.tsx b/x-pack/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterTitleButton.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterTitleButton.tsx rename to x-pack/plugins/apm/public/components/shared/LocalUIFilters/Filter/FilterTitleButton.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/index.tsx b/x-pack/plugins/apm/public/components/shared/LocalUIFilters/Filter/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/Filter/index.tsx rename to x-pack/plugins/apm/public/components/shared/LocalUIFilters/Filter/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/TransactionTypeFilter/index.tsx b/x-pack/plugins/apm/public/components/shared/LocalUIFilters/TransactionTypeFilter/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/TransactionTypeFilter/index.tsx rename to x-pack/plugins/apm/public/components/shared/LocalUIFilters/TransactionTypeFilter/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/index.tsx b/x-pack/plugins/apm/public/components/shared/LocalUIFilters/index.tsx similarity index 91% rename from x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/index.tsx rename to x-pack/plugins/apm/public/components/shared/LocalUIFilters/index.tsx index cede3e394cfab2..2c755009ed13ac 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/LocalUIFilters/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/LocalUIFilters/index.tsx @@ -14,10 +14,10 @@ import { import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LocalUIFilterName } from '../../../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters/config'; +import { LocalUIFilterName } from '../../../../server/lib/ui_filters/local_ui_filters/config'; import { Filter } from './Filter'; import { useLocalUIFilters } from '../../../hooks/useLocalUIFilters'; -import { PROJECTION } from '../../../../../../../plugins/apm/common/projections/typings'; +import { PROJECTION } from '../../../../common/projections/typings'; interface Props { projection: PROJECTION; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/__test__/ManagedTable.test.js b/x-pack/plugins/apm/public/components/shared/ManagedTable/__test__/ManagedTable.test.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/__test__/ManagedTable.test.js rename to x-pack/plugins/apm/public/components/shared/ManagedTable/__test__/ManagedTable.test.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/__test__/__snapshots__/ManagedTable.test.js.snap b/x-pack/plugins/apm/public/components/shared/ManagedTable/__test__/__snapshots__/ManagedTable.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/__test__/__snapshots__/ManagedTable.test.js.snap rename to x-pack/plugins/apm/public/components/shared/ManagedTable/__test__/__snapshots__/ManagedTable.test.js.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/index.tsx b/x-pack/plugins/apm/public/components/shared/ManagedTable/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/ManagedTable/index.tsx rename to x-pack/plugins/apm/public/components/shared/ManagedTable/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx index 258788252379ae..1913cf79c79350 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/__test__/ErrorMetadata.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { ErrorMetadata } from '..'; import { render } from '@testing-library/react'; -import { APMError } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error'; +import { APMError } from '../../../../../../typings/es_schemas/ui/apm_error'; import { expectTextsInDocument, expectTextsNotInDocument diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx similarity index 87% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx index ce991d8b0dc001..7cae42a94322bb 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/index.tsx @@ -6,7 +6,7 @@ import React, { useMemo } from 'react'; import { ERROR_METADATA_SECTIONS } from './sections'; -import { APMError } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error'; +import { APMError } from '../../../../../typings/es_schemas/ui/apm_error'; import { getSectionsWithRows } from '../helper'; import { MetadataTable } from '..'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/sections.ts b/x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/sections.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/sections.ts rename to x-pack/plugins/apm/public/components/shared/MetadataTable/ErrorMetadata/sections.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/Section.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/Section.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/Section.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/Section.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx index 0059b7b8fb4b31..a46539fe72fcbf 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/__test__/SpanMetadata.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { SpanMetadata } from '..'; -import { Span } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/span'; +import { Span } from '../../../../../../typings/es_schemas/ui/span'; import { expectTextsInDocument, expectTextsNotInDocument diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx similarity index 88% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx index 2134f12531a7a8..abef083e39b9e5 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/index.tsx @@ -6,7 +6,7 @@ import React, { useMemo } from 'react'; import { SPAN_METADATA_SECTIONS } from './sections'; -import { Span } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/span'; +import { Span } from '../../../../../typings/es_schemas/ui/span'; import { getSectionsWithRows } from '../helper'; import { MetadataTable } from '..'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts b/x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts rename to x-pack/plugins/apm/public/components/shared/MetadataTable/SpanMetadata/sections.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx index 3d78f36db9786d..8ae46d359efc33 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/__test__/TransactionMetadata.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { TransactionMetadata } from '..'; import { render } from '@testing-library/react'; -import { Transaction } from '../../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../../typings/es_schemas/ui/transaction'; import { expectTextsInDocument, expectTextsNotInDocument diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx similarity index 87% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx index 6f93de4e87e491..86ecbba6a0aaaf 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/index.tsx @@ -6,7 +6,7 @@ import React, { useMemo } from 'react'; import { TRANSACTION_METADATA_SECTIONS } from './sections'; -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { getSectionsWithRows } from '../helper'; import { MetadataTable } from '..'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts b/x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts rename to x-pack/plugins/apm/public/components/shared/MetadataTable/TransactionMetadata/sections.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/MetadataTable.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/Section.test.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/Section.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/Section.test.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/Section.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts b/x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts rename to x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts index b65b52bf30a5c6..e754f7163ca117 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/__test__/helper.test.ts @@ -6,7 +6,7 @@ import { getSectionsWithRows, filterSectionsByTerm } from '../helper'; import { LABELS, HTTP, SERVICE } from '../sections'; -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; describe('MetadataTable Helper', () => { const sections = [ diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/helper.ts b/x-pack/plugins/apm/public/components/shared/MetadataTable/helper.ts similarity index 85% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/helper.ts rename to x-pack/plugins/apm/public/components/shared/MetadataTable/helper.ts index ef329abafa61b2..a8678ee596e43f 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/helper.ts +++ b/x-pack/plugins/apm/public/components/shared/MetadataTable/helper.ts @@ -6,9 +6,9 @@ import { get, pick, isEmpty } from 'lodash'; import { Section } from './sections'; -import { Transaction } from '../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; -import { APMError } from '../../../../../../../plugins/apm/typings/es_schemas/ui/apm_error'; -import { Span } from '../../../../../../../plugins/apm/typings/es_schemas/ui/span'; +import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; +import { APMError } from '../../../../typings/es_schemas/ui/apm_error'; +import { Span } from '../../../../typings/es_schemas/ui/span'; import { flattenObject, KeyValuePair } from '../../../utils/flattenObject'; export type SectionsWithRows = ReturnType; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/index.tsx b/x-pack/plugins/apm/public/components/shared/MetadataTable/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/index.tsx rename to x-pack/plugins/apm/public/components/shared/MetadataTable/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/sections.ts b/x-pack/plugins/apm/public/components/shared/MetadataTable/sections.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/MetadataTable/sections.ts rename to x-pack/plugins/apm/public/components/shared/MetadataTable/sections.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/SelectWithPlaceholder/index.tsx b/x-pack/plugins/apm/public/components/shared/SelectWithPlaceholder/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/SelectWithPlaceholder/index.tsx rename to x-pack/plugins/apm/public/components/shared/SelectWithPlaceholder/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ServiceAlertTrigger/PopoverExpression/index.tsx b/x-pack/plugins/apm/public/components/shared/ServiceAlertTrigger/PopoverExpression/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/ServiceAlertTrigger/PopoverExpression/index.tsx rename to x-pack/plugins/apm/public/components/shared/ServiceAlertTrigger/PopoverExpression/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ServiceAlertTrigger/index.tsx b/x-pack/plugins/apm/public/components/shared/ServiceAlertTrigger/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/ServiceAlertTrigger/index.tsx rename to x-pack/plugins/apm/public/components/shared/ServiceAlertTrigger/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.test.tsx b/x-pack/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.test.tsx rename to x-pack/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.tsx b/x-pack/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.tsx rename to x-pack/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.tsx index eab414ad47c2ce..6dfc8778fe1fcb 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.tsx +++ b/x-pack/plugins/apm/public/components/shared/Stacktrace/CauseStacktrace.tsx @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import { EuiAccordion, EuiTitle } from '@elastic/eui'; import { px, unit } from '../../../style/variables'; import { Stacktrace } from '.'; -import { IStackframe } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe'; +import { IStackframe } from '../../../../typings/es_schemas/raw/fields/stackframe'; // @ts-ignore Styled Components has trouble inferring the types of the default props here. const Accordion = styled(EuiAccordion)` diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Context.tsx b/x-pack/plugins/apm/public/components/shared/Stacktrace/Context.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Context.tsx rename to x-pack/plugins/apm/public/components/shared/Stacktrace/Context.tsx index d289539ca44b12..d48f4b4f51a6ae 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Context.tsx +++ b/x-pack/plugins/apm/public/components/shared/Stacktrace/Context.tsx @@ -22,7 +22,7 @@ import { registerLanguage } from 'react-syntax-highlighter/dist/light'; // @ts-ignore import { xcode } from 'react-syntax-highlighter/dist/styles'; import styled from 'styled-components'; -import { IStackframeWithLineContext } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe'; +import { IStackframeWithLineContext } from '../../../../typings/es_schemas/raw/fields/stackframe'; import { borderRadius, px, unit, units } from '../../../style/variables'; registerLanguage('javascript', javascript); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/FrameHeading.tsx b/x-pack/plugins/apm/public/components/shared/Stacktrace/FrameHeading.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/FrameHeading.tsx rename to x-pack/plugins/apm/public/components/shared/Stacktrace/FrameHeading.tsx index daa722255bdf33..4467fe7ad615e6 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/FrameHeading.tsx +++ b/x-pack/plugins/apm/public/components/shared/Stacktrace/FrameHeading.tsx @@ -7,7 +7,7 @@ import theme from '@elastic/eui/dist/eui_theme_light.json'; import React, { Fragment } from 'react'; import styled from 'styled-components'; -import { IStackframe } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe'; +import { IStackframe } from '../../../../typings/es_schemas/raw/fields/stackframe'; import { fontFamilyCode, fontSize, px, units } from '../../../style/variables'; const FileDetails = styled.div` diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.test.tsx b/x-pack/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.test.tsx rename to x-pack/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.tsx b/x-pack/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.tsx rename to x-pack/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.tsx index be6595153aa77e..009e97358428c8 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.tsx +++ b/x-pack/plugins/apm/public/components/shared/Stacktrace/LibraryStacktrace.tsx @@ -8,7 +8,7 @@ import { EuiAccordion } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import styled from 'styled-components'; -import { IStackframe } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe'; +import { IStackframe } from '../../../../typings/es_schemas/raw/fields/stackframe'; import { Stackframe } from './Stackframe'; import { px, unit } from '../../../style/variables'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Stackframe.tsx b/x-pack/plugins/apm/public/components/shared/Stacktrace/Stackframe.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Stackframe.tsx rename to x-pack/plugins/apm/public/components/shared/Stacktrace/Stackframe.tsx index 404d474a7960a2..4c55add56bc409 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Stackframe.tsx +++ b/x-pack/plugins/apm/public/components/shared/Stacktrace/Stackframe.tsx @@ -11,7 +11,7 @@ import { EuiAccordion } from '@elastic/eui'; import { IStackframe, IStackframeWithLineContext -} from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe'; +} from '../../../../typings/es_schemas/raw/fields/stackframe'; import { borderRadius, fontFamilyCode, diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Variables.tsx b/x-pack/plugins/apm/public/components/shared/Stacktrace/Variables.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Variables.tsx rename to x-pack/plugins/apm/public/components/shared/Stacktrace/Variables.tsx index 0786116a659c78..ec5fb39f83f8c8 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/Variables.tsx +++ b/x-pack/plugins/apm/public/components/shared/Stacktrace/Variables.tsx @@ -10,7 +10,7 @@ import { EuiAccordion } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { borderRadius, px, unit, units } from '../../../style/variables'; -import { IStackframe } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe'; +import { IStackframe } from '../../../../typings/es_schemas/raw/fields/stackframe'; import { KeyValueTable } from '../KeyValueTable'; import { flattenObject } from '../../../utils/flattenObject'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/Stackframe.test.tsx b/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/Stackframe.test.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/Stackframe.test.tsx rename to x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/Stackframe.test.tsx index 1b2268326e6be2..478f9cfe921d9b 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/Stackframe.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/Stackframe.test.tsx @@ -6,7 +6,7 @@ import { mount, ReactWrapper, shallow } from 'enzyme'; import React from 'react'; -import { IStackframe } from '../../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe'; +import { IStackframe } from '../../../../../typings/es_schemas/raw/fields/stackframe'; import { Stackframe } from '../Stackframe'; import stacktracesMock from './stacktraces.json'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/Stackframe.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/index.test.ts.snap b/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/index.test.ts.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/index.test.ts.snap rename to x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/__snapshots__/index.test.ts.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/index.test.ts b/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/index.test.ts similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/index.test.ts rename to x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/index.test.ts index 9b6d74033e1c54..22357b9590887e 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/index.test.ts +++ b/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/index.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { IStackframe } from '../../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe'; +import { IStackframe } from '../../../../../typings/es_schemas/raw/fields/stackframe'; import { getGroupedStackframes } from '../index'; import stacktracesMock from './stacktraces.json'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/stacktraces.json b/x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/stacktraces.json similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/__test__/stacktraces.json rename to x-pack/plugins/apm/public/components/shared/Stacktrace/__test__/stacktraces.json diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/index.tsx b/x-pack/plugins/apm/public/components/shared/Stacktrace/index.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/index.tsx rename to x-pack/plugins/apm/public/components/shared/Stacktrace/index.tsx index 141ed544a6166e..b6435f7c42183f 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Stacktrace/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/Stacktrace/index.tsx @@ -8,7 +8,7 @@ import { EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { isEmpty, last } from 'lodash'; import React, { Fragment } from 'react'; -import { IStackframe } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/stackframe'; +import { IStackframe } from '../../../../typings/es_schemas/raw/fields/stackframe'; import { EmptyMessage } from '../../shared/EmptyMessage'; import { LibraryStacktrace } from './LibraryStacktrace'; import { Stackframe } from './Stackframe'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/StickyProperties/StickyProperties.test.js b/x-pack/plugins/apm/public/components/shared/StickyProperties/StickyProperties.test.js similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/shared/StickyProperties/StickyProperties.test.js rename to x-pack/plugins/apm/public/components/shared/StickyProperties/StickyProperties.test.js index 08283dee3825d8..b6acb6904f865b 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/StickyProperties/StickyProperties.test.js +++ b/x-pack/plugins/apm/public/components/shared/StickyProperties/StickyProperties.test.js @@ -7,10 +7,7 @@ import React from 'react'; import { StickyProperties } from './index'; import { shallow } from 'enzyme'; -import { - USER_ID, - URL_FULL -} from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; +import { USER_ID, URL_FULL } from '../../../../common/elasticsearch_fieldnames'; import { mockMoment } from '../../../utils/testHelpers'; describe('StickyProperties', () => { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/StickyProperties/__snapshots__/StickyProperties.test.js.snap b/x-pack/plugins/apm/public/components/shared/StickyProperties/__snapshots__/StickyProperties.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/StickyProperties/__snapshots__/StickyProperties.test.js.snap rename to x-pack/plugins/apm/public/components/shared/StickyProperties/__snapshots__/StickyProperties.test.js.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/StickyProperties/index.tsx b/x-pack/plugins/apm/public/components/shared/StickyProperties/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/StickyProperties/index.tsx rename to x-pack/plugins/apm/public/components/shared/StickyProperties/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/DurationSummaryItem.tsx b/x-pack/plugins/apm/public/components/shared/Summary/DurationSummaryItem.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/DurationSummaryItem.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/DurationSummaryItem.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx b/x-pack/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItemBadge.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/__test__/HttpInfoSummaryItem.test.tsx b/x-pack/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/__test__/HttpInfoSummaryItem.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/__test__/HttpInfoSummaryItem.test.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/__test__/HttpInfoSummaryItem.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx b/x-pack/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpStatusBadge/__test__/HttpStatusBadge.test.tsx b/x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/__test__/HttpStatusBadge.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpStatusBadge/__test__/HttpStatusBadge.test.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/__test__/HttpStatusBadge.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpStatusBadge/index.tsx b/x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpStatusBadge/index.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpStatusBadge/statusCodes.ts b/x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/statusCodes.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpStatusBadge/statusCodes.ts rename to x-pack/plugins/apm/public/components/shared/Summary/HttpStatusBadge/statusCodes.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionResultSummaryItem.tsx b/x-pack/plugins/apm/public/components/shared/Summary/TransactionResultSummaryItem.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionResultSummaryItem.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/TransactionResultSummaryItem.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.test.tsx b/x-pack/plugins/apm/public/components/shared/Summary/TransactionSummary.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.test.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/TransactionSummary.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx b/x-pack/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx similarity index 91% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx index f0fe57e46f2fee..f24a806426510e 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx +++ b/x-pack/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import { Transaction } from '../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; import { Summary } from './'; import { TimestampTooltip } from '../TimestampTooltip'; import { DurationSummaryItem } from './DurationSummaryItem'; import { ErrorCountSummaryItemBadge } from './ErrorCountSummaryItemBadge'; -import { isRumAgentName } from '../../../../../../../plugins/apm/common/agent_name'; +import { isRumAgentName } from '../../../../common/agent_name'; import { HttpInfoSummaryItem } from './HttpInfoSummaryItem'; import { TransactionResultSummaryItem } from './TransactionResultSummaryItem'; import { UserAgentSummaryItem } from './UserAgentSummaryItem'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.test.tsx b/x-pack/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.test.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.tsx b/x-pack/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.tsx similarity index 90% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.tsx index 10a6bcc1ef7bd9..8173170b72f239 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.tsx +++ b/x-pack/plugins/apm/public/components/shared/Summary/UserAgentSummaryItem.tsx @@ -9,7 +9,7 @@ import styled from 'styled-components'; import theme from '@elastic/eui/dist/eui_theme_light.json'; import { EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { UserAgent } from '../../../../../../../plugins/apm/typings/es_schemas/raw/fields/user_agent'; +import { UserAgent } from '../../../../typings/es_schemas/raw/fields/user_agent'; type UserAgentSummaryItemProps = UserAgent; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts b/x-pack/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts rename to x-pack/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts index 05fb73a9e2749d..e1615934cd92e3 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts +++ b/x-pack/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; export const httpOk: Transaction = { '@timestamp': '0', diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx b/x-pack/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/__test__/ErrorCountSummaryItemBadge.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/index.tsx b/x-pack/plugins/apm/public/components/shared/Summary/index.tsx similarity index 94% rename from x-pack/legacy/plugins/apm/public/components/shared/Summary/index.tsx rename to x-pack/plugins/apm/public/components/shared/Summary/index.tsx index ef99f3a4933a78..ce6935d1858aac 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Summary/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/Summary/index.tsx @@ -8,7 +8,7 @@ import { EuiFlexGrid, EuiFlexItem } from '@elastic/eui'; import styled from 'styled-components'; import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; import { px, units } from '../../../../public/style/variables'; -import { Maybe } from '../../../../../../../plugins/apm/typings/common'; +import { Maybe } from '../../../../typings/common'; interface Props { items: Array>; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TimestampTooltip/__test__/index.test.tsx b/x-pack/plugins/apm/public/components/shared/TimestampTooltip/__test__/index.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/TimestampTooltip/__test__/index.test.tsx rename to x-pack/plugins/apm/public/components/shared/TimestampTooltip/__test__/index.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TimestampTooltip/index.tsx b/x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/TimestampTooltip/index.tsx rename to x-pack/plugins/apm/public/components/shared/TimestampTooltip/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.test.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.test.tsx similarity index 91% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.test.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.test.tsx index 8df6d952cfacd0..49f8fddb303bd0 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.test.tsx @@ -5,10 +5,10 @@ */ import React from 'react'; import { render, act, fireEvent } from '@testing-library/react'; -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { CustomLinkPopover } from './CustomLinkPopover'; import { expectTextsInDocument } from '../../../../utils/testHelpers'; -import { CustomLink } from '../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; +import { CustomLink } from '../../../../../common/custom_link/custom_link_types'; describe('CustomLinkPopover', () => { const customLinks = [ diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.tsx similarity index 90% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.tsx index 3aed1b7ac29537..a63c226a5c46e7 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkPopover.tsx @@ -12,8 +12,8 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; -import { CustomLink } from '../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { CustomLink } from '../../../../../common/custom_link/custom_link_types'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { CustomLinkSection } from './CustomLinkSection'; import { ManageCustomLink } from './ManageCustomLink'; import { px } from '../../../../style/variables'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.test.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.test.tsx similarity index 85% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.test.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.test.tsx index d429fa56894eb9..6cf8b9ee5e98a1 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.test.tsx @@ -10,8 +10,8 @@ import { expectTextsInDocument, expectTextsNotInDocument } from '../../../../utils/testHelpers'; -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; -import { CustomLink } from '../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; +import { CustomLink } from '../../../../../common/custom_link/custom_link_types'; describe('CustomLinkSection', () => { const customLinks = [ diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx similarity index 86% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx index bd00bcf600ffeb..e22f4b4a377455 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx @@ -7,8 +7,8 @@ import { EuiLink, EuiText } from '@elastic/eui'; import Mustache from 'mustache'; import React from 'react'; import styled from 'styled-components'; -import { CustomLink } from '../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { CustomLink } from '../../../../../common/custom_link/custom_link_types'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { px, truncate, units } from '../../../../style/variables'; const LinkContainer = styled.li` diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.test.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.test.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/ManageCustomLink.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx similarity index 94% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx index 9d1eeb9a3136d9..c7a2d77d85fa61 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { render, act, fireEvent } from '@testing-library/react'; import { CustomLink } from '.'; -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { FETCH_STATUS } from '../../../../hooks/useFetcher'; import { expectTextsInDocument, expectTextsNotInDocument } from '../../../../utils/testHelpers'; -import { CustomLink as CustomLinkType } from '../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; +import { CustomLink as CustomLinkType } from '../../../../../common/custom_link/custom_link_types'; describe('Custom links', () => { it('shows empty message when no custom link is available', () => { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx index 38b672a181fceb..710b2175e33778 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx @@ -15,12 +15,12 @@ import { import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; import { isEmpty } from 'lodash'; -import { CustomLink as CustomLinkType } from '../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { CustomLink as CustomLinkType } from '../../../../../common/custom_link/custom_link_types'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { ActionMenuDivider, SectionSubtitle -} from '../../../../../../../../plugins/observability/public'; +} from '../../../../../../observability/public'; import { CustomLinkSection } from './CustomLinkSection'; import { ManageCustomLink } from './ManageCustomLink'; import { FETCH_STATUS } from '../../../../hooks/useFetcher'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx index 7ebfe26b83630c..c9376cdc01b5bc 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx @@ -7,8 +7,8 @@ import { EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { FunctionComponent, useMemo, useState } from 'react'; -import { Filter } from '../../../../../../../plugins/apm/common/custom_link/custom_link_types'; -import { Transaction } from '../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Filter } from '../../../../common/custom_link/custom_link_types'; +import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; import { ActionMenu, ActionMenuDivider, @@ -17,7 +17,7 @@ import { SectionLinks, SectionSubtitle, SectionTitle -} from '../../../../../../../plugins/observability/public'; +} from '../../../../../observability/public'; import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; import { useFetcher } from '../../../hooks/useFetcher'; import { useLocation } from '../../../hooks/useLocation'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx similarity index 98% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx index 8dc2076eab5b56..cda602204469c6 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { render, fireEvent, act, wait } from '@testing-library/react'; import { TransactionActionMenu } from '../TransactionActionMenu'; -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import * as Transactions from './mockData'; import { expectTextsNotInDocument, @@ -15,7 +15,7 @@ import { } from '../../../../utils/testHelpers'; import * as hooks from '../../../../hooks/useFetcher'; import { LicenseContext } from '../../../../context/LicenseContext'; -import { License } from '../../../../../../../../plugins/licensing/common/license'; +import { License } from '../../../../../../licensing/common/license'; import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext'; import * as apmApi from '../../../../services/rest/createCallApmApi'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/__snapshots__/TransactionActionMenu.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/__snapshots__/TransactionActionMenu.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/__snapshots__/TransactionActionMenu.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/__snapshots__/TransactionActionMenu.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/mockData.ts b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/mockData.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/mockData.ts rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/mockData.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts similarity index 98% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts index 3032dd1704f4e5..b2f6f39e0b5964 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts @@ -5,7 +5,7 @@ */ import { Location } from 'history'; import { getSections } from '../sections'; -import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../../typings/es_schemas/ui/transaction'; import { AppMountContextBasePath } from '../../../../context/ApmPluginContext'; describe('Transaction action menu', () => { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts similarity index 98% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts rename to x-pack/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts index ffdf0b485da648..2c2f4bfcadd7d7 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts +++ b/x-pack/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts @@ -8,7 +8,7 @@ import { Location } from 'history'; import { pick, isEmpty } from 'lodash'; import moment from 'moment'; import url from 'url'; -import { Transaction } from '../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; +import { Transaction } from '../../../../typings/es_schemas/ui/transaction'; import { IUrlParams } from '../../../context/UrlParamsContext/types'; import { getDiscoverHref } from '../Links/DiscoverLinks/DiscoverLink'; import { getDiscoverQuery } from '../Links/DiscoverLinks/DiscoverTransactionLink'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx b/x-pack/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx similarity index 81% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx index 50ea169c017f9d..966cc64fde505c 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownGraph/index.tsx @@ -7,17 +7,14 @@ import React, { useMemo } from 'react'; import numeral from '@elastic/numeral'; import { throttle } from 'lodash'; -import { NOT_AVAILABLE_LABEL } from '../../../../../../../../plugins/apm/common/i18n'; -import { - Coordinate, - TimeSeries -} from '../../../../../../../../plugins/apm/typings/timeseries'; -import { Maybe } from '../../../../../../../../plugins/apm/typings/common'; +import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; +import { Coordinate, TimeSeries } from '../../../../../typings/timeseries'; +import { Maybe } from '../../../../../typings/common'; import { TransactionLineChart } from '../../charts/TransactionCharts/TransactionLineChart'; import { asPercent } from '../../../../utils/formatters'; import { unit } from '../../../../style/variables'; import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue'; -import { useUiTracker } from '../../../../../../../../plugins/observability/public'; +import { useUiTracker } from '../../../../../../observability/public'; interface Props { timeseries: TimeSeries[]; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownHeader.tsx b/x-pack/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownHeader.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownHeader.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownHeader.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownKpiList.tsx b/x-pack/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownKpiList.tsx similarity index 95% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownKpiList.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownKpiList.tsx index 91f5f4e0a71769..c4a8e07fb30048 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownKpiList.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionBreakdown/TransactionBreakdownKpiList.tsx @@ -13,10 +13,7 @@ import { EuiIcon } from '@elastic/eui'; import styled from 'styled-components'; -import { - FORMATTERS, - InfraFormatterType -} from '../../../../../../../plugins/infra/public'; +import { FORMATTERS, InfraFormatterType } from '../../../../../infra/public'; interface TransactionBreakdownKpi { name: string; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/index.tsx b/x-pack/plugins/apm/public/components/shared/TransactionBreakdown/index.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/index.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionBreakdown/index.tsx index 85f5f83fb920e9..be5860190c11e8 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionBreakdown/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionBreakdown/index.tsx @@ -11,7 +11,7 @@ import { TransactionBreakdownHeader } from './TransactionBreakdownHeader'; import { TransactionBreakdownKpiList } from './TransactionBreakdownKpiList'; import { TransactionBreakdownGraph } from './TransactionBreakdownGraph'; import { FETCH_STATUS } from '../../../hooks/useFetcher'; -import { useUiTracker } from '../../../../../../../plugins/observability/public'; +import { useUiTracker } from '../../../../../observability/public'; const emptyMessage = i18n.translate('xpack.apm.transactionBreakdown.noData', { defaultMessage: 'No data within this time range.' diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionDurationAlertTrigger/index.stories.tsx b/x-pack/plugins/apm/public/components/shared/TransactionDurationAlertTrigger/index.stories.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionDurationAlertTrigger/index.stories.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionDurationAlertTrigger/index.stories.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionDurationAlertTrigger/index.tsx b/x-pack/plugins/apm/public/components/shared/TransactionDurationAlertTrigger/index.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/shared/TransactionDurationAlertTrigger/index.tsx rename to x-pack/plugins/apm/public/components/shared/TransactionDurationAlertTrigger/index.tsx index 077e6535a8b216..1e9fbd2c1c1351 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionDurationAlertTrigger/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/TransactionDurationAlertTrigger/index.tsx @@ -7,11 +7,11 @@ import React from 'react'; import { map } from 'lodash'; import { EuiFieldNumber, EuiSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ForLastExpression } from '../../../../../../../plugins/triggers_actions_ui/public'; +import { ForLastExpression } from '../../../../../triggers_actions_ui/public'; import { TRANSACTION_ALERT_AGGREGATION_TYPES, ALERT_TYPES_CONFIG -} from '../../../../../../../plugins/apm/common/alert_types'; +} from '../../../../common/alert_types'; import { ServiceAlertTrigger } from '../ServiceAlertTrigger'; import { useUrlParams } from '../../../hooks/useUrlParams'; import { useServiceTransactionTypes } from '../../../hooks/useServiceTransactionTypes'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx similarity index 92% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx index ec6168df5b1340..6eff4759b2e7cb 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/AnnotationsPlot.tsx @@ -14,8 +14,8 @@ import { EuiText } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Maybe } from '../../../../../../../../plugins/apm/typings/common'; -import { Annotation } from '../../../../../../../../plugins/apm/common/annotations'; +import { Maybe } from '../../../../../typings/common'; +import { Annotation } from '../../../../../common/annotations'; import { PlotValues, SharedPlot } from './plotUtils'; import { asAbsoluteDateTime } from '../../../../utils/formatters'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/InteractivePlot.js b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/InteractivePlot.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/InteractivePlot.js rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/InteractivePlot.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/Legends.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/SelectionMarker.js b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/SelectionMarker.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/SelectionMarker.js rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/SelectionMarker.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/StaticPlot.js b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/StaticPlot.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/StaticPlot.js rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/StaticPlot.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/StatusText.js b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/StatusText.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/StatusText.js rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/StatusText.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/VoronoiPlot.js b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/VoronoiPlot.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/VoronoiPlot.js rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/VoronoiPlot.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/getEmptySeries.ts b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/getEmptySeries.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/getEmptySeries.ts rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/getEmptySeries.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.test.ts b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.test.ts rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.test.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.ts b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.ts rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/getTimezoneOffsetInMs.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/index.js b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/index.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/index.js rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/index.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts similarity index 94% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts index bfc5c7c243f313..b130deed7f098c 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts +++ b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.test.ts @@ -6,10 +6,7 @@ // @ts-ignore import * as plotUtils from './plotUtils'; -import { - TimeSeries, - Coordinate -} from '../../../../../../../../plugins/apm/typings/timeseries'; +import { TimeSeries, Coordinate } from '../../../../../typings/timeseries'; describe('plotUtils', () => { describe('getPlotValues', () => { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx index c489c270d19acd..64350a57416473 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/plotUtils.tsx @@ -11,10 +11,7 @@ import d3 from 'd3'; import PropTypes from 'prop-types'; import React from 'react'; -import { - TimeSeries, - Coordinate -} from '../../../../../../../../plugins/apm/typings/timeseries'; +import { TimeSeries, Coordinate } from '../../../../../typings/timeseries'; import { unit } from '../../../../style/variables'; import { getDomainTZ, getTimeTicksTZ } from '../helper/timezone'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/CustomPlot.test.js b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/CustomPlot.test.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/CustomPlot.test.js rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/CustomPlot.test.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/__snapshots__/CustomPlot.test.js.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/responseWithData.json b/x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/responseWithData.json similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/CustomPlot/test/responseWithData.json rename to x-pack/plugins/apm/public/components/shared/charts/CustomPlot/test/responseWithData.json diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/SingleRect.js b/x-pack/plugins/apm/public/components/shared/charts/Histogram/SingleRect.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/SingleRect.js rename to x-pack/plugins/apm/public/components/shared/charts/Histogram/SingleRect.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/__test__/Histogram.test.js b/x-pack/plugins/apm/public/components/shared/charts/Histogram/__test__/Histogram.test.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/__test__/Histogram.test.js rename to x-pack/plugins/apm/public/components/shared/charts/Histogram/__test__/Histogram.test.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/__test__/__snapshots__/Histogram.test.js.snap b/x-pack/plugins/apm/public/components/shared/charts/Histogram/__test__/__snapshots__/Histogram.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/__test__/__snapshots__/Histogram.test.js.snap rename to x-pack/plugins/apm/public/components/shared/charts/Histogram/__test__/__snapshots__/Histogram.test.js.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/__test__/response.json b/x-pack/plugins/apm/public/components/shared/charts/Histogram/__test__/response.json similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/__test__/response.json rename to x-pack/plugins/apm/public/components/shared/charts/Histogram/__test__/response.json diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/index.js b/x-pack/plugins/apm/public/components/shared/charts/Histogram/index.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Histogram/index.js rename to x-pack/plugins/apm/public/components/shared/charts/Histogram/index.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/Legend/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Legend/index.tsx rename to x-pack/plugins/apm/public/components/shared/charts/Legend/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx similarity index 90% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx rename to x-pack/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx index 2ceac87d9aab38..862f2a8987067e 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/MetricsChart/index.tsx @@ -6,7 +6,7 @@ import { EuiTitle } from '@elastic/eui'; import React from 'react'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { GenericMetricsChart } from '../../../../../../../../plugins/apm/server/lib/metrics/transform_metrics_chart'; +import { GenericMetricsChart } from '../../../../../server/lib/metrics/transform_metrics_chart'; // @ts-ignore import CustomPlot from '../CustomPlot'; import { @@ -17,10 +17,10 @@ import { getFixedByteFormatter, asDuration } from '../../../../utils/formatters'; -import { Coordinate } from '../../../../../../../../plugins/apm/typings/timeseries'; +import { Coordinate } from '../../../../../typings/timeseries'; import { isValidCoordinateValue } from '../../../../utils/isValidCoordinateValue'; import { useChartsSync } from '../../../../hooks/useChartsSync'; -import { Maybe } from '../../../../../../../../plugins/apm/typings/common'; +import { Maybe } from '../../../../../typings/common'; interface Props { start: Maybe; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/LastTickValue.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/LastTickValue.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/LastTickValue.tsx rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/LastTickValue.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/AgentMarker.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx similarity index 97% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx index 48265ce7c80a8f..51368a4fb946d5 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/ErrorMarker.tsx @@ -11,7 +11,7 @@ import styled from 'styled-components'; import { TRACE_ID, TRANSACTION_ID -} from '../../../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; +} from '../../../../../../common/elasticsearch_fieldnames'; import { useUrlParams } from '../../../../../hooks/useUrlParams'; import { px, unit, units } from '../../../../../style/variables'; import { asDuration } from '../../../../../utils/formatters'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/AgentMarker.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/AgentMarker.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/AgentMarker.test.tsx rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/AgentMarker.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/ErrorMarker.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/ErrorMarker.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/ErrorMarker.test.tsx rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/ErrorMarker.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/Marker.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/Marker.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/Marker.test.tsx rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/Marker.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/AgentMarker.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/AgentMarker.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/AgentMarker.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/AgentMarker.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/ErrorMarker.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/ErrorMarker.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/ErrorMarker.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/ErrorMarker.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/Marker.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/Marker.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/Marker.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/__test__/__snapshots__/Marker.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Marker/index.tsx rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/Marker/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Timeline.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/Timeline.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/Timeline.test.tsx rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/Timeline.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/TimelineAxis.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/VerticalLines.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/VerticalLines.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/VerticalLines.tsx rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/VerticalLines.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/__snapshots__/Timeline.test.tsx.snap diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/Timeline/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/index.tsx rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/plotUtils.ts b/x-pack/plugins/apm/public/components/shared/charts/Timeline/plotUtils.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Timeline/plotUtils.ts rename to x-pack/plugins/apm/public/components/shared/charts/Timeline/plotUtils.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/Tooltip/index.js b/x-pack/plugins/apm/public/components/shared/charts/Tooltip/index.js similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/Tooltip/index.js rename to x-pack/plugins/apm/public/components/shared/charts/Tooltip/index.js diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.test.tsx rename to x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.tsx rename to x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/BrowserLineChart.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/ChoroplethToolTip.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/ChoroplethToolTip.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/ChoroplethToolTip.tsx rename to x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/ChoroplethToolTip.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/index.tsx rename to x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/ChoroplethMap/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx rename to x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/DurationByCountryMap/index.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx rename to x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx index c9c31b05e264cd..27c829f63cf0a0 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/TransactionLineChart/index.tsx @@ -8,7 +8,7 @@ import React, { useCallback } from 'react'; import { Coordinate, RectCoordinate -} from '../../../../../../../../../plugins/apm/typings/timeseries'; +} from '../../../../../../typings/timeseries'; import { useChartsSync } from '../../../../../hooks/useChartsSync'; // @ts-ignore import CustomPlot from '../../CustomPlot'; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx rename to x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx index 368a39e4ad228e..b0555da705a30e 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/TransactionCharts/index.tsx @@ -19,11 +19,8 @@ import { Location } from 'history'; import React, { Component } from 'react'; import { isEmpty, flatten } from 'lodash'; import styled from 'styled-components'; -import { NOT_AVAILABLE_LABEL } from '../../../../../../../../plugins/apm/common/i18n'; -import { - Coordinate, - TimeSeries -} from '../../../../../../../../plugins/apm/typings/timeseries'; +import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; +import { Coordinate, TimeSeries } from '../../../../../typings/timeseries'; import { ITransactionChartData } from '../../../../selectors/chartSelectors'; import { IUrlParams } from '../../../../context/UrlParamsContext/types'; import { @@ -42,7 +39,7 @@ import { TRANSACTION_PAGE_LOAD, TRANSACTION_ROUTE_CHANGE, TRANSACTION_REQUEST -} from '../../../../../../../../plugins/apm/common/transaction_types'; +} from '../../../../../common/transaction_types'; interface TransactionChartProps { hasMLJob: boolean; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/helper/__test__/timezone.test.ts b/x-pack/plugins/apm/public/components/shared/charts/helper/__test__/timezone.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/helper/__test__/timezone.test.ts rename to x-pack/plugins/apm/public/components/shared/charts/helper/__test__/timezone.test.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/charts/helper/timezone.ts b/x-pack/plugins/apm/public/components/shared/charts/helper/timezone.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/charts/helper/timezone.ts rename to x-pack/plugins/apm/public/components/shared/charts/helper/timezone.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.test.tsx b/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.test.tsx rename to x-pack/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.ts b/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.ts rename to x-pack/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx b/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx rename to x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/components/shared/useDelayedVisibility/index.ts b/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/shared/useDelayedVisibility/index.ts rename to x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.ts diff --git a/x-pack/legacy/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx b/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx similarity index 93% rename from x-pack/legacy/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx rename to x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx index cc2e3826116280..865e3dbe6dafcf 100644 --- a/x-pack/legacy/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx +++ b/x-pack/plugins/apm/public/context/ApmPluginContext/MockApmPluginContext.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { ApmPluginContext, ApmPluginContextValue } from '.'; import { createCallApmApi } from '../../services/rest/createCallApmApi'; -import { ConfigSchema } from '../../new-platform/plugin'; +import { ConfigSchema } from '../..'; const mockCore = { chrome: { @@ -30,7 +30,6 @@ const mockCore = { }; const mockConfig: ConfigSchema = { - indexPatternTitle: 'apm-*', serviceMapEnabled: true, ui: { enabled: false diff --git a/x-pack/legacy/plugins/apm/public/context/ApmPluginContext/index.tsx b/x-pack/plugins/apm/public/context/ApmPluginContext/index.tsx similarity index 87% rename from x-pack/legacy/plugins/apm/public/context/ApmPluginContext/index.tsx rename to x-pack/plugins/apm/public/context/ApmPluginContext/index.tsx index acc3886586889d..37304d292540d6 100644 --- a/x-pack/legacy/plugins/apm/public/context/ApmPluginContext/index.tsx +++ b/x-pack/plugins/apm/public/context/ApmPluginContext/index.tsx @@ -6,7 +6,8 @@ import { createContext } from 'react'; import { AppMountContext } from 'kibana/public'; -import { ApmPluginSetupDeps, ConfigSchema } from '../../new-platform/plugin'; +import { ConfigSchema } from '../..'; +import { ApmPluginSetupDeps } from '../../plugin'; export type AppMountContextBasePath = AppMountContext['core']['http']['basePath']; diff --git a/x-pack/legacy/plugins/apm/public/context/ChartsSyncContext.tsx b/x-pack/plugins/apm/public/context/ChartsSyncContext.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/context/ChartsSyncContext.tsx rename to x-pack/plugins/apm/public/context/ChartsSyncContext.tsx diff --git a/x-pack/legacy/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx b/x-pack/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx rename to x-pack/plugins/apm/public/context/LicenseContext/InvalidLicenseNotification.tsx diff --git a/x-pack/legacy/plugins/apm/public/context/LicenseContext/index.tsx b/x-pack/plugins/apm/public/context/LicenseContext/index.tsx similarity index 94% rename from x-pack/legacy/plugins/apm/public/context/LicenseContext/index.tsx rename to x-pack/plugins/apm/public/context/LicenseContext/index.tsx index 62cdbd3bbc995b..e6615a2fc98bf0 100644 --- a/x-pack/legacy/plugins/apm/public/context/LicenseContext/index.tsx +++ b/x-pack/plugins/apm/public/context/LicenseContext/index.tsx @@ -6,7 +6,7 @@ import React from 'react'; import useObservable from 'react-use/lib/useObservable'; -import { ILicense } from '../../../../../../plugins/licensing/public'; +import { ILicense } from '../../../../licensing/public'; import { useApmPluginContext } from '../../hooks/useApmPluginContext'; import { InvalidLicenseNotification } from './InvalidLicenseNotification'; diff --git a/x-pack/legacy/plugins/apm/public/context/LoadingIndicatorContext.tsx b/x-pack/plugins/apm/public/context/LoadingIndicatorContext.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/context/LoadingIndicatorContext.tsx rename to x-pack/plugins/apm/public/context/LoadingIndicatorContext.tsx diff --git a/x-pack/legacy/plugins/apm/public/context/LocationContext.tsx b/x-pack/plugins/apm/public/context/LocationContext.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/context/LocationContext.tsx rename to x-pack/plugins/apm/public/context/LocationContext.tsx diff --git a/x-pack/legacy/plugins/apm/public/context/MatchedRouteContext.tsx b/x-pack/plugins/apm/public/context/MatchedRouteContext.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/context/MatchedRouteContext.tsx rename to x-pack/plugins/apm/public/context/MatchedRouteContext.tsx diff --git a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/MockUrlParamsContextProvider.tsx b/x-pack/plugins/apm/public/context/UrlParamsContext/MockUrlParamsContextProvider.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/context/UrlParamsContext/MockUrlParamsContextProvider.tsx rename to x-pack/plugins/apm/public/context/UrlParamsContext/MockUrlParamsContextProvider.tsx diff --git a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/__tests__/UrlParamsContext.test.tsx b/x-pack/plugins/apm/public/context/UrlParamsContext/__tests__/UrlParamsContext.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/context/UrlParamsContext/__tests__/UrlParamsContext.test.tsx rename to x-pack/plugins/apm/public/context/UrlParamsContext/__tests__/UrlParamsContext.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/constants.ts b/x-pack/plugins/apm/public/context/UrlParamsContext/constants.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/context/UrlParamsContext/constants.ts rename to x-pack/plugins/apm/public/context/UrlParamsContext/constants.ts diff --git a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/helpers.ts b/x-pack/plugins/apm/public/context/UrlParamsContext/helpers.ts similarity index 97% rename from x-pack/legacy/plugins/apm/public/context/UrlParamsContext/helpers.ts rename to x-pack/plugins/apm/public/context/UrlParamsContext/helpers.ts index b80db0e9ae073b..f1e45fe45255d0 100644 --- a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/helpers.ts +++ b/x-pack/plugins/apm/public/context/UrlParamsContext/helpers.ts @@ -7,7 +7,7 @@ import { compact, pick } from 'lodash'; import datemath from '@elastic/datemath'; import { IUrlParams } from './types'; -import { ProcessorEvent } from '../../../../../../plugins/apm/common/processor_event'; +import { ProcessorEvent } from '../../../common/processor_event'; interface PathParams { processorEvent?: ProcessorEvent; diff --git a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/index.tsx b/x-pack/plugins/apm/public/context/UrlParamsContext/index.tsx similarity index 92% rename from x-pack/legacy/plugins/apm/public/context/UrlParamsContext/index.tsx rename to x-pack/plugins/apm/public/context/UrlParamsContext/index.tsx index 588936039c2bcb..7a929380bce371 100644 --- a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/index.tsx +++ b/x-pack/plugins/apm/public/context/UrlParamsContext/index.tsx @@ -16,13 +16,13 @@ import { uniqueId, mapValues } from 'lodash'; import { IUrlParams } from './types'; import { getParsedDate } from './helpers'; import { resolveUrlParams } from './resolveUrlParams'; -import { UIFilters } from '../../../../../../plugins/apm/typings/ui_filters'; +import { UIFilters } from '../../../typings/ui_filters'; import { localUIFilterNames, LocalUIFilterName // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters/config'; -import { pickKeys } from '../../utils/pickKeys'; +} from '../../../server/lib/ui_filters/local_ui_filters/config'; +import { pickKeys } from '../../../common/utils/pick_keys'; import { useDeepObjectIdentity } from '../../hooks/useDeepObjectIdentity'; interface TimeRange { diff --git a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/resolveUrlParams.ts b/x-pack/plugins/apm/public/context/UrlParamsContext/resolveUrlParams.ts similarity index 93% rename from x-pack/legacy/plugins/apm/public/context/UrlParamsContext/resolveUrlParams.ts rename to x-pack/plugins/apm/public/context/UrlParamsContext/resolveUrlParams.ts index f022d2084583bb..34af18431a2dfb 100644 --- a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/resolveUrlParams.ts +++ b/x-pack/plugins/apm/public/context/UrlParamsContext/resolveUrlParams.ts @@ -18,8 +18,8 @@ import { import { toQuery } from '../../components/shared/Links/url_helpers'; import { TIMEPICKER_DEFAULTS } from './constants'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { localUIFilterNames } from '../../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters/config'; -import { pickKeys } from '../../utils/pickKeys'; +import { localUIFilterNames } from '../../../server/lib/ui_filters/local_ui_filters/config'; +import { pickKeys } from '../../../common/utils/pick_keys'; type TimeUrlParams = Pick< IUrlParams, diff --git a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/types.ts b/x-pack/plugins/apm/public/context/UrlParamsContext/types.ts similarity index 83% rename from x-pack/legacy/plugins/apm/public/context/UrlParamsContext/types.ts rename to x-pack/plugins/apm/public/context/UrlParamsContext/types.ts index acde09308ab46f..78fe662b88d75b 100644 --- a/x-pack/legacy/plugins/apm/public/context/UrlParamsContext/types.ts +++ b/x-pack/plugins/apm/public/context/UrlParamsContext/types.ts @@ -5,8 +5,8 @@ */ // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LocalUIFilterName } from '../../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters/config'; -import { ProcessorEvent } from '../../../../../../plugins/apm/common/processor_event'; +import { LocalUIFilterName } from '../../../server/lib/ui_filters/local_ui_filters/config'; +import { ProcessorEvent } from '../../../common/processor_event'; export type IUrlParams = { detailTab?: string; diff --git a/x-pack/legacy/plugins/apm/public/new-platform/featureCatalogueEntry.ts b/x-pack/plugins/apm/public/featureCatalogueEntry.ts similarity index 88% rename from x-pack/legacy/plugins/apm/public/new-platform/featureCatalogueEntry.ts rename to x-pack/plugins/apm/public/featureCatalogueEntry.ts index 7a150de6d5d02c..f76c6f5169dc52 100644 --- a/x-pack/legacy/plugins/apm/public/new-platform/featureCatalogueEntry.ts +++ b/x-pack/plugins/apm/public/featureCatalogueEntry.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { FeatureCatalogueCategory } from '../../../../../../src/plugins/home/public'; +import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public'; export const featureCatalogueEntry = { id: 'apm', diff --git a/x-pack/legacy/plugins/apm/public/hooks/useAgentName.ts b/x-pack/plugins/apm/public/hooks/useAgentName.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useAgentName.ts rename to x-pack/plugins/apm/public/hooks/useAgentName.ts diff --git a/x-pack/legacy/plugins/apm/public/hooks/useApmPluginContext.ts b/x-pack/plugins/apm/public/hooks/useApmPluginContext.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useApmPluginContext.ts rename to x-pack/plugins/apm/public/hooks/useApmPluginContext.ts diff --git a/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.test.ts b/x-pack/plugins/apm/public/hooks/useAvgDurationByBrowser.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.test.ts rename to x-pack/plugins/apm/public/hooks/useAvgDurationByBrowser.test.ts diff --git a/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.ts b/x-pack/plugins/apm/public/hooks/useAvgDurationByBrowser.ts similarity index 83% rename from x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.ts rename to x-pack/plugins/apm/public/hooks/useAvgDurationByBrowser.ts index 256c2fa68bfbc0..5d0c9d1435798d 100644 --- a/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByBrowser.ts +++ b/x-pack/plugins/apm/public/hooks/useAvgDurationByBrowser.ts @@ -8,9 +8,9 @@ import theme from '@elastic/eui/dist/eui_theme_light.json'; import { useFetcher } from './useFetcher'; import { useUrlParams } from './useUrlParams'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AvgDurationByBrowserAPIResponse } from '../../../../../plugins/apm/server/lib/transactions/avg_duration_by_browser'; -import { TimeSeries } from '../../../../../plugins/apm/typings/timeseries'; -import { getVizColorForIndex } from '../../../../../plugins/apm/common/viz_colors'; +import { AvgDurationByBrowserAPIResponse } from '../../server/lib/transactions/avg_duration_by_browser'; +import { TimeSeries } from '../../typings/timeseries'; +import { getVizColorForIndex } from '../../common/viz_colors'; function toTimeSeries(data?: AvgDurationByBrowserAPIResponse): TimeSeries[] { if (!data) { diff --git a/x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByCountry.ts b/x-pack/plugins/apm/public/hooks/useAvgDurationByCountry.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useAvgDurationByCountry.ts rename to x-pack/plugins/apm/public/hooks/useAvgDurationByCountry.ts diff --git a/x-pack/legacy/plugins/apm/public/hooks/useCallApi.ts b/x-pack/plugins/apm/public/hooks/useCallApi.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useCallApi.ts rename to x-pack/plugins/apm/public/hooks/useCallApi.ts diff --git a/x-pack/legacy/plugins/apm/public/hooks/useChartsSync.tsx b/x-pack/plugins/apm/public/hooks/useChartsSync.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useChartsSync.tsx rename to x-pack/plugins/apm/public/hooks/useChartsSync.tsx diff --git a/x-pack/legacy/plugins/apm/public/hooks/useComponentId.tsx b/x-pack/plugins/apm/public/hooks/useComponentId.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useComponentId.tsx rename to x-pack/plugins/apm/public/hooks/useComponentId.tsx diff --git a/x-pack/legacy/plugins/apm/public/hooks/useDeepObjectIdentity.ts b/x-pack/plugins/apm/public/hooks/useDeepObjectIdentity.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useDeepObjectIdentity.ts rename to x-pack/plugins/apm/public/hooks/useDeepObjectIdentity.ts diff --git a/x-pack/legacy/plugins/apm/public/hooks/useDynamicIndexPattern.ts b/x-pack/plugins/apm/public/hooks/useDynamicIndexPattern.ts similarity index 89% rename from x-pack/legacy/plugins/apm/public/hooks/useDynamicIndexPattern.ts rename to x-pack/plugins/apm/public/hooks/useDynamicIndexPattern.ts index ee3d2e81f259f5..9a95bd925d6e1c 100644 --- a/x-pack/legacy/plugins/apm/public/hooks/useDynamicIndexPattern.ts +++ b/x-pack/plugins/apm/public/hooks/useDynamicIndexPattern.ts @@ -5,7 +5,7 @@ */ import { useFetcher } from './useFetcher'; -import { ProcessorEvent } from '../../../../../plugins/apm/common/processor_event'; +import { ProcessorEvent } from '../../common/processor_event'; export function useDynamicIndexPattern( processorEvent: ProcessorEvent | undefined diff --git a/x-pack/legacy/plugins/apm/public/hooks/useFetcher.integration.test.tsx b/x-pack/plugins/apm/public/hooks/useFetcher.integration.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useFetcher.integration.test.tsx rename to x-pack/plugins/apm/public/hooks/useFetcher.integration.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/hooks/useFetcher.test.tsx b/x-pack/plugins/apm/public/hooks/useFetcher.test.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useFetcher.test.tsx rename to x-pack/plugins/apm/public/hooks/useFetcher.test.tsx diff --git a/x-pack/legacy/plugins/apm/public/hooks/useFetcher.tsx b/x-pack/plugins/apm/public/hooks/useFetcher.tsx similarity index 98% rename from x-pack/legacy/plugins/apm/public/hooks/useFetcher.tsx rename to x-pack/plugins/apm/public/hooks/useFetcher.tsx index 95cebd6b2a465f..5d5128d969aad3 100644 --- a/x-pack/legacy/plugins/apm/public/hooks/useFetcher.tsx +++ b/x-pack/plugins/apm/public/hooks/useFetcher.tsx @@ -9,7 +9,7 @@ import React, { useContext, useEffect, useState, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { IHttpFetchError } from 'src/core/public'; -import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public'; +import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { LoadingIndicatorContext } from '../context/LoadingIndicatorContext'; import { APMClient, callApmApi } from '../services/rest/createCallApmApi'; import { useApmPluginContext } from './useApmPluginContext'; diff --git a/x-pack/legacy/plugins/apm/public/hooks/useKibanaUrl.ts b/x-pack/plugins/apm/public/hooks/useKibanaUrl.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useKibanaUrl.ts rename to x-pack/plugins/apm/public/hooks/useKibanaUrl.ts diff --git a/x-pack/legacy/plugins/apm/public/hooks/useLicense.ts b/x-pack/plugins/apm/public/hooks/useLicense.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useLicense.ts rename to x-pack/plugins/apm/public/hooks/useLicense.ts diff --git a/x-pack/legacy/plugins/apm/public/hooks/useLoadingIndicator.ts b/x-pack/plugins/apm/public/hooks/useLoadingIndicator.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useLoadingIndicator.ts rename to x-pack/plugins/apm/public/hooks/useLoadingIndicator.ts diff --git a/x-pack/legacy/plugins/apm/public/hooks/useLocalUIFilters.ts b/x-pack/plugins/apm/public/hooks/useLocalUIFilters.ts similarity index 88% rename from x-pack/legacy/plugins/apm/public/hooks/useLocalUIFilters.ts rename to x-pack/plugins/apm/public/hooks/useLocalUIFilters.ts index 9f14b2b25fc940..1dfd3ec7c3ee3a 100644 --- a/x-pack/legacy/plugins/apm/public/hooks/useLocalUIFilters.ts +++ b/x-pack/plugins/apm/public/hooks/useLocalUIFilters.ts @@ -7,18 +7,18 @@ import { omit } from 'lodash'; import { useFetcher } from './useFetcher'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { LocalUIFiltersAPIResponse } from '../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters'; +import { LocalUIFiltersAPIResponse } from '../../server/lib/ui_filters/local_ui_filters'; import { useUrlParams } from './useUrlParams'; import { LocalUIFilterName, localUIFilters // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../plugins/apm/server/lib/ui_filters/local_ui_filters/config'; +} from '../../server/lib/ui_filters/local_ui_filters/config'; import { history } from '../utils/history'; import { toQuery, fromQuery } from '../components/shared/Links/url_helpers'; import { removeUndefinedProps } from '../context/UrlParamsContext/helpers'; -import { PROJECTION } from '../../../../../plugins/apm/common/projections/typings'; -import { pickKeys } from '../utils/pickKeys'; +import { PROJECTION } from '../../common/projections/typings'; +import { pickKeys } from '../../common/utils/pick_keys'; import { useCallApi } from './useCallApi'; const getInitialData = ( diff --git a/x-pack/legacy/plugins/apm/public/hooks/useLocation.tsx b/x-pack/plugins/apm/public/hooks/useLocation.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useLocation.tsx rename to x-pack/plugins/apm/public/hooks/useLocation.tsx diff --git a/x-pack/legacy/plugins/apm/public/hooks/useMatchedRoutes.tsx b/x-pack/plugins/apm/public/hooks/useMatchedRoutes.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useMatchedRoutes.tsx rename to x-pack/plugins/apm/public/hooks/useMatchedRoutes.tsx diff --git a/x-pack/legacy/plugins/apm/public/hooks/useServiceMetricCharts.ts b/x-pack/plugins/apm/public/hooks/useServiceMetricCharts.ts similarity index 91% rename from x-pack/legacy/plugins/apm/public/hooks/useServiceMetricCharts.ts rename to x-pack/plugins/apm/public/hooks/useServiceMetricCharts.ts index 72618a6254f4ca..ebcd6ab063708a 100644 --- a/x-pack/legacy/plugins/apm/public/hooks/useServiceMetricCharts.ts +++ b/x-pack/plugins/apm/public/hooks/useServiceMetricCharts.ts @@ -5,7 +5,7 @@ */ // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { MetricsChartsByAgentAPIResponse } from '../../../../../plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent'; +import { MetricsChartsByAgentAPIResponse } from '../../server/lib/metrics/get_metrics_chart_data_by_agent'; import { IUrlParams } from '../context/UrlParamsContext/types'; import { useUiFilters } from '../context/UrlParamsContext'; import { useFetcher } from './useFetcher'; diff --git a/x-pack/legacy/plugins/apm/public/hooks/useServiceTransactionTypes.tsx b/x-pack/plugins/apm/public/hooks/useServiceTransactionTypes.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useServiceTransactionTypes.tsx rename to x-pack/plugins/apm/public/hooks/useServiceTransactionTypes.tsx diff --git a/x-pack/legacy/plugins/apm/public/hooks/useTransactionBreakdown.ts b/x-pack/plugins/apm/public/hooks/useTransactionBreakdown.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useTransactionBreakdown.ts rename to x-pack/plugins/apm/public/hooks/useTransactionBreakdown.ts diff --git a/x-pack/legacy/plugins/apm/public/hooks/useTransactionCharts.ts b/x-pack/plugins/apm/public/hooks/useTransactionCharts.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useTransactionCharts.ts rename to x-pack/plugins/apm/public/hooks/useTransactionCharts.ts diff --git a/x-pack/legacy/plugins/apm/public/hooks/useTransactionDistribution.ts b/x-pack/plugins/apm/public/hooks/useTransactionDistribution.ts similarity index 93% rename from x-pack/legacy/plugins/apm/public/hooks/useTransactionDistribution.ts rename to x-pack/plugins/apm/public/hooks/useTransactionDistribution.ts index 9a93a2334924ac..152980b5655d66 100644 --- a/x-pack/legacy/plugins/apm/public/hooks/useTransactionDistribution.ts +++ b/x-pack/plugins/apm/public/hooks/useTransactionDistribution.ts @@ -8,7 +8,7 @@ import { IUrlParams } from '../context/UrlParamsContext/types'; import { useFetcher } from './useFetcher'; import { useUiFilters } from '../context/UrlParamsContext'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { TransactionDistributionAPIResponse } from '../../../../../plugins/apm/server/lib/transactions/distribution'; +import { TransactionDistributionAPIResponse } from '../../server/lib/transactions/distribution'; const INITIAL_DATA = { buckets: [] as TransactionDistributionAPIResponse['buckets'], diff --git a/x-pack/legacy/plugins/apm/public/hooks/useTransactionList.ts b/x-pack/plugins/apm/public/hooks/useTransactionList.ts similarity index 94% rename from x-pack/legacy/plugins/apm/public/hooks/useTransactionList.ts rename to x-pack/plugins/apm/public/hooks/useTransactionList.ts index 6ede77023790b7..e048e8fe0e3cb3 100644 --- a/x-pack/legacy/plugins/apm/public/hooks/useTransactionList.ts +++ b/x-pack/plugins/apm/public/hooks/useTransactionList.ts @@ -9,7 +9,7 @@ import { IUrlParams } from '../context/UrlParamsContext/types'; import { useUiFilters } from '../context/UrlParamsContext'; import { useFetcher } from './useFetcher'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { TransactionGroupListAPIResponse } from '../../../../../plugins/apm/server/lib/transaction_groups'; +import { TransactionGroupListAPIResponse } from '../../server/lib/transaction_groups'; const getRelativeImpact = ( impact: number, diff --git a/x-pack/legacy/plugins/apm/public/hooks/useUrlParams.tsx b/x-pack/plugins/apm/public/hooks/useUrlParams.tsx similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useUrlParams.tsx rename to x-pack/plugins/apm/public/hooks/useUrlParams.tsx diff --git a/x-pack/legacy/plugins/apm/public/hooks/useWaterfall.ts b/x-pack/plugins/apm/public/hooks/useWaterfall.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/hooks/useWaterfall.ts rename to x-pack/plugins/apm/public/hooks/useWaterfall.ts diff --git a/x-pack/legacy/plugins/apm/public/icon.svg b/x-pack/plugins/apm/public/icon.svg similarity index 100% rename from x-pack/legacy/plugins/apm/public/icon.svg rename to x-pack/plugins/apm/public/icon.svg diff --git a/x-pack/legacy/plugins/apm/public/images/apm-ml-anomaly-detection-example.png b/x-pack/plugins/apm/public/images/apm-ml-anomaly-detection-example.png similarity index 100% rename from x-pack/legacy/plugins/apm/public/images/apm-ml-anomaly-detection-example.png rename to x-pack/plugins/apm/public/images/apm-ml-anomaly-detection-example.png diff --git a/x-pack/plugins/apm/public/index.ts b/x-pack/plugins/apm/public/index.ts new file mode 100644 index 00000000000000..4ac06e1eb8a1c2 --- /dev/null +++ b/x-pack/plugins/apm/public/index.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + PluginInitializer, + PluginInitializerContext +} from '../../../../src/core/public'; +import { ApmPlugin, ApmPluginSetup, ApmPluginStart } from './plugin'; + +export interface ConfigSchema { + serviceMapEnabled: boolean; + ui: { + enabled: boolean; + }; +} + +export const plugin: PluginInitializer = ( + pluginInitializerContext: PluginInitializerContext +) => new ApmPlugin(pluginInitializerContext); + +export { ApmPluginSetup, ApmPluginStart }; +export { getTraceUrl } from './components/shared/Links/apm/ExternalLinks'; diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts new file mode 100644 index 00000000000000..f13c8853d05827 --- /dev/null +++ b/x-pack/plugins/apm/public/plugin.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { + AppMountParameters, + CoreSetup, + CoreStart, + Plugin, + PluginInitializerContext +} from '../../../../src/core/public'; +import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils'; + +import { + PluginSetupContract as AlertingPluginPublicSetup, + PluginStartContract as AlertingPluginPublicStart +} from '../../alerting/public'; +import { FeaturesPluginSetup } from '../../features/public'; +import { + DataPublicPluginSetup, + DataPublicPluginStart +} from '../../../../src/plugins/data/public'; +import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; +import { LicensingPluginSetup } from '../../licensing/public'; +import { + TriggersAndActionsUIPublicPluginSetup, + TriggersAndActionsUIPublicPluginStart +} from '../../triggers_actions_ui/public'; +import { ConfigSchema } from '.'; +import { createCallApmApi } from './services/rest/createCallApmApi'; +import { featureCatalogueEntry } from './featureCatalogueEntry'; +import { AlertType } from '../common/alert_types'; +import { ErrorRateAlertTrigger } from './components/shared/ErrorRateAlertTrigger'; +import { TransactionDurationAlertTrigger } from './components/shared/TransactionDurationAlertTrigger'; +import { setHelpExtension } from './setHelpExtension'; +import { toggleAppLinkInNav } from './toggleAppLinkInNav'; +import { setReadonlyBadge } from './updateBadge'; +import { createStaticIndexPattern } from './services/rest/index_pattern'; + +export type ApmPluginSetup = void; +export type ApmPluginStart = void; + +export interface ApmPluginSetupDeps { + alerting?: AlertingPluginPublicSetup; + data: DataPublicPluginSetup; + features: FeaturesPluginSetup; + home: HomePublicPluginSetup; + licensing: LicensingPluginSetup; + triggers_actions_ui: TriggersAndActionsUIPublicPluginSetup; +} + +export interface ApmPluginStartDeps { + alerting?: AlertingPluginPublicStart; + data: DataPublicPluginStart; + home: void; + licensing: void; + triggers_actions_ui: TriggersAndActionsUIPublicPluginStart; +} + +export class ApmPlugin implements Plugin { + private readonly initializerContext: PluginInitializerContext; + constructor(initializerContext: PluginInitializerContext) { + this.initializerContext = initializerContext; + } + public setup(core: CoreSetup, plugins: ApmPluginSetupDeps) { + const config = this.initializerContext.config.get(); + const pluginSetupDeps = plugins; + + pluginSetupDeps.home.environment.update({ apmUi: true }); + pluginSetupDeps.home.featureCatalogue.register(featureCatalogueEntry); + + core.application.register({ + id: 'apm', + title: 'APM', + order: 8100, + euiIconType: 'apmApp', + appRoute: '/app/apm', + icon: 'plugins/apm/public/icon.svg', + category: DEFAULT_APP_CATEGORIES.observability, + + async mount(params: AppMountParameters) { + // Load application bundle + const { renderApp } = await import('./application'); + // Get start services + const [coreStart] = await core.getStartServices(); + + // render APM feedback link in global help menu + setHelpExtension(coreStart); + setReadonlyBadge(coreStart); + + // Automatically creates static index pattern and stores as saved object + createStaticIndexPattern().catch(e => { + // eslint-disable-next-line no-console + console.log('Error creating static index pattern', e); + }); + + return renderApp(coreStart, pluginSetupDeps, params, config); + } + }); + } + public start(core: CoreStart, plugins: ApmPluginStartDeps) { + createCallApmApi(core.http); + + toggleAppLinkInNav(core, this.initializerContext.config.get()); + + plugins.triggers_actions_ui.alertTypeRegistry.register({ + id: AlertType.ErrorRate, + name: i18n.translate('xpack.apm.alertTypes.errorRate', { + defaultMessage: 'Error rate' + }), + iconClass: 'bell', + alertParamsExpression: ErrorRateAlertTrigger, + validate: () => ({ + errors: [] + }) + }); + + plugins.triggers_actions_ui.alertTypeRegistry.register({ + id: AlertType.TransactionDuration, + name: i18n.translate('xpack.apm.alertTypes.transactionDuration', { + defaultMessage: 'Transaction duration' + }), + iconClass: 'bell', + alertParamsExpression: TransactionDurationAlertTrigger, + validate: () => ({ + errors: [] + }) + }); + } +} diff --git a/x-pack/legacy/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts b/x-pack/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts rename to x-pack/plugins/apm/public/selectors/__tests__/chartSelectors.test.ts diff --git a/x-pack/legacy/plugins/apm/public/selectors/__tests__/mockData/anomalyData.ts b/x-pack/plugins/apm/public/selectors/__tests__/mockData/anomalyData.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/selectors/__tests__/mockData/anomalyData.ts rename to x-pack/plugins/apm/public/selectors/__tests__/mockData/anomalyData.ts diff --git a/x-pack/legacy/plugins/apm/public/selectors/chartSelectors.ts b/x-pack/plugins/apm/public/selectors/chartSelectors.ts similarity index 94% rename from x-pack/legacy/plugins/apm/public/selectors/chartSelectors.ts rename to x-pack/plugins/apm/public/selectors/chartSelectors.ts index d60b63e243d718..e6ef9361ee52a3 100644 --- a/x-pack/legacy/plugins/apm/public/selectors/chartSelectors.ts +++ b/x-pack/plugins/apm/public/selectors/chartSelectors.ts @@ -10,14 +10,14 @@ import { difference, zipObject } from 'lodash'; import mean from 'lodash.mean'; import { rgba } from 'polished'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { TimeSeriesAPIResponse } from '../../../../../plugins/apm/server/lib/transactions/charts'; +import { TimeSeriesAPIResponse } from '../../server/lib/transactions/charts'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ApmTimeSeriesResponse } from '../../../../../plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform'; +import { ApmTimeSeriesResponse } from '../../server/lib/transactions/charts/get_timeseries_data/transform'; import { Coordinate, RectCoordinate, TimeSeries -} from '../../../../../plugins/apm/typings/timeseries'; +} from '../../typings/timeseries'; import { asDecimal, tpmUnit, convertTo } from '../utils/formatters'; import { IUrlParams } from '../context/UrlParamsContext/types'; import { getEmptySeries } from '../components/shared/charts/CustomPlot/getEmptySeries'; diff --git a/x-pack/legacy/plugins/apm/public/services/__test__/SessionStorageMock.ts b/x-pack/plugins/apm/public/services/__test__/SessionStorageMock.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/services/__test__/SessionStorageMock.ts rename to x-pack/plugins/apm/public/services/__test__/SessionStorageMock.ts diff --git a/x-pack/legacy/plugins/apm/public/services/__test__/callApi.test.ts b/x-pack/plugins/apm/public/services/__test__/callApi.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/services/__test__/callApi.test.ts rename to x-pack/plugins/apm/public/services/__test__/callApi.test.ts diff --git a/x-pack/legacy/plugins/apm/public/services/__test__/callApmApi.test.ts b/x-pack/plugins/apm/public/services/__test__/callApmApi.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/services/__test__/callApmApi.test.ts rename to x-pack/plugins/apm/public/services/__test__/callApmApi.test.ts diff --git a/x-pack/legacy/plugins/apm/public/services/rest/callApi.ts b/x-pack/plugins/apm/public/services/rest/callApi.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/services/rest/callApi.ts rename to x-pack/plugins/apm/public/services/rest/callApi.ts diff --git a/x-pack/legacy/plugins/apm/public/services/rest/createCallApmApi.ts b/x-pack/plugins/apm/public/services/rest/createCallApmApi.ts similarity index 89% rename from x-pack/legacy/plugins/apm/public/services/rest/createCallApmApi.ts rename to x-pack/plugins/apm/public/services/rest/createCallApmApi.ts index 2fffb40d353fc7..1027e8b885d711 100644 --- a/x-pack/legacy/plugins/apm/public/services/rest/createCallApmApi.ts +++ b/x-pack/plugins/apm/public/services/rest/createCallApmApi.ts @@ -6,9 +6,9 @@ import { HttpSetup } from 'kibana/public'; import { callApi, FetchOptions } from './callApi'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { APMAPI } from '../../../../../../plugins/apm/server/routes/create_apm_api'; +import { APMAPI } from '../../../server/routes/create_apm_api'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { Client } from '../../../../../../plugins/apm/server/routes/typings'; +import { Client } from '../../../server/routes/typings'; export type APMClient = Client; export type APMClientOptions = Omit & { diff --git a/x-pack/legacy/plugins/apm/public/services/rest/index_pattern.ts b/x-pack/plugins/apm/public/services/rest/index_pattern.ts similarity index 76% rename from x-pack/legacy/plugins/apm/public/services/rest/index_pattern.ts rename to x-pack/plugins/apm/public/services/rest/index_pattern.ts index 1efcc98bbbd665..ac7a0d3cf734bf 100644 --- a/x-pack/legacy/plugins/apm/public/services/rest/index_pattern.ts +++ b/x-pack/plugins/apm/public/services/rest/index_pattern.ts @@ -12,3 +12,9 @@ export const createStaticIndexPattern = async () => { pathname: '/api/apm/index_pattern/static' }); }; + +export const getApmIndexPatternTitle = async () => { + return await callApmApi({ + pathname: '/api/apm/index_pattern/title' + }); +}; diff --git a/x-pack/legacy/plugins/apm/public/services/rest/ml.ts b/x-pack/plugins/apm/public/services/rest/ml.ts similarity index 91% rename from x-pack/legacy/plugins/apm/public/services/rest/ml.ts rename to x-pack/plugins/apm/public/services/rest/ml.ts index 0cd1bdf9075314..b333a08d2eb054 100644 --- a/x-pack/legacy/plugins/apm/public/services/rest/ml.ts +++ b/x-pack/plugins/apm/public/services/rest/ml.ts @@ -9,14 +9,14 @@ import { PROCESSOR_EVENT, SERVICE_NAME, TRANSACTION_TYPE -} from '../../../../../../plugins/apm/common/elasticsearch_fieldnames'; +} from '../../../common/elasticsearch_fieldnames'; import { getMlJobId, getMlPrefix, encodeForMlApi -} from '../../../../../../plugins/apm/common/ml_job_constants'; +} from '../../../common/ml_job_constants'; import { callApi } from './callApi'; -import { ESFilter } from '../../../../../../plugins/apm/typings/elasticsearch'; +import { ESFilter } from '../../../typings/elasticsearch'; import { callApmApi } from './createCallApmApi'; interface MlResponseItem { diff --git a/x-pack/legacy/plugins/apm/public/services/rest/watcher.ts b/x-pack/plugins/apm/public/services/rest/watcher.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/services/rest/watcher.ts rename to x-pack/plugins/apm/public/services/rest/watcher.ts diff --git a/x-pack/legacy/plugins/apm/public/new-platform/setHelpExtension.ts b/x-pack/plugins/apm/public/setHelpExtension.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/new-platform/setHelpExtension.ts rename to x-pack/plugins/apm/public/setHelpExtension.ts diff --git a/x-pack/legacy/plugins/apm/public/style/variables.ts b/x-pack/plugins/apm/public/style/variables.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/style/variables.ts rename to x-pack/plugins/apm/public/style/variables.ts diff --git a/x-pack/legacy/plugins/apm/public/new-platform/toggleAppLinkInNav.ts b/x-pack/plugins/apm/public/toggleAppLinkInNav.ts similarity index 91% rename from x-pack/legacy/plugins/apm/public/new-platform/toggleAppLinkInNav.ts rename to x-pack/plugins/apm/public/toggleAppLinkInNav.ts index c807cebf975253..8204e1a022d7e1 100644 --- a/x-pack/legacy/plugins/apm/public/new-platform/toggleAppLinkInNav.ts +++ b/x-pack/plugins/apm/public/toggleAppLinkInNav.ts @@ -5,7 +5,7 @@ */ import { CoreStart } from 'kibana/public'; -import { ConfigSchema } from './plugin'; +import { ConfigSchema } from '.'; export function toggleAppLinkInNav(core: CoreStart, { ui }: ConfigSchema) { if (ui.enabled === false) { diff --git a/x-pack/legacy/plugins/apm/public/new-platform/updateBadge.ts b/x-pack/plugins/apm/public/updateBadge.ts similarity index 99% rename from x-pack/legacy/plugins/apm/public/new-platform/updateBadge.ts rename to x-pack/plugins/apm/public/updateBadge.ts index b3e29bb891c23e..10849754313c49 100644 --- a/x-pack/legacy/plugins/apm/public/new-platform/updateBadge.ts +++ b/x-pack/plugins/apm/public/updateBadge.ts @@ -10,7 +10,6 @@ import { CoreStart } from 'kibana/public'; export function setReadonlyBadge({ application, chrome }: CoreStart) { const canSave = application.capabilities.apm.save; const { setBadge } = chrome; - setBadge( !canSave ? { diff --git a/x-pack/legacy/plugins/apm/public/utils/__test__/flattenObject.test.ts b/x-pack/plugins/apm/public/utils/__test__/flattenObject.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/utils/__test__/flattenObject.test.ts rename to x-pack/plugins/apm/public/utils/__test__/flattenObject.test.ts diff --git a/x-pack/legacy/plugins/apm/public/utils/flattenObject.ts b/x-pack/plugins/apm/public/utils/flattenObject.ts similarity index 94% rename from x-pack/legacy/plugins/apm/public/utils/flattenObject.ts rename to x-pack/plugins/apm/public/utils/flattenObject.ts index 020bfec2cbd6a6..295ea1f9f900fd 100644 --- a/x-pack/legacy/plugins/apm/public/utils/flattenObject.ts +++ b/x-pack/plugins/apm/public/utils/flattenObject.ts @@ -5,7 +5,7 @@ */ import { compact, isObject } from 'lodash'; -import { Maybe } from '../../../../../plugins/apm/typings/common'; +import { Maybe } from '../../typings/common'; export interface KeyValuePair { key: string; diff --git a/x-pack/legacy/plugins/apm/public/utils/formatters/__test__/datetime.test.ts b/x-pack/plugins/apm/public/utils/formatters/__test__/datetime.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/utils/formatters/__test__/datetime.test.ts rename to x-pack/plugins/apm/public/utils/formatters/__test__/datetime.test.ts diff --git a/x-pack/legacy/plugins/apm/public/utils/formatters/__test__/duration.test.ts b/x-pack/plugins/apm/public/utils/formatters/__test__/duration.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/utils/formatters/__test__/duration.test.ts rename to x-pack/plugins/apm/public/utils/formatters/__test__/duration.test.ts diff --git a/x-pack/legacy/plugins/apm/public/utils/formatters/__test__/formatters.test.ts b/x-pack/plugins/apm/public/utils/formatters/__test__/formatters.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/utils/formatters/__test__/formatters.test.ts rename to x-pack/plugins/apm/public/utils/formatters/__test__/formatters.test.ts diff --git a/x-pack/legacy/plugins/apm/public/utils/formatters/__test__/size.test.ts b/x-pack/plugins/apm/public/utils/formatters/__test__/size.test.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/utils/formatters/__test__/size.test.ts rename to x-pack/plugins/apm/public/utils/formatters/__test__/size.test.ts diff --git a/x-pack/legacy/plugins/apm/public/utils/formatters/datetime.ts b/x-pack/plugins/apm/public/utils/formatters/datetime.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/utils/formatters/datetime.ts rename to x-pack/plugins/apm/public/utils/formatters/datetime.ts diff --git a/x-pack/legacy/plugins/apm/public/utils/formatters/duration.ts b/x-pack/plugins/apm/public/utils/formatters/duration.ts similarity index 96% rename from x-pack/legacy/plugins/apm/public/utils/formatters/duration.ts rename to x-pack/plugins/apm/public/utils/formatters/duration.ts index 681d876ca3beb7..39341e1ff4443b 100644 --- a/x-pack/legacy/plugins/apm/public/utils/formatters/duration.ts +++ b/x-pack/plugins/apm/public/utils/formatters/duration.ts @@ -7,10 +7,10 @@ import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { memoize } from 'lodash'; -import { NOT_AVAILABLE_LABEL } from '../../../../../../plugins/apm/common/i18n'; +import { NOT_AVAILABLE_LABEL } from '../../../common/i18n'; import { asDecimal, asInteger } from './formatters'; import { TimeUnit } from './datetime'; -import { Maybe } from '../../../../../../plugins/apm/typings/common'; +import { Maybe } from '../../../typings/common'; interface FormatterOptions { defaultValue?: string; diff --git a/x-pack/legacy/plugins/apm/public/utils/formatters/formatters.ts b/x-pack/plugins/apm/public/utils/formatters/formatters.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/utils/formatters/formatters.ts rename to x-pack/plugins/apm/public/utils/formatters/formatters.ts diff --git a/x-pack/legacy/plugins/apm/public/utils/formatters/index.ts b/x-pack/plugins/apm/public/utils/formatters/index.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/utils/formatters/index.ts rename to x-pack/plugins/apm/public/utils/formatters/index.ts diff --git a/x-pack/legacy/plugins/apm/public/utils/formatters/size.ts b/x-pack/plugins/apm/public/utils/formatters/size.ts similarity index 95% rename from x-pack/legacy/plugins/apm/public/utils/formatters/size.ts rename to x-pack/plugins/apm/public/utils/formatters/size.ts index 8fe6ebf3e573da..2cdf8af1d46de8 100644 --- a/x-pack/legacy/plugins/apm/public/utils/formatters/size.ts +++ b/x-pack/plugins/apm/public/utils/formatters/size.ts @@ -5,7 +5,7 @@ */ import { memoize } from 'lodash'; import { asDecimal } from './formatters'; -import { Maybe } from '../../../../../../plugins/apm/typings/common'; +import { Maybe } from '../../../typings/common'; function asKilobytes(value: number) { return `${asDecimal(value / 1000)} KB`; diff --git a/x-pack/legacy/plugins/apm/public/utils/getRangeFromTimeSeries.ts b/x-pack/plugins/apm/public/utils/getRangeFromTimeSeries.ts similarity index 88% rename from x-pack/legacy/plugins/apm/public/utils/getRangeFromTimeSeries.ts rename to x-pack/plugins/apm/public/utils/getRangeFromTimeSeries.ts index 4301ead2fc79ff..1865d5ae574a7d 100644 --- a/x-pack/legacy/plugins/apm/public/utils/getRangeFromTimeSeries.ts +++ b/x-pack/plugins/apm/public/utils/getRangeFromTimeSeries.ts @@ -5,7 +5,7 @@ */ import { flatten } from 'lodash'; -import { TimeSeries } from '../../../../../plugins/apm/typings/timeseries'; +import { TimeSeries } from '../../typings/timeseries'; export const getRangeFromTimeSeries = (timeseries: TimeSeries[]) => { const dataPoints = flatten(timeseries.map(series => series.data)); diff --git a/x-pack/legacy/plugins/apm/public/utils/history.ts b/x-pack/plugins/apm/public/utils/history.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/utils/history.ts rename to x-pack/plugins/apm/public/utils/history.ts diff --git a/x-pack/legacy/plugins/apm/public/utils/httpStatusCodeToColor.ts b/x-pack/plugins/apm/public/utils/httpStatusCodeToColor.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/utils/httpStatusCodeToColor.ts rename to x-pack/plugins/apm/public/utils/httpStatusCodeToColor.ts diff --git a/x-pack/legacy/plugins/apm/public/utils/isValidCoordinateValue.ts b/x-pack/plugins/apm/public/utils/isValidCoordinateValue.ts similarity index 84% rename from x-pack/legacy/plugins/apm/public/utils/isValidCoordinateValue.ts rename to x-pack/plugins/apm/public/utils/isValidCoordinateValue.ts index f7c13603c3535a..c36efc232b7824 100644 --- a/x-pack/legacy/plugins/apm/public/utils/isValidCoordinateValue.ts +++ b/x-pack/plugins/apm/public/utils/isValidCoordinateValue.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Maybe } from '../../../../../plugins/apm/typings/common'; +import { Maybe } from '../../typings/common'; export const isValidCoordinateValue = (value: Maybe): value is number => value !== null && value !== undefined; diff --git a/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx similarity index 96% rename from x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx rename to x-pack/plugins/apm/public/utils/testHelpers.tsx index 36c0e18777bfdf..def41a1cabd614 100644 --- a/x-pack/legacy/plugins/apm/public/utils/testHelpers.tsx +++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx @@ -16,14 +16,14 @@ import { render, waitForElement } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import { MemoryRouter } from 'react-router-dom'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { APMConfig } from '../../../../../plugins/apm/server'; +import { APMConfig } from '../../server'; import { LocationProvider } from '../context/LocationContext'; -import { PromiseReturnType } from '../../../../../plugins/apm/typings/common'; +import { PromiseReturnType } from '../../typings/common'; import { ESFilter, ESSearchResponse, ESSearchRequest -} from '../../../../../plugins/apm/typings/elasticsearch'; +} from '../../typings/elasticsearch'; import { MockApmPluginContextWrapper } from '../context/ApmPluginContext/MockApmPluginContext'; export function toJson(wrapper: ReactWrapper) { diff --git a/x-pack/legacy/plugins/apm/readme.md b/x-pack/plugins/apm/readme.md similarity index 92% rename from x-pack/legacy/plugins/apm/readme.md rename to x-pack/plugins/apm/readme.md index e8e2514c83fcb2..62465e920d793c 100644 --- a/x-pack/legacy/plugins/apm/readme.md +++ b/x-pack/plugins/apm/readme.md @@ -32,7 +32,7 @@ _Docker Compose is required_ ### E2E (Cypress) tests ```sh -x-pack/legacy/plugins/apm/e2e/run-e2e.sh +x-pack/plugins/apm/e2e/run-e2e.sh ``` _Starts Kibana (:5701), APM Server (:8201) and Elasticsearch (:9201). Ingests sample data into Elasticsearch via APM Server and runs the Cypress tests_ @@ -94,13 +94,13 @@ _Note: Run the following commands from `kibana/`._ #### Prettier ``` -yarn prettier "./x-pack/legacy/plugins/apm/**/*.{tsx,ts,js}" --write +yarn prettier "./x-pack/plugins/apm/**/*.{tsx,ts,js}" --write ``` #### ESLint ``` -yarn eslint ./x-pack/legacy/plugins/apm --fix +yarn eslint ./x-pack/plugins/apm --fix ``` ### Setup default APM users @@ -117,7 +117,7 @@ For testing purposes APM uses 3 custom users: To create the users with the correct roles run the following script: ```sh -node x-pack/legacy/plugins/apm/scripts/setup-kibana-security.js --role-suffix +node x-pack/plugins/apm/scripts/setup-kibana-security.js --role-suffix ``` The users will be created with the password specified in kibana.dev.yml for `elasticsearch.password` diff --git a/x-pack/legacy/plugins/apm/scripts/.gitignore b/x-pack/plugins/apm/scripts/.gitignore similarity index 100% rename from x-pack/legacy/plugins/apm/scripts/.gitignore rename to x-pack/plugins/apm/scripts/.gitignore diff --git a/x-pack/legacy/plugins/apm/scripts/kibana-security/setup-custom-kibana-user-role.ts b/x-pack/plugins/apm/scripts/kibana-security/setup-custom-kibana-user-role.ts similarity index 100% rename from x-pack/legacy/plugins/apm/scripts/kibana-security/setup-custom-kibana-user-role.ts rename to x-pack/plugins/apm/scripts/kibana-security/setup-custom-kibana-user-role.ts diff --git a/x-pack/legacy/plugins/apm/scripts/optimize-tsconfig.js b/x-pack/plugins/apm/scripts/optimize-tsconfig.js similarity index 100% rename from x-pack/legacy/plugins/apm/scripts/optimize-tsconfig.js rename to x-pack/plugins/apm/scripts/optimize-tsconfig.js diff --git a/x-pack/legacy/plugins/apm/scripts/optimize-tsconfig/optimize.js b/x-pack/plugins/apm/scripts/optimize-tsconfig/optimize.js similarity index 100% rename from x-pack/legacy/plugins/apm/scripts/optimize-tsconfig/optimize.js rename to x-pack/plugins/apm/scripts/optimize-tsconfig/optimize.js diff --git a/x-pack/legacy/plugins/apm/scripts/optimize-tsconfig/paths.js b/x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js similarity index 100% rename from x-pack/legacy/plugins/apm/scripts/optimize-tsconfig/paths.js rename to x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js diff --git a/x-pack/legacy/plugins/apm/scripts/optimize-tsconfig/tsconfig.json b/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json similarity index 60% rename from x-pack/legacy/plugins/apm/scripts/optimize-tsconfig/tsconfig.json rename to x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json index 8f6b0f35e4b52d..5e05d3962eccb3 100644 --- a/x-pack/legacy/plugins/apm/scripts/optimize-tsconfig/tsconfig.json +++ b/x-pack/plugins/apm/scripts/optimize-tsconfig/tsconfig.json @@ -1,11 +1,10 @@ { "include": [ "./plugins/apm/**/*", - "./legacy/plugins/apm/**/*", "./typings/**/*" ], "exclude": [ "**/__fixtures__/**/*", - "./legacy/plugins/apm/e2e/cypress/**/*" + "./plugins/apm/e2e/cypress/**/*" ] } diff --git a/x-pack/legacy/plugins/apm/scripts/optimize-tsconfig/unoptimize.js b/x-pack/plugins/apm/scripts/optimize-tsconfig/unoptimize.js similarity index 100% rename from x-pack/legacy/plugins/apm/scripts/optimize-tsconfig/unoptimize.js rename to x-pack/plugins/apm/scripts/optimize-tsconfig/unoptimize.js diff --git a/x-pack/legacy/plugins/apm/scripts/package.json b/x-pack/plugins/apm/scripts/package.json similarity index 100% rename from x-pack/legacy/plugins/apm/scripts/package.json rename to x-pack/plugins/apm/scripts/package.json diff --git a/x-pack/legacy/plugins/apm/scripts/setup-kibana-security.js b/x-pack/plugins/apm/scripts/setup-kibana-security.js similarity index 100% rename from x-pack/legacy/plugins/apm/scripts/setup-kibana-security.js rename to x-pack/plugins/apm/scripts/setup-kibana-security.js diff --git a/x-pack/legacy/plugins/apm/scripts/storybook.js b/x-pack/plugins/apm/scripts/storybook.js similarity index 100% rename from x-pack/legacy/plugins/apm/scripts/storybook.js rename to x-pack/plugins/apm/scripts/storybook.js diff --git a/x-pack/legacy/plugins/apm/scripts/unoptimize-tsconfig.js b/x-pack/plugins/apm/scripts/unoptimize-tsconfig.js similarity index 100% rename from x-pack/legacy/plugins/apm/scripts/unoptimize-tsconfig.js rename to x-pack/plugins/apm/scripts/unoptimize-tsconfig.js diff --git a/x-pack/legacy/plugins/apm/scripts/upload-telemetry-data.js b/x-pack/plugins/apm/scripts/upload-telemetry-data.js similarity index 100% rename from x-pack/legacy/plugins/apm/scripts/upload-telemetry-data.js rename to x-pack/plugins/apm/scripts/upload-telemetry-data.js diff --git a/x-pack/legacy/plugins/apm/scripts/upload-telemetry-data/download-telemetry-template.ts b/x-pack/plugins/apm/scripts/upload-telemetry-data/download-telemetry-template.ts similarity index 100% rename from x-pack/legacy/plugins/apm/scripts/upload-telemetry-data/download-telemetry-template.ts rename to x-pack/plugins/apm/scripts/upload-telemetry-data/download-telemetry-template.ts diff --git a/x-pack/legacy/plugins/apm/scripts/upload-telemetry-data/generate-sample-documents.ts b/x-pack/plugins/apm/scripts/upload-telemetry-data/generate-sample-documents.ts similarity index 97% rename from x-pack/legacy/plugins/apm/scripts/upload-telemetry-data/generate-sample-documents.ts rename to x-pack/plugins/apm/scripts/upload-telemetry-data/generate-sample-documents.ts index 8d76063a7fdf6a..390609996874b2 100644 --- a/x-pack/legacy/plugins/apm/scripts/upload-telemetry-data/generate-sample-documents.ts +++ b/x-pack/plugins/apm/scripts/upload-telemetry-data/generate-sample-documents.ts @@ -18,7 +18,7 @@ import { CollectTelemetryParams, collectDataTelemetry // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../plugins/apm/server/lib/apm_telemetry/collect_data_telemetry'; +} from '../../server/lib/apm_telemetry/collect_data_telemetry'; interface GenerateOptions { days: number; diff --git a/x-pack/legacy/plugins/apm/scripts/upload-telemetry-data/index.ts b/x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts similarity index 98% rename from x-pack/legacy/plugins/apm/scripts/upload-telemetry-data/index.ts rename to x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts index bdc57eac412fcd..4f69a3a3bd2138 100644 --- a/x-pack/legacy/plugins/apm/scripts/upload-telemetry-data/index.ts +++ b/x-pack/plugins/apm/scripts/upload-telemetry-data/index.ts @@ -25,7 +25,7 @@ import { Logger } from 'kibana/server'; // @ts-ignore import consoleStamp from 'console-stamp'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { CollectTelemetryParams } from '../../../../../plugins/apm/server/lib/apm_telemetry/collect_data_telemetry'; +import { CollectTelemetryParams } from '../../server/lib/apm_telemetry/collect_data_telemetry'; import { downloadTelemetryTemplate } from './download-telemetry-template'; import mapping from '../../mappings.json'; import { generateSampleDocuments } from './generate-sample-documents'; diff --git a/x-pack/plugins/apm/server/feature.ts b/x-pack/plugins/apm/server/feature.ts new file mode 100644 index 00000000000000..ae0f5510cd80e8 --- /dev/null +++ b/x-pack/plugins/apm/server/feature.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const APM_FEATURE = { + id: 'apm', + name: i18n.translate('xpack.apm.featureRegistry.apmFeatureName', { + defaultMessage: 'APM' + }), + order: 900, + icon: 'apmApp', + navLinkId: 'apm', + app: ['apm', 'kibana'], + catalogue: ['apm'], + // see x-pack/plugins/features/common/feature_kibana_privileges.ts + privileges: { + all: { + app: ['apm', 'kibana'], + api: [ + 'apm', + 'apm_write', + 'actions-read', + 'actions-all', + 'alerting-read', + 'alerting-all' + ], + catalogue: ['apm'], + savedObject: { + all: ['alert', 'action', 'action_task_params'], + read: [] + }, + ui: [ + 'show', + 'save', + 'alerting:show', + 'actions:show', + 'alerting:save', + 'actions:save', + 'alerting:delete', + 'actions:delete' + ] + }, + read: { + app: ['apm', 'kibana'], + api: [ + 'apm', + 'actions-read', + 'actions-all', + 'alerting-read', + 'alerting-all' + ], + catalogue: ['apm'], + savedObject: { + all: ['alert', 'action', 'action_task_params'], + read: [] + }, + ui: [ + 'show', + 'alerting:show', + 'actions:show', + 'alerting:save', + 'actions:save', + 'alerting:delete', + 'actions:delete' + ] + } + } +}; diff --git a/x-pack/plugins/apm/server/index.ts b/x-pack/plugins/apm/server/index.ts index 77655568a7e9cd..90090087906312 100644 --- a/x-pack/plugins/apm/server/index.ts +++ b/x-pack/plugins/apm/server/index.ts @@ -73,4 +73,4 @@ export type APMConfig = ReturnType; export const plugin = (initContext: PluginInitializerContext) => new APMPlugin(initContext); -export { APMPlugin, APMPluginContract } from './plugin'; +export { APMPlugin, APMPluginSetup } from './plugin'; diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/queries.test.ts b/x-pack/plugins/apm/server/lib/errors/distribution/queries.test.ts index 0d539a09f091bb..fcc456c6533038 100644 --- a/x-pack/plugins/apm/server/lib/errors/distribution/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/errors/distribution/queries.test.ts @@ -8,7 +8,7 @@ import { getErrorDistribution } from './get_distribution'; import { SearchParamsMock, inspectSearchParams -} from '../../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../../public/utils/testHelpers'; describe('error distribution queries', () => { let mock: SearchParamsMock; diff --git a/x-pack/plugins/apm/server/lib/errors/queries.test.ts b/x-pack/plugins/apm/server/lib/errors/queries.test.ts index 5b063c2fb2b61a..f1e5d31efd4bdb 100644 --- a/x-pack/plugins/apm/server/lib/errors/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/errors/queries.test.ts @@ -9,7 +9,7 @@ import { getErrorGroups } from './get_error_groups'; import { SearchParamsMock, inspectSearchParams -} from '../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../public/utils/testHelpers'; describe('error queries', () => { let mock: SearchParamsMock; diff --git a/x-pack/plugins/apm/server/lib/helpers/es_client.ts b/x-pack/plugins/apm/server/lib/helpers/es_client.ts index c22084dbb71681..de9ab87b69fc85 100644 --- a/x-pack/plugins/apm/server/lib/helpers/es_client.ts +++ b/x-pack/plugins/apm/server/lib/helpers/es_client.ts @@ -20,7 +20,7 @@ import { ESSearchResponse } from '../../../typings/elasticsearch'; import { OBSERVER_VERSION_MAJOR } from '../../../common/elasticsearch_fieldnames'; -import { pickKeys } from '../../../../../legacy/plugins/apm/public/utils/pickKeys'; +import { pickKeys } from '../../../common/utils/pick_keys'; import { APMRequestHandlerContext } from '../../routes/typings'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts index 8e8cf698a84cfa..40a2a0e7216a09 100644 --- a/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts +++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.test.ts @@ -39,19 +39,6 @@ function getMockRequest() { _debug: false } }, - __LEGACY: { - server: { - plugins: { - elasticsearch: { - getCluster: jest.fn().mockReturnValue({ callWithInternalUser: {} }) - } - }, - savedObjects: { - SavedObjectsClient: jest.fn(), - getSavedObjectsRepository: jest.fn() - } - } - }, core: { elasticsearch: { dataClient: { diff --git a/x-pack/plugins/apm/server/lib/index_pattern/get_apm_index_pattern_title.ts b/x-pack/plugins/apm/server/lib/index_pattern/get_apm_index_pattern_title.ts new file mode 100644 index 00000000000000..b3b40bac7bd544 --- /dev/null +++ b/x-pack/plugins/apm/server/lib/index_pattern/get_apm_index_pattern_title.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { APMRequestHandlerContext } from '../../routes/typings'; + +export function getApmIndexPatternTitle(context: APMRequestHandlerContext) { + return context.config['apm_oss.indexPattern']; +} diff --git a/x-pack/plugins/apm/server/lib/metrics/queries.test.ts b/x-pack/plugins/apm/server/lib/metrics/queries.test.ts index ac4e13ae442cfd..f276fa69e20e39 100644 --- a/x-pack/plugins/apm/server/lib/metrics/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/metrics/queries.test.ts @@ -12,7 +12,7 @@ import { getThreadCountChart } from './by_agent/java/thread_count'; import { SearchParamsMock, inspectSearchParams -} from '../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../public/utils/testHelpers'; import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes'; describe('metrics queries', () => { diff --git a/x-pack/plugins/apm/server/lib/service_nodes/queries.test.ts b/x-pack/plugins/apm/server/lib/service_nodes/queries.test.ts index 672d568b3a5e3e..80cd94b1549d7a 100644 --- a/x-pack/plugins/apm/server/lib/service_nodes/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/service_nodes/queries.test.ts @@ -13,7 +13,7 @@ import { getServiceNodes } from './'; import { SearchParamsMock, inspectSearchParams -} from '../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../public/utils/testHelpers'; import { getServiceNodeMetadata } from '../services/get_service_node_metadata'; import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes'; diff --git a/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts b/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts index 0e52982c6de283..614014ee37afce 100644 --- a/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts +++ b/x-pack/plugins/apm/server/lib/services/annotations/index.test.ts @@ -7,7 +7,7 @@ import { getServiceAnnotations } from '.'; import { SearchParamsMock, inspectSearchParams -} from '../../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../../public/utils/testHelpers'; import noVersions from './__fixtures__/no_versions.json'; import oneVersion from './__fixtures__/one_version.json'; import multipleVersions from './__fixtures__/multiple_versions.json'; diff --git a/x-pack/plugins/apm/server/lib/services/queries.test.ts b/x-pack/plugins/apm/server/lib/services/queries.test.ts index e75d9ad05648c2..16490ace777449 100644 --- a/x-pack/plugins/apm/server/lib/services/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/services/queries.test.ts @@ -12,7 +12,7 @@ import { hasHistoricalAgentData } from './get_services/has_historical_agent_data import { SearchParamsMock, inspectSearchParams -} from '../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../public/utils/testHelpers'; describe('services queries', () => { let mock: SearchParamsMock; diff --git a/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts b/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts index b951b7f350eed4..a1a915e6a84a56 100644 --- a/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/settings/agent_configuration/queries.test.ts @@ -12,7 +12,7 @@ import { searchConfigurations } from './search_configurations'; import { SearchParamsMock, inspectSearchParams -} from '../../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../../public/utils/testHelpers'; import { findExactConfiguration } from './find_exact_configuration'; describe('agent configuration queries', () => { diff --git a/x-pack/plugins/apm/server/lib/settings/custom_link/create_or_update_custom_link.test.ts b/x-pack/plugins/apm/server/lib/settings/custom_link/create_or_update_custom_link.test.ts index 9bd6c78797605a..5d0bf329368f71 100644 --- a/x-pack/plugins/apm/server/lib/settings/custom_link/create_or_update_custom_link.test.ts +++ b/x-pack/plugins/apm/server/lib/settings/custom_link/create_or_update_custom_link.test.ts @@ -5,7 +5,7 @@ */ import { Setup } from '../../helpers/setup_request'; -import { mockNow } from '../../../../../../legacy/plugins/apm/public/utils/testHelpers'; +import { mockNow } from '../../../../public/utils/testHelpers'; import { CustomLink } from '../../../../common/custom_link/custom_link_types'; import { createOrUpdateCustomLink } from './create_or_update_custom_link'; diff --git a/x-pack/plugins/apm/server/lib/settings/custom_link/get_transaction.test.ts b/x-pack/plugins/apm/server/lib/settings/custom_link/get_transaction.test.ts index 514e473b8e78cc..0254660e3523f4 100644 --- a/x-pack/plugins/apm/server/lib/settings/custom_link/get_transaction.test.ts +++ b/x-pack/plugins/apm/server/lib/settings/custom_link/get_transaction.test.ts @@ -6,7 +6,7 @@ import { inspectSearchParams, SearchParamsMock -} from '../../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../../public/utils/testHelpers'; import { getTransaction } from './get_transaction'; import { Setup } from '../../helpers/setup_request'; import { diff --git a/x-pack/plugins/apm/server/lib/settings/custom_link/list_custom_links.test.ts b/x-pack/plugins/apm/server/lib/settings/custom_link/list_custom_links.test.ts index 45610e36786b78..6a67c30bee197a 100644 --- a/x-pack/plugins/apm/server/lib/settings/custom_link/list_custom_links.test.ts +++ b/x-pack/plugins/apm/server/lib/settings/custom_link/list_custom_links.test.ts @@ -8,7 +8,7 @@ import { listCustomLinks } from './list_custom_links'; import { inspectSearchParams, SearchParamsMock -} from '../../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../../public/utils/testHelpers'; import { Setup } from '../../helpers/setup_request'; import { SERVICE_NAME, diff --git a/x-pack/plugins/apm/server/lib/traces/queries.test.ts b/x-pack/plugins/apm/server/lib/traces/queries.test.ts index 0c2ac4d0f92017..871d0fd1c7fb6b 100644 --- a/x-pack/plugins/apm/server/lib/traces/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/traces/queries.test.ts @@ -8,7 +8,7 @@ import { getTraceItems } from './get_trace_items'; import { SearchParamsMock, inspectSearchParams -} from '../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../public/utils/testHelpers'; describe('trace queries', () => { let mock: SearchParamsMock; diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/queries.test.ts b/x-pack/plugins/apm/server/lib/transaction_groups/queries.test.ts index 8d6495c2e0b5fa..73122d8580134f 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/queries.test.ts @@ -8,7 +8,7 @@ import { transactionGroupsFetcher } from './fetcher'; import { SearchParamsMock, inspectSearchParams -} from '../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../public/utils/testHelpers'; describe('transaction group queries', () => { let mock: SearchParamsMock; diff --git a/x-pack/plugins/apm/server/lib/transactions/queries.test.ts b/x-pack/plugins/apm/server/lib/transactions/queries.test.ts index 116738da5ef9b1..a9e4204fde1ad9 100644 --- a/x-pack/plugins/apm/server/lib/transactions/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/transactions/queries.test.ts @@ -11,7 +11,7 @@ import { getTransaction } from './get_transaction'; import { SearchParamsMock, inspectSearchParams -} from '../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../public/utils/testHelpers'; describe('transaction queries', () => { let mock: SearchParamsMock; diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts index 21cc35da72cb90..b72186f528f287 100644 --- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts @@ -8,7 +8,7 @@ import { getLocalUIFilters } from './'; import { SearchParamsMock, inspectSearchParams -} from '../../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../../public/utils/testHelpers'; import { getServicesProjection } from '../../../../common/projections/services'; describe('local ui filter queries', () => { diff --git a/x-pack/plugins/apm/server/lib/ui_filters/queries.test.ts b/x-pack/plugins/apm/server/lib/ui_filters/queries.test.ts index 63c8c3e494bb04..079ab64f32db36 100644 --- a/x-pack/plugins/apm/server/lib/ui_filters/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/ui_filters/queries.test.ts @@ -8,7 +8,7 @@ import { getEnvironments } from './get_environments'; import { SearchParamsMock, inspectSearchParams -} from '../../../../../legacy/plugins/apm/public/utils/testHelpers'; +} from '../../../public/utils/testHelpers'; describe('ui filter queries', () => { let mock: SearchParamsMock; diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index b434d41982f4c0..8cf29de5b8b730 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -10,10 +10,8 @@ import { CoreStart, Logger } from 'src/core/server'; -import { Observable, combineLatest, AsyncSubject } from 'rxjs'; +import { Observable, combineLatest } from 'rxjs'; import { map, take } from 'rxjs/operators'; -import { Server } from 'hapi'; -import { once } from 'lodash'; import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/server'; import { TaskManagerSetupContract } from '../../task_manager/server'; import { AlertingPlugin } from '../../alerting/server'; @@ -31,24 +29,20 @@ import { getInternalSavedObjectsClient } from './lib/helpers/get_internal_saved_ import { LicensingPluginSetup } from '../../licensing/public'; import { registerApmAlerts } from './lib/alerts/register_apm_alerts'; import { createApmTelemetry } from './lib/apm_telemetry'; +import { PluginSetupContract as FeaturesPluginSetup } from '../../../plugins/features/server'; +import { APM_FEATURE } from './feature'; +import { apmIndices, apmTelemetry } from './saved_objects'; -export interface LegacySetup { - server: Server; -} - -export interface APMPluginContract { +export interface APMPluginSetup { config$: Observable; - registerLegacyAPI: (__LEGACY: LegacySetup) => void; getApmIndices: () => ReturnType; } -export class APMPlugin implements Plugin { +export class APMPlugin implements Plugin { private currentConfig?: APMConfig; private logger?: Logger; - legacySetup$: AsyncSubject; constructor(private readonly initContext: PluginInitializerContext) { this.initContext = initContext; - this.legacySetup$ = new AsyncSubject(); } public async setup( @@ -62,6 +56,7 @@ export class APMPlugin implements Plugin { taskManager?: TaskManagerSetupContract; alerting?: AlertingPlugin['setup']; actions?: ActionsPlugin['setup']; + features: FeaturesPluginSetup; } ) { this.logger = this.initContext.logger.get(); @@ -70,6 +65,9 @@ export class APMPlugin implements Plugin { map(([apmOssConfig, apmConfig]) => mergeConfigs(apmOssConfig, apmConfig)) ); + core.savedObjects.registerType(apmIndices); + core.savedObjects.registerType(apmTelemetry); + if (plugins.actions && plugins.alerting) { registerApmAlerts({ alerting: plugins.alerting, @@ -78,14 +76,6 @@ export class APMPlugin implements Plugin { }); } - this.legacySetup$.subscribe(__LEGACY => { - createApmApi().init(core, { - config$: mergedConfig$, - logger: this.logger!, - __LEGACY - }); - }); - this.currentConfig = await mergedConfig$.pipe(take(1)).toPromise(); if ( @@ -116,13 +106,15 @@ export class APMPlugin implements Plugin { } }) ); + plugins.features.registerFeature(APM_FEATURE); + + createApmApi().init(core, { + config$: mergedConfig$, + logger: this.logger! + }); return { config$: mergedConfig$, - registerLegacyAPI: once((__LEGACY: LegacySetup) => { - this.legacySetup$.next(__LEGACY); - this.legacySetup$.complete(); - }), getApmIndices: async () => getApmIndices({ savedObjectsClient: await getInternalSavedObjectsClient(core), diff --git a/x-pack/plugins/apm/server/routes/create_api/index.test.ts b/x-pack/plugins/apm/server/routes/create_api/index.test.ts index 312dae1d1f9d24..20c586868a9792 100644 --- a/x-pack/plugins/apm/server/routes/create_api/index.test.ts +++ b/x-pack/plugins/apm/server/routes/create_api/index.test.ts @@ -9,7 +9,6 @@ import { CoreSetup, Logger } from 'src/core/server'; import { Params } from '../typings'; import { BehaviorSubject } from 'rxjs'; import { APMConfig } from '../..'; -import { LegacySetup } from '../../plugin'; const getCoreMock = () => { const get = jest.fn(); @@ -40,8 +39,7 @@ const getCoreMock = () => { config$: new BehaviorSubject({} as APMConfig), logger: ({ error: jest.fn() - } as unknown) as Logger, - __LEGACY: {} as LegacySetup + } as unknown) as Logger } }; }; diff --git a/x-pack/plugins/apm/server/routes/create_api/index.ts b/x-pack/plugins/apm/server/routes/create_api/index.ts index e216574f8a02e8..a97e2f30fc2b61 100644 --- a/x-pack/plugins/apm/server/routes/create_api/index.ts +++ b/x-pack/plugins/apm/server/routes/create_api/index.ts @@ -30,7 +30,7 @@ export function createApi() { factoryFns.push(fn); return this as any; }, - init(core, { config$, logger, __LEGACY }) { + init(core, { config$, logger }) { const router = core.http.createRouter(); let config = {} as APMConfig; @@ -136,7 +136,6 @@ export function createApi() { request, context: { ...context, - __LEGACY, // Only return values for parameters that have runtime types, // but always include query as _debug is always set even if // it's not defined in the route. diff --git a/x-pack/plugins/apm/server/routes/create_apm_api.ts b/x-pack/plugins/apm/server/routes/create_apm_api.ts index 57b3f282852c48..7964d8b0268e87 100644 --- a/x-pack/plugins/apm/server/routes/create_apm_api.ts +++ b/x-pack/plugins/apm/server/routes/create_apm_api.ts @@ -6,7 +6,8 @@ import { staticIndexPatternRoute, - dynamicIndexPatternRoute + dynamicIndexPatternRoute, + apmIndexPatternTitleRoute } from './index_pattern'; import { errorDistributionRoute, @@ -73,6 +74,7 @@ const createApmApi = () => { // index pattern .add(staticIndexPatternRoute) .add(dynamicIndexPatternRoute) + .add(apmIndexPatternTitleRoute) // Errors .add(errorDistributionRoute) diff --git a/x-pack/plugins/apm/server/routes/index_pattern.ts b/x-pack/plugins/apm/server/routes/index_pattern.ts index b7964dc8e91ed7..e296057203ff15 100644 --- a/x-pack/plugins/apm/server/routes/index_pattern.ts +++ b/x-pack/plugins/apm/server/routes/index_pattern.ts @@ -8,6 +8,7 @@ import { createStaticIndexPattern } from '../lib/index_pattern/create_static_ind import { createRoute } from './create_route'; import { setupRequest } from '../lib/helpers/setup_request'; import { getInternalSavedObjectsClient } from '../lib/helpers/get_internal_saved_objects_client'; +import { getApmIndexPatternTitle } from '../lib/index_pattern/get_apm_index_pattern_title'; export const staticIndexPatternRoute = createRoute(core => ({ method: 'POST', @@ -38,3 +39,10 @@ export const dynamicIndexPatternRoute = createRoute(() => ({ return { dynamicIndexPattern }; } })); + +export const apmIndexPatternTitleRoute = createRoute(() => ({ + path: '/api/apm/index_pattern/title', + handler: async ({ context }) => { + return getApmIndexPatternTitle(context); + } +})); diff --git a/x-pack/plugins/apm/server/routes/typings.ts b/x-pack/plugins/apm/server/routes/typings.ts index 3dc485630c1805..6543f2015599b9 100644 --- a/x-pack/plugins/apm/server/routes/typings.ts +++ b/x-pack/plugins/apm/server/routes/typings.ts @@ -14,7 +14,8 @@ import { import { PickByValue, Optional } from 'utility-types'; import { Observable } from 'rxjs'; import { Server } from 'hapi'; -import { FetchOptions } from '../../../../legacy/plugins/apm/public/services/rest/callApi'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { FetchOptions } from '../../public/services/rest/callApi'; import { APMConfig } from '..'; export interface Params { @@ -61,9 +62,6 @@ export type APMRequestHandlerContext< params: { query: { _debug: boolean } } & TDecodedParams; config: APMConfig; logger: Logger; - __LEGACY: { - server: APMLegacyServer; - }; }; export type RouteFactoryFn< @@ -107,7 +105,6 @@ export interface ServerAPI { context: { config$: Observable; logger: Logger; - __LEGACY: { server: Server }; } ) => void; } diff --git a/x-pack/plugins/apm/server/saved_objects/apm_indices.ts b/x-pack/plugins/apm/server/saved_objects/apm_indices.ts new file mode 100644 index 00000000000000..c641f4546aae7b --- /dev/null +++ b/x-pack/plugins/apm/server/saved_objects/apm_indices.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { SavedObjectsType } from 'src/core/server'; + +export const apmIndices: SavedObjectsType = { + name: 'apm-indices', + hidden: false, + namespaceType: 'agnostic', + mappings: { + properties: { + 'apm_oss.sourcemapIndices': { + type: 'keyword' + }, + 'apm_oss.errorIndices': { + type: 'keyword' + }, + 'apm_oss.onboardingIndices': { + type: 'keyword' + }, + 'apm_oss.spanIndices': { + type: 'keyword' + }, + 'apm_oss.transactionIndices': { + type: 'keyword' + }, + 'apm_oss.metricsIndices': { + type: 'keyword' + } + } + } +}; diff --git a/x-pack/plugins/apm/server/saved_objects/apm_telemetry.ts b/x-pack/plugins/apm/server/saved_objects/apm_telemetry.ts new file mode 100644 index 00000000000000..f711e85076e146 --- /dev/null +++ b/x-pack/plugins/apm/server/saved_objects/apm_telemetry.ts @@ -0,0 +1,921 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { SavedObjectsType } from 'src/core/server'; + +export const apmTelemetry: SavedObjectsType = { + name: 'apm-telemetry', + hidden: false, + namespaceType: 'agnostic', + mappings: { + properties: { + agents: { + properties: { + dotnet: { + properties: { + agent: { + properties: { + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + service: { + properties: { + framework: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + language: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + runtime: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + } + } + } + } + }, + go: { + properties: { + agent: { + properties: { + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + service: { + properties: { + framework: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + language: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + runtime: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + } + } + } + } + }, + java: { + properties: { + agent: { + properties: { + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + service: { + properties: { + framework: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + language: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + runtime: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + } + } + } + } + }, + 'js-base': { + properties: { + agent: { + properties: { + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + service: { + properties: { + framework: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + language: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + runtime: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + } + } + } + } + }, + nodejs: { + properties: { + agent: { + properties: { + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + service: { + properties: { + framework: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + language: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + runtime: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + } + } + } + } + }, + python: { + properties: { + agent: { + properties: { + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + service: { + properties: { + framework: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + language: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + runtime: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + } + } + } + } + }, + ruby: { + properties: { + agent: { + properties: { + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + service: { + properties: { + framework: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + language: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + runtime: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + } + } + } + } + }, + 'rum-js': { + properties: { + agent: { + properties: { + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + service: { + properties: { + framework: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + language: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + }, + runtime: { + properties: { + composite: { + type: 'keyword', + ignore_above: 1024 + }, + name: { + type: 'keyword', + ignore_above: 1024 + }, + version: { + type: 'keyword', + ignore_above: 1024 + } + } + } + } + } + } + } + } + }, + counts: { + properties: { + agent_configuration: { + properties: { + all: { + type: 'long' + } + } + }, + error: { + properties: { + '1d': { + type: 'long' + }, + all: { + type: 'long' + } + } + }, + max_error_groups_per_service: { + properties: { + '1d': { + type: 'long' + } + } + }, + max_transaction_groups_per_service: { + properties: { + '1d': { + type: 'long' + } + } + }, + metric: { + properties: { + '1d': { + type: 'long' + }, + all: { + type: 'long' + } + } + }, + onboarding: { + properties: { + '1d': { + type: 'long' + }, + all: { + type: 'long' + } + } + }, + services: { + properties: { + '1d': { + type: 'long' + } + } + }, + sourcemap: { + properties: { + '1d': { + type: 'long' + }, + all: { + type: 'long' + } + } + }, + span: { + properties: { + '1d': { + type: 'long' + }, + all: { + type: 'long' + } + } + }, + traces: { + properties: { + '1d': { + type: 'long' + } + } + }, + transaction: { + properties: { + '1d': { + type: 'long' + }, + all: { + type: 'long' + } + } + } + } + }, + cardinality: { + properties: { + user_agent: { + properties: { + original: { + properties: { + all_agents: { + properties: { + '1d': { + type: 'long' + } + } + }, + rum: { + properties: { + '1d': { + type: 'long' + } + } + } + } + } + } + }, + transaction: { + properties: { + name: { + properties: { + all_agents: { + properties: { + '1d': { + type: 'long' + } + } + }, + rum: { + properties: { + '1d': { + type: 'long' + } + } + } + } + } + } + } + } + }, + has_any_services: { + type: 'boolean' + }, + indices: { + properties: { + all: { + properties: { + total: { + properties: { + docs: { + properties: { + count: { + type: 'long' + } + } + }, + store: { + properties: { + size_in_bytes: { + type: 'long' + } + } + } + } + } + } + }, + shards: { + properties: { + total: { + type: 'long' + } + } + } + } + }, + integrations: { + properties: { + ml: { + properties: { + all_jobs_count: { + type: 'long' + } + } + } + } + }, + retainment: { + properties: { + error: { + properties: { + ms: { + type: 'long' + } + } + }, + metric: { + properties: { + ms: { + type: 'long' + } + } + }, + onboarding: { + properties: { + ms: { + type: 'long' + } + } + }, + span: { + properties: { + ms: { + type: 'long' + } + } + }, + transaction: { + properties: { + ms: { + type: 'long' + } + } + } + } + }, + services_per_agent: { + properties: { + dotnet: { + type: 'long', + null_value: 0 + }, + go: { + type: 'long', + null_value: 0 + }, + java: { + type: 'long', + null_value: 0 + }, + 'js-base': { + type: 'long', + null_value: 0 + }, + nodejs: { + type: 'long', + null_value: 0 + }, + python: { + type: 'long', + null_value: 0 + }, + ruby: { + type: 'long', + null_value: 0 + }, + 'rum-js': { + type: 'long', + null_value: 0 + } + } + }, + tasks: { + properties: { + agent_configuration: { + properties: { + took: { + properties: { + ms: { + type: 'long' + } + } + } + } + }, + agents: { + properties: { + took: { + properties: { + ms: { + type: 'long' + } + } + } + } + }, + cardinality: { + properties: { + took: { + properties: { + ms: { + type: 'long' + } + } + } + } + }, + groupings: { + properties: { + took: { + properties: { + ms: { + type: 'long' + } + } + } + } + }, + indices_stats: { + properties: { + took: { + properties: { + ms: { + type: 'long' + } + } + } + } + }, + integrations: { + properties: { + took: { + properties: { + ms: { + type: 'long' + } + } + } + } + }, + processor_events: { + properties: { + took: { + properties: { + ms: { + type: 'long' + } + } + } + } + }, + services: { + properties: { + took: { + properties: { + ms: { + type: 'long' + } + } + } + } + }, + versions: { + properties: { + took: { + properties: { + ms: { + type: 'long' + } + } + } + } + } + } + }, + version: { + properties: { + apm_server: { + properties: { + major: { + type: 'long' + }, + minor: { + type: 'long' + }, + patch: { + type: 'long' + } + } + } + } + } + } as SavedObjectsType['mappings']['properties'] + } +}; diff --git a/x-pack/plugins/apm/server/saved_objects/index.ts b/x-pack/plugins/apm/server/saved_objects/index.ts new file mode 100644 index 00000000000000..30c557c526356f --- /dev/null +++ b/x-pack/plugins/apm/server/saved_objects/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; + * you may not use this file except in compliance with the Elastic License. + */ + +export { apmIndices } from './apm_indices'; +export { apmTelemetry } from './apm_telemetry'; diff --git a/x-pack/plugins/apm/typings/common.d.ts b/x-pack/plugins/apm/typings/common.d.ts index bdd2c75f161e91..eeeb85cd1e7c3a 100644 --- a/x-pack/plugins/apm/typings/common.d.ts +++ b/x-pack/plugins/apm/typings/common.d.ts @@ -4,12 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../legacy/plugins/infra/types/rison_node'; -import '../../../legacy/plugins/infra/types/eui'; +import '../../../typings/rison_node'; +import '../../infra/types/eui'; // EUIBasicTable -import '../../../legacy/plugins/reporting/public/components/report_listing'; -// .svg -import '../../../legacy/plugins/canvas/types/webpack'; +import '../../reporting/public/components/report_listing'; // Allow unknown properties in an object export type AllowUnknownProperties = T extends Array diff --git a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx index 206e9821190fb6..a8597b7073c951 100644 --- a/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_entry_flyout/log_entry_actions_menu.tsx @@ -9,7 +9,7 @@ import { EuiButtonEmpty, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } f import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo } from 'react'; import { useVisibilityState } from '../../../utils/use_visibility_state'; -import { getTraceUrl } from '../../../../../../legacy/plugins/apm/public/components/shared/Links/apm/ExternalLinks'; +import { getTraceUrl } from '../../../../../apm/public'; import { LogEntriesItem } from '../../../../common/http_api'; import { useLinkProps, LinkDescriptor } from '../../../hooks/use_link_props'; import { decodeOrThrow } from '../../../../common/runtime_types'; diff --git a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts index 038fd457fb6c73..4bbbf8dcdee03a 100644 --- a/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts +++ b/x-pack/plugins/infra/server/lib/adapters/framework/adapter_types.ts @@ -11,7 +11,7 @@ import { RouteMethod, RouteConfig } from '../../../../../../../src/core/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../plugins/features/server'; import { SpacesPluginSetup } from '../../../../../../plugins/spaces/server'; import { VisTypeTimeseriesSetup } from '../../../../../../../src/plugins/vis_type_timeseries/server'; -import { APMPluginContract } from '../../../../../../plugins/apm/server'; +import { APMPluginSetup } from '../../../../../../plugins/apm/server'; import { HomeServerPluginSetup } from '../../../../../../../src/plugins/home/server'; import { PluginSetupContract as AlertingPluginContract } from '../../../../../../plugins/alerting/server'; @@ -22,7 +22,7 @@ export interface InfraServerPluginDeps { usageCollection: UsageCollectionSetup; visTypeTimeseries: VisTypeTimeseriesSetup; features: FeaturesPluginSetup; - apm: APMPluginContract; + apm: APMPluginSetup; alerting: AlertingPluginContract; } diff --git a/x-pack/plugins/siem/scripts/optimize_tsconfig/README.md b/x-pack/plugins/siem/scripts/optimize_tsconfig/README.md index 2b402367c1db3a..fbcd3329312aab 100644 --- a/x-pack/plugins/siem/scripts/optimize_tsconfig/README.md +++ b/x-pack/plugins/siem/scripts/optimize_tsconfig/README.md @@ -1,5 +1,5 @@ Hard forked from here: -x-pack/legacy/plugins/apm/scripts/optimize-tsconfig.js +x-pack/plugins/apm/scripts/optimize-tsconfig.js #### Optimizing TypeScript diff --git a/x-pack/plugins/siem/server/utils/build_query/calculate_timeseries_interval.ts b/x-pack/plugins/siem/server/utils/build_query/calculate_timeseries_interval.ts index 5b667f461fc602..78aadf75e54c36 100644 --- a/x-pack/plugins/siem/server/utils/build_query/calculate_timeseries_interval.ts +++ b/x-pack/plugins/siem/server/utils/build_query/calculate_timeseries_interval.ts @@ -5,7 +5,7 @@ */ /* ** Applying the same logic as: - ** x-pack/legacy/plugins/apm/server/lib/helpers/get_bucket_size/calculate_auto.js + ** x-pack/plugins/apm/server/lib/helpers/get_bucket_size/calculate_auto.js */ import moment from 'moment'; import { get } from 'lodash/fp'; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 4cd152f2d297d7..28e290c90687b6 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4184,7 +4184,6 @@ "xpack.apm.alertTypes.errorRate": "エラー率", "xpack.apm.alertTypes.transactionDuration": "トランザクション期間", "xpack.apm.apmDescription": "アプリケーション内から自動的に詳細なパフォーマンスメトリックやエラーを集めます。", - "xpack.apm.apmForESDescription": "Elastic Stack 用の APM", "xpack.apm.applyFilter": "{title} フィルターを適用", "xpack.apm.applyOptions": "オプションを適用", "xpack.apm.breadcrumb.errorsTitle": "エラー", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 619ea0646eb44a..321fd533aab6dc 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4185,7 +4185,6 @@ "xpack.apm.alertTypes.errorRate": "错误率", "xpack.apm.alertTypes.transactionDuration": "事务持续时间", "xpack.apm.apmDescription": "自动从您的应用程序内收集深入全面的性能指标和错误。", - "xpack.apm.apmForESDescription": "Elastic Stack 的 APM", "xpack.apm.applyFilter": "应用 {title} 筛选", "xpack.apm.applyOptions": "应用选项", "xpack.apm.breadcrumb.errorsTitle": "错误", diff --git a/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.js b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.js index 936eae04ffa641..282cf1311a7a9d 100644 --- a/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.js +++ b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestion.js @@ -14,7 +14,8 @@ import { units, fontSizes, unit, -} from '../../../../../../../legacy/plugins/apm/public/style/variables'; + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../../apm/public/style/variables'; import { tint } from 'polished'; import theme from '@elastic/eui/dist/eui_theme_light.json'; diff --git a/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.js b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.js index ac6832050b9d3c..0fdf8c3400562e 100644 --- a/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.js +++ b/x-pack/plugins/uptime/public/components/overview/kuery_bar/typeahead/suggestions.js @@ -9,7 +9,8 @@ import PropTypes from 'prop-types'; import styled from 'styled-components'; import { isEmpty } from 'lodash'; import Suggestion from './suggestion'; -import { units, px, unit } from '../../../../../../../legacy/plugins/apm/public/style/variables'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { units, px, unit } from '../../../../../../apm/public/style/variables'; import { tint } from 'polished'; import theme from '@elastic/eui/dist/eui_theme_light.json'; diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json index a540c7e3c97868..81b29732377dab 100644 --- a/x-pack/tsconfig.json +++ b/x-pack/tsconfig.json @@ -12,7 +12,7 @@ "exclude": [ "test/**/*", "plugins/siem/cypress/**/*", - "legacy/plugins/apm/e2e/cypress/**/*", + "plugins/apm/e2e/cypress/**/*", "**/typespec_tests.ts" ], "compilerOptions": { From 47b8ba5d5b80e51cfe7263ef60390463a25a1673 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Fri, 1 May 2020 00:20:00 +0300 Subject: [PATCH 31/47] [SIEM][CASE] Refactor Connectors - Jira Connector (#63450) --- x-pack/plugins/actions/README.md | 60 +- .../server/builtin_action_types/case/api.ts | 93 ++ .../{servicenow => case}/constants.ts | 1 - .../builtin_action_types/case/schema.ts | 98 ++ .../case/transformers.test.ts | 131 +++ .../builtin_action_types/case/transformers.ts | 29 + .../builtin_action_types/case/translations.ts | 55 ++ .../server/builtin_action_types/case/types.ts | 161 ++++ .../helpers.test.ts => case/utils.test.ts} | 319 +++++-- .../server/builtin_action_types/case/utils.ts | 249 +++++ .../builtin_action_types/case/validators.ts | 35 + .../server/builtin_action_types/index.ts | 6 +- .../builtin_action_types/jira/api.test.ts | 517 +++++++++++ .../server/builtin_action_types/jira/api.ts | 7 + .../builtin_action_types/jira/config.ts | 13 + .../server/builtin_action_types/jira/index.ts | 24 + .../server/builtin_action_types/jira/mocks.ts | 124 +++ .../builtin_action_types/jira/schema.ts | 22 + .../builtin_action_types/jira/service.test.ts | 297 ++++++ .../builtin_action_types/jira/service.ts | 156 ++++ .../builtin_action_types/jira/translations.ts | 11 + .../server/builtin_action_types/jira/types.ts | 32 + .../builtin_action_types/jira/validators.ts | 13 + .../servicenow/action_handlers.test.ts | 850 ------------------ .../servicenow/action_handlers.ts | 129 --- .../servicenow/api.test.ts | 523 +++++++++++ .../builtin_action_types/servicenow/api.ts | 7 + .../builtin_action_types/servicenow/config.ts | 13 + .../servicenow/helpers.ts | 125 --- .../servicenow/index.test.ts | 268 ------ .../builtin_action_types/servicenow/index.ts | 124 +-- .../servicenow/lib/constants.ts | 13 - .../servicenow/lib/index.test.ts | 334 ------- .../servicenow/lib/index.ts | 186 ---- .../servicenow/lib/types.ts | 32 - .../builtin_action_types/servicenow/mock.ts | 115 --- .../builtin_action_types/servicenow/mocks.ts | 117 +++ .../builtin_action_types/servicenow/schema.ts | 70 -- .../servicenow/service.test.ts | 255 ++++++ .../servicenow/service.ts | 138 +++ .../servicenow/transformers.ts | 43 - .../servicenow/translations.ts | 73 +- .../builtin_action_types/servicenow/types.ts | 102 +-- .../servicenow/validators.ts | 13 + x-pack/plugins/case/common/api/cases/case.ts | 41 +- x-pack/plugins/case/common/constants.ts | 2 + .../api/cases/configure/get_connectors.ts | 11 +- .../siem/public/containers/case/api.test.tsx | 4 +- .../siem/public/containers/case/api.ts | 4 +- .../siem/public/containers/case/mock.ts | 7 +- .../case/use_post_push_to_service.test.tsx | 4 +- .../case/use_post_push_to_service.tsx | 6 +- .../components/connector_flyout/index.tsx | 148 +++ .../siem/public/lib/connectors/config.ts | 16 +- .../siem/public/lib/connectors/index.ts | 1 + .../siem/public/lib/connectors/jira/config.ts | 20 + .../public/lib/connectors/jira/flyout.tsx | 110 +++ .../siem/public/lib/connectors/jira/index.tsx | 54 ++ .../{logos/servicenow.svg => jira/logo.svg} | 0 .../lib/connectors/jira/translations.ts | 28 + .../siem/public/lib/connectors/jira/types.ts | 18 + .../siem/public/lib/connectors/servicenow.tsx | 246 ----- .../lib/connectors/servicenow/config.ts | 20 + .../lib/connectors/servicenow/flyout.tsx | 83 ++ .../lib/connectors/servicenow/index.tsx | 48 + .../public/lib/connectors/servicenow/logo.svg | 5 + .../lib/connectors/servicenow/translations.ts | 23 + .../public/lib/connectors/servicenow/types.ts | 18 + .../public/lib/connectors/translations.ts | 65 +- .../siem/public/lib/connectors/types.ts | 44 +- .../siem/public/lib/connectors/utils.ts | 71 ++ .../configure_cases/connectors_dropdown.tsx | 4 +- .../components/configure_cases/index.test.tsx | 10 + .../case/components/configure_cases/index.tsx | 13 +- x-pack/plugins/siem/public/plugin.tsx | 3 +- .../translations/translations/ja-JP.json | 18 - .../translations/translations/zh-CN.json | 18 - .../alerting_api_integration/common/config.ts | 1 + .../plugins/actions_simulators/index.ts | 9 +- .../actions_simulators/jira_simulation.ts | 101 +++ .../servicenow_simulation.ts | 8 +- .../actions/builtin_action_types/jira.ts | 549 +++++++++++ .../builtin_action_types/servicenow.ts | 750 +++++++++------- .../tests/actions/index.ts | 1 + 84 files changed, 5330 insertions(+), 3235 deletions(-) create mode 100644 x-pack/plugins/actions/server/builtin_action_types/case/api.ts rename x-pack/plugins/actions/server/builtin_action_types/{servicenow => case}/constants.ts (87%) create mode 100644 x-pack/plugins/actions/server/builtin_action_types/case/schema.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/case/transformers.test.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/case/transformers.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/case/translations.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/case/types.ts rename x-pack/plugins/actions/server/builtin_action_types/{servicenow/helpers.test.ts => case/utils.test.ts} (56%) create mode 100644 x-pack/plugins/actions/server/builtin_action_types/case/utils.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/case/validators.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/jira/api.test.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/jira/api.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/jira/config.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/jira/index.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/jira/mocks.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/jira/service.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/jira/translations.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/jira/types.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/jira/validators.ts delete mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/action_handlers.test.ts delete mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/action_handlers.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/api.test.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/api.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/config.ts delete mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.ts delete mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/index.test.ts delete mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/constants.ts delete mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/index.test.ts delete mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/index.ts delete mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/types.ts delete mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/mock.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/mocks.ts delete mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/service.test.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/service.ts delete mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/transformers.ts create mode 100644 x-pack/plugins/actions/server/builtin_action_types/servicenow/validators.ts create mode 100644 x-pack/plugins/siem/public/lib/connectors/components/connector_flyout/index.tsx create mode 100644 x-pack/plugins/siem/public/lib/connectors/jira/config.ts create mode 100644 x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx create mode 100644 x-pack/plugins/siem/public/lib/connectors/jira/index.tsx rename x-pack/plugins/siem/public/lib/connectors/{logos/servicenow.svg => jira/logo.svg} (100%) mode change 100755 => 100644 create mode 100644 x-pack/plugins/siem/public/lib/connectors/jira/translations.ts create mode 100644 x-pack/plugins/siem/public/lib/connectors/jira/types.ts delete mode 100644 x-pack/plugins/siem/public/lib/connectors/servicenow.tsx create mode 100644 x-pack/plugins/siem/public/lib/connectors/servicenow/config.ts create mode 100644 x-pack/plugins/siem/public/lib/connectors/servicenow/flyout.tsx create mode 100644 x-pack/plugins/siem/public/lib/connectors/servicenow/index.tsx create mode 100644 x-pack/plugins/siem/public/lib/connectors/servicenow/logo.svg create mode 100644 x-pack/plugins/siem/public/lib/connectors/servicenow/translations.ts create mode 100644 x-pack/plugins/siem/public/lib/connectors/servicenow/types.ts create mode 100644 x-pack/plugins/siem/public/lib/connectors/utils.ts create mode 100644 x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/jira_simulation.ts create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index decd170ca5dd64..4c8cc3aa503e6e 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -28,7 +28,7 @@ Table of Contents - [RESTful API](#restful-api) - [`POST /api/action`: Create action](#post-apiaction-create-action) - [`DELETE /api/action/{id}`: Delete action](#delete-apiactionid-delete-action) - - [`GET /api/action/_getAll`: Get all actions](#get-apiaction-get-all-actions) + - [`GET /api/action/_getAll`: Get all actions](#get-apiactiongetall-get-all-actions) - [`GET /api/action/{id}`: Get action](#get-apiactionid-get-action) - [`GET /api/action/types`: List action types](#get-apiactiontypes-list-action-types) - [`PUT /api/action/{id}`: Update action](#put-apiactionid-update-action) @@ -64,6 +64,12 @@ Table of Contents - [`config`](#config-6) - [`secrets`](#secrets-6) - [`params`](#params-6) + - [`subActionParams (pushToService)`](#subactionparams-pushtoservice) + - [Jira](#jira) + - [`config`](#config-7) + - [`secrets`](#secrets-7) + - [`params`](#params-7) + - [`subActionParams (pushToService)`](#subactionparams-pushtoservice-1) - [Command Line Utility](#command-line-utility) ## Terminology @@ -143,8 +149,8 @@ This is the primary function for an action type. Whenever the action needs to ex | actionId | The action saved object id that the action type is executing for. | | config | The decrypted configuration given to an action. This comes from the action saved object that is partially or fully encrypted within the data store. If you would like to validate the config before being passed to the executor, define `validate.config` within the action type. | | params | Parameters for the execution. These will be given at execution time by either an alert or manually provided when calling the plugin provided execute function. | -| services.callCluster(path, opts) | Use this to do Elasticsearch queries on the cluster Kibana connects to. This function is the same as any other `callCluster` in Kibana but runs in the context of the user who is calling the action when security is enabled.| -| services.getScopedCallCluster | This function scopes an instance of CallCluster by returning a `callCluster(path, opts)` function that runs in the context of the user who is calling the action when security is enabled. This must only be called with instances of CallCluster provided by core.| +| services.callCluster(path, opts) | Use this to do Elasticsearch queries on the cluster Kibana connects to. This function is the same as any other `callCluster` in Kibana but runs in the context of the user who is calling the action when security is enabled. | +| services.getScopedCallCluster | This function scopes an instance of CallCluster by returning a `callCluster(path, opts)` function that runs in the context of the user who is calling the action when security is enabled. This must only be called with instances of CallCluster provided by core. | | services.savedObjectsClient | This is an instance of the saved objects client. This provides the ability to do CRUD on any saved objects within the same space the alert lives in.

The scope of the saved objects client is tied to the user in context calling the execute API or the API key provided to the execute plugin function (only when security isenabled). | | services.log(tags, [data], [timestamp]) | Use this to create server logs. (This is the same function as server.log) | @@ -483,13 +489,59 @@ The ServiceNow action uses the [V2 Table API](https://developer.servicenow.com/a ### `params` +| Property | Description | Type | +| --------------- | ------------------------------------------------------------------------------------ | ------ | +| subAction | The sub action to perform. It can be `pushToService`, `handshake`, and `getIncident` | string | +| subActionParams | The parameters of the sub action | object | + +#### `subActionParams (pushToService)` + | Property | Description | Type | | ----------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------- | | caseId | The case id | string | | title | The title of the case | string _(optional)_ | | description | The description of the case | string _(optional)_ | | comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }` | object[] _(optional)_ | -| incidentID | The id of the incident in ServiceNow . If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | +| externalId | The id of the incident in ServiceNow . If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | + +--- + +## Jira + +ID: `.jira` + +The Jira action uses the [V2 API](https://developer.atlassian.com/cloud/jira/platform/rest/v2/) to create and update Jira incidents. + +### `config` + +| Property | Description | Type | +| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | +| apiUrl | ServiceNow instance URL. | string | +| casesConfiguration | Case configuration object. The object should contain an attribute called `mapping`. A `mapping` is an array of objects. Each mapping object should be of the form `{ source: string, target: string, actionType: string }`. `source` is the Case field. `target` is the Jira field where `source` will be mapped to. `actionType` can be one of `nothing`, `overwrite` or `append`. For example the `{ source: 'title', target: 'summary', actionType: 'overwrite' }` record, inside mapping array, means that the title of a case will be mapped to the short description of an incident in ServiceNow and will be overwrite on each update. | object | + +### `secrets` + +| Property | Description | Type | +| -------- | --------------------------------------- | ------ | +| email | email for HTTP Basic authentication | string | +| apiToken | API token for HTTP Basic authentication | string | + +### `params` + +| Property | Description | Type | +| --------------- | ------------------------------------------------------------------------------------ | ------ | +| subAction | The sub action to perform. It can be `pushToService`, `handshake`, and `getIncident` | string | +| subActionParams | The parameters of the sub action | object | + +#### `subActionParams (pushToService)` + +| Property | Description | Type | +| ----------- | ------------------------------------------------------------------------------------------------------------------- | --------------------- | +| caseId | The case id | string | +| title | The title of the case | string _(optional)_ | +| description | The description of the case | string _(optional)_ | +| comments | The comments of the case. A comment is of the form `{ commentId: string, version: string, comment: string }` | object[] _(optional)_ | +| externalId | The id of the incident in Jira. If presented the incident will be update. Otherwise a new incident will be created. | string _(optional)_ | # Command Line Utility diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/api.ts b/x-pack/plugins/actions/server/builtin_action_types/case/api.ts new file mode 100644 index 00000000000000..6dc8a9cc9af6ad --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/case/api.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + ExternalServiceApi, + ExternalServiceParams, + PushToServiceResponse, + GetIncidentApiHandlerArgs, + HandshakeApiHandlerArgs, + PushToServiceApiHandlerArgs, +} from './types'; +import { prepareFieldsForTransformation, transformFields, transformComments } from './utils'; + +const handshakeHandler = async ({ + externalService, + mapping, + params, +}: HandshakeApiHandlerArgs) => {}; +const getIncidentHandler = async ({ + externalService, + mapping, + params, +}: GetIncidentApiHandlerArgs) => {}; + +const pushToServiceHandler = async ({ + externalService, + mapping, + params, +}: PushToServiceApiHandlerArgs): Promise => { + const { externalId, comments } = params; + const updateIncident = externalId ? true : false; + const defaultPipes = updateIncident ? ['informationUpdated'] : ['informationCreated']; + let currentIncident: ExternalServiceParams | undefined; + let res: PushToServiceResponse; + + if (externalId) { + currentIncident = await externalService.getIncident(externalId); + } + + const fields = prepareFieldsForTransformation({ + params, + mapping, + defaultPipes, + }); + + const incident = transformFields({ + params, + fields, + currentIncident, + }); + + if (updateIncident) { + res = await externalService.updateIncident({ incidentId: externalId, incident }); + } else { + res = await externalService.createIncident({ incident }); + } + + if ( + comments && + Array.isArray(comments) && + comments.length > 0 && + mapping.get('comments')?.actionType !== 'nothing' + ) { + const commentsTransformed = transformComments(comments, ['informationAdded']); + + res.comments = []; + for (const currentComment of commentsTransformed) { + const comment = await externalService.createComment({ + incidentId: res.id, + comment: currentComment, + field: mapping.get('comments')?.target ?? 'comments', + }); + res.comments = [ + ...(res.comments ?? []), + { + commentId: comment.commentId, + pushedDate: comment.pushedDate, + }, + ]; + } + } + + return res; +}; + +export const api: ExternalServiceApi = { + handshake: handshakeHandler, + pushToService: pushToServiceHandler, + getIncident: getIncidentHandler, +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/constants.ts b/x-pack/plugins/actions/server/builtin_action_types/case/constants.ts similarity index 87% rename from x-pack/plugins/actions/server/builtin_action_types/servicenow/constants.ts rename to x-pack/plugins/actions/server/builtin_action_types/case/constants.ts index a0ffd859e14caa..1f2bc7f5e8e531 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/constants.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/case/constants.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export const ACTION_TYPE_ID = '.servicenow'; export const SUPPORTED_SOURCE_FIELDS = ['title', 'comments', 'description']; diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/case/schema.ts new file mode 100644 index 00000000000000..33b2ad6d186843 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/case/schema.ts @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; + +export const MappingActionType = schema.oneOf([ + schema.literal('nothing'), + schema.literal('overwrite'), + schema.literal('append'), +]); + +export const MapRecordSchema = schema.object({ + source: schema.string(), + target: schema.string(), + actionType: MappingActionType, +}); + +export const CaseConfigurationSchema = schema.object({ + mapping: schema.arrayOf(MapRecordSchema), +}); + +export const ExternalIncidentServiceConfiguration = { + apiUrl: schema.string(), + casesConfiguration: CaseConfigurationSchema, +}; + +export const ExternalIncidentServiceConfigurationSchema = schema.object( + ExternalIncidentServiceConfiguration +); + +export const ExternalIncidentServiceSecretConfiguration = { + password: schema.string(), + username: schema.string(), +}; + +export const ExternalIncidentServiceSecretConfigurationSchema = schema.object( + ExternalIncidentServiceSecretConfiguration +); + +export const UserSchema = schema.object({ + fullName: schema.nullable(schema.string()), + username: schema.nullable(schema.string()), +}); + +const EntityInformation = { + createdAt: schema.string(), + createdBy: UserSchema, + updatedAt: schema.nullable(schema.string()), + updatedBy: schema.nullable(UserSchema), +}; + +export const EntityInformationSchema = schema.object(EntityInformation); + +export const CommentSchema = schema.object({ + commentId: schema.string(), + comment: schema.string(), + ...EntityInformation, +}); + +export const ExecutorSubActionSchema = schema.oneOf([ + schema.literal('getIncident'), + schema.literal('pushToService'), + schema.literal('handshake'), +]); + +export const ExecutorSubActionPushParamsSchema = schema.object({ + caseId: schema.string(), + title: schema.string(), + description: schema.nullable(schema.string()), + comments: schema.nullable(schema.arrayOf(CommentSchema)), + externalId: schema.nullable(schema.string()), + ...EntityInformation, +}); + +export const ExecutorSubActionGetIncidentParamsSchema = schema.object({ + externalId: schema.string(), +}); + +// Reserved for future implementation +export const ExecutorSubActionHandshakeParamsSchema = schema.object({}); + +export const ExecutorParamsSchema = schema.oneOf([ + schema.object({ + subAction: schema.literal('getIncident'), + subActionParams: ExecutorSubActionGetIncidentParamsSchema, + }), + schema.object({ + subAction: schema.literal('handshake'), + subActionParams: ExecutorSubActionHandshakeParamsSchema, + }), + schema.object({ + subAction: schema.literal('pushToService'), + subActionParams: ExecutorSubActionPushParamsSchema, + }), +]); diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/transformers.test.ts b/x-pack/plugins/actions/server/builtin_action_types/case/transformers.test.ts new file mode 100644 index 00000000000000..75dcab65ee9f21 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/case/transformers.test.ts @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { transformers } from './transformers'; + +const { informationCreated, informationUpdated, informationAdded, append } = transformers; + +describe('informationCreated', () => { + test('transforms correctly', () => { + const res = informationCreated({ + value: 'a value', + date: '2020-04-15T08:19:27.400Z', + user: 'elastic', + }); + expect(res).toEqual({ value: 'a value (created at 2020-04-15T08:19:27.400Z by elastic)' }); + }); + + test('transforms correctly without optional fields', () => { + const res = informationCreated({ + value: 'a value', + }); + expect(res).toEqual({ value: 'a value (created at by )' }); + }); + + test('returns correctly rest fields', () => { + const res = informationCreated({ + value: 'a value', + date: '2020-04-15T08:19:27.400Z', + user: 'elastic', + previousValue: 'previous value', + }); + expect(res).toEqual({ + value: 'a value (created at 2020-04-15T08:19:27.400Z by elastic)', + previousValue: 'previous value', + }); + }); +}); + +describe('informationUpdated', () => { + test('transforms correctly', () => { + const res = informationUpdated({ + value: 'a value', + date: '2020-04-15T08:19:27.400Z', + user: 'elastic', + }); + expect(res).toEqual({ value: 'a value (updated at 2020-04-15T08:19:27.400Z by elastic)' }); + }); + + test('transforms correctly without optional fields', () => { + const res = informationUpdated({ + value: 'a value', + }); + expect(res).toEqual({ value: 'a value (updated at by )' }); + }); + + test('returns correctly rest fields', () => { + const res = informationUpdated({ + value: 'a value', + date: '2020-04-15T08:19:27.400Z', + user: 'elastic', + previousValue: 'previous value', + }); + expect(res).toEqual({ + value: 'a value (updated at 2020-04-15T08:19:27.400Z by elastic)', + previousValue: 'previous value', + }); + }); +}); + +describe('informationAdded', () => { + test('transforms correctly', () => { + const res = informationAdded({ + value: 'a value', + date: '2020-04-15T08:19:27.400Z', + user: 'elastic', + }); + expect(res).toEqual({ value: 'a value (added at 2020-04-15T08:19:27.400Z by elastic)' }); + }); + + test('transforms correctly without optional fields', () => { + const res = informationAdded({ + value: 'a value', + }); + expect(res).toEqual({ value: 'a value (added at by )' }); + }); + + test('returns correctly rest fields', () => { + const res = informationAdded({ + value: 'a value', + date: '2020-04-15T08:19:27.400Z', + user: 'elastic', + previousValue: 'previous value', + }); + expect(res).toEqual({ + value: 'a value (added at 2020-04-15T08:19:27.400Z by elastic)', + previousValue: 'previous value', + }); + }); +}); + +describe('append', () => { + test('transforms correctly', () => { + const res = append({ + value: 'a value', + previousValue: 'previous value', + }); + expect(res).toEqual({ value: 'previous value \r\na value' }); + }); + + test('transforms correctly without optional fields', () => { + const res = append({ + value: 'a value', + }); + expect(res).toEqual({ value: 'a value' }); + }); + + test('returns correctly rest fields', () => { + const res = append({ + value: 'a value', + user: 'elastic', + previousValue: 'previous value', + }); + expect(res).toEqual({ + value: 'previous value \r\na value', + user: 'elastic', + }); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/transformers.ts b/x-pack/plugins/actions/server/builtin_action_types/case/transformers.ts new file mode 100644 index 00000000000000..3dca1fd7034301 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/case/transformers.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { TransformerArgs } from './types'; +import * as i18n from './translations'; + +export type Transformer = (args: TransformerArgs) => TransformerArgs; + +export const transformers: Record = { + informationCreated: ({ value, date, user, ...rest }: TransformerArgs): TransformerArgs => ({ + value: `${value} ${i18n.FIELD_INFORMATION('create', date, user)}`, + ...rest, + }), + informationUpdated: ({ value, date, user, ...rest }: TransformerArgs): TransformerArgs => ({ + value: `${value} ${i18n.FIELD_INFORMATION('update', date, user)}`, + ...rest, + }), + informationAdded: ({ value, date, user, ...rest }: TransformerArgs): TransformerArgs => ({ + value: `${value} ${i18n.FIELD_INFORMATION('add', date, user)}`, + ...rest, + }), + append: ({ value, previousValue, ...rest }: TransformerArgs): TransformerArgs => ({ + value: previousValue ? `${previousValue} \r\n${value}` : `${value}`, + ...rest, + }), +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/translations.ts b/x-pack/plugins/actions/server/builtin_action_types/case/translations.ts new file mode 100644 index 00000000000000..4842728b0e4e7e --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/case/translations.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const API_URL_REQUIRED = i18n.translate('xpack.actions.builtin.case.connectorApiNullError', { + defaultMessage: 'connector [apiUrl] is required', +}); + +export const FIELD_INFORMATION = ( + mode: string, + date: string | undefined, + user: string | undefined +) => { + switch (mode) { + case 'create': + return i18n.translate('xpack.actions.builtin.case.common.externalIncidentCreated', { + values: { date, user }, + defaultMessage: '(created at {date} by {user})', + }); + case 'update': + return i18n.translate('xpack.actions.builtin.case.common.externalIncidentUpdated', { + values: { date, user }, + defaultMessage: '(updated at {date} by {user})', + }); + case 'add': + return i18n.translate('xpack.actions.builtin.case.common.externalIncidentAdded', { + values: { date, user }, + defaultMessage: '(added at {date} by {user})', + }); + default: + return i18n.translate('xpack.actions.builtin.case.common.externalIncidentDefault', { + values: { date, user }, + defaultMessage: '(created at {date} by {user})', + }); + } +}; + +export const MAPPING_EMPTY = i18n.translate( + 'xpack.actions.builtin.case.configuration.emptyMapping', + { + defaultMessage: '[casesConfiguration.mapping]: expected non-empty but got empty', + } +); + +export const WHITE_LISTED_ERROR = (message: string) => + i18n.translate('xpack.actions.builtin.case.configuration.apiWhitelistError', { + defaultMessage: 'error configuring connector action: {message}', + values: { + message, + }, + }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/types.ts b/x-pack/plugins/actions/server/builtin_action_types/case/types.ts new file mode 100644 index 00000000000000..459e9d2b03f926 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/case/types.ts @@ -0,0 +1,161 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// This will have to remain `any` until we can extend connectors with generics +// and circular dependencies eliminated. +/* eslint-disable @typescript-eslint/no-explicit-any */ + +import { TypeOf } from '@kbn/config-schema'; + +import { + ExternalIncidentServiceConfigurationSchema, + ExternalIncidentServiceSecretConfigurationSchema, + ExecutorParamsSchema, + CaseConfigurationSchema, + MapRecordSchema, + CommentSchema, + ExecutorSubActionPushParamsSchema, + ExecutorSubActionGetIncidentParamsSchema, + ExecutorSubActionHandshakeParamsSchema, +} from './schema'; + +export interface AnyParams { + [index: string]: string | number | object | undefined | null; +} + +export type ExternalIncidentServiceConfiguration = TypeOf< + typeof ExternalIncidentServiceConfigurationSchema +>; +export type ExternalIncidentServiceSecretConfiguration = TypeOf< + typeof ExternalIncidentServiceSecretConfigurationSchema +>; + +export type ExecutorParams = TypeOf; +export type ExecutorSubActionPushParams = TypeOf; + +export type ExecutorSubActionGetIncidentParams = TypeOf< + typeof ExecutorSubActionGetIncidentParamsSchema +>; + +export type ExecutorSubActionHandshakeParams = TypeOf< + typeof ExecutorSubActionHandshakeParamsSchema +>; + +export type CaseConfiguration = TypeOf; +export type MapRecord = TypeOf; +export type Comment = TypeOf; + +export interface ExternalServiceConfiguration { + id: string; + name: string; +} + +export interface ExternalServiceCredentials { + config: Record; + secrets: Record; +} + +export interface ExternalServiceValidation { + config: (configurationUtilities: any, configObject: any) => void; + secrets: (configurationUtilities: any, secrets: any) => void; +} + +export interface ExternalServiceIncidentResponse { + id: string; + title: string; + url: string; + pushedDate: string; +} + +export interface ExternalServiceCommentResponse { + commentId: string; + pushedDate: string; + externalCommentId?: string; +} + +export interface ExternalServiceParams { + [index: string]: any; +} + +export interface ExternalService { + getIncident: (id: string) => Promise; + createIncident: (params: ExternalServiceParams) => Promise; + updateIncident: (params: ExternalServiceParams) => Promise; + createComment: (params: ExternalServiceParams) => Promise; +} + +export interface PushToServiceApiParams extends ExecutorSubActionPushParams { + externalCase: Record; +} + +export interface ExternalServiceApiHandlerArgs { + externalService: ExternalService; + mapping: Map; +} + +export interface PushToServiceApiHandlerArgs extends ExternalServiceApiHandlerArgs { + params: PushToServiceApiParams; +} + +export interface GetIncidentApiHandlerArgs extends ExternalServiceApiHandlerArgs { + params: ExecutorSubActionGetIncidentParams; +} + +export interface HandshakeApiHandlerArgs extends ExternalServiceApiHandlerArgs { + params: ExecutorSubActionHandshakeParams; +} + +export interface PushToServiceResponse extends ExternalServiceIncidentResponse { + comments?: ExternalServiceCommentResponse[]; +} + +export interface ExternalServiceApi { + handshake: (args: HandshakeApiHandlerArgs) => Promise; + pushToService: (args: PushToServiceApiHandlerArgs) => Promise; + getIncident: (args: GetIncidentApiHandlerArgs) => Promise; +} + +export interface CreateExternalServiceBasicArgs { + api: ExternalServiceApi; + createExternalService: (credentials: ExternalServiceCredentials) => ExternalService; +} + +export interface CreateExternalServiceArgs extends CreateExternalServiceBasicArgs { + config: ExternalServiceConfiguration; + validate: ExternalServiceValidation; + validationSchema: { config: any; secrets: any }; +} + +export interface CreateActionTypeArgs { + configurationUtilities: any; + executor?: any; +} + +export interface PipedField { + key: string; + value: string; + actionType: string; + pipes: string[]; +} + +export interface PrepareFieldsForTransformArgs { + params: PushToServiceApiParams; + mapping: Map; + defaultPipes?: string[]; +} + +export interface TransformFieldsArgs { + params: PushToServiceApiParams; + fields: PipedField[]; + currentIncident?: ExternalServiceParams; +} + +export interface TransformerArgs { + value: string; + date?: string; + user?: string; + previousValue?: string; +} diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.test.ts b/x-pack/plugins/actions/server/builtin_action_types/case/utils.test.ts similarity index 56% rename from x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.test.ts rename to x-pack/plugins/actions/server/builtin_action_types/case/utils.test.ts index cbcefe6364e8f2..1e8cc3eda20e55 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/case/utils.test.ts @@ -4,28 +4,65 @@ * you may not use this file except in compliance with the Elastic License. */ +import axios from 'axios'; + import { normalizeMapping, buildMap, mapParams, - appendField, - appendInformationToField, prepareFieldsForTransformation, transformFields, transformComments, -} from './helpers'; -import { mapping, finalMapping } from './mock'; + addTimeZoneToDate, + throwIfNotAlive, + request, + patch, + getErrorMessage, +} from './utils'; + import { SUPPORTED_SOURCE_FIELDS } from './constants'; -import { MapEntry, Params, Comment } from './types'; +import { Comment, MapRecord, PushToServiceApiParams } from './types'; + +jest.mock('axios'); +const axiosMock = (axios as unknown) as jest.Mock; + +const mapping: MapRecord[] = [ + { source: 'title', target: 'short_description', actionType: 'overwrite' }, + { source: 'description', target: 'description', actionType: 'append' }, + { source: 'comments', target: 'comments', actionType: 'append' }, +]; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const finalMapping: Map = new Map(); + +finalMapping.set('title', { + target: 'short_description', + actionType: 'overwrite', +}); -const maliciousMapping: MapEntry[] = [ +finalMapping.set('description', { + target: 'description', + actionType: 'append', +}); + +finalMapping.set('comments', { + target: 'comments', + actionType: 'append', +}); + +finalMapping.set('short_description', { + target: 'title', + actionType: 'overwrite', +}); + +const maliciousMapping: MapRecord[] = [ { source: '__proto__', target: 'short_description', actionType: 'nothing' }, { source: 'description', target: '__proto__', actionType: 'nothing' }, { source: 'comments', target: 'comments', actionType: 'nothing' }, { source: 'unsupportedSource', target: 'comments', actionType: 'nothing' }, ]; -const fullParams: Params = { +const fullParams: PushToServiceApiParams = { caseId: 'd4387ac5-0899-4dc2-bbfa-0dd605c934aa', title: 'a title', description: 'a description', @@ -33,15 +70,14 @@ const fullParams: Params = { createdBy: { fullName: 'Elastic User', username: 'elastic' }, updatedAt: null, updatedBy: null, - incidentId: null, - incident: { + externalId: null, + externalCase: { short_description: 'a title', description: 'a description', }, comments: [ { commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', - version: 'WzU3LDFd', comment: 'first comment', createdAt: '2020-03-13T08:34:53.450Z', createdBy: { fullName: 'Elastic User', username: 'elastic' }, @@ -50,7 +86,6 @@ const fullParams: Params = { }, { commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', - version: 'WzU3LDFd', comment: 'second comment', createdAt: '2020-03-13T08:34:53.450Z', createdBy: { fullName: 'Elastic User', username: 'elastic' }, @@ -60,7 +95,7 @@ const fullParams: Params = { ], }; -describe('sanitizeMapping', () => { +describe('normalizeMapping', () => { test('remove malicious fields', () => { const sanitizedMapping = normalizeMapping(SUPPORTED_SOURCE_FIELDS, maliciousMapping); expect(sanitizedMapping.every(m => m.source !== '__proto__' && m.target !== '__proto__')).toBe( @@ -194,7 +229,10 @@ describe('transformFields', () => { params: { ...fullParams, updatedAt: '2020-03-15T08:34:53.450Z', - updatedBy: { username: 'anotherUser', fullName: 'Another User' }, + updatedBy: { + username: 'anotherUser', + fullName: 'Another User', + }, }, mapping: finalMapping, defaultPipes: ['informationUpdated'], @@ -204,7 +242,10 @@ describe('transformFields', () => { params: { ...fullParams, updatedAt: '2020-03-15T08:34:53.450Z', - updatedBy: { username: 'anotherUser', fullName: 'Another User' }, + updatedBy: { + username: 'anotherUser', + fullName: 'Another User', + }, }, fields, currentIncident: { @@ -244,7 +285,10 @@ describe('transformFields', () => { }); const res = transformFields({ - params: { ...fullParams, createdBy: { fullName: null, username: 'elastic' } }, + params: { + ...fullParams, + createdBy: { fullName: '', username: 'elastic' }, + }, fields, }); @@ -259,7 +303,10 @@ describe('transformFields', () => { params: { ...fullParams, updatedAt: '2020-03-15T08:34:53.450Z', - updatedBy: { username: 'anotherUser', fullName: 'Another User' }, + updatedBy: { + username: 'anotherUser', + fullName: 'Another User', + }, }, mapping: finalMapping, defaultPipes: ['informationUpdated'], @@ -269,7 +316,7 @@ describe('transformFields', () => { params: { ...fullParams, updatedAt: '2020-03-15T08:34:53.450Z', - updatedBy: { username: 'anotherUser', fullName: null }, + updatedBy: { username: 'anotherUser', fullName: '' }, }, fields, }); @@ -281,60 +328,11 @@ describe('transformFields', () => { }); }); -describe('appendField', () => { - test('prefix correctly', () => { - expect('my_prefixmy_value ').toEqual(appendField({ value: 'my_value', prefix: 'my_prefix' })); - }); - - test('suffix correctly', () => { - expect('my_value my_suffix').toEqual(appendField({ value: 'my_value', suffix: 'my_suffix' })); - }); - - test('prefix and suffix correctly', () => { - expect('my_prefixmy_value my_suffix').toEqual( - appendField({ value: 'my_value', prefix: 'my_prefix', suffix: 'my_suffix' }) - ); - }); -}); - -describe('appendInformationToField', () => { - test('creation mode', () => { - const res = appendInformationToField({ - value: 'my value', - user: 'Elastic Test User', - date: '2020-03-13T08:34:53.450Z', - mode: 'create', - }); - expect(res).toEqual('my value (created at 2020-03-13T08:34:53.450Z by Elastic Test User)'); - }); - - test('update mode', () => { - const res = appendInformationToField({ - value: 'my value', - user: 'Elastic Test User', - date: '2020-03-13T08:34:53.450Z', - mode: 'update', - }); - expect(res).toEqual('my value (updated at 2020-03-13T08:34:53.450Z by Elastic Test User)'); - }); - - test('add mode', () => { - const res = appendInformationToField({ - value: 'my value', - user: 'Elastic Test User', - date: '2020-03-13T08:34:53.450Z', - mode: 'add', - }); - expect(res).toEqual('my value (added at 2020-03-13T08:34:53.450Z by Elastic Test User)'); - }); -}); - describe('transformComments', () => { test('transform creation comments', () => { const comments: Comment[] = [ { commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', - version: 'WzU3LDFd', comment: 'first comment', createdAt: '2020-03-13T08:34:53.450Z', createdBy: { fullName: 'Elastic User', username: 'elastic' }, @@ -342,11 +340,10 @@ describe('transformComments', () => { updatedBy: null, }, ]; - const res = transformComments(comments, fullParams, ['informationCreated']); + const res = transformComments(comments, ['informationCreated']); expect(res).toEqual([ { commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', - version: 'WzU3LDFd', comment: 'first comment (created at 2020-03-13T08:34:53.450Z by Elastic User)', createdAt: '2020-03-13T08:34:53.450Z', createdBy: { fullName: 'Elastic User', username: 'elastic' }, @@ -360,32 +357,36 @@ describe('transformComments', () => { const comments: Comment[] = [ { commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', - version: 'WzU3LDFd', comment: 'first comment', createdAt: '2020-03-13T08:34:53.450Z', createdBy: { fullName: 'Elastic User', username: 'elastic' }, updatedAt: '2020-03-15T08:34:53.450Z', - updatedBy: { fullName: 'Another User', username: 'anotherUser' }, + updatedBy: { + fullName: 'Another User', + username: 'anotherUser', + }, }, ]; - const res = transformComments(comments, fullParams, ['informationUpdated']); + const res = transformComments(comments, ['informationUpdated']); expect(res).toEqual([ { commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', - version: 'WzU3LDFd', comment: 'first comment (updated at 2020-03-15T08:34:53.450Z by Another User)', createdAt: '2020-03-13T08:34:53.450Z', createdBy: { fullName: 'Elastic User', username: 'elastic' }, updatedAt: '2020-03-15T08:34:53.450Z', - updatedBy: { fullName: 'Another User', username: 'anotherUser' }, + updatedBy: { + fullName: 'Another User', + username: 'anotherUser', + }, }, ]); }); + test('transform added comments', () => { const comments: Comment[] = [ { commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', - version: 'WzU3LDFd', comment: 'first comment', createdAt: '2020-03-13T08:34:53.450Z', createdBy: { fullName: 'Elastic User', username: 'elastic' }, @@ -393,11 +394,10 @@ describe('transformComments', () => { updatedBy: null, }, ]; - const res = transformComments(comments, fullParams, ['informationAdded']); + const res = transformComments(comments, ['informationAdded']); expect(res).toEqual([ { commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', - version: 'WzU3LDFd', comment: 'first comment (added at 2020-03-13T08:34:53.450Z by Elastic User)', createdAt: '2020-03-13T08:34:53.450Z', createdBy: { fullName: 'Elastic User', username: 'elastic' }, @@ -406,4 +406,171 @@ describe('transformComments', () => { }, ]); }); + + test('transform comments without fullname', () => { + const comments: Comment[] = [ + { + commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', + comment: 'first comment', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: '', username: 'elastic' }, + updatedAt: null, + updatedBy: null, + }, + ]; + const res = transformComments(comments, ['informationAdded']); + expect(res).toEqual([ + { + commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', + comment: 'first comment (added at 2020-03-13T08:34:53.450Z by elastic)', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: '', username: 'elastic' }, + updatedAt: null, + updatedBy: null, + }, + ]); + }); + + test('adds update user correctly', () => { + const comments: Comment[] = [ + { + commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', + comment: 'first comment', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: 'Elastic', username: 'elastic' }, + updatedAt: '2020-04-13T08:34:53.450Z', + updatedBy: { fullName: 'Elastic2', username: 'elastic' }, + }, + ]; + const res = transformComments(comments, ['informationAdded']); + expect(res).toEqual([ + { + commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', + comment: 'first comment (added at 2020-04-13T08:34:53.450Z by Elastic2)', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: 'Elastic', username: 'elastic' }, + updatedAt: '2020-04-13T08:34:53.450Z', + updatedBy: { fullName: 'Elastic2', username: 'elastic' }, + }, + ]); + }); + + test('adds update user with empty fullname correctly', () => { + const comments: Comment[] = [ + { + commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', + comment: 'first comment', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: 'Elastic', username: 'elastic' }, + updatedAt: '2020-04-13T08:34:53.450Z', + updatedBy: { fullName: '', username: 'elastic2' }, + }, + ]; + const res = transformComments(comments, ['informationAdded']); + expect(res).toEqual([ + { + commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', + comment: 'first comment (added at 2020-04-13T08:34:53.450Z by elastic2)', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: 'Elastic', username: 'elastic' }, + updatedAt: '2020-04-13T08:34:53.450Z', + updatedBy: { fullName: '', username: 'elastic2' }, + }, + ]); + }); +}); + +describe('addTimeZoneToDate', () => { + test('adds timezone with default', () => { + const date = addTimeZoneToDate('2020-04-14T15:01:55.456Z'); + expect(date).toBe('2020-04-14T15:01:55.456Z GMT'); + }); + + test('adds timezone correctly', () => { + const date = addTimeZoneToDate('2020-04-14T15:01:55.456Z', 'PST'); + expect(date).toBe('2020-04-14T15:01:55.456Z PST'); + }); +}); + +describe('throwIfNotAlive ', () => { + test('throws correctly when status is invalid', async () => { + expect(() => { + throwIfNotAlive(404, 'application/json'); + }).toThrow('Instance is not alive.'); + }); + + test('throws correctly when content is invalid', () => { + expect(() => { + throwIfNotAlive(200, 'application/html'); + }).toThrow('Instance is not alive.'); + }); + + test('do NOT throws with custom validStatusCodes', async () => { + expect(() => { + throwIfNotAlive(404, 'application/json', [404]); + }).not.toThrow('Instance is not alive.'); + }); +}); + +describe('request', () => { + beforeEach(() => { + axiosMock.mockImplementation(() => ({ + status: 200, + headers: { 'content-type': 'application/json' }, + data: { incidentId: '123' }, + })); + }); + + test('it fetch correctly with defaults', async () => { + const res = await request({ axios, url: '/test' }); + + expect(axiosMock).toHaveBeenCalledWith('/test', { method: 'get', data: {} }); + expect(res).toEqual({ + status: 200, + headers: { 'content-type': 'application/json' }, + data: { incidentId: '123' }, + }); + }); + + test('it fetch correctly', async () => { + const res = await request({ axios, url: '/test', method: 'post', data: { id: '123' } }); + + expect(axiosMock).toHaveBeenCalledWith('/test', { method: 'post', data: { id: '123' } }); + expect(res).toEqual({ + status: 200, + headers: { 'content-type': 'application/json' }, + data: { incidentId: '123' }, + }); + }); + + test('it throws correctly', async () => { + axiosMock.mockImplementation(() => ({ + status: 404, + headers: { 'content-type': 'application/json' }, + data: { incidentId: '123' }, + })); + + await expect(request({ axios, url: '/test' })).rejects.toThrow(); + }); +}); + +describe('patch', () => { + beforeEach(() => { + axiosMock.mockImplementation(() => ({ + status: 200, + headers: { 'content-type': 'application/json' }, + })); + }); + + test('it fetch correctly', async () => { + await patch({ axios, url: '/test', data: { id: '123' } }); + expect(axiosMock).toHaveBeenCalledWith('/test', { method: 'patch', data: { id: '123' } }); + }); +}); + +describe('getErrorMessage', () => { + test('it returns the correct error message', () => { + const msg = getErrorMessage('My connector name', 'An error has occurred'); + expect(msg).toBe('[Action][My connector name]: An error has occurred'); + }); }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/utils.ts b/x-pack/plugins/actions/server/builtin_action_types/case/utils.ts new file mode 100644 index 00000000000000..7d69b2791f6240 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/case/utils.ts @@ -0,0 +1,249 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { curry, flow, get } from 'lodash'; +import { schema } from '@kbn/config-schema'; +import { AxiosInstance, Method, AxiosResponse } from 'axios'; + +import { ActionTypeExecutorOptions, ActionTypeExecutorResult, ActionType } from '../../types'; + +import { ExecutorParamsSchema } from './schema'; + +import { + CreateExternalServiceArgs, + CreateActionTypeArgs, + ExecutorParams, + MapRecord, + AnyParams, + CreateExternalServiceBasicArgs, + PrepareFieldsForTransformArgs, + PipedField, + TransformFieldsArgs, + Comment, + ExecutorSubActionPushParams, +} from './types'; + +import { transformers, Transformer } from './transformers'; + +import { SUPPORTED_SOURCE_FIELDS } from './constants'; + +export const normalizeMapping = (supportedFields: string[], mapping: MapRecord[]): MapRecord[] => { + // Prevent prototype pollution and remove unsupported fields + return mapping.filter( + m => m.source !== '__proto__' && m.target !== '__proto__' && supportedFields.includes(m.source) + ); +}; + +export const buildMap = (mapping: MapRecord[]): Map => { + return normalizeMapping(SUPPORTED_SOURCE_FIELDS, mapping).reduce((fieldsMap, field) => { + const { source, target, actionType } = field; + fieldsMap.set(source, { target, actionType }); + fieldsMap.set(target, { target: source, actionType }); + return fieldsMap; + }, new Map()); +}; + +export const mapParams = ( + params: Partial, + mapping: Map +): AnyParams => { + return Object.keys(params).reduce((prev: AnyParams, curr: string): AnyParams => { + const field = mapping.get(curr); + if (field) { + prev[field.target] = get(params, curr); + } + return prev; + }, {}); +}; + +export const createConnectorExecutor = ({ + api, + createExternalService, +}: CreateExternalServiceBasicArgs) => async ( + execOptions: ActionTypeExecutorOptions +): Promise => { + const { actionId, config, params, secrets } = execOptions; + const { subAction, subActionParams } = params as ExecutorParams; + let data = {}; + + const res: Pick & + Pick = { + status: 'ok', + actionId, + }; + + const externalService = createExternalService({ + config, + secrets, + }); + + if (!api[subAction]) { + throw new Error('[Action][ExternalService] Unsupported subAction type.'); + } + + if (subAction !== 'pushToService') { + throw new Error('[Action][ExternalService] subAction not implemented.'); + } + + if (subAction === 'pushToService') { + const pushToServiceParams = subActionParams as ExecutorSubActionPushParams; + const { comments, externalId, ...restParams } = pushToServiceParams; + + const mapping = buildMap(config.casesConfiguration.mapping); + const externalCase = mapParams(restParams, mapping); + + data = await api.pushToService({ + externalService, + mapping, + params: { ...pushToServiceParams, externalCase }, + }); + } + + return { + ...res, + data, + }; +}; + +export const createConnector = ({ + api, + config, + validate, + createExternalService, + validationSchema, +}: CreateExternalServiceArgs) => { + return ({ + configurationUtilities, + executor = createConnectorExecutor({ api, createExternalService }), + }: CreateActionTypeArgs): ActionType => ({ + id: config.id, + name: config.name, + minimumLicenseRequired: 'platinum', + validate: { + config: schema.object(validationSchema.config, { + validate: curry(validate.config)(configurationUtilities), + }), + secrets: schema.object(validationSchema.secrets, { + validate: curry(validate.secrets)(configurationUtilities), + }), + params: ExecutorParamsSchema, + }, + executor, + }); +}; + +export const throwIfNotAlive = ( + status: number, + contentType: string, + validStatusCodes: number[] = [200, 201, 204] +) => { + if (!validStatusCodes.includes(status) || !contentType.includes('application/json')) { + throw new Error('Instance is not alive.'); + } +}; + +export const request = async ({ + axios, + url, + method = 'get', + data, +}: { + axios: AxiosInstance; + url: string; + method?: Method; + data?: T; +}): Promise => { + const res = await axios(url, { method, data: data ?? {} }); + throwIfNotAlive(res.status, res.headers['content-type']); + return res; +}; + +export const patch = async ({ + axios, + url, + data, +}: { + axios: AxiosInstance; + url: string; + data: T; +}): Promise => { + return request({ + axios, + url, + method: 'patch', + data, + }); +}; + +export const addTimeZoneToDate = (date: string, timezone = 'GMT'): string => { + return `${date} ${timezone}`; +}; + +export const prepareFieldsForTransformation = ({ + params, + mapping, + defaultPipes = ['informationCreated'], +}: PrepareFieldsForTransformArgs): PipedField[] => { + return Object.keys(params.externalCase) + .filter(p => mapping.get(p)?.actionType != null && mapping.get(p)?.actionType !== 'nothing') + .map(p => { + const actionType = mapping.get(p)?.actionType ?? 'nothing'; + return { + key: p, + value: params.externalCase[p], + actionType, + pipes: actionType === 'append' ? [...defaultPipes, 'append'] : defaultPipes, + }; + }); +}; + +export const transformFields = ({ + params, + fields, + currentIncident, +}: TransformFieldsArgs): Record => { + return fields.reduce((prev, cur) => { + const transform = flow(...cur.pipes.map(p => transformers[p])); + return { + ...prev, + [cur.key]: transform({ + value: cur.value, + date: params.updatedAt ?? params.createdAt, + user: + (params.updatedBy != null + ? params.updatedBy.fullName + ? params.updatedBy.fullName + : params.updatedBy.username + : params.createdBy.fullName + ? params.createdBy.fullName + : params.createdBy.username) ?? '', + previousValue: currentIncident ? currentIncident[cur.key] : '', + }).value, + }; + }, {}); +}; + +export const transformComments = (comments: Comment[], pipes: string[]): Comment[] => { + return comments.map(c => ({ + ...c, + comment: flow(...pipes.map(p => transformers[p]))({ + value: c.comment, + date: c.updatedAt ?? c.createdAt, + user: + (c.updatedBy != null + ? c.updatedBy.fullName + ? c.updatedBy.fullName + : c.updatedBy.username + : c.createdBy.fullName + ? c.createdBy.fullName + : c.createdBy.username) ?? '', + }).value, + })); +}; + +export const getErrorMessage = (connector: string, msg: string) => { + return `[Action][${connector}]: ${msg}`; +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/case/validators.ts b/x-pack/plugins/actions/server/builtin_action_types/case/validators.ts new file mode 100644 index 00000000000000..80e301e5be082e --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/case/validators.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { isEmpty } from 'lodash'; + +import { ActionsConfigurationUtilities } from '../../actions_config'; +import { + ExternalIncidentServiceConfiguration, + ExternalIncidentServiceSecretConfiguration, +} from './types'; + +import * as i18n from './translations'; + +export const validateCommonConfig = ( + configurationUtilities: ActionsConfigurationUtilities, + configObject: ExternalIncidentServiceConfiguration +) => { + try { + if (isEmpty(configObject.casesConfiguration.mapping)) { + return i18n.MAPPING_EMPTY; + } + + configurationUtilities.ensureWhitelistedUri(configObject.apiUrl); + } catch (whitelistError) { + return i18n.WHITE_LISTED_ERROR(whitelistError.message); + } +}; + +export const validateCommonSecrets = ( + configurationUtilities: ActionsConfigurationUtilities, + secrets: ExternalIncidentServiceSecretConfiguration +) => {}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/index.ts b/x-pack/plugins/actions/server/builtin_action_types/index.ts index a92a279d084399..6ba4d7cfc7de03 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/index.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/index.ts @@ -12,9 +12,10 @@ import { getActionType as getEmailActionType } from './email'; import { getActionType as getIndexActionType } from './es_index'; import { getActionType as getPagerDutyActionType } from './pagerduty'; import { getActionType as getServerLogActionType } from './server_log'; -import { getActionType as getServiceNowActionType } from './servicenow'; import { getActionType as getSlackActionType } from './slack'; import { getActionType as getWebhookActionType } from './webhook'; +import { getActionType as getServiceNowActionType } from './servicenow'; +import { getActionType as getJiraActionType } from './jira'; export function registerBuiltInActionTypes({ actionsConfigUtils: configurationUtilities, @@ -29,7 +30,8 @@ export function registerBuiltInActionTypes({ actionTypeRegistry.register(getIndexActionType({ logger })); actionTypeRegistry.register(getPagerDutyActionType({ logger, configurationUtilities })); actionTypeRegistry.register(getServerLogActionType({ logger })); - actionTypeRegistry.register(getServiceNowActionType({ configurationUtilities })); actionTypeRegistry.register(getSlackActionType({ configurationUtilities })); actionTypeRegistry.register(getWebhookActionType({ logger, configurationUtilities })); + actionTypeRegistry.register(getServiceNowActionType({ configurationUtilities })); + actionTypeRegistry.register(getJiraActionType({ configurationUtilities })); } diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/api.test.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/api.test.ts new file mode 100644 index 00000000000000..bcfb82077d2861 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/api.test.ts @@ -0,0 +1,517 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { api } from '../case/api'; +import { externalServiceMock, mapping, apiParams } from './mocks'; +import { ExternalService } from '../case/types'; + +describe('api', () => { + let externalService: jest.Mocked; + + beforeEach(() => { + externalService = externalServiceMock.create(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('pushToService', () => { + describe('create incident', () => { + test('it creates an incident', async () => { + const params = { ...apiParams, externalId: null }; + const res = await api.pushToService({ externalService, mapping, params }); + + expect(res).toEqual({ + id: 'incident-1', + title: 'CK-1', + pushedDate: '2020-04-27T10:59:46.202Z', + url: 'https://siem-kibana.atlassian.net/browse/CK-1', + comments: [ + { + commentId: 'case-comment-1', + pushedDate: '2020-04-27T10:59:46.202Z', + }, + { + commentId: 'case-comment-2', + pushedDate: '2020-04-27T10:59:46.202Z', + }, + ], + }); + }); + + test('it creates an incident without comments', async () => { + const params = { ...apiParams, externalId: null, comments: [] }; + const res = await api.pushToService({ externalService, mapping, params }); + + expect(res).toEqual({ + id: 'incident-1', + title: 'CK-1', + pushedDate: '2020-04-27T10:59:46.202Z', + url: 'https://siem-kibana.atlassian.net/browse/CK-1', + }); + }); + + test('it calls createIncident correctly', async () => { + const params = { ...apiParams, externalId: null }; + await api.pushToService({ externalService, mapping, params }); + + expect(externalService.createIncident).toHaveBeenCalledWith({ + incident: { + description: + 'Incident description (created at 2020-04-27T10:59:46.202Z by Elastic User)', + summary: 'Incident title (created at 2020-04-27T10:59:46.202Z by Elastic User)', + }, + }); + expect(externalService.updateIncident).not.toHaveBeenCalled(); + }); + + test('it calls createComment correctly', async () => { + const params = { ...apiParams, externalId: null }; + await api.pushToService({ externalService, mapping, params }); + expect(externalService.createComment).toHaveBeenCalledTimes(2); + expect(externalService.createComment).toHaveBeenNthCalledWith(1, { + incidentId: 'incident-1', + comment: { + commentId: 'case-comment-1', + comment: 'A comment (added at 2020-04-27T10:59:46.202Z by Elastic User)', + createdAt: '2020-04-27T10:59:46.202Z', + createdBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + updatedAt: '2020-04-27T10:59:46.202Z', + updatedBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + }, + field: 'comments', + }); + + expect(externalService.createComment).toHaveBeenNthCalledWith(2, { + incidentId: 'incident-1', + comment: { + commentId: 'case-comment-2', + comment: 'Another comment (added at 2020-04-27T10:59:46.202Z by Elastic User)', + createdAt: '2020-04-27T10:59:46.202Z', + createdBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + updatedAt: '2020-04-27T10:59:46.202Z', + updatedBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + }, + field: 'comments', + }); + }); + }); + + describe('update incident', () => { + test('it updates an incident', async () => { + const res = await api.pushToService({ externalService, mapping, params: apiParams }); + + expect(res).toEqual({ + id: 'incident-1', + title: 'CK-1', + pushedDate: '2020-04-27T10:59:46.202Z', + url: 'https://siem-kibana.atlassian.net/browse/CK-1', + comments: [ + { + commentId: 'case-comment-1', + pushedDate: '2020-04-27T10:59:46.202Z', + }, + { + commentId: 'case-comment-2', + pushedDate: '2020-04-27T10:59:46.202Z', + }, + ], + }); + }); + + test('it updates an incident without comments', async () => { + const params = { ...apiParams, comments: [] }; + const res = await api.pushToService({ externalService, mapping, params }); + + expect(res).toEqual({ + id: 'incident-1', + title: 'CK-1', + pushedDate: '2020-04-27T10:59:46.202Z', + url: 'https://siem-kibana.atlassian.net/browse/CK-1', + }); + }); + + test('it calls updateIncident correctly', async () => { + const params = { ...apiParams }; + await api.pushToService({ externalService, mapping, params }); + + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + description: + 'Incident description (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + summary: 'Incident title (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + }, + }); + expect(externalService.createIncident).not.toHaveBeenCalled(); + }); + + test('it calls createComment correctly', async () => { + const params = { ...apiParams }; + await api.pushToService({ externalService, mapping, params }); + expect(externalService.createComment).toHaveBeenCalledTimes(2); + expect(externalService.createComment).toHaveBeenNthCalledWith(1, { + incidentId: 'incident-1', + comment: { + commentId: 'case-comment-1', + comment: 'A comment (added at 2020-04-27T10:59:46.202Z by Elastic User)', + createdAt: '2020-04-27T10:59:46.202Z', + createdBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + updatedAt: '2020-04-27T10:59:46.202Z', + updatedBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + }, + field: 'comments', + }); + + expect(externalService.createComment).toHaveBeenNthCalledWith(2, { + incidentId: 'incident-1', + comment: { + commentId: 'case-comment-2', + comment: 'Another comment (added at 2020-04-27T10:59:46.202Z by Elastic User)', + createdAt: '2020-04-27T10:59:46.202Z', + createdBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + updatedAt: '2020-04-27T10:59:46.202Z', + updatedBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + }, + field: 'comments', + }); + }); + }); + + describe('mapping variations', () => { + test('overwrite & append', async () => { + mapping.set('title', { + target: 'summary', + actionType: 'overwrite', + }); + + mapping.set('description', { + target: 'description', + actionType: 'append', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('summary', { + target: 'title', + actionType: 'overwrite', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + summary: 'Incident title (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + description: + 'description from jira \r\nIncident description (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + }, + }); + }); + + test('nothing & append', async () => { + mapping.set('title', { + target: 'summary', + actionType: 'nothing', + }); + + mapping.set('description', { + target: 'description', + actionType: 'append', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('summary', { + target: 'title', + actionType: 'nothing', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + description: + 'description from jira \r\nIncident description (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + }, + }); + }); + + test('append & append', async () => { + mapping.set('title', { + target: 'summary', + actionType: 'append', + }); + + mapping.set('description', { + target: 'description', + actionType: 'append', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('summary', { + target: 'title', + actionType: 'append', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + summary: + 'title from jira \r\nIncident title (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + description: + 'description from jira \r\nIncident description (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + }, + }); + }); + + test('nothing & nothing', async () => { + mapping.set('title', { + target: 'summary', + actionType: 'nothing', + }); + + mapping.set('description', { + target: 'description', + actionType: 'nothing', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('summary', { + target: 'title', + actionType: 'nothing', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: {}, + }); + }); + + test('overwrite & nothing', async () => { + mapping.set('title', { + target: 'summary', + actionType: 'overwrite', + }); + + mapping.set('description', { + target: 'description', + actionType: 'nothing', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('summary', { + target: 'title', + actionType: 'overwrite', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + summary: 'Incident title (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + }, + }); + }); + + test('overwrite & overwrite', async () => { + mapping.set('title', { + target: 'summary', + actionType: 'overwrite', + }); + + mapping.set('description', { + target: 'description', + actionType: 'overwrite', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('summary', { + target: 'title', + actionType: 'overwrite', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + summary: 'Incident title (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + description: + 'Incident description (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + }, + }); + }); + + test('nothing & overwrite', async () => { + mapping.set('title', { + target: 'summary', + actionType: 'nothing', + }); + + mapping.set('description', { + target: 'description', + actionType: 'overwrite', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('summary', { + target: 'title', + actionType: 'nothing', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + description: + 'Incident description (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + }, + }); + }); + + test('append & overwrite', async () => { + mapping.set('title', { + target: 'summary', + actionType: 'append', + }); + + mapping.set('description', { + target: 'description', + actionType: 'overwrite', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('summary', { + target: 'title', + actionType: 'append', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + summary: + 'title from jira \r\nIncident title (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + description: + 'Incident description (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + }, + }); + }); + + test('append & nothing', async () => { + mapping.set('title', { + target: 'summary', + actionType: 'append', + }); + + mapping.set('description', { + target: 'description', + actionType: 'nothing', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('summary', { + target: 'title', + actionType: 'append', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + summary: + 'title from jira \r\nIncident title (updated at 2020-04-27T10:59:46.202Z by Elastic User)', + }, + }); + }); + + test('comment nothing', async () => { + mapping.set('title', { + target: 'summary', + actionType: 'overwrite', + }); + + mapping.set('description', { + target: 'description', + actionType: 'nothing', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'nothing', + }); + + mapping.set('summary', { + target: 'title', + actionType: 'overwrite', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.createComment).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/api.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/api.ts new file mode 100644 index 00000000000000..3db66e5884af4a --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/api.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { api } from '../case/api'; diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/config.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/config.ts new file mode 100644 index 00000000000000..7e415109f1bd9d --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/config.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ExternalServiceConfiguration } from '../case/types'; +import * as i18n from './translations'; + +export const config: ExternalServiceConfiguration = { + id: '.jira', + name: i18n.NAME, +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts new file mode 100644 index 00000000000000..a2d7bb5930a754 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createConnector } from '../case/utils'; + +import { api } from './api'; +import { config } from './config'; +import { validate } from './validators'; +import { createExternalService } from './service'; +import { JiraSecretConfiguration, JiraPublicConfiguration } from './schema'; + +export const getActionType = createConnector({ + api, + config, + validate, + createExternalService, + validationSchema: { + config: JiraPublicConfiguration, + secrets: JiraSecretConfiguration, + }, +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/mocks.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/mocks.ts new file mode 100644 index 00000000000000..3ae0e9db36de0c --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/mocks.ts @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + ExternalService, + PushToServiceApiParams, + ExecutorSubActionPushParams, + MapRecord, +} from '../case/types'; + +const createMock = (): jest.Mocked => { + const service = { + getIncident: jest.fn().mockImplementation(() => + Promise.resolve({ + id: 'incident-1', + key: 'CK-1', + summary: 'title from jira', + description: 'description from jira', + created: '2020-04-27T10:59:46.202Z', + updated: '2020-04-27T10:59:46.202Z', + }) + ), + createIncident: jest.fn().mockImplementation(() => + Promise.resolve({ + id: 'incident-1', + title: 'CK-1', + pushedDate: '2020-04-27T10:59:46.202Z', + url: 'https://siem-kibana.atlassian.net/browse/CK-1', + }) + ), + updateIncident: jest.fn().mockImplementation(() => + Promise.resolve({ + id: 'incident-1', + title: 'CK-1', + pushedDate: '2020-04-27T10:59:46.202Z', + url: 'https://siem-kibana.atlassian.net/browse/CK-1', + }) + ), + createComment: jest.fn(), + }; + + service.createComment.mockImplementationOnce(() => + Promise.resolve({ + commentId: 'case-comment-1', + pushedDate: '2020-04-27T10:59:46.202Z', + externalCommentId: '1', + }) + ); + + service.createComment.mockImplementationOnce(() => + Promise.resolve({ + commentId: 'case-comment-2', + pushedDate: '2020-04-27T10:59:46.202Z', + externalCommentId: '2', + }) + ); + + return service; +}; + +const externalServiceMock = { + create: createMock, +}; + +const mapping: Map> = new Map(); + +mapping.set('title', { + target: 'summary', + actionType: 'overwrite', +}); + +mapping.set('description', { + target: 'description', + actionType: 'overwrite', +}); + +mapping.set('comments', { + target: 'comments', + actionType: 'append', +}); + +mapping.set('summary', { + target: 'title', + actionType: 'overwrite', +}); + +const executorParams: ExecutorSubActionPushParams = { + caseId: 'd4387ac5-0899-4dc2-bbfa-0dd605c934aa', + externalId: 'incident-3', + createdAt: '2020-04-27T10:59:46.202Z', + createdBy: { fullName: 'Elastic User', username: 'elastic' }, + updatedAt: '2020-04-27T10:59:46.202Z', + updatedBy: { fullName: 'Elastic User', username: 'elastic' }, + title: 'Incident title', + description: 'Incident description', + comments: [ + { + commentId: 'case-comment-1', + comment: 'A comment', + createdAt: '2020-04-27T10:59:46.202Z', + createdBy: { fullName: 'Elastic User', username: 'elastic' }, + updatedAt: '2020-04-27T10:59:46.202Z', + updatedBy: { fullName: 'Elastic User', username: 'elastic' }, + }, + { + commentId: 'case-comment-2', + comment: 'Another comment', + createdAt: '2020-04-27T10:59:46.202Z', + createdBy: { fullName: 'Elastic User', username: 'elastic' }, + updatedAt: '2020-04-27T10:59:46.202Z', + updatedBy: { fullName: 'Elastic User', username: 'elastic' }, + }, + ], +}; + +const apiParams: PushToServiceApiParams = { + ...executorParams, + externalCase: { summary: 'Incident title', description: 'Incident description' }, +}; + +export { externalServiceMock, mapping, executorParams, apiParams }; diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/schema.ts new file mode 100644 index 00000000000000..9c831e75d91c1c --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/schema.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { ExternalIncidentServiceConfiguration } from '../case/schema'; + +export const JiraPublicConfiguration = { + projectKey: schema.string(), + ...ExternalIncidentServiceConfiguration, +}; + +export const JiraPublicConfigurationSchema = schema.object(JiraPublicConfiguration); + +export const JiraSecretConfiguration = { + email: schema.string(), + apiToken: schema.string(), +}; + +export const JiraSecretConfigurationSchema = schema.object(JiraSecretConfiguration); diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts new file mode 100644 index 00000000000000..b9225b043d526e --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts @@ -0,0 +1,297 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import axios from 'axios'; + +import { createExternalService } from './service'; +import * as utils from '../case/utils'; +import { ExternalService } from '../case/types'; + +jest.mock('axios'); +jest.mock('../case/utils', () => { + const originalUtils = jest.requireActual('../case/utils'); + return { + ...originalUtils, + request: jest.fn(), + }; +}); + +axios.create = jest.fn(() => axios); +const requestMock = utils.request as jest.Mock; + +describe('Jira service', () => { + let service: ExternalService; + + beforeAll(() => { + service = createExternalService({ + config: { apiUrl: 'https://siem-kibana.atlassian.net', projectKey: 'CK' }, + secrets: { apiToken: 'token', email: 'elastic@elastic.com' }, + }); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('createExternalService', () => { + test('throws without url', () => { + expect(() => + createExternalService({ + config: { apiUrl: null, projectKey: 'CK' }, + secrets: { apiToken: 'token', email: 'elastic@elastic.com' }, + }) + ).toThrow(); + }); + + test('throws without projectKey', () => { + expect(() => + createExternalService({ + config: { apiUrl: 'test.com', projectKey: null }, + secrets: { apiToken: 'token', email: 'elastic@elastic.com' }, + }) + ).toThrow(); + }); + + test('throws without username', () => { + expect(() => + createExternalService({ + config: { apiUrl: 'test.com' }, + secrets: { apiToken: '', email: 'elastic@elastic.com' }, + }) + ).toThrow(); + }); + + test('throws without password', () => { + expect(() => + createExternalService({ + config: { apiUrl: 'test.com' }, + secrets: { apiToken: '', email: undefined }, + }) + ).toThrow(); + }); + }); + + describe('getIncident', () => { + test('it returns the incident correctly', async () => { + requestMock.mockImplementation(() => ({ + data: { id: '1', key: 'CK-1', fields: { summary: 'title', description: 'description' } }, + })); + const res = await service.getIncident('1'); + expect(res).toEqual({ id: '1', key: 'CK-1', summary: 'title', description: 'description' }); + }); + + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data: { id: '1', key: 'CK-1' }, + })); + + await service.getIncident('1'); + expect(requestMock).toHaveBeenCalledWith({ + axios, + url: 'https://siem-kibana.atlassian.net/rest/api/2/issue/1', + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + expect(service.getIncident('1')).rejects.toThrow( + 'Unable to get incident with id 1. Error: An error has occurred' + ); + }); + }); + + describe('createIncident', () => { + test('it creates the incident correctly', async () => { + // The response from Jira when creating an issue contains only the key and the id. + // The service makes two calls when creating an issue. One to create and one to get + // the created incident with all the necessary fields. + requestMock.mockImplementationOnce(() => ({ + data: { id: '1', key: 'CK-1', fields: { summary: 'title', description: 'description' } }, + })); + + requestMock.mockImplementationOnce(() => ({ + data: { id: '1', key: 'CK-1', fields: { created: '2020-04-27T10:59:46.202Z' } }, + })); + + const res = await service.createIncident({ + incident: { summary: 'title', description: 'desc' }, + }); + + expect(res).toEqual({ + title: 'CK-1', + id: '1', + pushedDate: '2020-04-27T10:59:46.202Z', + url: 'https://siem-kibana.atlassian.net/browse/CK-1', + }); + }); + + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data: { + id: '1', + key: 'CK-1', + fields: { created: '2020-04-27T10:59:46.202Z' }, + }, + })); + + await service.createIncident({ + incident: { summary: 'title', description: 'desc' }, + }); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + url: 'https://siem-kibana.atlassian.net/rest/api/2/issue', + method: 'post', + data: { + fields: { + summary: 'title', + description: 'desc', + project: { key: 'CK' }, + issuetype: { name: 'Task' }, + }, + }, + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + expect( + service.createIncident({ + incident: { summary: 'title', description: 'desc' }, + }) + ).rejects.toThrow('[Action][Jira]: Unable to create incident. Error: An error has occurred'); + }); + }); + + describe('updateIncident', () => { + test('it updates the incident correctly', async () => { + requestMock.mockImplementation(() => ({ + data: { + id: '1', + key: 'CK-1', + fields: { updated: '2020-04-27T10:59:46.202Z' }, + }, + })); + + const res = await service.updateIncident({ + incidentId: '1', + incident: { summary: 'title', description: 'desc' }, + }); + + expect(res).toEqual({ + title: 'CK-1', + id: '1', + pushedDate: '2020-04-27T10:59:46.202Z', + url: 'https://siem-kibana.atlassian.net/browse/CK-1', + }); + }); + + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data: { + id: '1', + key: 'CK-1', + fields: { updated: '2020-04-27T10:59:46.202Z' }, + }, + })); + + await service.updateIncident({ + incidentId: '1', + incident: { summary: 'title', description: 'desc' }, + }); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + method: 'put', + url: 'https://siem-kibana.atlassian.net/rest/api/2/issue/1', + data: { fields: { summary: 'title', description: 'desc' } }, + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + expect( + service.updateIncident({ + incidentId: '1', + incident: { summary: 'title', description: 'desc' }, + }) + ).rejects.toThrow( + '[Action][Jira]: Unable to update incident with id 1. Error: An error has occurred' + ); + }); + }); + + describe('createComment', () => { + test('it creates the comment correctly', async () => { + requestMock.mockImplementation(() => ({ + data: { + id: '1', + key: 'CK-1', + created: '2020-04-27T10:59:46.202Z', + }, + })); + + const res = await service.createComment({ + incidentId: '1', + comment: { comment: 'comment', commentId: 'comment-1' }, + field: 'comments', + }); + + expect(res).toEqual({ + commentId: 'comment-1', + pushedDate: '2020-04-27T10:59:46.202Z', + externalCommentId: '1', + }); + }); + + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data: { + id: '1', + key: 'CK-1', + created: '2020-04-27T10:59:46.202Z', + }, + })); + + await service.createComment({ + incidentId: '1', + comment: { comment: 'comment', commentId: 'comment-1' }, + field: 'my_field', + }); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + method: 'post', + url: 'https://siem-kibana.atlassian.net/rest/api/2/issue/1/comment', + data: { body: 'comment' }, + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + expect( + service.createComment({ + incidentId: '1', + comment: { comment: 'comment', commentId: 'comment-1' }, + field: 'comments', + }) + ).rejects.toThrow( + '[Action][Jira]: Unable to create comment at incident with id 1. Error: An error has occurred' + ); + }); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts new file mode 100644 index 00000000000000..ff22b8368e7dd2 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts @@ -0,0 +1,156 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import axios from 'axios'; + +import { ExternalServiceCredentials, ExternalService, ExternalServiceParams } from '../case/types'; +import { + JiraPublicConfigurationType, + JiraSecretConfigurationType, + CreateIncidentRequest, + UpdateIncidentRequest, + CreateCommentRequest, +} from './types'; + +import * as i18n from './translations'; +import { getErrorMessage, request } from '../case/utils'; + +const VERSION = '2'; +const BASE_URL = `rest/api/${VERSION}`; +const INCIDENT_URL = `issue`; +const COMMENT_URL = `comment`; + +const VIEW_INCIDENT_URL = `browse`; + +export const createExternalService = ({ + config, + secrets, +}: ExternalServiceCredentials): ExternalService => { + const { apiUrl: url, projectKey } = config as JiraPublicConfigurationType; + const { apiToken, email } = secrets as JiraSecretConfigurationType; + + if (!url || !projectKey || !apiToken || !email) { + throw Error(`[Action]${i18n.NAME}: Wrong configuration.`); + } + + const incidentUrl = `${url}/${BASE_URL}/${INCIDENT_URL}`; + const commentUrl = `${incidentUrl}/{issueId}/${COMMENT_URL}`; + const axiosInstance = axios.create({ + auth: { username: email, password: apiToken }, + }); + + const getIncidentViewURL = (key: string) => { + return `${url}/${VIEW_INCIDENT_URL}/${key}`; + }; + + const getCommentsURL = (issueId: string) => { + return commentUrl.replace('{issueId}', issueId); + }; + + const getIncident = async (id: string) => { + try { + const res = await request({ + axios: axiosInstance, + url: `${incidentUrl}/${id}`, + }); + + const { fields, ...rest } = res.data; + + return { ...rest, ...fields }; + } catch (error) { + throw new Error( + getErrorMessage(i18n.NAME, `Unable to get incident with id ${id}. Error: ${error.message}`) + ); + } + }; + + const createIncident = async ({ incident }: ExternalServiceParams) => { + // The response from Jira when creating an issue contains only the key and the id. + // The function makes two calls when creating an issue. One to create the issue and one to get + // the created issue with all the necessary fields. + try { + const res = await request({ + axios: axiosInstance, + url: `${incidentUrl}`, + method: 'post', + data: { + fields: { ...incident, project: { key: projectKey }, issuetype: { name: 'Task' } }, + }, + }); + + const updatedIncident = await getIncident(res.data.id); + + return { + title: updatedIncident.key, + id: updatedIncident.id, + pushedDate: new Date(updatedIncident.created).toISOString(), + url: getIncidentViewURL(updatedIncident.key), + }; + } catch (error) { + throw new Error( + getErrorMessage(i18n.NAME, `Unable to create incident. Error: ${error.message}`) + ); + } + }; + + const updateIncident = async ({ incidentId, incident }: ExternalServiceParams) => { + try { + await request({ + axios: axiosInstance, + method: 'put', + url: `${incidentUrl}/${incidentId}`, + data: { fields: { ...incident } }, + }); + + const updatedIncident = await getIncident(incidentId); + + return { + title: updatedIncident.key, + id: updatedIncident.id, + pushedDate: new Date(updatedIncident.updated).toISOString(), + url: getIncidentViewURL(updatedIncident.key), + }; + } catch (error) { + throw new Error( + getErrorMessage( + i18n.NAME, + `Unable to update incident with id ${incidentId}. Error: ${error.message}` + ) + ); + } + }; + + const createComment = async ({ incidentId, comment, field }: ExternalServiceParams) => { + try { + const res = await request({ + axios: axiosInstance, + method: 'post', + url: getCommentsURL(incidentId), + data: { body: comment.comment }, + }); + + return { + commentId: comment.commentId, + externalCommentId: res.data.id, + pushedDate: new Date(res.data.created).toISOString(), + }; + } catch (error) { + throw new Error( + getErrorMessage( + i18n.NAME, + `Unable to create comment at incident with id ${incidentId}. Error: ${error.message}` + ) + ); + } + }; + + return { + getIncident, + createIncident, + updateIncident, + createComment, + }; +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/translations.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/translations.ts new file mode 100644 index 00000000000000..dae0d75952e11a --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/translations.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const NAME = i18n.translate('xpack.actions.builtin.case.jiraTitle', { + defaultMessage: 'Jira', +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts new file mode 100644 index 00000000000000..8d9c6b92abb3b2 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/types.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { TypeOf } from '@kbn/config-schema'; +import { JiraPublicConfigurationSchema, JiraSecretConfigurationSchema } from './schema'; + +export type JiraPublicConfigurationType = TypeOf; +export type JiraSecretConfigurationType = TypeOf; + +interface CreateIncidentBasicRequestArgs { + summary: string; + description: string; +} +interface CreateIncidentRequestArgs extends CreateIncidentBasicRequestArgs { + project: { key: string }; + issuetype: { name: string }; +} + +export interface CreateIncidentRequest { + fields: CreateIncidentRequestArgs; +} + +export interface UpdateIncidentRequest { + fields: Partial; +} + +export interface CreateCommentRequest { + body: string; +} diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/validators.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/validators.ts new file mode 100644 index 00000000000000..7226071392bc63 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/validators.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { validateCommonConfig, validateCommonSecrets } from '../case/validators'; +import { ExternalServiceValidation } from '../case/types'; + +export const validate: ExternalServiceValidation = { + config: validateCommonConfig, + secrets: validateCommonSecrets, +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/action_handlers.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/action_handlers.test.ts deleted file mode 100644 index aa9b1dcfcf239b..00000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/action_handlers.test.ts +++ /dev/null @@ -1,850 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - handleCreateIncident, - handleUpdateIncident, - handleIncident, - createComments, -} from './action_handlers'; -import { ServiceNow } from './lib'; -import { Mapping } from './types'; - -jest.mock('./lib'); - -const ServiceNowMock = ServiceNow as jest.Mock; - -const finalMapping: Mapping = new Map(); - -finalMapping.set('title', { - target: 'short_description', - actionType: 'overwrite', -}); - -finalMapping.set('description', { - target: 'description', - actionType: 'overwrite', -}); - -finalMapping.set('comments', { - target: 'comments', - actionType: 'append', -}); - -finalMapping.set('short_description', { - target: 'title', - actionType: 'overwrite', -}); - -const params = { - caseId: '123', - title: 'a title', - description: 'a description', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: null, - updatedBy: null, - incidentId: null, - incident: { - short_description: 'a title', - description: 'a description', - }, - comments: [ - { - commentId: '456', - version: 'WzU3LDFd', - comment: 'first comment', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: null, - updatedBy: null, - }, - ], -}; - -beforeAll(() => { - ServiceNowMock.mockImplementation(() => { - return { - serviceNow: { - getUserID: jest.fn().mockResolvedValue('1234'), - getIncident: jest.fn().mockResolvedValue({ - short_description: 'servicenow title', - description: 'servicenow desc', - }), - createIncident: jest.fn().mockResolvedValue({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }), - updateIncident: jest.fn().mockResolvedValue({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }), - batchCreateComments: jest - .fn() - .mockResolvedValue([{ commentId: '456', pushedDate: '2020-03-10T12:24:20.000Z' }]), - }, - }; - }); -}); - -describe('handleIncident', () => { - test('create an incident', async () => { - const { serviceNow } = new ServiceNowMock(); - - const res = await handleIncident({ - incidentId: null, - serviceNow, - params, - comments: params.comments, - mapping: finalMapping, - }); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - comments: [ - { - commentId: '456', - pushedDate: '2020-03-10T12:24:20.000Z', - }, - ], - }); - }); - test('update an incident', async () => { - const { serviceNow } = new ServiceNowMock(); - - const res = await handleIncident({ - incidentId: '123', - serviceNow, - params, - comments: params.comments, - mapping: finalMapping, - }); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - comments: [ - { - commentId: '456', - pushedDate: '2020-03-10T12:24:20.000Z', - }, - ], - }); - }); -}); - -describe('handleCreateIncident', () => { - test('create an incident without comments', async () => { - const { serviceNow } = new ServiceNowMock(); - - const res = await handleCreateIncident({ - serviceNow, - params, - comments: [], - mapping: finalMapping, - }); - - expect(serviceNow.createIncident).toHaveBeenCalled(); - expect(serviceNow.createIncident).toHaveBeenCalledWith({ - short_description: 'a title (created at 2020-03-13T08:34:53.450Z by Elastic User)', - description: 'a description (created at 2020-03-13T08:34:53.450Z by Elastic User)', - }); - expect(serviceNow.createIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).not.toHaveBeenCalled(); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); - - test('create an incident with comments', async () => { - const { serviceNow } = new ServiceNowMock(); - - const res = await handleCreateIncident({ - serviceNow, - params, - comments: params.comments, - mapping: finalMapping, - }); - - expect(serviceNow.createIncident).toHaveBeenCalled(); - expect(serviceNow.createIncident).toHaveBeenCalledWith({ - description: 'a description (created at 2020-03-13T08:34:53.450Z by Elastic User)', - short_description: 'a title (created at 2020-03-13T08:34:53.450Z by Elastic User)', - }); - expect(serviceNow.createIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).toHaveBeenCalled(); - expect(serviceNow.batchCreateComments).toHaveBeenCalledWith( - '123', - [ - { - comment: 'first comment (added at 2020-03-13T08:34:53.450Z by Elastic User)', - commentId: '456', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { - fullName: 'Elastic User', - username: 'elastic', - }, - updatedAt: null, - updatedBy: null, - version: 'WzU3LDFd', - }, - ], - 'comments' - ); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - comments: [ - { - commentId: '456', - pushedDate: '2020-03-10T12:24:20.000Z', - }, - ], - }); - }); -}); - -describe('handleUpdateIncident', () => { - test('update an incident without comments', async () => { - const { serviceNow } = new ServiceNowMock(); - - const res = await handleUpdateIncident({ - incidentId: '123', - serviceNow, - params: { - ...params, - updatedAt: '2020-03-15T08:34:53.450Z', - updatedBy: { fullName: 'Another User', username: 'anotherUser' }, - }, - comments: [], - mapping: finalMapping, - }); - - expect(serviceNow.updateIncident).toHaveBeenCalled(); - expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', { - short_description: 'a title (updated at 2020-03-15T08:34:53.450Z by Another User)', - description: 'a description (updated at 2020-03-15T08:34:53.450Z by Another User)', - }); - expect(serviceNow.updateIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).not.toHaveBeenCalled(); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); - - test('update an incident with comments', async () => { - const { serviceNow } = new ServiceNowMock(); - serviceNow.batchCreateComments.mockResolvedValue([ - { commentId: '456', pushedDate: '2020-03-10T12:24:20.000Z' }, - { commentId: '789', pushedDate: '2020-03-10T12:24:20.000Z' }, - ]); - - const res = await handleUpdateIncident({ - incidentId: '123', - serviceNow, - params: { - ...params, - updatedAt: '2020-03-15T08:34:53.450Z', - updatedBy: { fullName: 'Another User', username: 'anotherUser' }, - }, - comments: [ - { - comment: 'first comment', - commentId: '456', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { - fullName: 'Elastic User', - username: 'elastic', - }, - updatedAt: null, - updatedBy: null, - version: 'WzU3LDFd', - }, - { - comment: 'second comment', - commentId: '789', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { - fullName: 'Elastic User', - username: 'elastic', - }, - updatedAt: '2020-03-16T08:34:53.450Z', - updatedBy: { - fullName: 'Another User', - username: 'anotherUser', - }, - version: 'WzU3LDFd', - }, - ], - mapping: finalMapping, - }); - - expect(serviceNow.updateIncident).toHaveBeenCalled(); - expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', { - description: 'a description (updated at 2020-03-15T08:34:53.450Z by Another User)', - short_description: 'a title (updated at 2020-03-15T08:34:53.450Z by Another User)', - }); - expect(serviceNow.updateIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).toHaveBeenCalled(); - expect(serviceNow.batchCreateComments).toHaveBeenCalledWith( - '123', - [ - { - comment: 'first comment (added at 2020-03-13T08:34:53.450Z by Elastic User)', - commentId: '456', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { - fullName: 'Elastic User', - username: 'elastic', - }, - updatedAt: null, - updatedBy: null, - version: 'WzU3LDFd', - }, - { - comment: 'second comment (added at 2020-03-16T08:34:53.450Z by Another User)', - commentId: '789', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { - fullName: 'Elastic User', - username: 'elastic', - }, - updatedAt: '2020-03-16T08:34:53.450Z', - updatedBy: { - fullName: 'Another User', - username: 'anotherUser', - }, - version: 'WzU3LDFd', - }, - ], - 'comments' - ); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - comments: [ - { - commentId: '456', - pushedDate: '2020-03-10T12:24:20.000Z', - }, - { - commentId: '789', - pushedDate: '2020-03-10T12:24:20.000Z', - }, - ], - }); - }); -}); - -describe('handleUpdateIncident: different action types', () => { - test('overwrite & append', async () => { - const { serviceNow } = new ServiceNowMock(); - finalMapping.set('title', { - target: 'short_description', - actionType: 'overwrite', - }); - - finalMapping.set('description', { - target: 'description', - actionType: 'append', - }); - - finalMapping.set('comments', { - target: 'comments', - actionType: 'append', - }); - - finalMapping.set('short_description', { - target: 'title', - actionType: 'overwrite', - }); - - const res = await handleUpdateIncident({ - incidentId: '123', - serviceNow, - params, - comments: [], - mapping: finalMapping, - }); - - expect(serviceNow.updateIncident).toHaveBeenCalled(); - expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', { - short_description: 'a title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - description: - 'servicenow desc \r\na description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - }); - expect(serviceNow.updateIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).not.toHaveBeenCalled(); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); - - test('nothing & append', async () => { - const { serviceNow } = new ServiceNowMock(); - finalMapping.set('title', { - target: 'short_description', - actionType: 'nothing', - }); - - finalMapping.set('description', { - target: 'description', - actionType: 'append', - }); - - finalMapping.set('comments', { - target: 'comments', - actionType: 'append', - }); - - finalMapping.set('short_description', { - target: 'title', - actionType: 'nothing', - }); - - const res = await handleUpdateIncident({ - incidentId: '123', - serviceNow, - params, - comments: [], - mapping: finalMapping, - }); - - expect(serviceNow.updateIncident).toHaveBeenCalled(); - expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', { - description: - 'servicenow desc \r\na description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - }); - expect(serviceNow.updateIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).not.toHaveBeenCalled(); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); - - test('append & append', async () => { - const { serviceNow } = new ServiceNowMock(); - finalMapping.set('title', { - target: 'short_description', - actionType: 'append', - }); - - finalMapping.set('description', { - target: 'description', - actionType: 'append', - }); - - finalMapping.set('comments', { - target: 'comments', - actionType: 'append', - }); - - finalMapping.set('short_description', { - target: 'title', - actionType: 'append', - }); - - const res = await handleUpdateIncident({ - incidentId: '123', - serviceNow, - params, - comments: [], - mapping: finalMapping, - }); - - expect(serviceNow.updateIncident).toHaveBeenCalled(); - expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', { - short_description: - 'servicenow title \r\na title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - description: - 'servicenow desc \r\na description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - }); - expect(serviceNow.updateIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).not.toHaveBeenCalled(); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); - - test('nothing & nothing', async () => { - const { serviceNow } = new ServiceNowMock(); - finalMapping.set('title', { - target: 'short_description', - actionType: 'nothing', - }); - - finalMapping.set('description', { - target: 'description', - actionType: 'nothing', - }); - - finalMapping.set('comments', { - target: 'comments', - actionType: 'append', - }); - - finalMapping.set('short_description', { - target: 'title', - actionType: 'nothing', - }); - - const res = await handleUpdateIncident({ - incidentId: '123', - serviceNow, - params, - comments: [], - mapping: finalMapping, - }); - - expect(serviceNow.updateIncident).toHaveBeenCalled(); - expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', {}); - expect(serviceNow.updateIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).not.toHaveBeenCalled(); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); - - test('overwrite & nothing', async () => { - const { serviceNow } = new ServiceNowMock(); - finalMapping.set('title', { - target: 'short_description', - actionType: 'overwrite', - }); - - finalMapping.set('description', { - target: 'description', - actionType: 'nothing', - }); - - finalMapping.set('comments', { - target: 'comments', - actionType: 'append', - }); - - finalMapping.set('short_description', { - target: 'title', - actionType: 'overwrite', - }); - - const res = await handleUpdateIncident({ - incidentId: '123', - serviceNow, - params, - comments: [], - mapping: finalMapping, - }); - - expect(serviceNow.updateIncident).toHaveBeenCalled(); - expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', { - short_description: 'a title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - }); - expect(serviceNow.updateIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).not.toHaveBeenCalled(); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); - - test('overwrite & overwrite', async () => { - const { serviceNow } = new ServiceNowMock(); - finalMapping.set('title', { - target: 'short_description', - actionType: 'overwrite', - }); - - finalMapping.set('description', { - target: 'description', - actionType: 'overwrite', - }); - - finalMapping.set('comments', { - target: 'comments', - actionType: 'append', - }); - - finalMapping.set('short_description', { - target: 'title', - actionType: 'overwrite', - }); - - const res = await handleUpdateIncident({ - incidentId: '123', - serviceNow, - params, - comments: [], - mapping: finalMapping, - }); - - expect(serviceNow.updateIncident).toHaveBeenCalled(); - expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', { - short_description: 'a title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - description: 'a description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - }); - expect(serviceNow.updateIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).not.toHaveBeenCalled(); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); - - test('nothing & overwrite', async () => { - const { serviceNow } = new ServiceNowMock(); - finalMapping.set('title', { - target: 'short_description', - actionType: 'nothing', - }); - - finalMapping.set('description', { - target: 'description', - actionType: 'overwrite', - }); - - finalMapping.set('comments', { - target: 'comments', - actionType: 'append', - }); - - finalMapping.set('short_description', { - target: 'title', - actionType: 'nothing', - }); - - const res = await handleUpdateIncident({ - incidentId: '123', - serviceNow, - params, - comments: [], - mapping: finalMapping, - }); - - expect(serviceNow.updateIncident).toHaveBeenCalled(); - expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', { - description: 'a description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - }); - expect(serviceNow.updateIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).not.toHaveBeenCalled(); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); - - test('append & overwrite', async () => { - const { serviceNow } = new ServiceNowMock(); - finalMapping.set('title', { - target: 'short_description', - actionType: 'append', - }); - - finalMapping.set('description', { - target: 'description', - actionType: 'overwrite', - }); - - finalMapping.set('comments', { - target: 'comments', - actionType: 'append', - }); - - finalMapping.set('short_description', { - target: 'title', - actionType: 'append', - }); - - const res = await handleUpdateIncident({ - incidentId: '123', - serviceNow, - params, - comments: [], - mapping: finalMapping, - }); - - expect(serviceNow.updateIncident).toHaveBeenCalled(); - expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', { - short_description: - 'servicenow title \r\na title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - description: 'a description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - }); - expect(serviceNow.updateIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).not.toHaveBeenCalled(); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); - - test('append & nothing', async () => { - const { serviceNow } = new ServiceNowMock(); - finalMapping.set('title', { - target: 'short_description', - actionType: 'append', - }); - - finalMapping.set('description', { - target: 'description', - actionType: 'nothing', - }); - - finalMapping.set('comments', { - target: 'comments', - actionType: 'append', - }); - - finalMapping.set('short_description', { - target: 'title', - actionType: 'append', - }); - - const res = await handleUpdateIncident({ - incidentId: '123', - serviceNow, - params, - comments: [], - mapping: finalMapping, - }); - - expect(serviceNow.updateIncident).toHaveBeenCalled(); - expect(serviceNow.updateIncident).toHaveBeenCalledWith('123', { - short_description: - 'servicenow title \r\na title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', - }); - expect(serviceNow.updateIncident).toHaveReturned(); - expect(serviceNow.batchCreateComments).not.toHaveBeenCalled(); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); -}); - -describe('createComments', () => { - test('create comments correctly', async () => { - const { serviceNow } = new ServiceNowMock(); - serviceNow.batchCreateComments.mockResolvedValue([ - { commentId: '456', pushedDate: '2020-03-10T12:24:20.000Z' }, - { commentId: '789', pushedDate: '2020-03-10T12:24:20.000Z' }, - ]); - - const comments = [ - { - comment: 'first comment', - commentId: '456', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { - fullName: 'Elastic User', - username: 'elastic', - }, - updatedAt: null, - updatedBy: null, - version: 'WzU3LDFd', - }, - { - comment: 'second comment', - commentId: '789', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { - fullName: 'Elastic User', - username: 'elastic', - }, - updatedAt: '2020-03-13T08:34:53.450Z', - updatedBy: { - fullName: 'Elastic User', - username: 'elastic', - }, - version: 'WzU3LDFd', - }, - ]; - - const res = await createComments(serviceNow, '123', 'comments', comments); - - expect(serviceNow.batchCreateComments).toHaveBeenCalled(); - expect(serviceNow.batchCreateComments).toHaveBeenCalledWith( - '123', - [ - { - comment: 'first comment', - commentId: '456', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { - fullName: 'Elastic User', - username: 'elastic', - }, - updatedAt: null, - updatedBy: null, - version: 'WzU3LDFd', - }, - { - comment: 'second comment', - commentId: '789', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { - fullName: 'Elastic User', - username: 'elastic', - }, - updatedAt: '2020-03-13T08:34:53.450Z', - updatedBy: { - fullName: 'Elastic User', - username: 'elastic', - }, - version: 'WzU3LDFd', - }, - ], - 'comments' - ); - expect(res).toEqual([ - { - commentId: '456', - pushedDate: '2020-03-10T12:24:20.000Z', - }, - { - commentId: '789', - pushedDate: '2020-03-10T12:24:20.000Z', - }, - ]); - }); -}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/action_handlers.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/action_handlers.ts deleted file mode 100644 index 9166f53cf757e4..00000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/action_handlers.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { zipWith } from 'lodash'; -import { CommentResponse } from './lib/types'; -import { - HandlerResponse, - Comment, - SimpleComment, - CreateHandlerArguments, - UpdateHandlerArguments, - IncidentHandlerArguments, -} from './types'; -import { ServiceNow } from './lib'; -import { transformFields, prepareFieldsForTransformation, transformComments } from './helpers'; - -export const createComments = async ( - serviceNow: ServiceNow, - incidentId: string, - key: string, - comments: Comment[] -): Promise => { - const createdComments = await serviceNow.batchCreateComments(incidentId, comments, key); - - return zipWith(comments, createdComments, (a: Comment, b: CommentResponse) => ({ - commentId: a.commentId, - pushedDate: b.pushedDate, - })); -}; - -export const handleCreateIncident = async ({ - serviceNow, - params, - comments, - mapping, -}: CreateHandlerArguments): Promise => { - const fields = prepareFieldsForTransformation({ - params, - mapping, - }); - - const incident = transformFields({ - params, - fields, - }); - - const createdIncident = await serviceNow.createIncident({ - ...incident, - }); - - const res: HandlerResponse = { ...createdIncident }; - - if ( - comments && - Array.isArray(comments) && - comments.length > 0 && - mapping.get('comments')?.actionType !== 'nothing' - ) { - comments = transformComments(comments, params, ['informationAdded']); - res.comments = [ - ...(await createComments( - serviceNow, - res.incidentId, - mapping.get('comments')!.target, - comments - )), - ]; - } - - return { ...res }; -}; - -export const handleUpdateIncident = async ({ - incidentId, - serviceNow, - params, - comments, - mapping, -}: UpdateHandlerArguments): Promise => { - const currentIncident = await serviceNow.getIncident(incidentId); - const fields = prepareFieldsForTransformation({ - params, - mapping, - defaultPipes: ['informationUpdated'], - }); - - const incident = transformFields({ - params, - fields, - currentIncident, - }); - - const updatedIncident = await serviceNow.updateIncident(incidentId, { - ...incident, - }); - - const res: HandlerResponse = { ...updatedIncident }; - - if ( - comments && - Array.isArray(comments) && - comments.length > 0 && - mapping.get('comments')?.actionType !== 'nothing' - ) { - comments = transformComments(comments, params, ['informationAdded']); - res.comments = [ - ...(await createComments(serviceNow, incidentId, mapping.get('comments')!.target, comments)), - ]; - } - - return { ...res }; -}; - -export const handleIncident = async ({ - incidentId, - serviceNow, - params, - comments, - mapping, -}: IncidentHandlerArguments): Promise => { - if (!incidentId) { - return await handleCreateIncident({ serviceNow, params, comments, mapping }); - } else { - return await handleUpdateIncident({ incidentId, serviceNow, params, comments, mapping }); - } -}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.test.ts new file mode 100644 index 00000000000000..86a83188412712 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.test.ts @@ -0,0 +1,523 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { api } from '../case/api'; +import { externalServiceMock, mapping, apiParams } from './mocks'; +import { ExternalService } from '../case/types'; + +describe('api', () => { + let externalService: jest.Mocked; + + beforeEach(() => { + externalService = externalServiceMock.create(); + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('pushToService', () => { + describe('create incident', () => { + test('it creates an incident', async () => { + const params = { ...apiParams, externalId: null }; + const res = await api.pushToService({ externalService, mapping, params }); + + expect(res).toEqual({ + id: 'incident-1', + title: 'INC01', + pushedDate: '2020-03-10T12:24:20.000Z', + url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', + comments: [ + { + commentId: 'case-comment-1', + pushedDate: '2020-03-10T12:24:20.000Z', + }, + { + commentId: 'case-comment-2', + pushedDate: '2020-03-10T12:24:20.000Z', + }, + ], + }); + }); + + test('it creates an incident without comments', async () => { + const params = { ...apiParams, externalId: null, comments: [] }; + const res = await api.pushToService({ externalService, mapping, params }); + + expect(res).toEqual({ + id: 'incident-1', + title: 'INC01', + pushedDate: '2020-03-10T12:24:20.000Z', + url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', + }); + }); + + test('it calls createIncident correctly', async () => { + const params = { ...apiParams, externalId: null }; + await api.pushToService({ externalService, mapping, params }); + + expect(externalService.createIncident).toHaveBeenCalledWith({ + incident: { + description: + 'Incident description (created at 2020-03-13T08:34:53.450Z by Elastic User)', + short_description: + 'Incident title (created at 2020-03-13T08:34:53.450Z by Elastic User)', + }, + }); + expect(externalService.updateIncident).not.toHaveBeenCalled(); + }); + + test('it calls createComment correctly', async () => { + const params = { ...apiParams, externalId: null }; + await api.pushToService({ externalService, mapping, params }); + expect(externalService.createComment).toHaveBeenCalledTimes(2); + expect(externalService.createComment).toHaveBeenNthCalledWith(1, { + incidentId: 'incident-1', + comment: { + commentId: 'case-comment-1', + comment: 'A comment (added at 2020-03-13T08:34:53.450Z by Elastic User)', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + updatedAt: '2020-03-13T08:34:53.450Z', + updatedBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + }, + field: 'comments', + }); + + expect(externalService.createComment).toHaveBeenNthCalledWith(2, { + incidentId: 'incident-1', + comment: { + commentId: 'case-comment-2', + comment: 'Another comment (added at 2020-03-13T08:34:53.450Z by Elastic User)', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + updatedAt: '2020-03-13T08:34:53.450Z', + updatedBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + }, + field: 'comments', + }); + }); + }); + + describe('update incident', () => { + test('it updates an incident', async () => { + const res = await api.pushToService({ externalService, mapping, params: apiParams }); + + expect(res).toEqual({ + id: 'incident-2', + title: 'INC02', + pushedDate: '2020-03-10T12:24:20.000Z', + url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', + comments: [ + { + commentId: 'case-comment-1', + pushedDate: '2020-03-10T12:24:20.000Z', + }, + { + commentId: 'case-comment-2', + pushedDate: '2020-03-10T12:24:20.000Z', + }, + ], + }); + }); + + test('it updates an incident without comments', async () => { + const params = { ...apiParams, comments: [] }; + const res = await api.pushToService({ externalService, mapping, params }); + + expect(res).toEqual({ + id: 'incident-2', + title: 'INC02', + pushedDate: '2020-03-10T12:24:20.000Z', + url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', + }); + }); + + test('it calls updateIncident correctly', async () => { + const params = { ...apiParams }; + await api.pushToService({ externalService, mapping, params }); + + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + description: + 'Incident description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + short_description: + 'Incident title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + }, + }); + expect(externalService.createIncident).not.toHaveBeenCalled(); + }); + + test('it calls createComment correctly', async () => { + const params = { ...apiParams }; + await api.pushToService({ externalService, mapping, params }); + expect(externalService.createComment).toHaveBeenCalledTimes(2); + expect(externalService.createComment).toHaveBeenNthCalledWith(1, { + incidentId: 'incident-2', + comment: { + commentId: 'case-comment-1', + comment: 'A comment (added at 2020-03-13T08:34:53.450Z by Elastic User)', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + updatedAt: '2020-03-13T08:34:53.450Z', + updatedBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + }, + field: 'comments', + }); + + expect(externalService.createComment).toHaveBeenNthCalledWith(2, { + incidentId: 'incident-2', + comment: { + commentId: 'case-comment-2', + comment: 'Another comment (added at 2020-03-13T08:34:53.450Z by Elastic User)', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + updatedAt: '2020-03-13T08:34:53.450Z', + updatedBy: { + fullName: 'Elastic User', + username: 'elastic', + }, + }, + field: 'comments', + }); + }); + }); + + describe('mapping variations', () => { + test('overwrite & append', async () => { + mapping.set('title', { + target: 'short_description', + actionType: 'overwrite', + }); + + mapping.set('description', { + target: 'description', + actionType: 'append', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('short_description', { + target: 'title', + actionType: 'overwrite', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + short_description: + 'Incident title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + description: + 'description from servicenow \r\nIncident description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + }, + }); + }); + + test('nothing & append', async () => { + mapping.set('title', { + target: 'short_description', + actionType: 'nothing', + }); + + mapping.set('description', { + target: 'description', + actionType: 'append', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('short_description', { + target: 'title', + actionType: 'nothing', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + description: + 'description from servicenow \r\nIncident description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + }, + }); + }); + + test('append & append', async () => { + mapping.set('title', { + target: 'short_description', + actionType: 'append', + }); + + mapping.set('description', { + target: 'description', + actionType: 'append', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('short_description', { + target: 'title', + actionType: 'append', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + short_description: + 'title from servicenow \r\nIncident title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + description: + 'description from servicenow \r\nIncident description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + }, + }); + }); + + test('nothing & nothing', async () => { + mapping.set('title', { + target: 'short_description', + actionType: 'nothing', + }); + + mapping.set('description', { + target: 'description', + actionType: 'nothing', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('short_description', { + target: 'title', + actionType: 'nothing', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: {}, + }); + }); + + test('overwrite & nothing', async () => { + mapping.set('title', { + target: 'short_description', + actionType: 'overwrite', + }); + + mapping.set('description', { + target: 'description', + actionType: 'nothing', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('short_description', { + target: 'title', + actionType: 'overwrite', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + short_description: + 'Incident title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + }, + }); + }); + + test('overwrite & overwrite', async () => { + mapping.set('title', { + target: 'short_description', + actionType: 'overwrite', + }); + + mapping.set('description', { + target: 'description', + actionType: 'overwrite', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('short_description', { + target: 'title', + actionType: 'overwrite', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + short_description: + 'Incident title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + description: + 'Incident description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + }, + }); + }); + + test('nothing & overwrite', async () => { + mapping.set('title', { + target: 'short_description', + actionType: 'nothing', + }); + + mapping.set('description', { + target: 'description', + actionType: 'overwrite', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('short_description', { + target: 'title', + actionType: 'nothing', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + description: + 'Incident description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + }, + }); + }); + + test('append & overwrite', async () => { + mapping.set('title', { + target: 'short_description', + actionType: 'append', + }); + + mapping.set('description', { + target: 'description', + actionType: 'overwrite', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('short_description', { + target: 'title', + actionType: 'append', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + short_description: + 'title from servicenow \r\nIncident title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + description: + 'Incident description (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + }, + }); + }); + + test('append & nothing', async () => { + mapping.set('title', { + target: 'short_description', + actionType: 'append', + }); + + mapping.set('description', { + target: 'description', + actionType: 'nothing', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'append', + }); + + mapping.set('short_description', { + target: 'title', + actionType: 'append', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.updateIncident).toHaveBeenCalledWith({ + incidentId: 'incident-3', + incident: { + short_description: + 'title from servicenow \r\nIncident title (updated at 2020-03-13T08:34:53.450Z by Elastic User)', + }, + }); + }); + + test('comment nothing', async () => { + mapping.set('title', { + target: 'short_description', + actionType: 'overwrite', + }); + + mapping.set('description', { + target: 'description', + actionType: 'nothing', + }); + + mapping.set('comments', { + target: 'comments', + actionType: 'nothing', + }); + + mapping.set('short_description', { + target: 'title', + actionType: 'overwrite', + }); + + await api.pushToService({ externalService, mapping, params: apiParams }); + expect(externalService.createComment).not.toHaveBeenCalled(); + }); + }); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.ts new file mode 100644 index 00000000000000..3db66e5884af4a --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/api.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { api } from '../case/api'; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/config.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/config.ts new file mode 100644 index 00000000000000..4ad8108c3b1374 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/config.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ExternalServiceConfiguration } from '../case/types'; +import * as i18n from './translations'; + +export const config: ExternalServiceConfiguration = { + id: '.servicenow', + name: i18n.NAME, +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.ts deleted file mode 100644 index 0a26996ea8d69a..00000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/helpers.ts +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import { flow } from 'lodash'; - -import { SUPPORTED_SOURCE_FIELDS } from './constants'; -import { - MapEntry, - Mapping, - AppendFieldArgs, - AppendInformationFieldArgs, - Params, - Comment, - TransformFieldsArgs, - PipedField, - PrepareFieldsForTransformArgs, - KeyAny, -} from './types'; -import { Incident } from './lib/types'; - -import * as transformers from './transformers'; -import * as i18n from './translations'; - -export const normalizeMapping = (supportedFields: string[], mapping: MapEntry[]): MapEntry[] => { - // Prevent prototype pollution and remove unsupported fields - return mapping.filter( - m => m.source !== '__proto__' && m.target !== '__proto__' && supportedFields.includes(m.source) - ); -}; - -export const buildMap = (mapping: MapEntry[]): Mapping => { - return normalizeMapping(SUPPORTED_SOURCE_FIELDS, mapping).reduce((fieldsMap, field) => { - const { source, target, actionType } = field; - fieldsMap.set(source, { target, actionType }); - fieldsMap.set(target, { target: source, actionType }); - return fieldsMap; - }, new Map()); -}; - -export const mapParams = (params: Record, mapping: Mapping) => { - return Object.keys(params).reduce((prev: KeyAny, curr: string): KeyAny => { - const field = mapping.get(curr); - if (field) { - prev[field.target] = params[curr]; - } - return prev; - }, {}); -}; - -export const appendField = ({ value, prefix = '', suffix = '' }: AppendFieldArgs): string => { - return `${prefix}${value} ${suffix}`; -}; - -const t = { ...transformers } as { [index: string]: Function }; // TODO: Find a better solution exists. - -export const prepareFieldsForTransformation = ({ - params, - mapping, - defaultPipes = ['informationCreated'], -}: PrepareFieldsForTransformArgs): PipedField[] => { - return Object.keys(params.incident) - .filter(p => mapping.get(p)!.actionType !== 'nothing') - .map(p => ({ - key: p, - value: params.incident[p] as string, - actionType: mapping.get(p)!.actionType, - pipes: [...defaultPipes], - })) - .map(p => ({ - ...p, - pipes: p.actionType === 'append' ? [...p.pipes, 'append'] : p.pipes, - })); -}; - -export const transformFields = ({ - params, - fields, - currentIncident, -}: TransformFieldsArgs): Incident => { - return fields.reduce((prev: Incident, cur) => { - const transform = flow(...cur.pipes.map(p => t[p])); - prev[cur.key] = transform({ - value: cur.value, - date: params.updatedAt ?? params.createdAt, - user: - params.updatedBy != null - ? params.updatedBy.fullName ?? params.updatedBy.username - : params.createdBy.fullName ?? params.createdBy.username, - previousValue: currentIncident ? currentIncident[cur.key] : '', - }).value; - return prev; - }, {} as Incident); -}; - -export const appendInformationToField = ({ - value, - user, - date, - mode = 'create', -}: AppendInformationFieldArgs): string => { - return appendField({ - value, - suffix: i18n.FIELD_INFORMATION(mode, date, user), - }); -}; - -export const transformComments = ( - comments: Comment[], - params: Params, - pipes: string[] -): Comment[] => { - return comments.map(c => ({ - ...c, - comment: flow(...pipes.map(p => t[p]))({ - value: c.comment, - date: c.updatedAt ?? c.createdAt, - user: - c.updatedBy != null - ? c.updatedBy.fullName ?? c.updatedBy.username - : c.createdBy.fullName ?? c.createdBy.username, - }).value, - })); -}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.test.ts deleted file mode 100644 index a6c3ae88765ac0..00000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.test.ts +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { getActionType } from '.'; -import { ActionType, Services, ActionTypeExecutorOptions } from '../../types'; -import { validateConfig, validateSecrets, validateParams } from '../../lib'; -import { createActionTypeRegistry } from '../index.test'; -import { actionsConfigMock } from '../../actions_config.mock'; -import { actionsMock } from '../../mocks'; - -import { ACTION_TYPE_ID } from './constants'; -import * as i18n from './translations'; - -import { handleIncident } from './action_handlers'; -import { incidentResponse } from './mock'; - -jest.mock('./action_handlers'); - -const handleIncidentMock = handleIncident as jest.Mock; - -const services: Services = actionsMock.createServices(); - -let actionType: ActionType; - -const mockOptions = { - name: 'servicenow-connector', - actionTypeId: '.servicenow', - secrets: { - username: 'secret-username', - password: 'secret-password', - }, - config: { - apiUrl: 'https://service-now.com', - casesConfiguration: { - mapping: [ - { - source: 'title', - target: 'short_description', - actionType: 'overwrite', - }, - { - source: 'description', - target: 'description', - actionType: 'overwrite', - }, - { - source: 'comments', - target: 'work_notes', - actionType: 'append', - }, - ], - }, - }, - params: { - caseId: 'd4387ac5-0899-4dc2-bbfa-0dd605c934aa', - incidentId: 'ceb5986e079f00100e48fbbf7c1ed06d', - title: 'Incident title', - description: 'Incident description', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: null, - updatedBy: null, - comments: [ - { - commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', - version: 'WzU3LDFd', - comment: 'A comment', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: null, - updatedBy: null, - }, - ], - }, -}; - -beforeAll(() => { - const { actionTypeRegistry } = createActionTypeRegistry(); - actionType = actionTypeRegistry.get(ACTION_TYPE_ID); -}); - -describe('get()', () => { - test('should return correct action type', () => { - expect(actionType.id).toEqual(ACTION_TYPE_ID); - expect(actionType.name).toEqual(i18n.NAME); - }); -}); - -describe('validateConfig()', () => { - test('should validate and pass when config is valid', () => { - const { config } = mockOptions; - expect(validateConfig(actionType, config)).toEqual(config); - }); - - test('should validate and throw error when config is invalid', () => { - expect(() => { - validateConfig(actionType, { shouldNotBeHere: true }); - }).toThrowErrorMatchingInlineSnapshot( - `"error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]"` - ); - }); - - test('should validate and pass when the servicenow url is whitelisted', () => { - actionType = getActionType({ - configurationUtilities: { - ...actionsConfigMock.create(), - ensureWhitelistedUri: url => { - expect(url).toEqual(mockOptions.config.apiUrl); - }, - }, - }); - - expect(validateConfig(actionType, mockOptions.config)).toEqual(mockOptions.config); - }); - - test('config validation returns an error if the specified URL isnt whitelisted', () => { - actionType = getActionType({ - configurationUtilities: { - ...actionsConfigMock.create(), - ensureWhitelistedUri: _ => { - throw new Error(`target url is not whitelisted`); - }, - }, - }); - - expect(() => { - validateConfig(actionType, mockOptions.config); - }).toThrowErrorMatchingInlineSnapshot( - `"error validating action type config: error configuring servicenow action: target url is not whitelisted"` - ); - }); -}); - -describe('validateSecrets()', () => { - test('should validate and pass when secrets is valid', () => { - const { secrets } = mockOptions; - expect(validateSecrets(actionType, secrets)).toEqual(secrets); - }); - - test('should validate and throw error when secrets is invalid', () => { - expect(() => { - validateSecrets(actionType, { username: false }); - }).toThrowErrorMatchingInlineSnapshot( - `"error validating action type secrets: [password]: expected value of type [string] but got [undefined]"` - ); - - expect(() => { - validateSecrets(actionType, { username: false, password: 'hello' }); - }).toThrowErrorMatchingInlineSnapshot( - `"error validating action type secrets: [username]: expected value of type [string] but got [boolean]"` - ); - }); -}); - -describe('validateParams()', () => { - test('should validate and pass when params is valid', () => { - const { params } = mockOptions; - expect(validateParams(actionType, params)).toEqual(params); - }); - - test('should validate and throw error when params is invalid', () => { - expect(() => { - validateParams(actionType, {}); - }).toThrowErrorMatchingInlineSnapshot( - `"error validating action params: [caseId]: expected value of type [string] but got [undefined]"` - ); - }); -}); - -describe('execute()', () => { - beforeEach(() => { - handleIncidentMock.mockReset(); - }); - - test('should create an incident', async () => { - const actionId = 'some-id'; - const { incidentId, ...rest } = mockOptions.params; - - const executorOptions: ActionTypeExecutorOptions = { - actionId, - config: mockOptions.config, - params: { ...rest }, - secrets: mockOptions.secrets, - services, - }; - - handleIncidentMock.mockImplementation(() => incidentResponse); - - const actionResponse = await actionType.executor(executorOptions); - expect(actionResponse).toEqual({ actionId, status: 'ok', data: incidentResponse }); - }); - - test('should throw an error when failed to create incident', async () => { - expect.assertions(1); - const { incidentId, ...rest } = mockOptions.params; - - const actionId = 'some-id'; - const executorOptions: ActionTypeExecutorOptions = { - actionId, - config: mockOptions.config, - params: { ...rest }, - secrets: mockOptions.secrets, - services, - }; - const errorMessage = 'Failed to create incident'; - - handleIncidentMock.mockImplementation(() => { - throw new Error(errorMessage); - }); - - try { - await actionType.executor(executorOptions); - } catch (error) { - expect(error.message).toEqual(errorMessage); - } - }); - - test('should update an incident', async () => { - const actionId = 'some-id'; - const executorOptions: ActionTypeExecutorOptions = { - actionId, - config: mockOptions.config, - params: { - ...mockOptions.params, - updatedAt: '2020-03-15T08:34:53.450Z', - updatedBy: { fullName: 'Another User', username: 'anotherUser' }, - }, - secrets: mockOptions.secrets, - services, - }; - - handleIncidentMock.mockImplementation(() => incidentResponse); - - const actionResponse = await actionType.executor(executorOptions); - expect(actionResponse).toEqual({ actionId, status: 'ok', data: incidentResponse }); - }); - - test('should throw an error when failed to update an incident', async () => { - expect.assertions(1); - - const actionId = 'some-id'; - const executorOptions: ActionTypeExecutorOptions = { - actionId, - config: mockOptions.config, - params: { - ...mockOptions.params, - updatedAt: '2020-03-15T08:34:53.450Z', - updatedBy: { fullName: 'Another User', username: 'anotherUser' }, - }, - secrets: mockOptions.secrets, - services, - }; - const errorMessage = 'Failed to update incident'; - - handleIncidentMock.mockImplementation(() => { - throw new Error(errorMessage); - }); - - try { - await actionType.executor(executorOptions); - } catch (error) { - expect(error.message).toEqual(errorMessage); - } - }); -}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts index 5066190d4fe561..dbb536d2fa53de 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/index.ts @@ -4,108 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { curry, isEmpty } from 'lodash'; -import { schema } from '@kbn/config-schema'; -import { - ActionType, - ActionTypeExecutorOptions, - ActionTypeExecutorResult, - ExecutorType, -} from '../../types'; -import { ActionsConfigurationUtilities } from '../../actions_config'; -import { ServiceNow } from './lib'; - -import * as i18n from './translations'; - -import { ACTION_TYPE_ID } from './constants'; -import { ConfigType, SecretsType, Comment, ExecutorParams } from './types'; - -import { ConfigSchemaProps, SecretsSchemaProps, ParamsSchema } from './schema'; - -import { buildMap, mapParams } from './helpers'; -import { handleIncident } from './action_handlers'; - -function validateConfig( - configurationUtilities: ActionsConfigurationUtilities, - configObject: ConfigType -) { - try { - if (isEmpty(configObject.casesConfiguration.mapping)) { - return i18n.MAPPING_EMPTY; - } - - configurationUtilities.ensureWhitelistedUri(configObject.apiUrl); - } catch (whitelistError) { - return i18n.WHITE_LISTED_ERROR(whitelistError.message); - } -} - -function validateSecrets( - configurationUtilities: ActionsConfigurationUtilities, - secrets: SecretsType -) {} +import { createConnector } from '../case/utils'; -// action type definition -export function getActionType({ - configurationUtilities, - executor = serviceNowExecutor, -}: { - configurationUtilities: ActionsConfigurationUtilities; - executor?: ExecutorType; -}): ActionType { - return { - id: ACTION_TYPE_ID, - name: i18n.NAME, - minimumLicenseRequired: 'platinum', - validate: { - config: schema.object(ConfigSchemaProps, { - validate: curry(validateConfig)(configurationUtilities), - }), - secrets: schema.object(SecretsSchemaProps, { - validate: curry(validateSecrets)(configurationUtilities), - }), - params: ParamsSchema, - }, - executor, - }; -} - -// action executor - -async function serviceNowExecutor( - execOptions: ActionTypeExecutorOptions -): Promise { - const actionId = execOptions.actionId; - const { - apiUrl, - casesConfiguration: { mapping: configurationMapping }, - } = execOptions.config as ConfigType; - const { username, password } = execOptions.secrets as SecretsType; - const params = execOptions.params as ExecutorParams; - const { comments, incidentId, ...restParams } = params; - - const mapping = buildMap(configurationMapping); - const incident = mapParams((restParams as unknown) as Record, mapping); - const serviceNow = new ServiceNow({ url: apiUrl, username, password }); - - const handlerInput = { - incidentId, - serviceNow, - params: { ...params, incident }, - comments: comments as Comment[], - mapping, - }; - - const res: Pick & - Pick = { - status: 'ok', - actionId, - }; - - const data = await handleIncident(handlerInput); - - return { - ...res, - data, - }; -} +import { api } from './api'; +import { config } from './config'; +import { validate } from './validators'; +import { createExternalService } from './service'; +import { + ExternalIncidentServiceConfiguration, + ExternalIncidentServiceSecretConfiguration, +} from '../case/schema'; + +export const getActionType = createConnector({ + api, + config, + validate, + createExternalService, + validationSchema: { + config: ExternalIncidentServiceConfiguration, + secrets: ExternalIncidentServiceSecretConfiguration, + }, +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/constants.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/constants.ts deleted file mode 100644 index 3f102ae19f437c..00000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/constants.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export const API_VERSION = 'v2'; -export const INCIDENT_URL = `api/now/${API_VERSION}/table/incident`; -export const USER_URL = `api/now/${API_VERSION}/table/sys_user?user_name=`; -export const COMMENT_URL = `api/now/${API_VERSION}/table/incident`; - -// Based on: https://docs.servicenow.com/bundle/orlando-platform-user-interface/page/use/navigation/reference/r_NavigatingByURLExamples.html -export const VIEW_INCIDENT_URL = `nav_to.do?uri=incident.do?sys_id=`; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/index.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/index.test.ts deleted file mode 100644 index 40eeb0f920f82c..00000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/index.test.ts +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import axios from 'axios'; -import { ServiceNow } from '.'; -import { instance, params } from '../mock'; - -jest.mock('axios'); - -axios.create = jest.fn(() => axios); -const axiosMock = (axios as unknown) as jest.Mock; - -let serviceNow: ServiceNow; - -const testMissingConfiguration = (field: string) => { - expect.assertions(1); - try { - new ServiceNow({ ...instance, [field]: '' }); - } catch (error) { - expect(error.message).toEqual('[Action][ServiceNow]: Wrong configuration.'); - } -}; - -const prependInstanceUrl = (url: string): string => `${instance.url}/${url}`; - -describe('ServiceNow lib', () => { - beforeEach(() => { - serviceNow = new ServiceNow(instance); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - test('should thrown an error if url is missing', () => { - testMissingConfiguration('url'); - }); - - test('should thrown an error if username is missing', () => { - testMissingConfiguration('username'); - }); - - test('should thrown an error if password is missing', () => { - testMissingConfiguration('password'); - }); - - test('get user id', async () => { - axiosMock.mockResolvedValue({ - status: 200, - headers: { - 'content-type': 'application/json', - }, - data: { result: [{ sys_id: '123' }] }, - }); - - const res = await serviceNow.getUserID(); - const [url, { method }] = axiosMock.mock.calls[0]; - - expect(url).toEqual(prependInstanceUrl('api/now/v2/table/sys_user?user_name=username')); - expect(method).toEqual('get'); - expect(res).toEqual('123'); - }); - - test('create incident', async () => { - axiosMock.mockResolvedValue({ - status: 200, - headers: { - 'content-type': 'application/json', - }, - data: { result: { sys_id: '123', number: 'INC01', sys_created_on: '2020-03-10 12:24:20' } }, - }); - - const res = await serviceNow.createIncident({ - short_description: 'A title', - description: 'A description', - caller_id: '123', - }); - const [url, { method, data }] = axiosMock.mock.calls[0]; - - expect(url).toEqual(prependInstanceUrl('api/now/v2/table/incident')); - expect(method).toEqual('post'); - expect(data).toEqual({ - short_description: 'A title', - description: 'A description', - caller_id: '123', - }); - - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); - - test('update incident', async () => { - axiosMock.mockResolvedValue({ - status: 200, - headers: { - 'content-type': 'application/json', - }, - data: { result: { sys_id: '123', number: 'INC01', sys_updated_on: '2020-03-10 12:24:20' } }, - }); - - const res = await serviceNow.updateIncident('123', { - short_description: params.title, - }); - const [url, { method, data }] = axiosMock.mock.calls[0]; - - expect(url).toEqual(prependInstanceUrl(`api/now/v2/table/incident/123`)); - expect(method).toEqual('patch'); - expect(data).toEqual({ short_description: params.title }); - expect(res).toEqual({ - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', - }); - }); - - test('create comment', async () => { - axiosMock.mockResolvedValue({ - status: 200, - headers: { - 'content-type': 'application/json', - }, - data: { result: { sys_updated_on: '2020-03-10 12:24:20' } }, - }); - - const comment = { - commentId: '456', - version: 'WzU3LDFd', - comment: 'A comment', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: null, - updatedBy: null, - }; - - const res = await serviceNow.createComment('123', comment, 'comments'); - - const [url, { method, data }] = axiosMock.mock.calls[0]; - - expect(url).toEqual(prependInstanceUrl(`api/now/v2/table/incident/123`)); - expect(method).toEqual('patch'); - expect(data).toEqual({ - comments: 'A comment', - }); - - expect(res).toEqual({ - commentId: '456', - pushedDate: '2020-03-10T12:24:20.000Z', - }); - }); - - test('create batch comment', async () => { - axiosMock.mockReturnValueOnce({ - status: 200, - headers: { - 'content-type': 'application/json', - }, - data: { result: { sys_updated_on: '2020-03-10 12:24:20' } }, - }); - - axiosMock.mockReturnValueOnce({ - status: 200, - headers: { - 'content-type': 'application/json', - }, - data: { result: { sys_updated_on: '2020-03-10 12:25:20' } }, - }); - - const comments = [ - { - commentId: '123', - version: 'WzU3LDFd', - comment: 'A comment', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: null, - updatedBy: null, - }, - { - commentId: '456', - version: 'WzU3LDFd', - comment: 'A second comment', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: null, - updatedBy: null, - }, - ]; - const res = await serviceNow.batchCreateComments('000', comments, 'comments'); - - comments.forEach((comment, index) => { - const [url, { method, data }] = axiosMock.mock.calls[index]; - expect(url).toEqual(prependInstanceUrl('api/now/v2/table/incident/000')); - expect(method).toEqual('patch'); - expect(data).toEqual({ - comments: comment.comment, - }); - expect(res).toEqual([ - { commentId: '123', pushedDate: '2020-03-10T12:24:20.000Z' }, - { commentId: '456', pushedDate: '2020-03-10T12:25:20.000Z' }, - ]); - }); - }); - - test('throw if not status is not ok', async () => { - expect.assertions(1); - - axiosMock.mockResolvedValue({ - status: 401, - headers: { - 'content-type': 'application/json', - }, - }); - try { - await serviceNow.getUserID(); - } catch (error) { - expect(error.message).toEqual( - '[Action][ServiceNow]: Unable to get user id. Error: [ServiceNow]: Instance is not alive.' - ); - } - }); - - test('throw if not content-type is not application/json', async () => { - expect.assertions(1); - - axiosMock.mockResolvedValue({ - status: 200, - headers: { - 'content-type': 'application/html', - }, - }); - try { - await serviceNow.getUserID(); - } catch (error) { - expect(error.message).toEqual( - '[Action][ServiceNow]: Unable to get user id. Error: [ServiceNow]: Instance is not alive.' - ); - } - }); - - test('check error when getting user', async () => { - expect.assertions(1); - - axiosMock.mockImplementationOnce(() => { - throw new Error('Bad request.'); - }); - try { - await serviceNow.getUserID(); - } catch (error) { - expect(error.message).toEqual( - '[Action][ServiceNow]: Unable to get user id. Error: Bad request.' - ); - } - }); - - test('check error when getting incident', async () => { - expect.assertions(1); - - axiosMock.mockImplementationOnce(() => { - throw new Error('Bad request.'); - }); - try { - await serviceNow.getIncident('123'); - } catch (error) { - expect(error.message).toEqual( - '[Action][ServiceNow]: Unable to get incident with id 123. Error: Bad request.' - ); - } - }); - - test('check error when creating incident', async () => { - expect.assertions(1); - - axiosMock.mockImplementationOnce(() => { - throw new Error('Bad request.'); - }); - try { - await serviceNow.createIncident({ short_description: 'title' }); - } catch (error) { - expect(error.message).toEqual( - '[Action][ServiceNow]: Unable to create incident. Error: Bad request.' - ); - } - }); - - test('check error when updating incident', async () => { - expect.assertions(1); - - axiosMock.mockImplementationOnce(() => { - throw new Error('Bad request.'); - }); - try { - await serviceNow.updateIncident('123', { short_description: 'title' }); - } catch (error) { - expect(error.message).toEqual( - '[Action][ServiceNow]: Unable to update incident with id 123. Error: Bad request.' - ); - } - }); - - test('check error when creating comment', async () => { - expect.assertions(1); - - axiosMock.mockImplementationOnce(() => { - throw new Error('Bad request.'); - }); - try { - await serviceNow.createComment( - '123', - { - commentId: '456', - version: 'WzU3LDFd', - comment: 'A second comment', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: null, - updatedBy: null, - }, - 'comment' - ); - } catch (error) { - expect(error.message).toEqual( - '[Action][ServiceNow]: Unable to create comment at incident with id 123. Error: Bad request.' - ); - } - }); -}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/index.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/index.ts deleted file mode 100644 index ed9cfe67a19a15..00000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/index.ts +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import axios, { AxiosInstance, Method, AxiosResponse } from 'axios'; - -import { INCIDENT_URL, USER_URL, COMMENT_URL, VIEW_INCIDENT_URL } from './constants'; -import { Instance, Incident, IncidentResponse, UpdateIncident, CommentResponse } from './types'; -import { Comment } from '../types'; - -const validStatusCodes = [200, 201]; - -class ServiceNow { - private readonly incidentUrl: string; - private readonly commentUrl: string; - private readonly userUrl: string; - private readonly axios: AxiosInstance; - - constructor(private readonly instance: Instance) { - if ( - !this.instance || - !this.instance.url || - !this.instance.username || - !this.instance.password - ) { - throw Error('[Action][ServiceNow]: Wrong configuration.'); - } - - this.incidentUrl = `${this.instance.url}/${INCIDENT_URL}`; - this.commentUrl = `${this.instance.url}/${COMMENT_URL}`; - this.userUrl = `${this.instance.url}/${USER_URL}`; - this.axios = axios.create({ - auth: { username: this.instance.username, password: this.instance.password }, - }); - } - - private _throwIfNotAlive(status: number, contentType: string) { - if (!validStatusCodes.includes(status) || !contentType.includes('application/json')) { - throw new Error('[ServiceNow]: Instance is not alive.'); - } - } - - private async _request({ - url, - method = 'get', - data = {}, - }: { - url: string; - method?: Method; - data?: unknown; - }): Promise { - const res = await this.axios(url, { method, data }); - this._throwIfNotAlive(res.status, res.headers['content-type']); - return res; - } - - private _patch({ url, data }: { url: string; data: unknown }): Promise { - return this._request({ - url, - method: 'patch', - data, - }); - } - - private _addTimeZoneToDate(date: string, timezone = 'GMT'): string { - return `${date} GMT`; - } - - private _getErrorMessage(msg: string) { - return `[Action][ServiceNow]: ${msg}`; - } - - private _getIncidentViewURL(id: string) { - return `${this.instance.url}/${VIEW_INCIDENT_URL}${id}`; - } - - async getUserID(): Promise { - try { - const res = await this._request({ url: `${this.userUrl}${this.instance.username}` }); - return res.data.result[0].sys_id; - } catch (error) { - throw new Error(this._getErrorMessage(`Unable to get user id. Error: ${error.message}`)); - } - } - - async getIncident(incidentId: string) { - try { - const res = await this._request({ - url: `${this.incidentUrl}/${incidentId}`, - }); - - return { ...res.data.result }; - } catch (error) { - throw new Error( - this._getErrorMessage( - `Unable to get incident with id ${incidentId}. Error: ${error.message}` - ) - ); - } - } - - async createIncident(incident: Incident): Promise { - try { - const res = await this._request({ - url: `${this.incidentUrl}`, - method: 'post', - data: { ...incident }, - }); - - return { - number: res.data.result.number, - incidentId: res.data.result.sys_id, - pushedDate: new Date(this._addTimeZoneToDate(res.data.result.sys_created_on)).toISOString(), - url: this._getIncidentViewURL(res.data.result.sys_id), - }; - } catch (error) { - throw new Error(this._getErrorMessage(`Unable to create incident. Error: ${error.message}`)); - } - } - - async updateIncident(incidentId: string, incident: UpdateIncident): Promise { - try { - const res = await this._patch({ - url: `${this.incidentUrl}/${incidentId}`, - data: { ...incident }, - }); - - return { - number: res.data.result.number, - incidentId: res.data.result.sys_id, - pushedDate: new Date(this._addTimeZoneToDate(res.data.result.sys_updated_on)).toISOString(), - url: this._getIncidentViewURL(res.data.result.sys_id), - }; - } catch (error) { - throw new Error( - this._getErrorMessage( - `Unable to update incident with id ${incidentId}. Error: ${error.message}` - ) - ); - } - } - - async batchCreateComments( - incidentId: string, - comments: Comment[], - field: string - ): Promise { - // Create comments sequentially. - const promises = comments.reduce(async (prevPromise, currentComment) => { - const totalComments = await prevPromise; - const res = await this.createComment(incidentId, currentComment, field); - return [...totalComments, res]; - }, Promise.resolve([] as CommentResponse[])); - - const res = await promises; - return res; - } - - async createComment( - incidentId: string, - comment: Comment, - field: string - ): Promise { - try { - const res = await this._patch({ - url: `${this.commentUrl}/${incidentId}`, - data: { [field]: comment.comment }, - }); - - return { - commentId: comment.commentId, - pushedDate: new Date(this._addTimeZoneToDate(res.data.result.sys_updated_on)).toISOString(), - }; - } catch (error) { - throw new Error( - this._getErrorMessage( - `Unable to create comment at incident with id ${incidentId}. Error: ${error.message}` - ) - ); - } - } -} - -export { ServiceNow }; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/types.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/types.ts deleted file mode 100644 index a65e417dbc486e..00000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/lib/types.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export interface Instance { - url: string; - username: string; - password: string; -} - -export interface Incident { - short_description: string; - description?: string; - caller_id?: string; - [index: string]: string | undefined; -} - -export interface IncidentResponse { - number: string; - incidentId: string; - pushedDate: string; - url: string; -} - -export interface CommentResponse { - commentId: string; - pushedDate: string; -} - -export type UpdateIncident = Partial; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/mock.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/mock.ts deleted file mode 100644 index 06c006fb378254..00000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/mock.ts +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { MapEntry, Mapping, ExecutorParams } from './types'; -import { Incident } from './lib/types'; - -const mapping: MapEntry[] = [ - { source: 'title', target: 'short_description', actionType: 'overwrite' }, - { source: 'description', target: 'description', actionType: 'append' }, - { source: 'comments', target: 'comments', actionType: 'append' }, -]; - -const finalMapping: Mapping = new Map(); - -finalMapping.set('title', { - target: 'short_description', - actionType: 'overwrite', -}); - -finalMapping.set('description', { - target: 'description', - actionType: 'append', -}); - -finalMapping.set('comments', { - target: 'comments', - actionType: 'append', -}); - -finalMapping.set('short_description', { - target: 'title', - actionType: 'overwrite', -}); - -const params: ExecutorParams = { - caseId: 'd4387ac5-0899-4dc2-bbfa-0dd605c934aa', - incidentId: 'ceb5986e079f00100e48fbbf7c1ed06d', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: '2020-03-13T08:34:53.450Z', - updatedBy: { fullName: 'Elastic User', username: 'elastic' }, - title: 'Incident title', - description: 'Incident description', - comments: [ - { - commentId: 'b5b4c4d0-574e-11ea-9e2e-21b90f8a9631', - version: 'WzU3LDFd', - comment: 'A comment', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: '2020-03-13T08:34:53.450Z', - updatedBy: { fullName: 'Elastic User', username: 'elastic' }, - }, - { - commentId: 'e3db587f-ca27-4ae9-ad2e-31f2dcc9bd0d', - version: 'WlK3LDFd', - comment: 'Another comment', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: '2020-03-13T08:34:53.450Z', - updatedBy: { fullName: 'Elastic User', username: 'elastic' }, - }, - ], -}; - -const incidentResponse = { - incidentId: 'c816f79cc0a8016401c5a33be04be441', - number: 'INC0010001', - pushedDate: '2020-03-13T08:34:53.450Z', - url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', -}; - -const userId = '2e9a0a5e2f79001016ab51172799b670'; - -const axiosResponse = { - status: 200, - headers: { - 'content-type': 'application/json', - }, -}; -const userIdResponse = { - result: [{ sys_id: userId }], -}; - -const incidentAxiosResponse = { - result: { sys_id: incidentResponse.incidentId, number: incidentResponse.number }, -}; - -const instance = { - url: 'https://instance.service-now.com', - username: 'username', - password: 'password', -}; - -const incident: Incident = { - short_description: params.title, - description: params.description, - caller_id: userId, -}; - -export { - mapping, - finalMapping, - params, - incidentResponse, - incidentAxiosResponse, - userId, - userIdResponse, - axiosResponse, - instance, - incident, -}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/mocks.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/mocks.ts new file mode 100644 index 00000000000000..37228380910b3d --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/mocks.ts @@ -0,0 +1,117 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + ExternalService, + PushToServiceApiParams, + ExecutorSubActionPushParams, + MapRecord, +} from '../case/types'; + +const createMock = (): jest.Mocked => { + const service = { + getIncident: jest.fn().mockImplementation(() => + Promise.resolve({ + short_description: 'title from servicenow', + description: 'description from servicenow', + }) + ), + createIncident: jest.fn().mockImplementation(() => + Promise.resolve({ + id: 'incident-1', + title: 'INC01', + pushedDate: '2020-03-10T12:24:20.000Z', + url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', + }) + ), + updateIncident: jest.fn().mockImplementation(() => + Promise.resolve({ + id: 'incident-2', + title: 'INC02', + pushedDate: '2020-03-10T12:24:20.000Z', + url: 'https://instance.service-now.com/nav_to.do?uri=incident.do?sys_id=123', + }) + ), + createComment: jest.fn(), + }; + + service.createComment.mockImplementationOnce(() => + Promise.resolve({ + commentId: 'case-comment-1', + pushedDate: '2020-03-10T12:24:20.000Z', + }) + ); + + service.createComment.mockImplementationOnce(() => + Promise.resolve({ + commentId: 'case-comment-2', + pushedDate: '2020-03-10T12:24:20.000Z', + }) + ); + return service; +}; + +const externalServiceMock = { + create: createMock, +}; + +const mapping: Map> = new Map(); + +mapping.set('title', { + target: 'short_description', + actionType: 'overwrite', +}); + +mapping.set('description', { + target: 'description', + actionType: 'overwrite', +}); + +mapping.set('comments', { + target: 'comments', + actionType: 'append', +}); + +mapping.set('short_description', { + target: 'title', + actionType: 'overwrite', +}); + +const executorParams: ExecutorSubActionPushParams = { + caseId: 'd4387ac5-0899-4dc2-bbfa-0dd605c934aa', + externalId: 'incident-3', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: 'Elastic User', username: 'elastic' }, + updatedAt: '2020-03-13T08:34:53.450Z', + updatedBy: { fullName: 'Elastic User', username: 'elastic' }, + title: 'Incident title', + description: 'Incident description', + comments: [ + { + commentId: 'case-comment-1', + comment: 'A comment', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: 'Elastic User', username: 'elastic' }, + updatedAt: '2020-03-13T08:34:53.450Z', + updatedBy: { fullName: 'Elastic User', username: 'elastic' }, + }, + { + commentId: 'case-comment-2', + comment: 'Another comment', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: 'Elastic User', username: 'elastic' }, + updatedAt: '2020-03-13T08:34:53.450Z', + updatedBy: { fullName: 'Elastic User', username: 'elastic' }, + }, + ], +}; + +const apiParams: PushToServiceApiParams = { + ...executorParams, + externalCase: { short_description: 'Incident title', description: 'Incident description' }, +}; + +export { externalServiceMock, mapping, executorParams, apiParams }; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts deleted file mode 100644 index 889b57c8e92e23..00000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { schema } from '@kbn/config-schema'; - -export const MapEntrySchema = schema.object({ - source: schema.string(), - target: schema.string(), - actionType: schema.oneOf([ - schema.literal('nothing'), - schema.literal('overwrite'), - schema.literal('append'), - ]), -}); - -export const CasesConfigurationSchema = schema.object({ - mapping: schema.arrayOf(MapEntrySchema), -}); - -export const ConfigSchemaProps = { - apiUrl: schema.string(), - casesConfiguration: CasesConfigurationSchema, -}; - -export const ConfigSchema = schema.object(ConfigSchemaProps); - -export const SecretsSchemaProps = { - password: schema.string(), - username: schema.string(), -}; - -export const SecretsSchema = schema.object(SecretsSchemaProps); - -export const UserSchema = schema.object({ - fullName: schema.nullable(schema.string()), - username: schema.string(), -}); - -const EntityInformationSchemaProps = { - createdAt: schema.string(), - createdBy: UserSchema, - updatedAt: schema.nullable(schema.string()), - updatedBy: schema.nullable(UserSchema), -}; - -export const EntityInformationSchema = schema.object(EntityInformationSchemaProps); - -export const CommentSchema = schema.object({ - commentId: schema.string(), - comment: schema.string(), - version: schema.maybe(schema.string()), - ...EntityInformationSchemaProps, -}); - -export const ExecutorAction = schema.oneOf([ - schema.literal('newIncident'), - schema.literal('updateIncident'), -]); - -export const ParamsSchema = schema.object({ - caseId: schema.string(), - title: schema.string(), - comments: schema.maybe(schema.arrayOf(CommentSchema)), - description: schema.maybe(schema.string()), - incidentId: schema.nullable(schema.string()), - ...EntityInformationSchemaProps, -}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.test.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.test.ts new file mode 100644 index 00000000000000..f65cd5430560ed --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.test.ts @@ -0,0 +1,255 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import axios from 'axios'; + +import { createExternalService } from './service'; +import * as utils from '../case/utils'; +import { ExternalService } from '../case/types'; + +jest.mock('axios'); +jest.mock('../case/utils', () => { + const originalUtils = jest.requireActual('../case/utils'); + return { + ...originalUtils, + request: jest.fn(), + patch: jest.fn(), + }; +}); + +axios.create = jest.fn(() => axios); +const requestMock = utils.request as jest.Mock; +const patchMock = utils.patch as jest.Mock; + +describe('ServiceNow service', () => { + let service: ExternalService; + + beforeAll(() => { + service = createExternalService({ + config: { apiUrl: 'https://dev102283.service-now.com' }, + secrets: { username: 'admin', password: 'admin' }, + }); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('createExternalService', () => { + test('throws without url', () => { + expect(() => + createExternalService({ + config: { apiUrl: null }, + secrets: { username: 'admin', password: 'admin' }, + }) + ).toThrow(); + }); + + test('throws without username', () => { + expect(() => + createExternalService({ + config: { apiUrl: 'test.com' }, + secrets: { username: '', password: 'admin' }, + }) + ).toThrow(); + }); + + test('throws without password', () => { + expect(() => + createExternalService({ + config: { apiUrl: 'test.com' }, + secrets: { username: '', password: undefined }, + }) + ).toThrow(); + }); + }); + + describe('getIncident', () => { + test('it returns the incident correctly', async () => { + requestMock.mockImplementation(() => ({ + data: { result: { sys_id: '1', number: 'INC01' } }, + })); + const res = await service.getIncident('1'); + expect(res).toEqual({ sys_id: '1', number: 'INC01' }); + }); + + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data: { result: { sys_id: '1', number: 'INC01' } }, + })); + + await service.getIncident('1'); + expect(requestMock).toHaveBeenCalledWith({ + axios, + url: 'https://dev102283.service-now.com/api/now/v2/table/incident/1', + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + expect(service.getIncident('1')).rejects.toThrow( + 'Unable to get incident with id 1. Error: An error has occurred' + ); + }); + }); + + describe('createIncident', () => { + test('it creates the incident correctly', async () => { + requestMock.mockImplementation(() => ({ + data: { result: { sys_id: '1', number: 'INC01', sys_created_on: '2020-03-10 12:24:20' } }, + })); + + const res = await service.createIncident({ + incident: { short_description: 'title', description: 'desc' }, + }); + + expect(res).toEqual({ + title: 'INC01', + id: '1', + pushedDate: '2020-03-10T12:24:20.000Z', + url: 'https://dev102283.service-now.com/nav_to.do?uri=incident.do?sys_id=1', + }); + }); + + test('it should call request with correct arguments', async () => { + requestMock.mockImplementation(() => ({ + data: { result: { sys_id: '1', number: 'INC01', sys_created_on: '2020-03-10 12:24:20' } }, + })); + + await service.createIncident({ + incident: { short_description: 'title', description: 'desc' }, + }); + + expect(requestMock).toHaveBeenCalledWith({ + axios, + url: 'https://dev102283.service-now.com/api/now/v2/table/incident', + method: 'post', + data: { short_description: 'title', description: 'desc' }, + }); + }); + + test('it should throw an error', async () => { + requestMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + expect( + service.createIncident({ + incident: { short_description: 'title', description: 'desc' }, + }) + ).rejects.toThrow( + '[Action][ServiceNow]: Unable to create incident. Error: An error has occurred' + ); + }); + }); + + describe('updateIncident', () => { + test('it updates the incident correctly', async () => { + patchMock.mockImplementation(() => ({ + data: { result: { sys_id: '1', number: 'INC01', sys_updated_on: '2020-03-10 12:24:20' } }, + })); + + const res = await service.updateIncident({ + incidentId: '1', + incident: { short_description: 'title', description: 'desc' }, + }); + + expect(res).toEqual({ + title: 'INC01', + id: '1', + pushedDate: '2020-03-10T12:24:20.000Z', + url: 'https://dev102283.service-now.com/nav_to.do?uri=incident.do?sys_id=1', + }); + }); + + test('it should call request with correct arguments', async () => { + patchMock.mockImplementation(() => ({ + data: { result: { sys_id: '1', number: 'INC01', sys_updated_on: '2020-03-10 12:24:20' } }, + })); + + await service.updateIncident({ + incidentId: '1', + incident: { short_description: 'title', description: 'desc' }, + }); + + expect(patchMock).toHaveBeenCalledWith({ + axios, + url: 'https://dev102283.service-now.com/api/now/v2/table/incident/1', + data: { short_description: 'title', description: 'desc' }, + }); + }); + + test('it should throw an error', async () => { + patchMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + expect( + service.updateIncident({ + incidentId: '1', + incident: { short_description: 'title', description: 'desc' }, + }) + ).rejects.toThrow( + '[Action][ServiceNow]: Unable to update incident with id 1. Error: An error has occurred' + ); + }); + }); + + describe('createComment', () => { + test('it creates the comment correctly', async () => { + patchMock.mockImplementation(() => ({ + data: { result: { sys_id: '1', number: 'INC01', sys_updated_on: '2020-03-10 12:24:20' } }, + })); + + const res = await service.createComment({ + incidentId: '1', + comment: { comment: 'comment', commentId: 'comment-1' }, + field: 'comments', + }); + + expect(res).toEqual({ + commentId: 'comment-1', + pushedDate: '2020-03-10T12:24:20.000Z', + }); + }); + + test('it should call request with correct arguments', async () => { + patchMock.mockImplementation(() => ({ + data: { result: { sys_id: '1', number: 'INC01', sys_updated_on: '2020-03-10 12:24:20' } }, + })); + + await service.createComment({ + incidentId: '1', + comment: { comment: 'comment', commentId: 'comment-1' }, + field: 'my_field', + }); + + expect(patchMock).toHaveBeenCalledWith({ + axios, + url: 'https://dev102283.service-now.com/api/now/v2/table/incident/1', + data: { my_field: 'comment' }, + }); + }); + + test('it should throw an error', async () => { + patchMock.mockImplementation(() => { + throw new Error('An error has occurred'); + }); + + expect( + service.createComment({ + incidentId: '1', + comment: { comment: 'comment', commentId: 'comment-1' }, + field: 'comments', + }) + ).rejects.toThrow( + '[Action][ServiceNow]: Unable to create comment at incident with id 1. Error: An error has occurred' + ); + }); + }); +}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.ts new file mode 100644 index 00000000000000..541fefce2f2ff5 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/service.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import axios from 'axios'; + +import { ExternalServiceCredentials, ExternalService, ExternalServiceParams } from '../case/types'; +import { addTimeZoneToDate, patch, request, getErrorMessage } from '../case/utils'; + +import * as i18n from './translations'; +import { + ServiceNowPublicConfigurationType, + ServiceNowSecretConfigurationType, + CreateIncidentRequest, + UpdateIncidentRequest, + CreateCommentRequest, +} from './types'; + +const API_VERSION = 'v2'; +const INCIDENT_URL = `api/now/${API_VERSION}/table/incident`; +const COMMENT_URL = `api/now/${API_VERSION}/table/incident`; + +// Based on: https://docs.servicenow.com/bundle/orlando-platform-user-interface/page/use/navigation/reference/r_NavigatingByURLExamples.html +const VIEW_INCIDENT_URL = `nav_to.do?uri=incident.do?sys_id=`; + +export const createExternalService = ({ + config, + secrets, +}: ExternalServiceCredentials): ExternalService => { + const { apiUrl: url } = config as ServiceNowPublicConfigurationType; + const { username, password } = secrets as ServiceNowSecretConfigurationType; + + if (!url || !username || !password) { + throw Error(`[Action]${i18n.NAME}: Wrong configuration.`); + } + + const incidentUrl = `${url}/${INCIDENT_URL}`; + const commentUrl = `${url}/${COMMENT_URL}`; + const axiosInstance = axios.create({ + auth: { username, password }, + }); + + const getIncidentViewURL = (id: string) => { + return `${url}/${VIEW_INCIDENT_URL}${id}`; + }; + + const getIncident = async (id: string) => { + try { + const res = await request({ + axios: axiosInstance, + url: `${incidentUrl}/${id}`, + }); + + return { ...res.data.result }; + } catch (error) { + throw new Error( + getErrorMessage(i18n.NAME, `Unable to get incident with id ${id}. Error: ${error.message}`) + ); + } + }; + + const createIncident = async ({ incident }: ExternalServiceParams) => { + try { + const res = await request({ + axios: axiosInstance, + url: `${incidentUrl}`, + method: 'post', + data: { ...incident }, + }); + + return { + title: res.data.result.number, + id: res.data.result.sys_id, + pushedDate: new Date(addTimeZoneToDate(res.data.result.sys_created_on)).toISOString(), + url: getIncidentViewURL(res.data.result.sys_id), + }; + } catch (error) { + throw new Error( + getErrorMessage(i18n.NAME, `Unable to create incident. Error: ${error.message}`) + ); + } + }; + + const updateIncident = async ({ incidentId, incident }: ExternalServiceParams) => { + try { + const res = await patch({ + axios: axiosInstance, + url: `${incidentUrl}/${incidentId}`, + data: { ...incident }, + }); + + return { + title: res.data.result.number, + id: res.data.result.sys_id, + pushedDate: new Date(addTimeZoneToDate(res.data.result.sys_updated_on)).toISOString(), + url: getIncidentViewURL(res.data.result.sys_id), + }; + } catch (error) { + throw new Error( + getErrorMessage( + i18n.NAME, + `Unable to update incident with id ${incidentId}. Error: ${error.message}` + ) + ); + } + }; + + const createComment = async ({ incidentId, comment, field }: ExternalServiceParams) => { + try { + const res = await patch({ + axios: axiosInstance, + url: `${commentUrl}/${incidentId}`, + data: { [field]: comment.comment }, + }); + + return { + commentId: comment.commentId, + pushedDate: new Date(addTimeZoneToDate(res.data.result.sys_updated_on)).toISOString(), + }; + } catch (error) { + throw new Error( + getErrorMessage( + i18n.NAME, + `Unable to create comment at incident with id ${incidentId}. Error: ${error.message}` + ) + ); + } + }; + + return { + getIncident, + createIncident, + updateIncident, + createComment, + }; +}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/transformers.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/transformers.ts deleted file mode 100644 index dc0a03fab8c715..00000000000000 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/transformers.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { TransformerArgs } from './types'; -import * as i18n from './translations'; - -export const informationCreated = ({ - value, - date, - user, - ...rest -}: TransformerArgs): TransformerArgs => ({ - value: `${value} ${i18n.FIELD_INFORMATION('create', date, user)}`, - ...rest, -}); - -export const informationUpdated = ({ - value, - date, - user, - ...rest -}: TransformerArgs): TransformerArgs => ({ - value: `${value} ${i18n.FIELD_INFORMATION('update', date, user)}`, - ...rest, -}); - -export const informationAdded = ({ - value, - date, - user, - ...rest -}: TransformerArgs): TransformerArgs => ({ - value: `${value} ${i18n.FIELD_INFORMATION('add', date, user)}`, - ...rest, -}); - -export const append = ({ value, previousValue, ...rest }: TransformerArgs): TransformerArgs => ({ - value: previousValue ? `${previousValue} \r\n${value}` : `${value}`, - ...rest, -}); diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/translations.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/translations.ts index 3b216a6c3260ac..3d6138169c4cc2 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/translations.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/translations.ts @@ -6,77 +6,6 @@ import { i18n } from '@kbn/i18n'; -export const API_URL_REQUIRED = i18n.translate( - 'xpack.actions.builtin.servicenow.servicenowApiNullError', - { - defaultMessage: 'ServiceNow [apiUrl] is required', - } -); - -export const WHITE_LISTED_ERROR = (message: string) => - i18n.translate('xpack.actions.builtin.servicenow.servicenowApiWhitelistError', { - defaultMessage: 'error configuring servicenow action: {message}', - values: { - message, - }, - }); - -export const NAME = i18n.translate('xpack.actions.builtin.servicenowTitle', { +export const NAME = i18n.translate('xpack.actions.builtin.case.servicenowTitle', { defaultMessage: 'ServiceNow', }); - -export const MAPPING_EMPTY = i18n.translate('xpack.actions.builtin.servicenow.emptyMapping', { - defaultMessage: '[casesConfiguration.mapping]: expected non-empty but got empty', -}); - -export const ERROR_POSTING = i18n.translate( - 'xpack.actions.builtin.servicenow.postingErrorMessage', - { - defaultMessage: 'error posting servicenow event', - } -); - -export const RETRY_POSTING = (status: number) => - i18n.translate('xpack.actions.builtin.servicenow.postingRetryErrorMessage', { - defaultMessage: 'error posting servicenow event: http status {status}, retry later', - values: { - status, - }, - }); - -export const UNEXPECTED_STATUS = (status: number) => - i18n.translate('xpack.actions.builtin.servicenow.postingUnexpectedErrorMessage', { - defaultMessage: 'error posting servicenow event: unexpected status {status}', - values: { - status, - }, - }); - -export const FIELD_INFORMATION = ( - mode: string, - date: string | undefined, - user: string | undefined -) => { - switch (mode) { - case 'create': - return i18n.translate('xpack.actions.builtin.servicenow.informationCreated', { - values: { date, user }, - defaultMessage: '(created at {date} by {user})', - }); - case 'update': - return i18n.translate('xpack.actions.builtin.servicenow.informationUpdated', { - values: { date, user }, - defaultMessage: '(updated at {date} by {user})', - }); - case 'add': - return i18n.translate('xpack.actions.builtin.servicenow.informationAdded', { - values: { date, user }, - defaultMessage: '(added at {date} by {user})', - }); - default: - return i18n.translate('xpack.actions.builtin.servicenow.informationDefault', { - values: { date, user }, - defaultMessage: '(created at {date} by {user})', - }); - } -}; diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts index c5ef282aeffa72..d8476b7dca54a5 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/types.ts @@ -4,100 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TypeOf } from '@kbn/config-schema'; +export { + ExternalIncidentServiceConfiguration as ServiceNowPublicConfigurationType, + ExternalIncidentServiceSecretConfiguration as ServiceNowSecretConfigurationType, +} from '../case/types'; -import { - ConfigSchema, - SecretsSchema, - ParamsSchema, - CasesConfigurationSchema, - MapEntrySchema, - CommentSchema, -} from './schema'; - -import { ServiceNow } from './lib'; -import { Incident, IncidentResponse } from './lib/types'; - -// config definition -export type ConfigType = TypeOf; - -// secrets definition -export type SecretsType = TypeOf; - -export type ExecutorParams = TypeOf; - -export type CasesConfigurationType = TypeOf; -export type MapEntry = TypeOf; -export type Comment = TypeOf; - -export type Mapping = Map>; - -export interface Params extends ExecutorParams { - incident: Record; +export interface CreateIncidentRequest { + summary: string; + description: string; } -export interface CreateHandlerArguments { - serviceNow: ServiceNow; - params: Params; - comments: Comment[]; - mapping: Mapping; -} - -export type UpdateHandlerArguments = CreateHandlerArguments & { - incidentId: string; -}; - -export type IncidentHandlerArguments = CreateHandlerArguments & { - incidentId: string | null; -}; -export interface HandlerResponse extends IncidentResponse { - comments?: SimpleComment[]; -} - -export interface SimpleComment { - commentId: string; - pushedDate: string; -} - -export interface AppendFieldArgs { - value: string; - prefix?: string; - suffix?: string; -} - -export interface KeyAny { - [index: string]: unknown; -} - -export interface AppendInformationFieldArgs { - value: string; - user: string; - date: string; - mode: string; -} - -export interface TransformerArgs { - value: string; - date?: string; - user?: string; - previousValue?: string; -} - -export interface PrepareFieldsForTransformArgs { - params: Params; - mapping: Mapping; - defaultPipes?: string[]; -} - -export interface PipedField { - key: string; - value: string; - actionType: string; - pipes: string[]; -} +export type UpdateIncidentRequest = Partial; -export interface TransformFieldsArgs { - params: Params; - fields: PipedField[]; - currentIncident?: Incident; +export interface CreateCommentRequest { + [key: string]: string; } diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/validators.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/validators.ts new file mode 100644 index 00000000000000..7226071392bc63 --- /dev/null +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/validators.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { validateCommonConfig, validateCommonSecrets } from '../case/validators'; +import { ExternalServiceValidation } from '../case/types'; + +export const validate: ExternalServiceValidation = { + config: validateCommonConfig, + secrets: validateCommonSecrets, +}; diff --git a/x-pack/plugins/case/common/api/cases/case.ts b/x-pack/plugins/case/common/api/cases/case.ts index 1f08a410249057..d1bcae549805e8 100644 --- a/x-pack/plugins/case/common/api/cases/case.ts +++ b/x-pack/plugins/case/common/api/cases/case.ts @@ -127,35 +127,34 @@ export const ServiceConnectorCommentParamsRt = rt.type({ updatedBy: rt.union([ServiceConnectorUserParams, rt.null]), }); -export const ServiceConnectorCaseParamsRt = rt.intersection([ - rt.type({ - caseId: rt.string, - createdAt: rt.string, - createdBy: ServiceConnectorUserParams, - incidentId: rt.union([rt.string, rt.null]), - title: rt.string, - updatedAt: rt.union([rt.string, rt.null]), - updatedBy: rt.union([ServiceConnectorUserParams, rt.null]), - }), - rt.partial({ - description: rt.string, - comments: rt.array(ServiceConnectorCommentParamsRt), - }), -]); +export const ServiceConnectorCaseParamsRt = rt.type({ + caseId: rt.string, + createdAt: rt.string, + createdBy: ServiceConnectorUserParams, + externalId: rt.union([rt.string, rt.null]), + title: rt.string, + updatedAt: rt.union([rt.string, rt.null]), + updatedBy: rt.union([ServiceConnectorUserParams, rt.null]), + description: rt.union([rt.string, rt.null]), + comments: rt.union([rt.array(ServiceConnectorCommentParamsRt), rt.null]), +}); export const ServiceConnectorCaseResponseRt = rt.intersection([ rt.type({ - number: rt.string, - incidentId: rt.string, + title: rt.string, + id: rt.string, pushedDate: rt.string, url: rt.string, }), rt.partial({ comments: rt.array( - rt.type({ - commentId: rt.string, - pushedDate: rt.string, - }) + rt.intersection([ + rt.type({ + commentId: rt.string, + pushedDate: rt.string, + }), + rt.partial({ externalCommentId: rt.string }), + ]) ), }), ]); diff --git a/x-pack/plugins/case/common/constants.ts b/x-pack/plugins/case/common/constants.ts index dcfa46bfa60191..855a5c3d63507a 100644 --- a/x-pack/plugins/case/common/constants.ts +++ b/x-pack/plugins/case/common/constants.ts @@ -27,3 +27,5 @@ export const CASE_USER_ACTIONS_URL = `${CASE_DETAILS_URL}/user_actions`; export const ACTION_URL = '/api/action'; export const ACTION_TYPES_URL = '/api/action/types'; + +export const SUPPORTED_CONNECTORS = ['.servicenow', '.jira']; diff --git a/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts b/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts index 00575655d4c426..43167d56de0159 100644 --- a/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts +++ b/x-pack/plugins/case/server/routes/api/cases/configure/get_connectors.ts @@ -8,14 +8,15 @@ import Boom from 'boom'; import { RouteDeps } from '../../types'; import { wrapError } from '../../utils'; -import { CASE_CONFIGURE_CONNECTORS_URL } from '../../../../../common/constants'; +import { + CASE_CONFIGURE_CONNECTORS_URL, + SUPPORTED_CONNECTORS, +} from '../../../../../common/constants'; /* * Be aware that this api will only return 20 connectors */ -const CASE_SERVICE_NOW_ACTION = '.servicenow'; - export function initCaseConfigureGetActionConnector({ caseService, router }: RouteDeps) { router.get( { @@ -30,8 +31,8 @@ export function initCaseConfigureGetActionConnector({ caseService, router }: Rou throw Boom.notFound('Action client have not been found'); } - const results = (await actionsClient.getAll()).filter( - action => action.actionTypeId === CASE_SERVICE_NOW_ACTION + const results = (await actionsClient.getAll()).filter(action => + SUPPORTED_CONNECTORS.includes(action.actionTypeId) ); return response.ok({ body: results }); } catch (error) { diff --git a/x-pack/plugins/siem/public/containers/case/api.test.tsx b/x-pack/plugins/siem/public/containers/case/api.test.tsx index ad61e2b46f6c5f..174738098fa107 100644 --- a/x-pack/plugins/siem/public/containers/case/api.test.tsx +++ b/x-pack/plugins/siem/public/containers/case/api.test.tsx @@ -418,7 +418,9 @@ describe('Case Configuration API', () => { await pushToService(connectorId, casePushParams, abortCtrl.signal); expect(fetchMock).toHaveBeenCalledWith(`/api/action/${connectorId}/_execute`, { method: 'POST', - body: JSON.stringify({ params: casePushParams }), + body: JSON.stringify({ + params: { subAction: 'pushToService', subActionParams: casePushParams }, + }), signal: abortCtrl.signal, }); }); diff --git a/x-pack/plugins/siem/public/containers/case/api.ts b/x-pack/plugins/siem/public/containers/case/api.ts index b97f94a5a6b597..438eae9d88a448 100644 --- a/x-pack/plugins/siem/public/containers/case/api.ts +++ b/x-pack/plugins/siem/public/containers/case/api.ts @@ -245,7 +245,9 @@ export const pushToService = async ( `${ACTION_URL}/${connectorId}/_execute`, { method: 'POST', - body: JSON.stringify({ params: casePushParams }), + body: JSON.stringify({ + params: { subAction: 'pushToService', subActionParams: casePushParams }, + }), signal, } ); diff --git a/x-pack/plugins/siem/public/containers/case/mock.ts b/x-pack/plugins/siem/public/containers/case/mock.ts index 0f44b3a1594ba0..a3a8db2c40950c 100644 --- a/x-pack/plugins/siem/public/containers/case/mock.ts +++ b/x-pack/plugins/siem/public/containers/case/mock.ts @@ -103,8 +103,8 @@ export const pushedCase: Case = { }; export const serviceConnector: ServiceConnectorCaseResponse = { - number: '123', - incidentId: '444', + title: '123', + id: '444', pushedDate: basicUpdatedAt, url: 'connector.com', comments: [ @@ -129,12 +129,13 @@ export const casePushParams = { caseId: basicCaseId, createdAt: basicCreatedAt, createdBy: elasticUser, - incidentId: null, + externalId: null, title: 'what a cool value', commentId: null, updatedAt: basicCreatedAt, updatedBy: elasticUser, description: 'nice', + comments: null, }; export const actionTypeExecutorResult = { actionId: 'string', diff --git a/x-pack/plugins/siem/public/containers/case/use_post_push_to_service.test.tsx b/x-pack/plugins/siem/public/containers/case/use_post_push_to_service.test.tsx index b07a346a8da46f..b9698c3e864e3f 100644 --- a/x-pack/plugins/siem/public/containers/case/use_post_push_to_service.test.tsx +++ b/x-pack/plugins/siem/public/containers/case/use_post_push_to_service.test.tsx @@ -55,8 +55,8 @@ describe('usePostPushToService', () => { { connector_id: samplePush.connectorId, connector_name: samplePush.connectorName, - external_id: serviceConnector.incidentId, - external_title: serviceConnector.number, + external_id: serviceConnector.id, + external_title: serviceConnector.title, external_url: serviceConnector.url, }, abortCtrl.signal diff --git a/x-pack/plugins/siem/public/containers/case/use_post_push_to_service.tsx b/x-pack/plugins/siem/public/containers/case/use_post_push_to_service.tsx index acd4b92ee430dc..c9d1b963f411aa 100644 --- a/x-pack/plugins/siem/public/containers/case/use_post_push_to_service.tsx +++ b/x-pack/plugins/siem/public/containers/case/use_post_push_to_service.tsx @@ -98,8 +98,8 @@ export const usePostPushToService = (): UsePostPushToService => { { connector_id: connectorId, connector_name: connectorName, - external_id: responseService.incidentId, - external_title: responseService.number, + external_id: responseService.id, + external_title: responseService.title, external_url: responseService.url, }, abortCtrl.signal @@ -180,7 +180,7 @@ export const formatServiceRequestData = (myCase: Case): ServiceConnectorCasePara : null, })), description, - incidentId: externalService?.externalId ?? null, + externalId: externalService?.externalId ?? null, title, updatedAt, updatedBy: diff --git a/x-pack/plugins/siem/public/lib/connectors/components/connector_flyout/index.tsx b/x-pack/plugins/siem/public/lib/connectors/components/connector_flyout/index.tsx new file mode 100644 index 00000000000000..c5a35da56284d9 --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/components/connector_flyout/index.tsx @@ -0,0 +1,148 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useCallback, useEffect } from 'react'; +import { EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSpacer } from '@elastic/eui'; + +import { isEmpty, get } from 'lodash/fp'; + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ActionConnectorFieldsProps } from '../../../../../../triggers_actions_ui/public/types'; +import { FieldMapping } from '../../../../pages/case/components/configure_cases/field_mapping'; + +import { defaultMapping } from '../../config'; +import { CasesConfigurationMapping } from '../../../../containers/case/configure/types'; + +import * as i18n from '../../translations'; +import { ActionConnector, ConnectorFlyoutHOCProps } from '../../types'; + +export const withConnectorFlyout = ({ + ConnectorFormComponent, + secretKeys = [], + configKeys = [], +}: ConnectorFlyoutHOCProps) => { + const ConnectorFlyout: React.FC> = ({ + action, + editActionConfig, + editActionSecrets, + errors, + }) => { + /* We do not provide defaults values to the fields (like empty string for apiUrl) intentionally. + * If we do, errors will be shown the first time the flyout is open even though the user did not + * interact with the form. Also, we would like to show errors for empty fields provided by the user. + /*/ + const { apiUrl, casesConfiguration: { mapping = [] } = {} } = action.config; + const configKeysWithDefault = [...configKeys, 'apiUrl']; + + const isApiUrlInvalid: boolean = errors.apiUrl.length > 0 && apiUrl != null; + + /** + * We need to distinguish between the add flyout and the edit flyout. + * useEffect will run only once on component mount. + * This guarantees that the function below will run only once. + * On the first render of the component the apiUrl can be either undefined or filled. + * If it is filled then we are on the edit flyout. Otherwise we are on the add flyout. + */ + + useEffect(() => { + if (!isEmpty(apiUrl)) { + secretKeys.forEach((key: string) => editActionSecrets(key, '')); + } + }, []); + + if (isEmpty(mapping)) { + editActionConfig('casesConfiguration', { + ...action.config.casesConfiguration, + mapping: defaultMapping, + }); + } + + const handleOnChangeActionConfig = useCallback( + (key: string, value: string) => editActionConfig(key, value), + [] + ); + + const handleOnBlurActionConfig = useCallback( + (key: string) => { + if (configKeysWithDefault.includes(key) && get(key, action.config) == null) { + editActionConfig(key, ''); + } + }, + [action.config] + ); + + const handleOnChangeSecretConfig = useCallback( + (key: string, value: string) => editActionSecrets(key, value), + [] + ); + + const handleOnBlurSecretConfig = useCallback( + (key: string) => { + if (secretKeys.includes(key) && get(key, action.secrets) == null) { + editActionSecrets(key, ''); + } + }, + [action.secrets] + ); + + const handleOnChangeMappingConfig = useCallback( + (newMapping: CasesConfigurationMapping[]) => + editActionConfig('casesConfiguration', { + ...action.config.casesConfiguration, + mapping: newMapping, + }), + [action.config] + ); + + return ( + <> + + + + handleOnChangeActionConfig('apiUrl', evt.target.value)} + onBlur={handleOnBlurActionConfig.bind(null, 'apiUrl')} + /> + + + + + + + + + + + + + ); + }; + + return ConnectorFlyout; +}; diff --git a/x-pack/plugins/siem/public/lib/connectors/config.ts b/x-pack/plugins/siem/public/lib/connectors/config.ts index baeb69b3f69436..98473e49622a95 100644 --- a/x-pack/plugins/siem/public/lib/connectors/config.ts +++ b/x-pack/plugins/siem/public/lib/connectors/config.ts @@ -5,17 +5,17 @@ */ import { CasesConfigurationMapping } from '../../containers/case/configure/types'; -import serviceNowLogo from './logos/servicenow.svg'; + import { Connector } from './types'; +import { connector as serviceNowConnectorConfig } from './servicenow/config'; +import { connector as jiraConnectorConfig } from './jira/config'; -const connectors: Record = { - '.servicenow': { - actionTypeId: '.servicenow', - logo: serviceNowLogo, - }, +export const connectorsConfiguration: Record = { + '.servicenow': serviceNowConnectorConfig, + '.jira': jiraConnectorConfig, }; -const defaultMapping: CasesConfigurationMapping[] = [ +export const defaultMapping: CasesConfigurationMapping[] = [ { source: 'title', target: 'short_description', @@ -32,5 +32,3 @@ const defaultMapping: CasesConfigurationMapping[] = [ actionType: 'append', }, ]; - -export { connectors, defaultMapping }; diff --git a/x-pack/plugins/siem/public/lib/connectors/index.ts b/x-pack/plugins/siem/public/lib/connectors/index.ts index fdf337b5ef1204..2ce61bef49c5ed 100644 --- a/x-pack/plugins/siem/public/lib/connectors/index.ts +++ b/x-pack/plugins/siem/public/lib/connectors/index.ts @@ -5,3 +5,4 @@ */ export { getActionType as serviceNowActionType } from './servicenow'; +export { getActionType as jiraActionType } from './jira'; diff --git a/x-pack/plugins/siem/public/lib/connectors/jira/config.ts b/x-pack/plugins/siem/public/lib/connectors/jira/config.ts new file mode 100644 index 00000000000000..42bd1b9cdc1915 --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/jira/config.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Connector } from '../types'; + +import { JIRA_TITLE } from './translations'; +import logo from './logo.svg'; + +export const connector: Connector = { + id: '.jira', + name: JIRA_TITLE, + logo, + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'platinum', +}; diff --git a/x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx b/x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx new file mode 100644 index 00000000000000..482808fca53b1a --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/jira/flyout.tsx @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiFieldPassword, + EuiSpacer, +} from '@elastic/eui'; + +import * as i18n from './translations'; +import { ConnectorFlyoutFormProps } from '../types'; +import { JiraActionConnector } from './types'; +import { withConnectorFlyout } from '../components/connector_flyout'; + +const JiraConnectorForm: React.FC> = ({ + errors, + action, + onChangeSecret, + onBlurSecret, + onChangeConfig, + onBlurConfig, +}) => { + const { projectKey } = action.config; + const { email, apiToken } = action.secrets; + const isProjectKeyInvalid: boolean = errors.projectKey.length > 0 && projectKey != null; + const isEmailInvalid: boolean = errors.email.length > 0 && email != null; + const isApiTokenInvalid: boolean = errors.apiToken.length > 0 && apiToken != null; + + return ( + <> + + + + onChangeConfig('projectKey', evt.target.value)} + onBlur={() => onBlurConfig('projectKey')} + /> + + + + + + + + onChangeSecret('email', evt.target.value)} + onBlur={() => onBlurSecret('email')} + /> + + + + + + + + onChangeSecret('apiToken', evt.target.value)} + onBlur={() => onBlurSecret('apiToken')} + /> + + + + + ); +}; + +export const JiraConnectorFlyout = withConnectorFlyout({ + ConnectorFormComponent: JiraConnectorForm, + secretKeys: ['email', 'apiToken'], + configKeys: ['projectKey'], +}); diff --git a/x-pack/plugins/siem/public/lib/connectors/jira/index.tsx b/x-pack/plugins/siem/public/lib/connectors/jira/index.tsx new file mode 100644 index 00000000000000..ada9608e37c983 --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/jira/index.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + ValidationResult, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../triggers_actions_ui/public/types'; + +import { connector } from './config'; +import { createActionType } from '../utils'; +import logo from './logo.svg'; +import { JiraActionConnector } from './types'; +import { JiraConnectorFlyout } from './flyout'; +import * as i18n from './translations'; + +interface Errors { + projectKey: string[]; + email: string[]; + apiToken: string[]; +} + +const validateConnector = (action: JiraActionConnector): ValidationResult => { + const errors: Errors = { + projectKey: [], + email: [], + apiToken: [], + }; + + if (!action.config.projectKey) { + errors.projectKey = [...errors.projectKey, i18n.JIRA_PROJECT_KEY_REQUIRED]; + } + + if (!action.secrets.email) { + errors.email = [...errors.email, i18n.EMAIL_REQUIRED]; + } + + if (!action.secrets.apiToken) { + errors.apiToken = [...errors.apiToken, i18n.API_TOKEN_REQUIRED]; + } + + return { errors }; +}; + +export const getActionType = createActionType({ + id: connector.id, + iconClass: logo, + selectMessage: i18n.JIRA_DESC, + actionTypeTitle: connector.name, + validateConnector, + actionConnectorFields: JiraConnectorFlyout, +}); diff --git a/x-pack/plugins/siem/public/lib/connectors/logos/servicenow.svg b/x-pack/plugins/siem/public/lib/connectors/jira/logo.svg old mode 100755 new mode 100644 similarity index 100% rename from x-pack/plugins/siem/public/lib/connectors/logos/servicenow.svg rename to x-pack/plugins/siem/public/lib/connectors/jira/logo.svg diff --git a/x-pack/plugins/siem/public/lib/connectors/jira/translations.ts b/x-pack/plugins/siem/public/lib/connectors/jira/translations.ts new file mode 100644 index 00000000000000..751aaecdad9641 --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/jira/translations.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export * from '../translations'; + +export const JIRA_DESC = i18n.translate('xpack.siem.case.connectors.jira.selectMessageText', { + defaultMessage: 'Push or update SIEM case data to a new issue in Jira', +}); + +export const JIRA_TITLE = i18n.translate('xpack.siem.case.connectors.jira.actionTypeTitle', { + defaultMessage: 'Jira', +}); + +export const JIRA_PROJECT_KEY_LABEL = i18n.translate('xpack.siem.case.connectors.jira.projectKey', { + defaultMessage: 'Project key', +}); + +export const JIRA_PROJECT_KEY_REQUIRED = i18n.translate( + 'xpack.siem.case.connectors.jira.requiredProjectKeyTextField', + { + defaultMessage: 'Project key is required', + } +); diff --git a/x-pack/plugins/siem/public/lib/connectors/jira/types.ts b/x-pack/plugins/siem/public/lib/connectors/jira/types.ts new file mode 100644 index 00000000000000..13e4e8f6a289ef --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/jira/types.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; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable no-restricted-imports */ +/* eslint-disable @kbn/eslint/no-restricted-paths */ + +import { + JiraPublicConfigurationType, + JiraSecretConfigurationType, +} from '../../../../../actions/server/builtin_action_types/jira/types'; + +export interface JiraActionConnector { + config: JiraPublicConfigurationType; + secrets: JiraSecretConfigurationType; +} diff --git a/x-pack/plugins/siem/public/lib/connectors/servicenow.tsx b/x-pack/plugins/siem/public/lib/connectors/servicenow.tsx deleted file mode 100644 index 9fe0b4a957cebd..00000000000000 --- a/x-pack/plugins/siem/public/lib/connectors/servicenow.tsx +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import React, { useCallback, ChangeEvent, useEffect } from 'react'; -import { - EuiFieldText, - EuiFlexGroup, - EuiFlexItem, - EuiFormRow, - EuiFieldPassword, - EuiSpacer, -} from '@elastic/eui'; - -import { isEmpty, get } from 'lodash/fp'; - -import { - ActionConnectorFieldsProps, - ActionTypeModel, - ValidationResult, - ActionParamsProps, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../triggers_actions_ui/public/types'; - -import { FieldMapping } from '../../pages/case/components/configure_cases/field_mapping'; - -import * as i18n from './translations'; - -import { ServiceNowActionConnector } from './types'; -import { isUrlInvalid } from './validators'; - -import { connectors, defaultMapping } from './config'; -import { CasesConfigurationMapping } from '../../containers/case/configure/types'; - -const serviceNowDefinition = connectors['.servicenow']; - -interface ServiceNowActionParams { - message: string; -} - -interface Errors { - apiUrl: string[]; - username: string[]; - password: string[]; -} - -export function getActionType(): ActionTypeModel { - return { - id: serviceNowDefinition.actionTypeId, - iconClass: serviceNowDefinition.logo, - selectMessage: i18n.SERVICENOW_DESC, - actionTypeTitle: i18n.SERVICENOW_TITLE, - validateConnector: (action: ServiceNowActionConnector): ValidationResult => { - const errors: Errors = { - apiUrl: [], - username: [], - password: [], - }; - - if (!action.config.apiUrl) { - errors.apiUrl = [...errors.apiUrl, i18n.SERVICENOW_API_URL_REQUIRED]; - } - - if (isUrlInvalid(action.config.apiUrl)) { - errors.apiUrl = [...errors.apiUrl, i18n.SERVICENOW_API_URL_INVALID]; - } - - if (!action.secrets.username) { - errors.username = [...errors.username, i18n.SERVICENOW_USERNAME_REQUIRED]; - } - - if (!action.secrets.password) { - errors.password = [...errors.password, i18n.SERVICENOW_PASSWORD_REQUIRED]; - } - - return { errors }; - }, - validateParams: (actionParams: ServiceNowActionParams): ValidationResult => { - return { errors: {} }; - }, - actionConnectorFields: ServiceNowConnectorFields, - actionParamsFields: ServiceNowParamsFields, - }; -} - -const ServiceNowConnectorFields: React.FunctionComponent> = ({ action, editActionConfig, editActionSecrets, errors }) => { - /* We do not provide defaults values to the fields (like empty string for apiUrl) intentionally. - * If we do, errors will be shown the first time the flyout is open even though the user did not - * interact with the form. Also, we would like to show errors for empty fields provided by the user. - /*/ - const { apiUrl, casesConfiguration: { mapping = [] } = {} } = action.config; - const { username, password } = action.secrets; - - const isApiUrlInvalid: boolean = errors.apiUrl.length > 0 && apiUrl != null; - const isUsernameInvalid: boolean = errors.username.length > 0 && username != null; - const isPasswordInvalid: boolean = errors.password.length > 0 && password != null; - - /** - * We need to distinguish between the add flyout and the edit flyout. - * useEffect will run only once on component mount. - * This guarantees that the function below will run only once. - * On the first render of the component the apiUrl can be either undefined or filled. - * If it is filled then we are on the edit flyout. Otherwise we are on the add flyout. - */ - - useEffect(() => { - if (!isEmpty(apiUrl)) { - editActionSecrets('username', ''); - editActionSecrets('password', ''); - } - }, []); - - if (isEmpty(mapping)) { - editActionConfig('casesConfiguration', { - ...action.config.casesConfiguration, - mapping: defaultMapping, - }); - } - - const handleOnChangeActionConfig = useCallback( - (key: string, evt: ChangeEvent) => editActionConfig(key, evt.target.value), - [] - ); - - const handleOnBlurActionConfig = useCallback( - (key: string) => { - if (key === 'apiUrl' && action.config[key] == null) { - editActionConfig(key, ''); - } - }, - [action.config] - ); - - const handleOnChangeSecretConfig = useCallback( - (key: string, evt: ChangeEvent) => editActionSecrets(key, evt.target.value), - [] - ); - - const handleOnBlurSecretConfig = useCallback( - (key: string) => { - if (['username', 'password'].includes(key) && get(key, action.secrets) == null) { - editActionSecrets(key, ''); - } - }, - [action.secrets] - ); - - const handleOnChangeMappingConfig = useCallback( - (newMapping: CasesConfigurationMapping[]) => - editActionConfig('casesConfiguration', { - ...action.config.casesConfiguration, - mapping: newMapping, - }), - [action.config] - ); - - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -const ServiceNowParamsFields: React.FunctionComponent> = ({ actionParams, editAction, index, errors }) => { - return null; -}; diff --git a/x-pack/plugins/siem/public/lib/connectors/servicenow/config.ts b/x-pack/plugins/siem/public/lib/connectors/servicenow/config.ts new file mode 100644 index 00000000000000..7bc1b117b34226 --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/servicenow/config.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Connector } from '../types'; + +import { SERVICENOW_TITLE } from './translations'; +import logo from './logo.svg'; + +export const connector: Connector = { + id: '.servicenow', + name: SERVICENOW_TITLE, + logo, + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'platinum', +}; diff --git a/x-pack/plugins/siem/public/lib/connectors/servicenow/flyout.tsx b/x-pack/plugins/siem/public/lib/connectors/servicenow/flyout.tsx new file mode 100644 index 00000000000000..bcde802e7bd1e0 --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/servicenow/flyout.tsx @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiFieldPassword, + EuiSpacer, +} from '@elastic/eui'; + +import * as i18n from './translations'; +import { ConnectorFlyoutFormProps } from '../types'; +import { ServiceNowActionConnector } from './types'; +import { withConnectorFlyout } from '../components/connector_flyout'; + +const ServiceNowConnectorForm: React.FC> = ({ + errors, + action, + onChangeSecret, + onBlurSecret, +}) => { + const { username, password } = action.secrets; + const isUsernameInvalid: boolean = errors.username.length > 0 && username != null; + const isPasswordInvalid: boolean = errors.password.length > 0 && password != null; + + return ( + <> + + + + onChangeSecret('username', evt.target.value)} + onBlur={() => onBlurSecret('username')} + /> + + + + + + + + onChangeSecret('password', evt.target.value)} + onBlur={() => onBlurSecret('password')} + /> + + + + + ); +}; + +export const ServiceNowConnectorFlyout = withConnectorFlyout({ + ConnectorFormComponent: ServiceNowConnectorForm, + secretKeys: ['username', 'password'], +}); diff --git a/x-pack/plugins/siem/public/lib/connectors/servicenow/index.tsx b/x-pack/plugins/siem/public/lib/connectors/servicenow/index.tsx new file mode 100644 index 00000000000000..1f8e61b6d3ea7a --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/servicenow/index.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + ValidationResult, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../triggers_actions_ui/public/types'; + +import { connector } from './config'; +import { createActionType } from '../utils'; +import logo from './logo.svg'; +import { ServiceNowActionConnector } from './types'; +import { ServiceNowConnectorFlyout } from './flyout'; +import * as i18n from './translations'; + +interface Errors { + username: string[]; + password: string[]; +} + +const validateConnector = (action: ServiceNowActionConnector): ValidationResult => { + const errors: Errors = { + username: [], + password: [], + }; + + if (!action.secrets.username) { + errors.username = [...errors.username, i18n.USERNAME_REQUIRED]; + } + + if (!action.secrets.password) { + errors.password = [...errors.password, i18n.PASSWORD_REQUIRED]; + } + + return { errors }; +}; + +export const getActionType = createActionType({ + id: connector.id, + iconClass: logo, + selectMessage: i18n.SERVICENOW_DESC, + actionTypeTitle: connector.name, + validateConnector, + actionConnectorFields: ServiceNowConnectorFlyout, +}); diff --git a/x-pack/plugins/siem/public/lib/connectors/servicenow/logo.svg b/x-pack/plugins/siem/public/lib/connectors/servicenow/logo.svg new file mode 100644 index 00000000000000..dcd022a8dca18d --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/servicenow/logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/x-pack/plugins/siem/public/lib/connectors/servicenow/translations.ts b/x-pack/plugins/siem/public/lib/connectors/servicenow/translations.ts new file mode 100644 index 00000000000000..5dac9eddd15369 --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/servicenow/translations.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export * from '../translations'; + +export const SERVICENOW_DESC = i18n.translate( + 'xpack.siem.case.connectors.servicenow.selectMessageText', + { + defaultMessage: 'Push or update SIEM case data to a new incident in ServiceNow', + } +); + +export const SERVICENOW_TITLE = i18n.translate( + 'xpack.siem.case.connectors.servicenow.actionTypeTitle', + { + defaultMessage: 'ServiceNow', + } +); diff --git a/x-pack/plugins/siem/public/lib/connectors/servicenow/types.ts b/x-pack/plugins/siem/public/lib/connectors/servicenow/types.ts new file mode 100644 index 00000000000000..b7f0e79eb37e34 --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/servicenow/types.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; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable no-restricted-imports */ +/* eslint-disable @kbn/eslint/no-restricted-paths */ + +import { + ServiceNowPublicConfigurationType, + ServiceNowSecretConfigurationType, +} from '../../../../../actions/server/builtin_action_types/servicenow/types'; + +export interface ServiceNowActionConnector { + config: ServiceNowPublicConfigurationType; + secrets: ServiceNowSecretConfigurationType; +} diff --git a/x-pack/plugins/siem/public/lib/connectors/translations.ts b/x-pack/plugins/siem/public/lib/connectors/translations.ts index ae2084120255c1..b9c1d0fa2a17fa 100644 --- a/x-pack/plugins/siem/public/lib/connectors/translations.ts +++ b/x-pack/plugins/siem/public/lib/connectors/translations.ts @@ -6,65 +6,76 @@ import { i18n } from '@kbn/i18n'; -export const SERVICENOW_DESC = i18n.translate( - 'xpack.siem.case.connectors.servicenow.selectMessageText', +export const API_URL_LABEL = i18n.translate( + 'xpack.siem.case.connectors.common.apiUrlTextFieldLabel', { - defaultMessage: 'Push or update SIEM case data to a new incident in ServiceNow', + defaultMessage: 'URL', } ); -export const SERVICENOW_TITLE = i18n.translate( - 'xpack.siem.case.connectors.servicenow.actionTypeTitle', +export const API_URL_REQUIRED = i18n.translate( + 'xpack.siem.case.connectors.common.requiredApiUrlTextField', { - defaultMessage: 'ServiceNow', + defaultMessage: 'URL is required', } ); -export const SERVICENOW_API_URL_LABEL = i18n.translate( - 'xpack.siem.case.connectors.servicenow.apiUrlTextFieldLabel', +export const API_URL_INVALID = i18n.translate( + 'xpack.siem.case.connectors.common.invalidApiUrlTextField', { - defaultMessage: 'URL', + defaultMessage: 'URL is invalid', } ); -export const SERVICENOW_API_URL_REQUIRED = i18n.translate( - 'xpack.siem.case.connectors.servicenow.requiredApiUrlTextField', +export const USERNAME_LABEL = i18n.translate( + 'xpack.siem.case.connectors.common.usernameTextFieldLabel', { - defaultMessage: 'URL is required', + defaultMessage: 'Username', } ); -export const SERVICENOW_API_URL_INVALID = i18n.translate( - 'xpack.siem.case.connectors.servicenow.invalidApiUrlTextField', +export const USERNAME_REQUIRED = i18n.translate( + 'xpack.siem.case.connectors.common.requiredUsernameTextField', { - defaultMessage: 'URL is invalid', + defaultMessage: 'Username is required', } ); -export const SERVICENOW_USERNAME_LABEL = i18n.translate( - 'xpack.siem.case.connectors.servicenow.usernameTextFieldLabel', +export const PASSWORD_LABEL = i18n.translate( + 'xpack.siem.case.connectors.common.passwordTextFieldLabel', { - defaultMessage: 'Username', + defaultMessage: 'Password', } ); -export const SERVICENOW_USERNAME_REQUIRED = i18n.translate( - 'xpack.siem.case.connectors.servicenow.requiredUsernameTextField', +export const PASSWORD_REQUIRED = i18n.translate( + 'xpack.siem.case.connectors.common.requiredPasswordTextField', { - defaultMessage: 'Username is required', + defaultMessage: 'Password is required', } ); -export const SERVICENOW_PASSWORD_LABEL = i18n.translate( - 'xpack.siem.case.connectors.servicenow.passwordTextFieldLabel', +export const API_TOKEN_LABEL = i18n.translate( + 'xpack.siem.case.connectors.common.apiTokenTextFieldLabel', { - defaultMessage: 'Password', + defaultMessage: 'Api token', } ); -export const SERVICENOW_PASSWORD_REQUIRED = i18n.translate( - 'xpack.siem.case.connectors.servicenow.requiredPasswordTextField', +export const API_TOKEN_REQUIRED = i18n.translate( + 'xpack.siem.case.connectors.common.requiredApiTokenTextField', { - defaultMessage: 'Password is required', + defaultMessage: 'Api token is required', + } +); + +export const EMAIL_LABEL = i18n.translate('xpack.siem.case.connectors.common.emailTextFieldLabel', { + defaultMessage: 'Email', +}); + +export const EMAIL_REQUIRED = i18n.translate( + 'xpack.siem.case.connectors.common.requiredEmailTextField', + { + defaultMessage: 'Email is required', } ); diff --git a/x-pack/plugins/siem/public/lib/connectors/types.ts b/x-pack/plugins/siem/public/lib/connectors/types.ts index 2def4b5107aee8..9af60f4995e54c 100644 --- a/x-pack/plugins/siem/public/lib/connectors/types.ts +++ b/x-pack/plugins/siem/public/lib/connectors/types.ts @@ -7,17 +7,39 @@ /* eslint-disable no-restricted-imports */ /* eslint-disable @kbn/eslint/no-restricted-paths */ -import { - ConfigType, - SecretsType, -} from '../../../../actions/server/builtin_action_types/servicenow/types'; - -export interface ServiceNowActionConnector { - config: ConfigType; - secrets: SecretsType; -} +import { ActionType } from '../../../../triggers_actions_ui/public'; +import { ExternalIncidentServiceConfiguration } from '../../../../actions/server/builtin_action_types/case/types'; -export interface Connector { - actionTypeId: string; +export interface Connector extends ActionType { logo: string; } + +export interface ActionConnector { + config: ExternalIncidentServiceConfiguration; + secrets: {}; +} + +export interface ActionConnectorParams { + message: string; +} + +export interface ActionConnectorValidationErrors { + apiUrl: string[]; +} + +export type Optional = Omit & Partial; + +export interface ConnectorFlyoutFormProps { + errors: { [key: string]: string[] }; + action: T; + onChangeSecret: (key: string, value: string) => void; + onBlurSecret: (key: string) => void; + onChangeConfig: (key: string, value: string) => void; + onBlurConfig: (key: string) => void; +} + +export interface ConnectorFlyoutHOCProps { + ConnectorFormComponent: React.FC>; + configKeys?: string[]; + secretKeys?: string[]; +} diff --git a/x-pack/plugins/siem/public/lib/connectors/utils.ts b/x-pack/plugins/siem/public/lib/connectors/utils.ts new file mode 100644 index 00000000000000..5b5270ade5a654 --- /dev/null +++ b/x-pack/plugins/siem/public/lib/connectors/utils.ts @@ -0,0 +1,71 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + ActionTypeModel, + ValidationResult, + ActionParamsProps, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../triggers_actions_ui/public/types'; + +import { + ActionConnector, + ActionConnectorParams, + ActionConnectorValidationErrors, + Optional, +} from './types'; +import { isUrlInvalid } from './validators'; + +import * as i18n from './translations'; + +export const createActionType = ({ + id, + actionTypeTitle, + selectMessage, + iconClass, + validateConnector, + validateParams = connectorParamsValidator, + actionConnectorFields, + actionParamsFields = ConnectorParamsFields, +}: Optional) => (): ActionTypeModel => { + return { + id, + iconClass, + selectMessage, + actionTypeTitle, + validateConnector: (action: ActionConnector): ValidationResult => { + const errors: ActionConnectorValidationErrors = { + apiUrl: [], + }; + + if (!action.config.apiUrl) { + errors.apiUrl = [...errors.apiUrl, i18n.API_URL_REQUIRED]; + } + + if (isUrlInvalid(action.config.apiUrl)) { + errors.apiUrl = [...errors.apiUrl, i18n.API_URL_INVALID]; + } + + return { errors: { ...errors, ...validateConnector(action).errors } }; + }, + validateParams, + actionConnectorFields, + actionParamsFields, + }; +}; + +const ConnectorParamsFields: React.FunctionComponent> = ({ + actionParams, + editAction, + index, + errors, +}) => { + return null; +}; + +const connectorParamsValidator = (actionParams: ActionConnectorParams): ValidationResult => { + return { errors: {} }; +}; diff --git a/x-pack/plugins/siem/public/pages/case/components/configure_cases/connectors_dropdown.tsx b/x-pack/plugins/siem/public/pages/case/components/configure_cases/connectors_dropdown.tsx index 15066e73eee829..d5575f3bac4c8c 100644 --- a/x-pack/plugins/siem/public/pages/case/components/configure_cases/connectors_dropdown.tsx +++ b/x-pack/plugins/siem/public/pages/case/components/configure_cases/connectors_dropdown.tsx @@ -9,7 +9,7 @@ import { EuiIcon, EuiSuperSelect } from '@elastic/eui'; import styled from 'styled-components'; import { Connector } from '../../../../containers/case/configure/types'; -import { connectors as connectorsDefinition } from '../../../../lib/connectors/config'; +import { connectorsConfiguration } from '../../../../lib/connectors/config'; import * as i18n from './translations'; export interface Props { @@ -54,7 +54,7 @@ const ConnectorsDropdownComponent: React.FC = ({ inputDisplay: ( <> {connector.name} diff --git a/x-pack/plugins/siem/public/pages/case/components/configure_cases/index.test.tsx b/x-pack/plugins/siem/public/pages/case/components/configure_cases/index.test.tsx index 545eceb0f73a1b..fde179f3d25fc1 100644 --- a/x-pack/plugins/siem/public/pages/case/components/configure_cases/index.test.tsx +++ b/x-pack/plugins/siem/public/pages/case/components/configure_cases/index.test.tsx @@ -186,6 +186,16 @@ describe('ConfigureCases', () => { id: '.servicenow', name: 'ServiceNow', enabled: true, + logo: 'test-file-stub', + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'platinum', + }, + { + id: '.jira', + name: 'Jira', + logo: 'test-file-stub', + enabled: true, enabledInConfig: true, enabledInLicense: true, minimumLicenseRequired: 'platinum', diff --git a/x-pack/plugins/siem/public/pages/case/components/configure_cases/index.tsx b/x-pack/plugins/siem/public/pages/case/components/configure_cases/index.tsx index a970fe895eb716..66eef9e3ec7bf8 100644 --- a/x-pack/plugins/siem/public/pages/case/components/configure_cases/index.tsx +++ b/x-pack/plugins/siem/public/pages/case/components/configure_cases/index.tsx @@ -32,6 +32,8 @@ import { ActionConnectorTableItem } from '../../../../../../triggers_actions_ui/ import { getCaseUrl } from '../../../../components/link_to'; import { useGetUrlSearch } from '../../../../components/navigation/use_get_url_search'; import { CCMapsCombinedActionAttributes } from '../../../../containers/case/configure/types'; +import { connectorsConfiguration } from '../../../../lib/connectors/config'; + import { Connectors } from '../configure_cases/connectors'; import { ClosureOptions } from '../configure_cases/closure_options'; import { Mapping } from '../configure_cases/mapping'; @@ -54,16 +56,7 @@ const FormWrapper = styled.div` `} `; -const actionTypes: ActionType[] = [ - { - id: '.servicenow', - name: 'ServiceNow', - enabled: true, - enabledInConfig: true, - enabledInLicense: true, - minimumLicenseRequired: 'platinum', - }, -]; +const actionTypes: ActionType[] = Object.values(connectorsConfiguration); interface ConfigureCasesComponentProps { userCanCrud: boolean; diff --git a/x-pack/plugins/siem/public/plugin.tsx b/x-pack/plugins/siem/public/plugin.tsx index f9c44bd341facb..2f2bd70569dcd4 100644 --- a/x-pack/plugins/siem/public/plugin.tsx +++ b/x-pack/plugins/siem/public/plugin.tsx @@ -31,7 +31,7 @@ import { SecurityPluginSetup } from '../../security/public'; import { APP_ID, APP_NAME, APP_PATH, APP_ICON } from '../common/constants'; import { initTelemetry } from './lib/telemetry'; import { KibanaServices } from './lib/kibana/services'; -import { serviceNowActionType } from './lib/connectors'; +import { serviceNowActionType, jiraActionType } from './lib/connectors'; export interface SetupPlugins { home: HomePublicPluginSetup; @@ -84,6 +84,7 @@ export class Plugin implements IPlugin getExternalServiceSimulatorPath(service) ); + allPaths.push(`/api/_${NAME}/${ExternalServiceSimulator.SERVICENOW}/api/now/v2/table/incident`); + allPaths.push(`/api/_${NAME}/${ExternalServiceSimulator.JIRA}/rest/api/2/issue`); return allPaths; } @@ -78,9 +82,10 @@ export default function(kibana: any) { }); initPagerduty(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.PAGERDUTY)); - initServiceNow(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.SERVICENOW)); initSlack(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.SLACK)); initWebhook(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.WEBHOOK)); + initServiceNow(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.SERVICENOW)); + initJira(server, getExternalServiceSimulatorPath(ExternalServiceSimulator.JIRA)); }, }); } diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/jira_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/jira_simulation.ts new file mode 100644 index 00000000000000..629d0197b22923 --- /dev/null +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/jira_simulation.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Hapi from 'hapi'; + +interface JiraRequest extends Hapi.Request { + payload: { + summary: string; + description?: string; + comments?: string; + }; +} +export function initPlugin(server: Hapi.Server, path: string) { + server.route({ + method: 'POST', + path: `${path}/rest/api/2/issue`, + options: { + auth: false, + }, + handler: createHandler as Hapi.Lifecycle.Method, + }); + + server.route({ + method: 'PUT', + path: `${path}/rest/api/2/issue/{id}`, + options: { + auth: false, + }, + handler: updateHandler as Hapi.Lifecycle.Method, + }); + + server.route({ + method: 'GET', + path: `${path}/rest/api/2/issue/{id}`, + options: { + auth: false, + }, + handler: getHandler as Hapi.Lifecycle.Method, + }); + + server.route({ + method: 'POST', + path: `${path}/rest/api/2/issue/{id}/comment`, + options: { + auth: false, + }, + handler: createCommentHanlder as Hapi.Lifecycle.Method, + }); +} + +// ServiceNow simulator: create a servicenow action pointing here, and you can get +// different responses based on the message posted. See the README.md for +// more info. +function createHandler(request: JiraRequest, h: any) { + return jsonResponse(h, 200, { + id: '123', + key: 'CK-1', + created: '2020-04-27T14:17:45.490Z', + }); +} + +function updateHandler(request: JiraRequest, h: any) { + return jsonResponse(h, 200, { + id: '123', + key: 'CK-1', + created: '2020-04-27T14:17:45.490Z', + updated: '2020-04-27T14:17:45.490Z', + }); +} + +function getHandler(request: JiraRequest, h: any) { + return jsonResponse(h, 200, { + id: '123', + key: 'CK-1', + created: '2020-04-27T14:17:45.490Z', + updated: '2020-04-27T14:17:45.490Z', + summary: 'title', + description: 'description', + }); +} + +function createCommentHanlder(request: JiraRequest, h: any) { + return jsonResponse(h, 200, { + id: '123', + created: '2020-04-27T14:17:45.490Z', + }); +} + +function jsonResponse(h: any, code: number, object?: any) { + if (object == null) { + return h.response('').code(code); + } + + return h + .response(JSON.stringify(object)) + .type('application/json') + .code(code); +} diff --git a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/servicenow_simulation.ts b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/servicenow_simulation.ts index a58738e387aeb0..cc9521369a47df 100644 --- a/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/servicenow_simulation.ts +++ b/x-pack/test/alerting_api_integration/common/fixtures/plugins/actions_simulators/servicenow_simulation.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import Joi from 'joi'; import Hapi from 'hapi'; interface ServiceNowRequest extends Hapi.Request { @@ -29,18 +28,13 @@ export function initPlugin(server: Hapi.Server, path: string) { path: `${path}/api/now/v2/table/incident/{id}`, options: { auth: false, - validate: { - params: Joi.object({ - id: Joi.string(), - }), - }, }, handler: updateHandler as Hapi.Lifecycle.Method, }); server.route({ method: 'GET', - path: `${path}/api/now/v2/table/incident`, + path: `${path}/api/now/v2/table/incident/{id}`, options: { auth: false, }, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts new file mode 100644 index 00000000000000..ed63d25d86aca6 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/jira.ts @@ -0,0 +1,549 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../../../common/ftr_provider_context'; + +import { + getExternalServiceSimulatorPath, + ExternalServiceSimulator, +} from '../../../../common/fixtures/plugins/actions_simulators'; + +const mapping = [ + { + source: 'title', + target: 'summary', + actionType: 'overwrite', + }, + { + source: 'description', + target: 'description', + actionType: 'overwrite', + }, + { + source: 'comments', + target: 'comments', + actionType: 'append', + }, +]; + +// eslint-disable-next-line import/no-default-export +export default function jiraTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + + const mockJira = { + config: { + apiUrl: 'www.jiraisinkibanaactions.com', + projectKey: 'CK', + casesConfiguration: { mapping }, + }, + secrets: { + apiToken: 'elastic', + email: 'elastic@elastic.co', + }, + params: { + subAction: 'pushToService', + subActionParams: { + caseId: '123', + title: 'a title', + description: 'a description', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: 'Elastic User', username: 'elastic' }, + updatedAt: null, + updatedBy: null, + externalId: null, + comments: [ + { + commentId: '456', + version: 'WzU3LDFd', + comment: 'first comment', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: 'Elastic User', username: 'elastic' }, + updatedAt: null, + updatedBy: null, + }, + ], + }, + }, + }; + + let jiraSimulatorURL: string = ''; + + describe('Jira', () => { + before(() => { + jiraSimulatorURL = kibanaServer.resolveUrl( + getExternalServiceSimulatorPath(ExternalServiceSimulator.JIRA) + ); + }); + + after(() => esArchiver.unload('empty_kibana')); + + describe('Jira - Action Creation', () => { + it('should return 200 when creating a jira action successfully', async () => { + const { body: createdAction } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A jira action', + actionTypeId: '.jira', + config: { + ...mockJira.config, + apiUrl: jiraSimulatorURL, + }, + secrets: mockJira.secrets, + }) + .expect(200); + + expect(createdAction).to.eql({ + id: createdAction.id, + isPreconfigured: false, + name: 'A jira action', + actionTypeId: '.jira', + config: { + apiUrl: jiraSimulatorURL, + projectKey: mockJira.config.projectKey, + casesConfiguration: mockJira.config.casesConfiguration, + }, + }); + + const { body: fetchedAction } = await supertest + .get(`/api/action/${createdAction.id}`) + .expect(200); + + expect(fetchedAction).to.eql({ + id: fetchedAction.id, + isPreconfigured: false, + name: 'A jira action', + actionTypeId: '.jira', + config: { + apiUrl: jiraSimulatorURL, + projectKey: mockJira.config.projectKey, + casesConfiguration: mockJira.config.casesConfiguration, + }, + }); + }); + + it('should respond with a 400 Bad Request when creating a jira action with no apiUrl', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A jira action', + actionTypeId: '.jira', + config: { projectKey: 'CK' }, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a jira action with no projectKey', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A jira action', + actionTypeId: '.jira', + config: { apiUrl: jiraSimulatorURL }, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [projectKey]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a jira action with a non whitelisted apiUrl', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A jira action', + actionTypeId: '.jira', + config: { + apiUrl: 'http://jira.mynonexistent.com', + projectKey: mockJira.config.projectKey, + casesConfiguration: mockJira.config.casesConfiguration, + }, + secrets: mockJira.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: error configuring connector action: target url "http://jira.mynonexistent.com" is not whitelisted in the Kibana config xpack.actions.whitelistedHosts', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a jira action without secrets', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A jira action', + actionTypeId: '.jira', + config: { + apiUrl: jiraSimulatorURL, + projectKey: mockJira.config.projectKey, + casesConfiguration: mockJira.config.casesConfiguration, + }, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: [email]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a jira action without casesConfiguration', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A jira action', + actionTypeId: '.jira', + config: { + apiUrl: jiraSimulatorURL, + projectKey: mockJira.config.projectKey, + }, + secrets: mockJira.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [casesConfiguration.mapping]: expected value of type [array] but got [undefined]', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a jira action with empty mapping', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A jira action', + actionTypeId: '.jira', + config: { + apiUrl: jiraSimulatorURL, + projectKey: mockJira.config.projectKey, + casesConfiguration: { mapping: [] }, + }, + secrets: mockJira.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [casesConfiguration.mapping]: expected non-empty but got empty', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a jira action with wrong actionType', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A jira action', + actionTypeId: '.jira', + config: { + apiUrl: jiraSimulatorURL, + projectKey: mockJira.config.projectKey, + casesConfiguration: { + mapping: [ + { + source: 'title', + target: 'description', + actionType: 'non-supported', + }, + ], + }, + }, + secrets: mockJira.secrets, + }) + .expect(400); + }); + }); + + describe('Jira - Executor', () => { + let simulatedActionId: string; + before(async () => { + const { body } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A jira simulator', + actionTypeId: '.jira', + config: { + apiUrl: jiraSimulatorURL, + projectKey: mockJira.config.projectKey, + casesConfiguration: mockJira.config.casesConfiguration, + }, + secrets: mockJira.secrets, + }); + simulatedActionId = body.id; + }); + + describe('Validation', () => { + it('should handle failing with a simulated success without action', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: `error validating action params: Cannot read property 'Symbol(Symbol.iterator)' of undefined`, + }); + }); + }); + + it('should handle failing with a simulated success without unsupported action', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'non-supported' }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subAction]: expected value to equal [pushToService]', + }); + }); + }); + + it('should handle failing with a simulated success without subActionParams', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'pushToService' }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.caseId]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should handle failing with a simulated success without caseId', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'pushToService', subActionParams: {} }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.caseId]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should handle failing with a simulated success without title', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockJira.params, + subActionParams: { + caseId: 'success', + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.title]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should handle failing with a simulated success without createdAt', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockJira.params, + subActionParams: { + caseId: 'success', + title: 'success', + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.createdAt]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should handle failing with a simulated success without commentId', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockJira.params, + subActionParams: { + ...mockJira.params.subActionParams, + caseId: 'success', + title: 'success', + createdAt: 'success', + createdBy: { username: 'elastic' }, + comments: [{}], + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]', + }); + }); + }); + + it('should handle failing with a simulated success without comment message', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockJira.params, + subActionParams: { + ...mockJira.params.subActionParams, + caseId: 'success', + title: 'success', + createdAt: 'success', + createdBy: { username: 'elastic' }, + comments: [{ commentId: 'success' }], + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]', + }); + }); + }); + + it('should handle failing with a simulated success without comment.createdAt', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockJira.params, + subActionParams: { + ...mockJira.params.subActionParams, + caseId: 'success', + title: 'success', + createdAt: 'success', + createdBy: { username: 'elastic' }, + comments: [{ commentId: 'success', comment: 'success' }], + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.createdAt]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]', + }); + }); + }); + }); + + describe('Execution', () => { + it('should handle creating an incident without comments', async () => { + const { body } = await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockJira.params, + subActionParams: { + ...mockJira.params.subActionParams, + comments: [], + }, + }, + }) + .expect(200); + + expect(body).to.eql({ + status: 'ok', + actionId: simulatedActionId, + data: { + id: '123', + title: 'CK-1', + pushedDate: '2020-04-27T14:17:45.490Z', + url: `${jiraSimulatorURL}/browse/CK-1`, + }, + }); + }); + }); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts index 399ae0f27f5b1a..04cd06999f4321 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts @@ -13,8 +13,6 @@ import { ExternalServiceSimulator, } from '../../../../common/fixtures/plugins/actions_simulators'; -// node ../scripts/functional_test_runner.js --grep "servicenow" --config=test/alerting_api_integration/security_and_spaces/config.ts - const mapping = [ { source: 'title', @@ -24,7 +22,7 @@ const mapping = [ { source: 'description', target: 'description', - actionType: 'append', + actionType: 'overwrite', }, { source: 'comments', @@ -42,40 +40,41 @@ export default function servicenowTest({ getService }: FtrProviderContext) { const mockServiceNow = { config: { apiUrl: 'www.servicenowisinkibanaactions.com', - casesConfiguration: { mapping: [...mapping] }, + casesConfiguration: { mapping }, }, secrets: { password: 'elastic', username: 'changeme', }, params: { - caseId: '123', - title: 'a title', - description: 'a description', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: null, - updatedBy: null, - incidentId: null, - comments: [ - { - commentId: '456', - version: 'WzU3LDFd', - comment: 'first comment', - createdAt: '2020-03-13T08:34:53.450Z', - createdBy: { fullName: 'Elastic User', username: 'elastic' }, - updatedAt: null, - updatedBy: null, - }, - ], + subAction: 'pushToService', + subActionParams: { + caseId: '123', + title: 'a title', + description: 'a description', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: 'Elastic User', username: 'elastic' }, + updatedAt: null, + updatedBy: null, + externalId: null, + comments: [ + { + commentId: '456', + version: 'WzU3LDFd', + comment: 'first comment', + createdAt: '2020-03-13T08:34:53.450Z', + createdBy: { fullName: 'Elastic User', username: 'elastic' }, + updatedAt: null, + updatedBy: null, + }, + ], + }, }, }; - describe('servicenow', () => { - let simulatedActionId = ''; - let servicenowSimulatorURL: string = ''; + let servicenowSimulatorURL: string = ''; - // need to wait for kibanaServer to settle ... + describe('ServiceNow', () => { before(() => { servicenowSimulatorURL = kibanaServer.resolveUrl( getExternalServiceSimulatorPath(ExternalServiceSimulator.SERVICENOW) @@ -84,351 +83,438 @@ export default function servicenowTest({ getService }: FtrProviderContext) { after(() => esArchiver.unload('empty_kibana')); - it('should return 200 when creating a servicenow action successfully', async () => { - const { body: createdAction } = await supertest - .post('/api/action') - .set('kbn-xsrf', 'foo') - .send({ + describe('ServiceNow - Action Creation', () => { + it('should return 200 when creating a servicenow action successfully', async () => { + const { body: createdAction } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + actionTypeId: '.servicenow', + config: { + apiUrl: servicenowSimulatorURL, + casesConfiguration: mockServiceNow.config.casesConfiguration, + }, + secrets: mockServiceNow.secrets, + }) + .expect(200); + + expect(createdAction).to.eql({ + id: createdAction.id, + isPreconfigured: false, name: 'A servicenow action', actionTypeId: '.servicenow', config: { apiUrl: servicenowSimulatorURL, - casesConfiguration: { ...mockServiceNow.config.casesConfiguration }, + casesConfiguration: mockServiceNow.config.casesConfiguration, }, - secrets: { ...mockServiceNow.secrets }, - }) - .expect(200); - - expect(createdAction).to.eql({ - id: createdAction.id, - isPreconfigured: false, - name: 'A servicenow action', - actionTypeId: '.servicenow', - config: { - apiUrl: servicenowSimulatorURL, - casesConfiguration: { ...mockServiceNow.config.casesConfiguration }, - }, - }); - - expect(typeof createdAction.id).to.be('string'); - - const { body: fetchedAction } = await supertest - .get(`/api/action/${createdAction.id}`) - .expect(200); - - expect(fetchedAction).to.eql({ - id: fetchedAction.id, - isPreconfigured: false, - name: 'A servicenow action', - actionTypeId: '.servicenow', - config: { - apiUrl: servicenowSimulatorURL, - casesConfiguration: { ...mockServiceNow.config.casesConfiguration }, - }, - }); - }); - - it('should respond with a 400 Bad Request when creating a servicenow action with no apiUrl', async () => { - await supertest - .post('/api/action') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - actionTypeId: '.servicenow', - config: {}, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', - }); }); - }); - it('should respond with a 400 Bad Request when creating a servicenow action with a non whitelisted apiUrl', async () => { - await supertest - .post('/api/action') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - actionTypeId: '.servicenow', - config: { - apiUrl: 'http://servicenow.mynonexistent.com', - casesConfiguration: { ...mockServiceNow.config.casesConfiguration }, - }, - secrets: { ...mockServiceNow.secrets }, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type config: error configuring servicenow action: target url "http://servicenow.mynonexistent.com" is not whitelisted in the Kibana config xpack.actions.whitelistedHosts', - }); - }); - }); + const { body: fetchedAction } = await supertest + .get(`/api/action/${createdAction.id}`) + .expect(200); - it('should respond with a 400 Bad Request when creating a servicenow action without secrets', async () => { - await supertest - .post('/api/action') - .set('kbn-xsrf', 'foo') - .send({ + expect(fetchedAction).to.eql({ + id: fetchedAction.id, + isPreconfigured: false, name: 'A servicenow action', actionTypeId: '.servicenow', config: { apiUrl: servicenowSimulatorURL, - casesConfiguration: { ...mockServiceNow.config.casesConfiguration }, + casesConfiguration: mockServiceNow.config.casesConfiguration, }, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type secrets: [password]: expected value of type [string] but got [undefined]', - }); }); - }); + }); - it('should respond with a 400 Bad Request when creating a servicenow action without casesConfiguration', async () => { - await supertest - .post('/api/action') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - actionTypeId: '.servicenow', - config: { - apiUrl: servicenowSimulatorURL, - }, - secrets: { ...mockServiceNow.secrets }, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type config: [casesConfiguration.mapping]: expected value of type [array] but got [undefined]', + it('should respond with a 400 Bad Request when creating a servicenow action with no apiUrl', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + actionTypeId: '.servicenow', + config: {}, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [apiUrl]: expected value of type [string] but got [undefined]', + }); }); - }); - }); + }); - it('should respond with a 400 Bad Request when creating a servicenow action with empty mapping', async () => { - await supertest - .post('/api/action') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - actionTypeId: '.servicenow', - config: { - apiUrl: servicenowSimulatorURL, - casesConfiguration: { mapping: [] }, - }, - secrets: { ...mockServiceNow.secrets }, - }) - .expect(400) - .then((resp: any) => { - expect(resp.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: - 'error validating action type config: [casesConfiguration.mapping]: expected non-empty but got empty', + it('should respond with a 400 Bad Request when creating a servicenow action with a non whitelisted apiUrl', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + actionTypeId: '.servicenow', + config: { + apiUrl: 'http://servicenow.mynonexistent.com', + casesConfiguration: mockServiceNow.config.casesConfiguration, + }, + secrets: mockServiceNow.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: error configuring connector action: target url "http://servicenow.mynonexistent.com" is not whitelisted in the Kibana config xpack.actions.whitelistedHosts', + }); }); - }); - }); + }); - it('should respond with a 400 Bad Request when creating a servicenow action with wrong actionType', async () => { - await supertest - .post('/api/action') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow action', - actionTypeId: '.servicenow', - config: { - apiUrl: servicenowSimulatorURL, - casesConfiguration: { - mapping: [ - { - source: 'title', - target: 'description', - actionType: 'non-supported', - }, - ], + it('should respond with a 400 Bad Request when creating a servicenow action without secrets', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + actionTypeId: '.servicenow', + config: { + apiUrl: servicenowSimulatorURL, + casesConfiguration: mockServiceNow.config.casesConfiguration, }, - }, - secrets: { ...mockServiceNow.secrets }, - }) - .expect(400); - }); - - it('should create our servicenow simulator action successfully', async () => { - const { body: createdSimulatedAction } = await supertest - .post('/api/action') - .set('kbn-xsrf', 'foo') - .send({ - name: 'A servicenow simulator', - actionTypeId: '.servicenow', - config: { - apiUrl: servicenowSimulatorURL, - casesConfiguration: { ...mockServiceNow.config.casesConfiguration }, - }, - secrets: { ...mockServiceNow.secrets }, - }) - .expect(200); + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: [password]: expected value of type [string] but got [undefined]', + }); + }); + }); - simulatedActionId = createdSimulatedAction.id; - }); + it('should respond with a 400 Bad Request when creating a servicenow action without casesConfiguration', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + actionTypeId: '.servicenow', + config: { + apiUrl: servicenowSimulatorURL, + }, + secrets: mockServiceNow.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [casesConfiguration.mapping]: expected value of type [array] but got [undefined]', + }); + }); + }); - it('should handle executing with a simulated success', async () => { - const { body: result } = await supertest - .post(`/api/action/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { ...mockServiceNow.params, title: 'success', comments: [] }, - }) - .expect(200); + it('should respond with a 400 Bad Request when creating a servicenow action with empty mapping', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + actionTypeId: '.servicenow', + config: { + apiUrl: servicenowSimulatorURL, + casesConfiguration: { mapping: [] }, + }, + secrets: mockServiceNow.secrets, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type config: [casesConfiguration.mapping]: expected non-empty but got empty', + }); + }); + }); - expect(result).to.eql({ - status: 'ok', - actionId: simulatedActionId, - data: { - incidentId: '123', - number: 'INC01', - pushedDate: '2020-03-10T12:24:20.000Z', - url: `${servicenowSimulatorURL}/nav_to.do?uri=incident.do?sys_id=123`, - }, + it('should respond with a 400 Bad Request when creating a servicenow action with wrong actionType', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow action', + actionTypeId: '.servicenow', + config: { + apiUrl: servicenowSimulatorURL, + casesConfiguration: { + mapping: [ + { + source: 'title', + target: 'description', + actionType: 'non-supported', + }, + ], + }, + }, + secrets: mockServiceNow.secrets, + }) + .expect(400); }); }); - it('should handle failing with a simulated success without caseId', async () => { - await supertest - .post(`/api/action/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: {}, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - actionId: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: [caseId]: expected value of type [string] but got [undefined]', + describe('ServiceNow - Executor', () => { + let simulatedActionId: string; + before(async () => { + const { body } = await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A servicenow simulator', + actionTypeId: '.servicenow', + config: { + apiUrl: servicenowSimulatorURL, + casesConfiguration: mockServiceNow.config.casesConfiguration, + }, + secrets: mockServiceNow.secrets, }); + simulatedActionId = body.id; + }); + + describe('Validation', () => { + it('should handle failing with a simulated success without action', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: `error validating action params: Cannot read property 'Symbol(Symbol.iterator)' of undefined`, + }); + }); }); - }); - it('should handle failing with a simulated success without title', async () => { - await supertest - .post(`/api/action/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { caseId: 'success' }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - actionId: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: [title]: expected value of type [string] but got [undefined]', - }); + it('should handle failing with a simulated success without unsupported action', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'non-supported' }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subAction]: expected value to equal [pushToService]', + }); + }); }); - }); - it('should handle failing with a simulated success without createdAt', async () => { - await supertest - .post(`/api/action/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { caseId: 'success', title: 'success' }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - actionId: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: [createdAt]: expected value of type [string] but got [undefined]', - }); + it('should handle failing with a simulated success without subActionParams', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'pushToService' }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.caseId]: expected value of type [string] but got [undefined]', + }); + }); }); - }); - it('should handle failing with a simulated success without commentId', async () => { - await supertest - .post(`/api/action/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - caseId: 'success', - title: 'success', - createdAt: 'success', - createdBy: { username: 'elastic' }, - comments: [{}], - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - actionId: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: [comments.0.commentId]: expected value of type [string] but got [undefined]', - }); + it('should handle failing with a simulated success without caseId', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { subAction: 'pushToService', subActionParams: {} }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.caseId]: expected value of type [string] but got [undefined]', + }); + }); }); - }); - it('should handle failing with a simulated success without comment message', async () => { - await supertest - .post(`/api/action/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - caseId: 'success', - title: 'success', - createdAt: 'success', - createdBy: { username: 'elastic' }, - comments: [{ commentId: 'success' }], - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ - actionId: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: [comments.0.comment]: expected value of type [string] but got [undefined]', - }); + it('should handle failing with a simulated success without title', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNow.params, + subActionParams: { + caseId: 'success', + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.title]: expected value of type [string] but got [undefined]', + }); + }); }); - }); - it('should handle failing with a simulated success without comment.createdAt', async () => { - await supertest - .post(`/api/action/${simulatedActionId}/_execute`) - .set('kbn-xsrf', 'foo') - .send({ - params: { - caseId: 'success', - title: 'success', - createdAt: 'success', - createdBy: { username: 'elastic' }, - comments: [{ commentId: 'success', comment: 'success' }], - }, - }) - .then((resp: any) => { - expect(resp.body).to.eql({ + it('should handle failing with a simulated success without createdAt', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNow.params, + subActionParams: { + caseId: 'success', + title: 'success', + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.createdAt]: expected value of type [string] but got [undefined]', + }); + }); + }); + + it('should handle failing with a simulated success without commentId', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNow.params, + subActionParams: { + ...mockServiceNow.params.subActionParams, + caseId: 'success', + title: 'success', + createdAt: 'success', + createdBy: { username: 'elastic' }, + comments: [{}], + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.commentId]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]', + }); + }); + }); + + it('should handle failing with a simulated success without comment message', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNow.params, + subActionParams: { + ...mockServiceNow.params.subActionParams, + caseId: 'success', + title: 'success', + createdAt: 'success', + createdBy: { username: 'elastic' }, + comments: [{ commentId: 'success' }], + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.comment]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]', + }); + }); + }); + + it('should handle failing with a simulated success without comment.createdAt', async () => { + await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNow.params, + subActionParams: { + ...mockServiceNow.params.subActionParams, + caseId: 'success', + title: 'success', + createdAt: 'success', + createdBy: { username: 'elastic' }, + comments: [{ commentId: 'success', comment: 'success' }], + }, + }, + }) + .then((resp: any) => { + expect(resp.body).to.eql({ + actionId: simulatedActionId, + status: 'error', + retry: false, + message: + 'error validating action params: types that failed validation:\n- [0.subAction]: expected value to equal [getIncident]\n- [1.subAction]: expected value to equal [handshake]\n- [2.subActionParams.comments]: types that failed validation:\n - [subActionParams.comments.0.0.createdAt]: expected value of type [string] but got [undefined]\n - [subActionParams.comments.1]: expected value to equal [null]', + }); + }); + }); + }); + + describe('Execution', () => { + it('should handle creating an incident without comments', async () => { + const { body: result } = await supertest + .post(`/api/action/${simulatedActionId}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: { + ...mockServiceNow.params, + subActionParams: { + ...mockServiceNow.params.subActionParams, + comments: [], + }, + }, + }) + .expect(200); + + expect(result).to.eql({ + status: 'ok', actionId: simulatedActionId, - status: 'error', - retry: false, - message: - 'error validating action params: [comments.0.createdAt]: expected value of type [string] but got [undefined]', + data: { + id: '123', + title: 'INC01', + pushedDate: '2020-03-10T12:24:20.000Z', + url: `${servicenowSimulatorURL}/nav_to.do?uri=incident.do?sys_id=123`, + }, }); }); + }); }); }); } diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts index 8e002bcc8d3daf..18b1714582d131 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts @@ -15,6 +15,7 @@ export default function actionsTests({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./builtin_action_types/pagerduty')); loadTestFile(require.resolve('./builtin_action_types/server_log')); loadTestFile(require.resolve('./builtin_action_types/servicenow')); + loadTestFile(require.resolve('./builtin_action_types/jira')); loadTestFile(require.resolve('./builtin_action_types/slack')); loadTestFile(require.resolve('./builtin_action_types/webhook')); loadTestFile(require.resolve('./create')); From c4e6789c28dd0c8fdd00810537b2687a6e94b760 Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Thu, 30 Apr 2020 23:44:16 +0200 Subject: [PATCH 32/47] [Lens] Trigger a filter action on click in datatable visualization (#63840) * wip: datatable * fix: empty values * fix: empty values * translations * using dataPlugin to get buckets * one more time, passing aggs data * tests: added * feat: new design applied * remove icon * feat: old design * CR corrections * better name * Fix merge issue * fix: design changes * feat: correction * fix: copy changes * Update x-pack/plugins/lens/public/datatable_visualization/_visualization.scss Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> * Update _visualization.scss Co-authored-by: Elastic Machine Co-authored-by: Wylie Conlon Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> --- .../__snapshots__/expression.test.tsx.snap | 41 +++++ .../_visualization.scss | 10 ++ .../expression.test.tsx | 156 ++++++++++++++++++ .../datatable_visualization/expression.tsx | 146 +++++++++++++--- .../public/datatable_visualization/index.ts | 23 ++- x-pack/plugins/lens/public/plugin.tsx | 1 + .../test/functional/apps/lens/smokescreen.ts | 25 ++- 7 files changed, 373 insertions(+), 29 deletions(-) create mode 100644 x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap create mode 100644 x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx diff --git a/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap b/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap new file mode 100644 index 00000000000000..76063d230bdb6a --- /dev/null +++ b/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`datatable_expression DatatableComponent it renders the title and value 1`] = ` + + + +`; diff --git a/x-pack/plugins/lens/public/datatable_visualization/_visualization.scss b/x-pack/plugins/lens/public/datatable_visualization/_visualization.scss index e36326d710f725..7d95d73143870d 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/_visualization.scss +++ b/x-pack/plugins/lens/public/datatable_visualization/_visualization.scss @@ -1,3 +1,13 @@ .lnsDataTable { align-self: flex-start; } + +.lnsDataTable__filter { + opacity: 0; + transition: opacity $euiAnimSpeedNormal ease-in-out; +} + +.lnsDataTable__cell:hover .lnsDataTable__filter, +.lnsDataTable__filter:focus-within { + opacity: 1; +} diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx new file mode 100644 index 00000000000000..6d5b1153ad1bc1 --- /dev/null +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -0,0 +1,156 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { datatable, DatatableComponent } from './expression'; +import { LensMultiTable } from '../types'; +import { DatatableProps } from './expression'; +import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; +import { IFieldFormat } from '../../../../../src/plugins/data/public'; +import { IAggType } from 'src/plugins/data/public'; +const executeTriggerActions = jest.fn(); + +function sampleArgs() { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + l1: { + type: 'kibana_datatable', + columns: [ + { id: 'a', name: 'a', meta: { type: 'count' } }, + { id: 'b', name: 'b', meta: { type: 'date_histogram', aggConfigParams: { field: 'b' } } }, + { id: 'c', name: 'c', meta: { type: 'cardinality' } }, + ], + rows: [{ a: 10110, b: 1588024800000, c: 3 }], + }, + }, + }; + + const args: DatatableProps['args'] = { + title: 'My fanci metric chart', + columns: { + columnIds: ['a', 'b', 'c'], + type: 'lens_datatable_columns', + }, + }; + + return { data, args }; +} + +describe('datatable_expression', () => { + describe('datatable renders', () => { + test('it renders with the specified data and args', () => { + const { data, args } = sampleArgs(); + const result = datatable.fn(data, args, createMockExecutionContext()); + + expect(result).toEqual({ + type: 'render', + as: 'lens_datatable_renderer', + value: { data, args }, + }); + }); + }); + + describe('DatatableComponent', () => { + test('it renders the title and value', () => { + const { data, args } = sampleArgs(); + + expect( + shallow( + x as IFieldFormat} + executeTriggerActions={executeTriggerActions} + getType={jest.fn()} + /> + ) + ).toMatchSnapshot(); + }); + + test('it invokes executeTriggerActions with correct context on click on top value', () => { + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + x as IFieldFormat} + executeTriggerActions={executeTriggerActions} + getType={jest.fn(() => ({ type: 'buckets' } as IAggType))} + /> + ); + + wrapper + .find('[data-test-subj="lensDatatableFilterOut"]') + .first() + .simulate('click'); + + expect(executeTriggerActions).toHaveBeenCalledWith('VALUE_CLICK_TRIGGER', { + data: { + data: [ + { + column: 0, + row: 0, + table: data.tables.l1, + value: 10110, + }, + ], + negate: true, + }, + timeFieldName: undefined, + }); + }); + + test('it invokes executeTriggerActions with correct context on click on timefield', () => { + const { args, data } = sampleArgs(); + + const wrapper = mountWithIntl( + x as IFieldFormat} + executeTriggerActions={executeTriggerActions} + getType={jest.fn(() => ({ type: 'buckets' } as IAggType))} + /> + ); + + wrapper + .find('[data-test-subj="lensDatatableFilterFor"]') + .at(3) + .simulate('click'); + + expect(executeTriggerActions).toHaveBeenCalledWith('VALUE_CLICK_TRIGGER', { + data: { + data: [ + { + column: 1, + row: 0, + table: data.tables.l1, + value: 1588024800000, + }, + ], + negate: false, + }, + timeFieldName: 'b', + }); + }); + }); +}); diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 772ee13168d026..71d29be1744bb1 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -7,7 +7,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { i18n } from '@kbn/i18n'; -import { EuiBasicTable } from '@elastic/eui'; +import { I18nProvider } from '@kbn/i18n/react'; +import { EuiBasicTable, EuiFlexGroup, EuiButtonIcon, EuiFlexItem, EuiToolTip } from '@elastic/eui'; +import { IAggType } from 'src/plugins/data/public'; import { FormatFactory, LensMultiTable } from '../types'; import { ExpressionFunctionDefinition, @@ -15,7 +17,10 @@ import { IInterpreterRenderHandlers, } from '../../../../../src/plugins/expressions/public'; import { VisualizationContainer } from '../visualization_container'; - +import { ValueClickTriggerContext } from '../../../../../src/plugins/embeddable/public'; +import { VIS_EVENT_TO_TRIGGER } from '../../../../../src/plugins/visualizations/public'; +import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; +import { getExecuteTriggerActions } from '../services'; export interface DatatableColumns { columnIds: string[]; } @@ -30,6 +35,12 @@ export interface DatatableProps { args: Args; } +type DatatableRenderProps = DatatableProps & { + formatFactory: FormatFactory; + executeTriggerActions: UiActionsStart['executeTriggerActions']; + getType: (name: string) => IAggType; +}; + export interface DatatableRender { type: 'render'; as: 'lens_datatable_renderer'; @@ -100,9 +111,10 @@ export const datatableColumns: ExpressionFunctionDefinition< }, }; -export const getDatatableRenderer = ( - formatFactory: Promise -): ExpressionRenderDefinition => ({ +export const getDatatableRenderer = (dependencies: { + formatFactory: Promise; + getType: Promise<(name: string) => IAggType>; +}): ExpressionRenderDefinition => ({ name: 'lens_datatable_renderer', displayName: i18n.translate('xpack.lens.datatable.visualizationName', { defaultMessage: 'Datatable', @@ -115,9 +127,18 @@ export const getDatatableRenderer = ( config: DatatableProps, handlers: IInterpreterRenderHandlers ) => { - const resolvedFormatFactory = await formatFactory; + const resolvedFormatFactory = await dependencies.formatFactory; + const executeTriggerActions = getExecuteTriggerActions(); + const resolvedGetType = await dependencies.getType; ReactDOM.render( - , + + + , domNode, () => { handlers.done(); @@ -127,7 +148,7 @@ export const getDatatableRenderer = ( }, }); -function DatatableComponent(props: DatatableProps & { formatFactory: FormatFactory }) { +export function DatatableComponent(props: DatatableRenderProps) { const [firstTable] = Object.values(props.data.tables); const formatters: Record> = {}; @@ -135,6 +156,29 @@ function DatatableComponent(props: DatatableProps & { formatFactory: FormatFacto formatters[column.id] = props.formatFactory(column.formatHint); }); + const handleFilterClick = (field: string, value: unknown, colIndex: number, negate = false) => { + const col = firstTable.columns[colIndex]; + const isDateHistogram = col.meta?.type === 'date_histogram'; + const timeFieldName = negate && isDateHistogram ? undefined : col?.meta?.aggConfigParams?.field; + const rowIndex = firstTable.rows.findIndex(row => row[field] === value); + + const context: ValueClickTriggerContext = { + data: { + negate, + data: [ + { + row: rowIndex, + column: colIndex, + value, + table: firstTable, + }, + ], + }, + timeFieldName, + }; + props.executeTriggerActions(VIS_EVENT_TO_TRIGGER.filter, context); + }; + return ( { const col = firstTable.columns.find(c => c.id === field); + const colIndex = firstTable.columns.findIndex(c => c.id === field); + + const filterable = col?.meta?.type && props.getType(col.meta.type)?.type === 'buckets'; return { field, name: (col && col.name) || '', + render: (value: unknown) => { + const formattedValue = formatters[field]?.convert(value); + const fieldName = col?.meta?.aggConfigParams?.field; + + if (filterable) { + return ( + + {formattedValue} + + + + handleFilterClick(field, value, colIndex)} + /> + + + + handleFilterClick(field, value, colIndex, true)} + /> + + + + + + ); + } + return {formattedValue}; + }, }; }) .filter(({ field }) => !!field)} - items={ - firstTable - ? firstTable.rows.map(row => { - const formattedRow: Record = {}; - Object.entries(formatters).forEach(([columnId, formatter]) => { - formattedRow[columnId] = formatter.convert(row[columnId]); - }); - return formattedRow; - }) - : [] - } + items={firstTable ? firstTable.rows : []} /> ); diff --git a/x-pack/plugins/lens/public/datatable_visualization/index.ts b/x-pack/plugins/lens/public/datatable_visualization/index.ts index ff036aadfd4cf3..44894d31da51d6 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/index.ts +++ b/x-pack/plugins/lens/public/datatable_visualization/index.ts @@ -4,12 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CoreSetup } from 'kibana/public'; +import { CoreSetup, CoreStart } from 'kibana/public'; import { datatableVisualization } from './visualization'; import { ExpressionsSetup } from '../../../../../src/plugins/expressions/public'; import { datatable, datatableColumns, getDatatableRenderer } from './expression'; import { EditorFrameSetup, FormatFactory } from '../types'; +import { setExecuteTriggerActions } from '../services'; +import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; +import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; +interface DatatableVisualizationPluginStartPlugins { + uiActions: UiActionsStart; + data: DataPublicPluginStart; +} export interface DatatableVisualizationPluginSetupPlugins { expressions: ExpressionsSetup; formatFactory: Promise; @@ -20,12 +27,22 @@ export class DatatableVisualization { constructor() {} setup( - _core: CoreSetup | null, + core: CoreSetup, { expressions, formatFactory, editorFrame }: DatatableVisualizationPluginSetupPlugins ) { expressions.registerFunction(() => datatableColumns); expressions.registerFunction(() => datatable); - expressions.registerRenderer(() => getDatatableRenderer(formatFactory)); + expressions.registerRenderer(() => + getDatatableRenderer({ + formatFactory, + getType: core + .getStartServices() + .then(([_, { data: dataStart }]) => dataStart.search.aggs.types.get), + }) + ); editorFrame.registerVisualization(datatableVisualization); } + start(core: CoreStart, { uiActions }: DatatableVisualizationPluginStartPlugins) { + setExecuteTriggerActions(uiActions.executeTriggerActions); + } } diff --git a/x-pack/plugins/lens/public/plugin.tsx b/x-pack/plugins/lens/public/plugin.tsx index 8d760eb0df5013..fe0e81177e2599 100644 --- a/x-pack/plugins/lens/public/plugin.tsx +++ b/x-pack/plugins/lens/public/plugin.tsx @@ -200,6 +200,7 @@ export class LensPlugin { start(core: CoreStart, startDependencies: LensPluginStartDependencies) { this.createEditorFrame = this.editorFrameService.start(core, startDependencies).createInstance; this.xyVisualization.start(core, startDependencies); + this.datatableVisualization.start(core, startDependencies); } stop() { diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index be7a2faae6711d..082008bccddd13 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -26,12 +26,12 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const filterBar = getService('filterBar'); - async function assertExpectedMetric() { + async function assertExpectedMetric(metricCount: string = '19,986') { await PageObjects.lens.assertExactText( '[data-test-subj="lns_metric_title"]', 'Maximum of bytes' ); - await PageObjects.lens.assertExactText('[data-test-subj="lns_metric_value"]', '19,986'); + await PageObjects.lens.assertExactText('[data-test-subj="lns_metric_value"]', metricCount); } async function assertExpectedTable() { @@ -40,8 +40,12 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { 'Maximum of bytes' ); await PageObjects.lens.assertExactText( - '[data-test-subj="lnsDataTable"] tbody .euiTableCellContent__text', - '19,986' + '[data-test-subj="lnsDataTable"] [data-test-subj="lnsDataTableCellValue"]', + '19,985' + ); + await PageObjects.lens.assertExactText( + '[data-test-subj="lnsDataTable"] [data-test-subj="lnsDataTableCellValueFilterable"]', + 'IN' ); } @@ -86,7 +90,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { await assertExpectedMetric(); }); - it('click on the bar in XYChart adds proper filters/timerange', async () => { + it('click on the bar in XYChart adds proper filters/timerange in dashboard', async () => { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.clickNewDashboard(); await dashboardAddPanel.clickOpenAddPanel(); @@ -102,15 +106,22 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { expect(hasIpFilter).to.be(true); }); - it('should allow seamless transition to and from table view', async () => { + it('should allow seamless transition to and from table view and add a filter', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens'); await PageObjects.lens.goToTimeRange(); await assertExpectedMetric(); await PageObjects.lens.switchToVisualization('lnsChartSwitchPopover_lnsDatatable'); + await PageObjects.lens.configureDimension({ + dimension: '[data-test-subj="lnsDatatable_column"] [data-test-subj="lns-empty-dimension"]', + operation: 'terms', + field: 'geo.dest', + }); + await PageObjects.lens.save('Artistpreviouslyknownaslens'); + await find.clickByCssSelector('[data-test-subj="lensDatatableFilterOut"]'); await assertExpectedTable(); await PageObjects.lens.switchToVisualization('lnsChartSwitchPopover_lnsMetric'); - await assertExpectedMetric(); + await assertExpectedMetric('19,985'); }); it('should allow creation of lens visualizations', async () => { From 127b324a5f400b9e26357529930879e24d5e9770 Mon Sep 17 00:00:00 2001 From: Catherine Liu Date: Thu, 30 Apr 2020 14:45:50 -0700 Subject: [PATCH 33/47] [Canvas] Adds refresh and autoplay options to view menu (#64375) --- .../legacy/plugins/canvas/i18n/components.ts | 22 +++- .../control_settings/control_settings.scss | 7 - .../control_settings/control_settings.tsx | 84 ------------ .../workpad_header/control_settings/index.ts | 35 ----- .../refresh_control/refresh_control.tsx | 1 + .../__snapshots__/view_menu.stories.storyshot | 66 ++++++++++ .../__examples__/view_menu.stories.tsx | 58 ++++++--- .../auto_refresh_controls.tsx | 36 +---- .../custom_interval.tsx | 0 .../workpad_header/view_menu/index.ts | 39 ++++-- .../kiosk_controls.tsx | 26 +--- .../workpad_header/view_menu/view_menu.scss | 4 + .../workpad_header/view_menu/view_menu.tsx | 123 ++++++++++++++++-- .../workpad_header/workpad_header.tsx | 6 +- .../plugins/canvas/public/style/index.scss | 2 +- .../footer/settings/autoplay_settings.tsx | 2 +- .../functional/page_objects/canvas_page.ts | 10 +- 17 files changed, 289 insertions(+), 232 deletions(-) delete mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/control_settings.scss delete mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/control_settings.tsx delete mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/index.ts rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{control_settings => view_menu}/auto_refresh_controls.tsx (83%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{control_settings => view_menu}/custom_interval.tsx (100%) rename x-pack/legacy/plugins/canvas/public/components/workpad_header/{control_settings => view_menu}/kiosk_controls.tsx (86%) create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/view_menu.scss diff --git a/x-pack/legacy/plugins/canvas/i18n/components.ts b/x-pack/legacy/plugins/canvas/i18n/components.ts index 7bd16c4933ce19..b1c2b41f062f1e 100644 --- a/x-pack/legacy/plugins/canvas/i18n/components.ts +++ b/x-pack/legacy/plugins/canvas/i18n/components.ts @@ -1079,12 +1079,6 @@ export const ComponentStrings = { defaultMessage: 'Refresh elements', }), }, - WorkpadHeaderControlSettings: { - getButtonLabel: () => - i18n.translate('xpack.canvas.workpadHeaderControlSettings.buttonLabel', { - defaultMessage: 'Options', - }), - }, WorkpadHeaderCustomInterval: { getButtonLabel: () => i18n.translate('xpack.canvas.workpadHeaderCustomInterval.confirmButtonLabel', { @@ -1305,6 +1299,18 @@ export const ComponentStrings = { }), }, WorkpadHeaderViewMenu: { + getAutoplayOffMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderViewMenu.autoplayOffMenuItemLabel', { + defaultMessage: 'Turn autoplay off', + }), + getAutoplayOnMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderViewMenu.autoplayOnMenuItemLabel', { + defaultMessage: 'Turn autoplay on', + }), + getAutoplaySettingsMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderViewMenu.autoplaySettingsMenuItemLabel', { + defaultMessage: 'Autoplay settings', + }), getFullscreenMenuItemLabel: () => i18n.translate('xpack.canvas.workpadHeaderViewMenu.fullscreenMenuLabel', { defaultMessage: 'Enter fullscreen mode', @@ -1317,6 +1323,10 @@ export const ComponentStrings = { i18n.translate('xpack.canvas.workpadHeaderViewMenu.refreshMenuItemLabel', { defaultMessage: 'Refresh data', }), + getRefreshSettingsMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderViewMenu.refreshSettingsMenuItemLabel', { + defaultMessage: 'Auto refresh settings', + }), getShowEditModeLabel: () => i18n.translate('xpack.canvas.workpadHeaderViewMenu.showEditModeLabel', { defaultMessage: 'Show editing controls', diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/control_settings.scss b/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/control_settings.scss deleted file mode 100644 index 3d217dd1fc1802..00000000000000 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/control_settings.scss +++ /dev/null @@ -1,7 +0,0 @@ -.canvasControlSettings__popover { - width: 600px; -} - -.canvasControlSettings__list { - columns: 2; -} diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/control_settings.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/control_settings.tsx deleted file mode 100644 index adc57ff4f815a8..00000000000000 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/control_settings.tsx +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { MouseEventHandler } from 'react'; -import PropTypes from 'prop-types'; -import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; -// @ts-ignore untyped local -import { Popover } from '../../popover'; -import { AutoRefreshControls } from './auto_refresh_controls'; -import { KioskControls } from './kiosk_controls'; - -import { ComponentStrings } from '../../../../i18n'; -const { WorkpadHeaderControlSettings: strings } = ComponentStrings; - -interface Props { - refreshInterval: number; - setRefreshInterval: (interval: number | undefined) => void; - autoplayEnabled: boolean; - autoplayInterval: number; - enableAutoplay: (enable: boolean) => void; - setAutoplayInterval: (interval: number | undefined) => void; -} - -export const ControlSettings = ({ - setRefreshInterval, - refreshInterval, - autoplayEnabled, - autoplayInterval, - enableAutoplay, - setAutoplayInterval, -}: Props) => { - const setRefresh = (val: number | undefined) => setRefreshInterval(val); - - const disableInterval = () => { - setRefresh(0); - }; - - const popoverButton = (handleClick: MouseEventHandler) => ( - - {strings.getButtonLabel()} - - ); - - return ( - - {() => ( - - - setRefresh(val)} - disableInterval={() => disableInterval()} - /> - - - - - - )} - - ); -}; - -ControlSettings.propTypes = { - refreshInterval: PropTypes.number, - setRefreshInterval: PropTypes.func.isRequired, - autoplayEnabled: PropTypes.bool, - autoplayInterval: PropTypes.number, - enableAutoplay: PropTypes.func.isRequired, - setAutoplayInterval: PropTypes.func.isRequired, -}; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/index.ts deleted file mode 100644 index 316a49c85c09da..00000000000000 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { connect } from 'react-redux'; -import { - setRefreshInterval, - enableAutoplay, - setAutoplayInterval, - // @ts-ignore untyped local -} from '../../../state/actions/workpad'; -// @ts-ignore untyped local -import { getRefreshInterval, getAutoplay } from '../../../state/selectors/workpad'; -import { State } from '../../../../types'; -import { ControlSettings as Component } from './control_settings'; - -const mapStateToProps = (state: State) => { - const { enabled, interval } = getAutoplay(state); - - return { - refreshInterval: getRefreshInterval(state), - autoplayEnabled: enabled, - autoplayInterval: interval, - }; -}; - -const mapDispatchToProps = { - setRefreshInterval, - enableAutoplay, - setAutoplayInterval, -}; - -export const ControlSettings = connect(mapStateToProps, mapDispatchToProps)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.tsx index 1768adf9be79d7..d651e649128f93 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/refresh_control/refresh_control.tsx @@ -32,6 +32,7 @@ export const RefreshControl = ({ doRefresh, inFlight }: Props) => ( iconType="refresh" aria-label={strings.getRefreshAriaLabel()} onClick={doRefresh} + data-test-subj="canvas-refresh-control" /> ); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/__snapshots__/view_menu.stories.storyshot b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/__snapshots__/view_menu.stories.storyshot index e1ecee0e152be4..eb45f97452ae19 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/__snapshots__/view_menu.stories.storyshot +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/__snapshots__/view_menu.stories.storyshot @@ -65,3 +65,69 @@ exports[`Storyshots components/WorkpadHeader/ViewMenu read only mode 1`] = `
`; + +exports[`Storyshots components/WorkpadHeader/ViewMenu with autoplay enabled 1`] = ` +
+
+ +
+
+`; + +exports[`Storyshots components/WorkpadHeader/ViewMenu with refresh enabled 1`] = ` +
+
+ +
+
+`; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/view_menu.stories.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/view_menu.stories.tsx index 60837ac1218e6a..5b4de05da3a3df 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/view_menu.stories.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/__examples__/view_menu.stories.tsx @@ -8,32 +8,58 @@ import { action } from '@storybook/addon-actions'; import React from 'react'; import { ViewMenu } from '../view_menu'; +const handlers = { + setZoomScale: action('setZoomScale'), + zoomIn: action('zoomIn'), + zoomOut: action('zoomOut'), + toggleWriteable: action('toggleWriteable'), + resetZoom: action('resetZoom'), + enterFullscreen: action('enterFullscreen'), + doRefresh: action('doRefresh'), + fitToWindow: action('fitToWindow'), + setRefreshInterval: action('setRefreshInterval'), + setAutoplayInterval: action('setAutoplayInterval'), + enableAutoplay: action('enableAutoplay'), +}; + storiesOf('components/WorkpadHeader/ViewMenu', module) .add('edit mode', () => ( )) .add('read only mode', () => ( + )) + .add('with refresh enabled', () => ( + + )) + .add('with autoplay enabled', () => ( + )); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/auto_refresh_controls.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/auto_refresh_controls.tsx similarity index 83% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/auto_refresh_controls.tsx rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/auto_refresh_controls.tsx index 97d8920d50dd36..cfd599b1d9f3f3 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/auto_refresh_controls.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/auto_refresh_controls.tsx @@ -22,7 +22,6 @@ import { htmlIdGenerator, } from '@elastic/eui'; import { timeDuration } from '../../../lib/time_duration'; -import { RefreshControl } from '../refresh_control'; import { CustomInterval } from './custom_interval'; import { ComponentStrings, UnitStrings } from '../../../../i18n'; @@ -69,7 +68,11 @@ export const AutoRefreshControls = ({ refreshInterval, setRefresh, disableInterv const intervalTitleId = generateId(); return ( - + @@ -97,9 +100,6 @@ export const AutoRefreshControls = ({ refreshInterval, setRefresh, disableInterv ) : null} - - - @@ -112,16 +112,6 @@ export const AutoRefreshControls = ({ refreshInterval, setRefresh, disableInterv - - - - - - setRefresh(value)} /> diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/custom_interval.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/custom_interval.tsx similarity index 100% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/custom_interval.tsx rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/custom_interval.tsx diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/index.ts index eee613183639c1..e1ad9782c8aef5 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/index.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/index.ts @@ -15,13 +15,21 @@ import { fetchAllRenderables } from '../../../state/actions/elements'; // @ts-ignore Untyped local import { setZoomScale, setFullscreen, selectToplevelNodes } from '../../../state/actions/transient'; // @ts-ignore Untyped local -import { setWriteable } from '../../../state/actions/workpad'; +import { + setWriteable, + setRefreshInterval, + enableAutoplay, + setAutoplayInterval, + // @ts-ignore Untyped local +} from '../../../state/actions/workpad'; import { getZoomScale, canUserWrite } from '../../../state/selectors/app'; import { getWorkpadBoundingBox, getWorkpadWidth, getWorkpadHeight, isWriteable, + getRefreshInterval, + getAutoplay, } from '../../../state/selectors/workpad'; import { ViewMenu as Component, Props as ComponentProps } from './view_menu'; import { getFitZoomScale } from './lib/get_fit_zoom_scale'; @@ -40,24 +48,35 @@ interface DispatchProps { setFullscreen: (showFullscreen: boolean) => void; } -const mapStateToProps = (state: State) => ({ - zoomScale: getZoomScale(state), - boundingBox: getWorkpadBoundingBox(state), - workpadWidth: getWorkpadWidth(state), - workpadHeight: getWorkpadHeight(state), - isWriteable: isWriteable(state) && canUserWrite(state), -}); +const mapStateToProps = (state: State) => { + const { enabled, interval } = getAutoplay(state); + + return { + zoomScale: getZoomScale(state), + boundingBox: getWorkpadBoundingBox(state), + workpadWidth: getWorkpadWidth(state), + workpadHeight: getWorkpadHeight(state), + isWriteable: isWriteable(state) && canUserWrite(state), + refreshInterval: getRefreshInterval(state), + autoplayEnabled: enabled, + autoplayInterval: interval, + }; +}; const mapDispatchToProps = (dispatch: Dispatch) => ({ setZoomScale: (scale: number) => dispatch(setZoomScale(scale)), setWriteable: (isWorkpadWriteable: boolean) => dispatch(setWriteable(isWorkpadWriteable)), setFullscreen: (value: boolean) => { dispatch(setFullscreen(value)); + if (value) { dispatch(selectToplevelNodes([])); } }, doRefresh: () => dispatch(fetchAllRenderables()), + setRefreshInterval: (interval: number) => dispatch(setRefreshInterval(interval)), + enableAutoplay: (autoplay: number) => dispatch(enableAutoplay(autoplay)), + setAutoplayInterval: (interval: number) => dispatch(setAutoplayInterval(interval)), }); const mergeProps = ( @@ -66,13 +85,15 @@ const mergeProps = ( ownProps: ComponentProps ): ComponentProps => { const { boundingBox, workpadWidth, workpadHeight, ...remainingStateProps } = stateProps; + return { ...remainingStateProps, ...dispatchProps, ...ownProps, toggleWriteable: () => dispatchProps.setWriteable(!stateProps.isWriteable), enterFullscreen: () => dispatchProps.setFullscreen(true), - fitToWindow: () => getFitZoomScale(boundingBox, workpadWidth, workpadHeight), + fitToWindow: () => + dispatchProps.setZoomScale(getFitZoomScale(boundingBox, workpadWidth, workpadHeight)), }; }; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/kiosk_controls.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/kiosk_controls.tsx similarity index 86% rename from x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/kiosk_controls.tsx rename to x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/kiosk_controls.tsx index 9e6f0a91c61206..e63eed9f9df532 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/control_settings/kiosk_controls.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/kiosk_controls.tsx @@ -14,7 +14,6 @@ import { EuiHorizontalRule, EuiLink, EuiSpacer, - EuiSwitch, EuiText, EuiFlexItem, EuiFlexGroup, @@ -29,9 +28,7 @@ const { time: timeStrings } = UnitStrings; const { getSecondsText, getMinutesText } = timeStrings; interface Props { - autoplayEnabled: boolean; autoplayInterval: number; - onSetEnabled: (enabled: boolean) => void; onSetInterval: (interval: number | undefined) => void; } @@ -54,12 +51,7 @@ const ListGroup = ({ children, ...rest }: ListGroupProps) => ( const generateId = htmlIdGenerator(); -export const KioskControls = ({ - autoplayEnabled, - autoplayInterval, - onSetEnabled, - onSetInterval, -}: Props) => { +export const KioskControls = ({ autoplayInterval, onSetInterval }: Props) => { const RefreshItem = ({ duration, label, descriptionId }: RefreshItemProps) => (
  • onSetInterval(duration)} aria-describedby={descriptionId}> @@ -72,7 +64,11 @@ export const KioskControls = ({ const intervalTitleId = generateId(); return ( - + {strings.getTitle()} @@ -81,14 +77,6 @@ export const KioskControls = ({ - - onSetEnabled(ev.target.checked)} - /> - -

    {strings.getCycleFormLabel()}

    @@ -137,8 +125,6 @@ export const KioskControls = ({ }; KioskControls.propTypes = { - autoplayEnabled: PropTypes.bool.isRequired, autoplayInterval: PropTypes.number.isRequired, - onSetEnabled: PropTypes.func.isRequired, onSetInterval: PropTypes.func.isRequired, }; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/view_menu.scss b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/view_menu.scss new file mode 100644 index 00000000000000..c4e06881981c7a --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/view_menu.scss @@ -0,0 +1,4 @@ +.canvasViewMenu__kioskSettings, +.canvasViewMenu__refreshSettings { + padding: $euiSize; +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/view_menu.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/view_menu.tsx index d1e08c5809579f..b6f108cda37f61 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/view_menu.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/view_menu/view_menu.tsx @@ -12,10 +12,16 @@ import { EuiIcon, EuiContextMenuPanelItemDescriptor, } from '@elastic/eui'; -import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from '../../../../common/lib/constants'; +import { + MAX_ZOOM_LEVEL, + MIN_ZOOM_LEVEL, + CONTEXT_MENU_TOP_BORDER_CLASSNAME, +} from '../../../../common/lib/constants'; import { ComponentStrings } from '../../../../i18n/components'; import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; import { Popover, ClosePopoverFn } from '../../popover'; +import { AutoRefreshControls } from './auto_refresh_controls'; +import { KioskControls } from './kiosk_controls'; const { WorkpadHeaderViewMenu: strings } = ComponentStrings; @@ -62,10 +68,33 @@ export interface Props { * triggers a refresh of the workpad */ doRefresh: () => void; + /** + * Current auto refresh interval + */ + refreshInterval: number; + /** + * Sets auto refresh interval + */ + setRefreshInterval: (interval?: number) => void; + /** + * Is autoplay enabled? + */ + autoplayEnabled: boolean; + /** + * Current autoplay interval + */ + autoplayInterval: number; + /** + * Enables autoplay + */ + enableAutoplay: (autoplay: boolean) => void; + /** + * Sets autoplay interval + */ + setAutoplayInterval: (interval?: number) => void; } export const ViewMenu: FunctionComponent = ({ - doRefresh, enterFullscreen, fitToWindow, isWriteable, @@ -75,7 +104,20 @@ export const ViewMenu: FunctionComponent = ({ zoomIn, zoomOut, zoomScale, + doRefresh, + refreshInterval, + setRefreshInterval, + autoplayEnabled, + autoplayInterval, + enableAutoplay, + setAutoplayInterval, }) => { + const setRefresh = (val: number | undefined) => setRefreshInterval(val); + + const disableInterval = () => { + setRefresh(0); + }; + const viewControl = (togglePopover: React.MouseEventHandler) => ( {strings.getViewMenuButtonLabel()} @@ -121,36 +163,76 @@ export const ViewMenu: FunctionComponent = ({ const getPanelTree = (closePopover: ClosePopoverFn) => ({ id: 0, - title: strings.getViewMenuLabel(), items: [ + { + name: strings.getRefreshMenuItemLabel(), + icon: 'refresh', + onClick: () => { + doRefresh(); + }, + }, + { + name: strings.getRefreshSettingsMenuItemLabel(), + icon: 'empty', + panel: { + id: 1, + title: strings.getRefreshSettingsMenuItemLabel(), + content: ( + setRefresh(val)} + disableInterval={() => disableInterval()} + /> + ), + }, + }, { name: strings.getFullscreenMenuItemLabel(), icon: , + className: CONTEXT_MENU_TOP_BORDER_CLASSNAME, onClick: () => { enterFullscreen(); closePopover(); }, }, { - name: isWriteable ? strings.getHideEditModeLabel() : strings.getShowEditModeLabel(), - icon: , + name: autoplayEnabled + ? strings.getAutoplayOffMenuItemLabel() + : strings.getAutoplayOnMenuItemLabel(), + icon: autoplayEnabled ? 'stop' : 'play', onClick: () => { - toggleWriteable(); + enableAutoplay(!autoplayEnabled); closePopover(); }, }, { - name: strings.getRefreshMenuItemLabel(), - icon: 'refresh', + name: strings.getAutoplaySettingsMenuItemLabel(), + icon: 'empty', + panel: { + id: 2, + title: strings.getAutoplaySettingsMenuItemLabel(), + content: ( + + ), + }, + }, + { + name: isWriteable ? strings.getHideEditModeLabel() : strings.getShowEditModeLabel(), + icon: , + className: CONTEXT_MENU_TOP_BORDER_CLASSNAME, onClick: () => { - doRefresh(); + toggleWriteable(); + closePopover(); }, }, { name: strings.getZoomMenuItemLabel(), icon: 'magnifyWithPlus', panel: { - id: 1, + id: 3, title: strings.getZoomMenuItemLabel(), items: getZoomMenuItems(), }, @@ -161,7 +243,11 @@ export const ViewMenu: FunctionComponent = ({ return ( {({ closePopover }: { closePopover: ClosePopoverFn }) => ( - + )} ); @@ -169,4 +255,19 @@ export const ViewMenu: FunctionComponent = ({ ViewMenu.propTypes = { isWriteable: PropTypes.bool.isRequired, + zoomScale: PropTypes.number.isRequired, + fitToWindow: PropTypes.func.isRequired, + setZoomScale: PropTypes.func.isRequired, + zoomIn: PropTypes.func.isRequired, + zoomOut: PropTypes.func.isRequired, + resetZoom: PropTypes.func.isRequired, + toggleWriteable: PropTypes.func.isRequired, + enterFullscreen: PropTypes.func.isRequired, + doRefresh: PropTypes.func.isRequired, + refreshInterval: PropTypes.number.isRequired, + setRefreshInterval: PropTypes.func.isRequired, + autoplayEnabled: PropTypes.bool.isRequired, + autoplayInterval: PropTypes.number.isRequired, + enableAutoplay: PropTypes.func.isRequired, + setAutoplayInterval: PropTypes.func.isRequired, }; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx index 253e6c68cfc9e3..1f8e47b556771b 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx @@ -11,7 +11,6 @@ import { Shortcuts } from 'react-shortcuts'; import { EuiFlexItem, EuiFlexGroup, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { ComponentStrings } from '../../../i18n'; import { ToolTipShortcut } from '../tool_tip_shortcut/'; -import { ControlSettings } from './control_settings'; // @ts-ignore untyped local import { RefreshControl } from './refresh_control'; // @ts-ignore untyped local @@ -103,9 +102,6 @@ export const WorkpadHeader: FunctionComponent = ({ - - -
    @@ -122,7 +118,7 @@ export const WorkpadHeader: FunctionComponent = ({ )} void; export type onSetIntervalFn = (interval: string) => void; diff --git a/x-pack/test/functional/page_objects/canvas_page.ts b/x-pack/test/functional/page_objects/canvas_page.ts index 94ad393ead3a3f..ce36385a2f9df5 100644 --- a/x-pack/test/functional/page_objects/canvas_page.ts +++ b/x-pack/test/functional/page_objects/canvas_page.ts @@ -65,14 +65,12 @@ export function CanvasPageProvider({ getService }: FtrProviderContext) { async expectNoAddElementButton() { // Ensure page is fully loaded first by waiting for the refresh button - const refreshPopoverExists = await find.existsByCssSelector('#auto-refresh-popover', 20000); + const refreshPopoverExists = await testSubjects.exists('canvas-refresh-control', { + timeout: 20000, + }); expect(refreshPopoverExists).to.be(true); - const addElementButtonExists = await find.existsByCssSelector( - 'button[data-test-subj=add-element-button]', - 10 // don't need much of a wait at all here, because we already waited for refresh button above - ); - expect(addElementButtonExists).to.be(false); + await testSubjects.missingOrFail('add-element-button'); }, }; } From f9c1033d41aac8dd86b05ca2ad01cd12b51cca69 Mon Sep 17 00:00:00 2001 From: Catherine Liu Date: Thu, 30 Apr 2020 14:51:24 -0700 Subject: [PATCH 34/47] [Canvas] Adds edit menu (#64738) --- .../legacy/plugins/canvas/i18n/components.ts | 162 ++--- .../legacy/plugins/canvas/i18n/shortcuts.ts | 4 +- .../apps/workpad/workpad_app/workpad_app.js | 4 +- .../keyboard_shortcuts_doc.stories.storyshot | 4 +- .../components/sidebar/sidebar_content.js | 40 +- .../sidebar_header.stories.storyshot | 140 ---- .../__examples__/sidebar_header.stories.tsx | 16 - .../sidebar_header/sidebar_header.tsx | 627 +++--------------- .../edit_menu.examples.storyshot | 205 ++++++ .../__examples__/edit_menu.examples.tsx | 69 ++ .../workpad_header/edit_menu/edit_menu.tsx | 448 +++++++++++++ .../workpad_header/edit_menu/index.ts | 123 ++++ .../element_menu/element_menu.tsx | 1 - .../workpad_header/share_menu/share_menu.tsx | 1 - .../workpad_header/workpad_header.tsx | 6 + .../canvas/public/state/selectors/workpad.ts | 6 +- .../legacy/plugins/canvas/types/elements.ts | 6 +- .../translations/translations/ja-JP.json | 28 +- .../translations/translations/zh-CN.json | 31 +- .../functional/apps/canvas/custom_elements.ts | 5 +- 20 files changed, 1100 insertions(+), 826 deletions(-) create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/__snapshots__/edit_menu.examples.storyshot create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/edit_menu.examples.tsx create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/edit_menu.tsx create mode 100644 x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/index.ts diff --git a/x-pack/legacy/plugins/canvas/i18n/components.ts b/x-pack/legacy/plugins/canvas/i18n/components.ts index b1c2b41f062f1e..de16bc2101e8cf 100644 --- a/x-pack/legacy/plugins/canvas/i18n/components.ts +++ b/x-pack/legacy/plugins/canvas/i18n/components.ts @@ -804,17 +804,6 @@ export const ComponentStrings = { }), }, SidebarHeader: { - getAlignmentMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.alignmentMenuItemLabel', { - defaultMessage: 'Alignment', - description: - 'This refers to the vertical (i.e. left, center, right) and horizontal (i.e. top, middle, bottom) ' + - 'alignment options of the selected elements', - }), - getBottomAlignMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.bottomAlignMenuItemLabel', { - defaultMessage: 'Bottom', - }), getBringForwardAriaLabel: () => i18n.translate('xpack.canvas.sidebarHeader.bringForwardArialLabel', { defaultMessage: 'Move element up one layer', @@ -823,56 +812,6 @@ export const ComponentStrings = { i18n.translate('xpack.canvas.sidebarHeader.bringToFrontArialLabel', { defaultMessage: 'Move element to top layer', }), - getCenterAlignMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.centerAlignMenuItemLabel', { - defaultMessage: 'Center', - description: 'This refers to alignment centered horizontally.', - }), - getContextMenuTitle: () => - i18n.translate('xpack.canvas.sidebarHeader.contextMenuAriaLabel', { - defaultMessage: 'Element options', - }), - getCreateElementModalTitle: () => - i18n.translate('xpack.canvas.sidebarHeader.createElementModalTitle', { - defaultMessage: 'Create new element', - }), - getDistributionMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.distributionMenutItemLabel', { - defaultMessage: 'Distribution', - description: - 'This refers to the options to evenly spacing the selected elements horizontall or vertically.', - }), - getGroupMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.groupMenuItemLabel', { - defaultMessage: 'Group', - description: 'This refers to grouping multiple selected elements.', - }), - getHorizontalDistributionMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.horizontalDistributionMenutItemLabel', { - defaultMessage: 'Horizontal', - }), - getLeftAlignMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.leftAlignMenuItemLabel', { - defaultMessage: 'Left', - }), - getMiddleAlignMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.middleAlignMenuItemLabel', { - defaultMessage: 'Middle', - description: 'This refers to alignment centered vertically.', - }), - getOrderMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.orderMenuItemLabel', { - defaultMessage: 'Order', - description: 'Refers to the order of the elements displayed on the page from front to back', - }), - getRightAlignMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.rightAlignMenuItemLabel', { - defaultMessage: 'Right', - }), - getSaveElementMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.savedElementMenuItemLabel', { - defaultMessage: 'Save as new element', - }), getSendBackwardAriaLabel: () => i18n.translate('xpack.canvas.sidebarHeader.sendBackwardArialLabel', { defaultMessage: 'Move element down one layer', @@ -881,19 +820,6 @@ export const ComponentStrings = { i18n.translate('xpack.canvas.sidebarHeader.sendToBackArialLabel', { defaultMessage: 'Move element to bottom layer', }), - getTopAlignMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.topAlignMenuItemLabel', { - defaultMessage: 'Top', - }), - getUngroupMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.ungroupMenuItemLabel', { - defaultMessage: 'Ungroup', - description: 'This refers to ungrouping a grouped element', - }), - getVerticalDistributionMenuItemLabel: () => - i18n.translate('xpack.canvas.sidebarHeader.verticalDistributionMenutItemLabel', { - defaultMessage: 'Vertical', - }), }, TextStylePicker: { getAlignCenterOption: () => @@ -1099,6 +1025,94 @@ export const ComponentStrings = { defaultMessage: 'Set a custom interval', }), }, + WorkpadHeaderEditMenu: { + getAlignmentMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.alignmentMenuItemLabel', { + defaultMessage: 'Alignment', + description: + 'This refers to the vertical (i.e. left, center, right) and horizontal (i.e. top, middle, bottom) ' + + 'alignment options of the selected elements', + }), + getBottomAlignMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.bottomAlignMenuItemLabel', { + defaultMessage: 'Bottom', + }), + getCenterAlignMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.centerAlignMenuItemLabel', { + defaultMessage: 'Center', + description: 'This refers to alignment centered horizontally.', + }), + getCreateElementModalTitle: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.createElementModalTitle', { + defaultMessage: 'Create new element', + }), + getDistributionMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.distributionMenutItemLabel', { + defaultMessage: 'Distribution', + description: + 'This refers to the options to evenly spacing the selected elements horizontall or vertically.', + }), + getEditMenuButtonLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.editMenuButtonLabel', { + defaultMessage: 'Edit', + }), + getEditMenuLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.editMenuLabel', { + defaultMessage: 'Edit options', + }), + getGroupMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.groupMenuItemLabel', { + defaultMessage: 'Group', + description: 'This refers to grouping multiple selected elements.', + }), + getHorizontalDistributionMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.horizontalDistributionMenutItemLabel', { + defaultMessage: 'Horizontal', + }), + getLeftAlignMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.leftAlignMenuItemLabel', { + defaultMessage: 'Left', + }), + getMiddleAlignMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.middleAlignMenuItemLabel', { + defaultMessage: 'Middle', + description: 'This refers to alignment centered vertically.', + }), + getOrderMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.orderMenuItemLabel', { + defaultMessage: 'Order', + description: 'Refers to the order of the elements displayed on the page from front to back', + }), + getRedoMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.redoMenuItemLabel', { + defaultMessage: 'Redo', + }), + getRightAlignMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.rightAlignMenuItemLabel', { + defaultMessage: 'Right', + }), + getSaveElementMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.savedElementMenuItemLabel', { + defaultMessage: 'Save as new element', + }), + getTopAlignMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.topAlignMenuItemLabel', { + defaultMessage: 'Top', + }), + getUndoMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.undoMenuItemLabel', { + defaultMessage: 'Undo', + }), + getUngroupMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.ungroupMenuItemLabel', { + defaultMessage: 'Ungroup', + description: 'This refers to ungrouping a grouped element', + }), + getVerticalDistributionMenuItemLabel: () => + i18n.translate('xpack.canvas.workpadHeaderEditMenu.verticalDistributionMenutItemLabel', { + defaultMessage: 'Vertical', + }), + }, WorkpadHeaderElementMenu: { getAssetsMenuItemLabel: () => i18n.translate('xpack.canvas.workpadHeaderElementMenu.manageAssetsMenuItemLabel', { diff --git a/x-pack/legacy/plugins/canvas/i18n/shortcuts.ts b/x-pack/legacy/plugins/canvas/i18n/shortcuts.ts index 32b07a45c17db2..124d70ff3095f4 100644 --- a/x-pack/legacy/plugins/canvas/i18n/shortcuts.ts +++ b/x-pack/legacy/plugins/canvas/i18n/shortcuts.ts @@ -42,10 +42,10 @@ export const ShortcutStrings = { defaultMessage: 'Delete', }), BRING_FORWARD: i18n.translate('xpack.canvas.keyboardShortcuts.bringFowardShortcutHelpText', { - defaultMessage: 'Bring to front', + defaultMessage: 'Bring forward', }), BRING_TO_FRONT: i18n.translate('xpack.canvas.keyboardShortcuts.bringToFrontShortcutHelpText', { - defaultMessage: 'Bring forward', + defaultMessage: 'Bring to front', }), SEND_BACKWARD: i18n.translate('xpack.canvas.keyboardShortcuts.sendBackwardShortcutHelpText', { defaultMessage: 'Send backward', diff --git a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.js b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.js index b8e1bece6ac305..fc3ac9922355a5 100644 --- a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.js +++ b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_app.js @@ -43,7 +43,7 @@ export class WorkpadApp extends React.PureComponent {
    - + {})} />
    - {})} /> +
    )}
    diff --git a/x-pack/legacy/plugins/canvas/public/components/keyboard_shortcuts_doc/__examples__/__snapshots__/keyboard_shortcuts_doc.stories.storyshot b/x-pack/legacy/plugins/canvas/public/components/keyboard_shortcuts_doc/__examples__/__snapshots__/keyboard_shortcuts_doc.stories.storyshot index 503677687ba12a..d59d03578a3633 100644 --- a/x-pack/legacy/plugins/canvas/public/components/keyboard_shortcuts_doc/__examples__/__snapshots__/keyboard_shortcuts_doc.stories.storyshot +++ b/x-pack/legacy/plugins/canvas/public/components/keyboard_shortcuts_doc/__examples__/__snapshots__/keyboard_shortcuts_doc.stories.storyshot @@ -212,7 +212,7 @@ exports[`Storyshots components/KeyboardShortcutsDoc default 1`] = `
    - Bring forward + Bring to front
    - Bring to front + Bring forward
    ({ selectedToplevelNodes: getSelectedToplevelNodes(state), selectedElementId: getSelectedElementId(state), - state, }); -const mergeProps = ( - { state, ...restStateProps }, - { dispatch, ...restDispatchProps }, - ownProps -) => ({ - ...ownProps, - ...restDispatchProps, - ...restStateProps, - updateGlobalState: globalStateUpdater(dispatch, state), -}); - -const withGlobalState = (commit, updateGlobalState) => (type, payload) => { - const newLayoutState = commit(type, payload); - if (newLayoutState.currentScene.gestureEnd) { - updateGlobalState(newLayoutState); - } -}; - -const MultiElementSidebar = ({ commit, updateGlobalState }) => ( +const MultiElementSidebar = () => ( - + ); -const GroupedElementSidebar = ({ commit, updateGlobalState }) => ( +const GroupedElementSidebar = () => ( - + @@ -92,7 +65,4 @@ const branches = [ ), ]; -export const SidebarContent = compose( - connect(mapStateToProps, null, mergeProps), - ...branches -)(GlobalConfig); +export const SidebarContent = compose(connect(mapStateToProps), ...branches)(GlobalConfig); diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/__snapshots__/sidebar_header.stories.storyshot b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/__snapshots__/sidebar_header.stories.storyshot index ac25cbe0b68329..4d5b9570ee20f3 100644 --- a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/__snapshots__/sidebar_header.stories.storyshot +++ b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/__snapshots__/sidebar_header.stories.storyshot @@ -20,80 +20,6 @@ exports[`Storyshots components/Sidebar/SidebarHeader default 1`] = ` Selected layer
    -
    -
    -
    - - - -
    -
    - -
    -
    -
  • `; @@ -224,72 +150,6 @@ exports[`Storyshots components/Sidebar/SidebarHeader with layer controls 1`] = ` -
    - - - -
    -
    - -
    diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/sidebar_header.stories.tsx b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/sidebar_header.stories.tsx index 9baff380fc3ebc..11c66906a6ef65 100644 --- a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/sidebar_header.stories.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/__examples__/sidebar_header.stories.tsx @@ -10,26 +10,10 @@ import { action } from '@storybook/addon-actions'; import { SidebarHeader } from '../sidebar_header'; const handlers = { - cloneNodes: action('cloneNodes'), - copyNodes: action('copyNodes'), - cutNodes: action('cutNodes'), - pasteNodes: action('pasteNodes'), - deleteNodes: action('deleteNodes'), bringToFront: action('bringToFront'), bringForward: action('bringForward'), sendBackward: action('sendBackward'), sendToBack: action('sendToBack'), - createCustomElement: action('createCustomElement'), - groupNodes: action('groupNodes'), - ungroupNodes: action('ungroupNodes'), - alignLeft: action('alignLeft'), - alignMiddle: action('alignMiddle'), - alignRight: action('alignRight'), - alignTop: action('alignTop'), - alignCenter: action('alignCenter'), - alignBottom: action('alignBottom'), - distributeHorizontally: action('distributeHorizontally'), - distributeVertically: action('distributeVertically'), }; storiesOf('components/Sidebar/SidebarHeader', module) diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/sidebar_header.tsx b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/sidebar_header.tsx index 925e68a565f040..024a2dbb41a24d 100644 --- a/x-pack/legacy/plugins/canvas/public/components/sidebar_header/sidebar_header.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/sidebar_header/sidebar_header.tsx @@ -4,25 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Component, Fragment } from 'react'; +import React, { FunctionComponent } from 'react'; import PropTypes from 'prop-types'; -import { - EuiFlexGroup, - EuiFlexItem, - EuiTitle, - EuiButtonIcon, - EuiContextMenu, - EuiToolTip, - EuiContextMenuPanelItemDescriptor, - EuiContextMenuPanelDescriptor, - EuiOverlayMask, -} from '@elastic/eui'; -import { Popover } from '../popover'; -import { CustomElementModal } from '../custom_element_modal'; +import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { ToolTipShortcut } from '../tool_tip_shortcut/'; import { ComponentStrings } from '../../../i18n/components'; import { ShortcutStrings } from '../../../i18n/shortcuts'; -import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../common/lib/constants'; const { SidebarHeader: strings } = ComponentStrings; const shortcutHelp = ShortcutStrings.getShortcutHelp(); @@ -36,26 +23,6 @@ interface Props { * indicated whether or not layer controls should be displayed */ showLayerControls?: boolean; - /** - * cuts selected elements - */ - cutNodes: () => void; - /** - * copies selected elements to clipboard - */ - copyNodes: () => void; - /** - * pastes elements stored in clipboard to page - */ - pasteNodes: () => void; - /** - * clones selected elements - */ - cloneNodes: () => void; - /** - * deletes selected elements - */ - deleteNodes: () => void; /** * moves selected element to top layer */ @@ -72,493 +39,117 @@ interface Props { * moves selected element to bottom layer */ sendToBack: () => void; - /** - * saves the selected elements as an custom-element saved object - */ - createCustomElement: (name: string, description: string, image: string) => void; - /** - * indicated whether the selected element is a group or not - */ - groupIsSelected: boolean; - /** - * only more than one selected element can be grouped - */ - selectedNodes: string[]; - /** - * groups selected elements - */ - groupNodes: () => void; - /** - * ungroups selected group - */ - ungroupNodes: () => void; - /** - * left align selected elements - */ - alignLeft: () => void; - /** - * center align selected elements - */ - alignCenter: () => void; - /** - * right align selected elements - */ - alignRight: () => void; - /** - * top align selected elements - */ - alignTop: () => void; - /** - * middle align selected elements - */ - alignMiddle: () => void; - /** - * bottom align selected elements - */ - alignBottom: () => void; - /** - * horizontally distribute selected elements - */ - distributeHorizontally: () => void; - /** - * vertically distribute selected elements - */ - distributeVertically: () => void; -} - -interface State { - /** - * indicates whether or not the custom element modal is open - */ - isModalVisible: boolean; -} - -interface MenuTuple { - menuItem: EuiContextMenuPanelItemDescriptor; - panel: EuiContextMenuPanelDescriptor; } -const contextMenuButton = (handleClick: React.MouseEventHandler) => ( - -); - -export class SidebarHeader extends Component { - public static propTypes = { - title: PropTypes.string.isRequired, - showLayerControls: PropTypes.bool, // TODO: remove when we support relayering multiple elements - cutNodes: PropTypes.func.isRequired, - copyNodes: PropTypes.func.isRequired, - pasteNodes: PropTypes.func.isRequired, - cloneNodes: PropTypes.func.isRequired, - deleteNodes: PropTypes.func.isRequired, - bringToFront: PropTypes.func.isRequired, - bringForward: PropTypes.func.isRequired, - sendBackward: PropTypes.func.isRequired, - sendToBack: PropTypes.func.isRequired, - createCustomElement: PropTypes.func.isRequired, - groupIsSelected: PropTypes.bool, - selectedNodes: PropTypes.array, - groupNodes: PropTypes.func.isRequired, - ungroupNodes: PropTypes.func.isRequired, - alignLeft: PropTypes.func.isRequired, - alignCenter: PropTypes.func.isRequired, - alignRight: PropTypes.func.isRequired, - alignTop: PropTypes.func.isRequired, - alignMiddle: PropTypes.func.isRequired, - alignBottom: PropTypes.func.isRequired, - distributeHorizontally: PropTypes.func.isRequired, - distributeVertically: PropTypes.func.isRequired, - }; - - public static defaultProps = { - groupIsSelected: false, - showLayerControls: false, - selectedNodes: [], - }; - - public state = { - isModalVisible: false, - }; - - private _isMounted = false; - private _showModal = () => this._isMounted && this.setState({ isModalVisible: true }); - private _hideModal = () => this._isMounted && this.setState({ isModalVisible: false }); - - public componentDidMount() { - this._isMounted = true; - } - - public componentWillUnmount() { - this._isMounted = false; - } - - private _renderLayoutControls = () => { - const { bringToFront, bringForward, sendBackward, sendToBack } = this.props; - return ( - - - - {shortcutHelp.BRING_TO_FRONT} - - - } - > - - - - - - {shortcutHelp.BRING_FORWARD} - - - } - > - - - - - - {shortcutHelp.SEND_BACKWARD} - - - } - > - - - - - - {shortcutHelp.SEND_TO_BACK} - - - } - > - - - - - ); - }; - - private _getLayerMenuItems = (): MenuTuple => { - const { bringToFront, bringForward, sendBackward, sendToBack } = this.props; - - return { - menuItem: { - name: strings.getOrderMenuItemLabel(), - className: CONTEXT_MENU_TOP_BORDER_CLASSNAME, - panel: 1, - }, - panel: { - id: 1, - title: strings.getOrderMenuItemLabel(), - items: [ - { - name: shortcutHelp.BRING_TO_FRONT, // TODO: check against current element position and disable if already top layer - icon: 'sortUp', - onClick: bringToFront, - }, - { - name: shortcutHelp.BRING_TO_FRONT, // TODO: same as above - icon: 'arrowUp', - onClick: bringForward, - }, - { - name: shortcutHelp.SEND_BACKWARD, // TODO: check against current element position and disable if already bottom layer - icon: 'arrowDown', - onClick: sendBackward, - }, - { - name: shortcutHelp.SEND_TO_BACK, // TODO: same as above - icon: 'sortDown', - onClick: sendToBack, - }, - ], - }, - }; - }; - - private _getAlignmentMenuItems = (close: (fn: () => void) => () => void): MenuTuple => { - const { alignLeft, alignCenter, alignRight, alignTop, alignMiddle, alignBottom } = this.props; - - return { - menuItem: { - name: strings.getAlignmentMenuItemLabel(), - className: 'canvasContextMenu', - panel: 2, - }, - panel: { - id: 2, - title: strings.getAlignmentMenuItemLabel(), - items: [ - { - name: strings.getLeftAlignMenuItemLabel(), - icon: 'editorItemAlignLeft', - onClick: close(alignLeft), - }, - { - name: strings.getCenterAlignMenuItemLabel(), - icon: 'editorItemAlignCenter', - onClick: close(alignCenter), - }, - { - name: strings.getRightAlignMenuItemLabel(), - icon: 'editorItemAlignRight', - onClick: close(alignRight), - }, - { - name: strings.getTopAlignMenuItemLabel(), - icon: 'editorItemAlignTop', - onClick: close(alignTop), - }, - { - name: strings.getMiddleAlignMenuItemLabel(), - icon: 'editorItemAlignMiddle', - onClick: close(alignMiddle), - }, - { - name: strings.getBottomAlignMenuItemLabel(), - icon: 'editorItemAlignBottom', - onClick: close(alignBottom), - }, - ], - }, - }; - }; - - private _getDistributionMenuItems = (close: (fn: () => void) => () => void): MenuTuple => { - const { distributeHorizontally, distributeVertically } = this.props; - - return { - menuItem: { - name: strings.getDistributionMenuItemLabel(), - className: 'canvasContextMenu', - panel: 3, - }, - panel: { - id: 3, - title: strings.getDistributionMenuItemLabel(), - items: [ - { - name: strings.getHorizontalDistributionMenuItemLabel(), - icon: 'editorDistributeHorizontal', - onClick: close(distributeHorizontally), - }, - { - name: strings.getVerticalDistributionMenuItemLabel(), - icon: 'editorDistributeVertical', - onClick: close(distributeVertically), - }, - ], - }, - }; - }; - - private _getGroupMenuItems = ( - close: (fn: () => void) => () => void - ): EuiContextMenuPanelItemDescriptor[] => { - const { groupIsSelected, ungroupNodes, groupNodes, selectedNodes } = this.props; - return groupIsSelected - ? [ - { - name: strings.getUngroupMenuItemLabel(), - className: CONTEXT_MENU_TOP_BORDER_CLASSNAME, - onClick: close(ungroupNodes), - }, - ] - : selectedNodes.length > 1 - ? [ - { - name: strings.getGroupMenuItemLabel(), - className: CONTEXT_MENU_TOP_BORDER_CLASSNAME, - onClick: close(groupNodes), - }, - ] - : []; - }; - - private _getPanels = (closePopover: () => void): EuiContextMenuPanelDescriptor[] => { - const { - showLayerControls, - cutNodes, - copyNodes, - pasteNodes, - deleteNodes, - cloneNodes, - } = this.props; - - // closes popover after invoking fn - const close = (fn: () => void) => () => { - fn(); - closePopover(); - }; - - const items: EuiContextMenuPanelItemDescriptor[] = [ - { - name: shortcutHelp.CUT, - icon: 'cut', - onClick: close(cutNodes), - }, - { - name: shortcutHelp.COPY, - icon: 'copy', - onClick: copyNodes, - }, - { - name: shortcutHelp.PASTE, // TODO: can this be disabled if clipboard is empty? - icon: 'copyClipboard', - onClick: close(pasteNodes), - }, - { - name: shortcutHelp.DELETE, - icon: 'trash', - onClick: close(deleteNodes), - }, - { - name: shortcutHelp.CLONE, - onClick: close(cloneNodes), - }, - ...this._getGroupMenuItems(close), - ]; - - const panels: EuiContextMenuPanelDescriptor[] = [ - { - id: 0, - title: strings.getContextMenuTitle(), - items, - }, - ]; - - const fillMenu = ({ menuItem, panel }: MenuTuple) => { - items.push(menuItem); // add Order menu item to first panel - panels.push(panel); // add nested panel for layers controls - }; - - if (showLayerControls) { - fillMenu(this._getLayerMenuItems()); - } - - if (this.props.selectedNodes.length > 1) { - fillMenu(this._getAlignmentMenuItems(close)); - } - - if (this.props.selectedNodes.length > 2) { - fillMenu(this._getDistributionMenuItems(close)); - } - - items.push({ - name: strings.getSaveElementMenuItemLabel(), - icon: 'indexOpen', - className: CONTEXT_MENU_TOP_BORDER_CLASSNAME, - onClick: this._showModal, - }); - - return panels; - }; - - private _renderContextMenu = () => ( - - {({ closePopover }: { closePopover: () => void }) => ( - - )} - - ); - - private _handleSave = (name: string, description: string, image: string) => { - const { createCustomElement } = this.props; - createCustomElement(name, description, image); - this._hideModal(); - }; - - render() { - const { title, showLayerControls } = this.props; - const { isModalVisible } = this.state; - - return ( - - +export const SidebarHeader: FunctionComponent = ({ + title, + showLayerControls, + bringToFront, + bringForward, + sendBackward, + sendToBack, +}) => ( + + + +

    {title}

    +
    +
    + {showLayerControls ? ( + + - -

    {title}

    -
    + + {shortcutHelp.BRING_TO_FRONT} + + + } + > + +
    - - {showLayerControls ? this._renderLayoutControls() : null} - - - - - - {this._renderContextMenu()} - + + {shortcutHelp.BRING_FORWARD} + + + } + > + + + + + + {shortcutHelp.SEND_BACKWARD} + + + } + > + + + + + + {shortcutHelp.SEND_TO_BACK} + + + } + > + +
    - {isModalVisible ? ( - - - - ) : null} -
    - ); - } -} +
    + ) : null} +
    +); + +SidebarHeader.propTypes = { + title: PropTypes.string.isRequired, + showLayerControls: PropTypes.bool, // TODO: remove when we support relayering multiple elements + bringToFront: PropTypes.func.isRequired, + bringForward: PropTypes.func.isRequired, + sendBackward: PropTypes.func.isRequired, + sendToBack: PropTypes.func.isRequired, +}; + +SidebarHeader.defaultProps = { + showLayerControls: false, +}; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/__snapshots__/edit_menu.examples.storyshot b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/__snapshots__/edit_menu.examples.storyshot new file mode 100644 index 00000000000000..42c59d41dca62f --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/__snapshots__/edit_menu.examples.storyshot @@ -0,0 +1,205 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Storyshots components/WorkpadHeader/EditMenu 2 elements selected 1`] = ` +
    +
    + +
    +
    +`; + +exports[`Storyshots components/WorkpadHeader/EditMenu 3+ elements selected 1`] = ` +
    +
    + +
    +
    +`; + +exports[`Storyshots components/WorkpadHeader/EditMenu clipboard data exists 1`] = ` +
    +
    + +
    +
    +`; + +exports[`Storyshots components/WorkpadHeader/EditMenu default 1`] = ` +
    +
    + +
    +
    +`; + +exports[`Storyshots components/WorkpadHeader/EditMenu single element selected 1`] = ` +
    +
    + +
    +
    +`; + +exports[`Storyshots components/WorkpadHeader/EditMenu single grouped element selected 1`] = ` +
    +
    + +
    +
    +`; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/edit_menu.examples.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/edit_menu.examples.tsx new file mode 100644 index 00000000000000..a0ab8d53485f56 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/__examples__/edit_menu.examples.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { EditMenu } from '../edit_menu'; + +const handlers = { + cutNodes: action('cutNodes'), + copyNodes: action('copyNodes'), + pasteNodes: action('pasteNodes'), + deleteNodes: action('deleteNodes'), + cloneNodes: action('cloneNodes'), + bringToFront: action('bringToFront'), + bringForward: action('bringForward'), + sendBackward: action('sendBackward'), + sendToBack: action('sendToBack'), + alignLeft: action('alignLeft'), + alignCenter: action('alignCenter'), + alignRight: action('alignRight'), + alignTop: action('alignTop'), + alignMiddle: action('alignMiddle'), + alignBottom: action('alignBottom'), + distributeHorizontally: action('distributeHorizontally'), + distributeVertically: action('distributeVertically'), + createCustomElement: action('createCustomElement'), + groupNodes: action('groupNodes'), + ungroupNodes: action('ungroupNodes'), + undoHistory: action('undoHistory'), + redoHistory: action('redoHistory'), +}; + +storiesOf('components/WorkpadHeader/EditMenu', module) + .add('default', () => ( + + )) + .add('clipboard data exists', () => ( + + )) + .add('single element selected', () => ( + + )) + .add('single grouped element selected', () => ( + + )) + .add('2 elements selected', () => ( + + )) + .add('3+ elements selected', () => ( + + )); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/edit_menu.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/edit_menu.tsx new file mode 100644 index 00000000000000..15191b8d416ff1 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/edit_menu.tsx @@ -0,0 +1,448 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment, FunctionComponent, useState } from 'react'; +import PropTypes from 'prop-types'; +import { EuiButtonEmpty, EuiContextMenu, EuiIcon, EuiOverlayMask } from '@elastic/eui'; +import { ComponentStrings } from '../../../../i18n/components'; +import { ShortcutStrings } from '../../../../i18n/shortcuts'; +import { flattenPanelTree } from '../../../lib/flatten_panel_tree'; +import { Popover, ClosePopoverFn } from '../../popover'; +import { CustomElementModal } from '../../custom_element_modal'; +import { CONTEXT_MENU_TOP_BORDER_CLASSNAME } from '../../../../common/lib/constants'; + +const { WorkpadHeaderEditMenu: strings } = ComponentStrings; +const shortcutHelp = ShortcutStrings.getShortcutHelp(); + +export interface Props { + /** + * cuts selected elements + */ + cutNodes: () => void; + /** + * copies selected elements to clipboard + */ + copyNodes: () => void; + /** + * pastes elements stored in clipboard to page + */ + pasteNodes: () => void; + /** + * clones selected elements + */ + cloneNodes: () => void; + /** + * deletes selected elements + */ + deleteNodes: () => void; + /** + * moves selected element to top layer + */ + bringToFront: () => void; + /** + * moves selected element up one layer + */ + bringForward: () => void; + /** + * moves selected element down one layer + */ + sendBackward: () => void; + /** + * moves selected element to bottom layer + */ + sendToBack: () => void; + /** + * saves the selected elements as an custom-element saved object + */ + createCustomElement: (name: string, description: string, image: string) => void; + /** + * indicated whether the selected element is a group or not + */ + groupIsSelected: boolean; + /** + * only more than one selected element can be grouped + */ + selectedNodes: string[]; + /** + * groups selected elements + */ + groupNodes: () => void; + /** + * ungroups selected group + */ + ungroupNodes: () => void; + /** + * left align selected elements + */ + alignLeft: () => void; + /** + * center align selected elements + */ + alignCenter: () => void; + /** + * right align selected elements + */ + alignRight: () => void; + /** + * top align selected elements + */ + alignTop: () => void; + /** + * middle align selected elements + */ + alignMiddle: () => void; + /** + * bottom align selected elements + */ + alignBottom: () => void; + /** + * horizontally distribute selected elements + */ + distributeHorizontally: () => void; + /** + * vertically distribute selected elements + */ + distributeVertically: () => void; + /** + * Reverts last change to the workpad + */ + undoHistory: () => void; + /** + * Reapplies last reverted change to the workpad + */ + redoHistory: () => void; + /** + * Is there element clipboard data to paste? + */ + hasPasteData: boolean; +} + +export const EditMenu: FunctionComponent = ({ + cutNodes, + copyNodes, + pasteNodes, + deleteNodes, + cloneNodes, + bringToFront, + bringForward, + sendBackward, + sendToBack, + alignLeft, + alignCenter, + alignRight, + alignTop, + alignMiddle, + alignBottom, + distributeHorizontally, + distributeVertically, + createCustomElement, + selectedNodes, + groupIsSelected, + groupNodes, + ungroupNodes, + undoHistory, + redoHistory, + hasPasteData, +}) => { + const [isModalVisible, setModalVisible] = useState(false); + const showModal = () => setModalVisible(true); + const hideModal = () => setModalVisible(false); + + const handleSave = (name: string, description: string, image: string) => { + createCustomElement(name, description, image); + hideModal(); + }; + + const editControl = (togglePopover: React.MouseEventHandler) => ( + + {strings.getEditMenuButtonLabel()} + + ); + + const getPanelTree = (closePopover: ClosePopoverFn) => { + const groupMenuItem = groupIsSelected + ? { + name: strings.getUngroupMenuItemLabel(), + className: CONTEXT_MENU_TOP_BORDER_CLASSNAME, + icon: , + onClick: () => { + ungroupNodes(); + closePopover(); + }, + } + : { + name: strings.getGroupMenuItemLabel(), + className: CONTEXT_MENU_TOP_BORDER_CLASSNAME, + icon: , + disabled: selectedNodes.length < 2, + onClick: () => { + groupNodes(); + closePopover(); + }, + }; + + const orderMenuItem = { + name: strings.getOrderMenuItemLabel(), + disabled: selectedNodes.length !== 1, // TODO: change to === 0 when we support relayering multiple elements + icon: , + panel: { + id: 1, + title: strings.getOrderMenuItemLabel(), + items: [ + { + name: shortcutHelp.BRING_TO_FRONT, // TODO: check against current element position and disable if already top layer + icon: 'sortUp', + onClick: bringToFront, + }, + { + name: shortcutHelp.BRING_FORWARD, // TODO: same as above + icon: 'arrowUp', + onClick: bringForward, + }, + { + name: shortcutHelp.SEND_BACKWARD, // TODO: check against current element position and disable if already bottom layer + icon: 'arrowDown', + onClick: sendBackward, + }, + { + name: shortcutHelp.SEND_TO_BACK, // TODO: same as above + icon: 'sortDown', + onClick: sendToBack, + }, + ], + }, + }; + + const alignmentMenuItem = { + name: strings.getAlignmentMenuItemLabel(), + className: 'canvasContextMenu', + disabled: groupIsSelected || selectedNodes.length < 2, + icon: , + panel: { + id: 2, + title: strings.getAlignmentMenuItemLabel(), + items: [ + { + name: strings.getLeftAlignMenuItemLabel(), + icon: 'editorItemAlignLeft', + onClick: () => { + alignLeft(); + closePopover(); + }, + }, + { + name: strings.getCenterAlignMenuItemLabel(), + icon: 'editorItemAlignCenter', + onClick: () => { + alignCenter(); + closePopover(); + }, + }, + { + name: strings.getRightAlignMenuItemLabel(), + icon: 'editorItemAlignRight', + onClick: () => { + alignRight(); + closePopover(); + }, + }, + { + name: strings.getTopAlignMenuItemLabel(), + icon: 'editorItemAlignTop', + onClick: () => { + alignTop(); + closePopover(); + }, + }, + { + name: strings.getMiddleAlignMenuItemLabel(), + icon: 'editorItemAlignMiddle', + onClick: () => { + alignMiddle(); + closePopover(); + }, + }, + { + name: strings.getBottomAlignMenuItemLabel(), + icon: 'editorItemAlignBottom', + onClick: () => { + alignBottom(); + closePopover(); + }, + }, + ], + }, + }; + + const distributionMenuItem = { + name: strings.getDistributionMenuItemLabel(), + className: 'canvasContextMenu', + disabled: groupIsSelected || selectedNodes.length < 3, + icon: , + panel: { + id: 3, + title: strings.getAlignmentMenuItemLabel(), + items: [ + { + name: strings.getHorizontalDistributionMenuItemLabel(), + icon: 'editorDistributeHorizontal', + onClick: () => { + distributeHorizontally(); + closePopover(); + }, + }, + { + name: strings.getVerticalDistributionMenuItemLabel(), + icon: 'editorDistributeVertical', + onClick: () => { + distributeVertically(); + closePopover(); + }, + }, + ], + }, + }; + + const savedElementMenuItem = { + name: strings.getSaveElementMenuItemLabel(), + icon: , + disabled: selectedNodes.length < 1, + className: CONTEXT_MENU_TOP_BORDER_CLASSNAME, + 'data-test-subj': 'canvasWorkpadEditMenu__saveElementButton', + onClick: () => { + showModal(); + closePopover(); + }, + }; + + const items = [ + { + // TODO: check history and disable when there are no more changes to revert + name: strings.getUndoMenuItemLabel(), + icon: , + onClick: () => { + undoHistory(); + }, + }, + { + // TODO: check history and disable when there are no more changes to reapply + name: strings.getRedoMenuItemLabel(), + icon: , + onClick: () => { + redoHistory(); + }, + }, + { + name: shortcutHelp.CUT, + icon: , + className: CONTEXT_MENU_TOP_BORDER_CLASSNAME, + disabled: selectedNodes.length < 1, + onClick: () => { + cutNodes(); + closePopover(); + }, + }, + { + name: shortcutHelp.COPY, + disabled: selectedNodes.length < 1, + icon: , + onClick: () => { + copyNodes(); + }, + }, + { + name: shortcutHelp.PASTE, // TODO: can this be disabled if clipboard is empty? + icon: , + disabled: !hasPasteData, + onClick: () => { + pasteNodes(); + closePopover(); + }, + }, + { + name: shortcutHelp.DELETE, + icon: , + disabled: selectedNodes.length < 1, + onClick: () => { + deleteNodes(); + closePopover(); + }, + }, + { + name: shortcutHelp.CLONE, + icon: , + disabled: selectedNodes.length < 1, + onClick: () => { + cloneNodes(); + closePopover(); + }, + }, + groupMenuItem, + orderMenuItem, + alignmentMenuItem, + distributionMenuItem, + savedElementMenuItem, + ]; + + return { + id: 0, + // title: strings.getEditMenuLabel(), + items, + }; + }; + + return ( + + + {({ closePopover }: { closePopover: ClosePopoverFn }) => ( + + )} + + {isModalVisible ? ( + + + + ) : null} + + ); +}; + +EditMenu.propTypes = { + cutNodes: PropTypes.func.isRequired, + copyNodes: PropTypes.func.isRequired, + pasteNodes: PropTypes.func.isRequired, + deleteNodes: PropTypes.func.isRequired, + cloneNodes: PropTypes.func.isRequired, + bringToFront: PropTypes.func.isRequired, + bringForward: PropTypes.func.isRequired, + sendBackward: PropTypes.func.isRequired, + sendToBack: PropTypes.func.isRequired, + alignLeft: PropTypes.func.isRequired, + alignCenter: PropTypes.func.isRequired, + alignRight: PropTypes.func.isRequired, + alignTop: PropTypes.func.isRequired, + alignMiddle: PropTypes.func.isRequired, + alignBottom: PropTypes.func.isRequired, + distributeHorizontally: PropTypes.func.isRequired, + distributeVertically: PropTypes.func.isRequired, + createCustomElement: PropTypes.func.isRequired, + selectedNodes: PropTypes.arrayOf(PropTypes.string).isRequired, + groupIsSelected: PropTypes.bool.isRequired, + groupNodes: PropTypes.func.isRequired, + ungroupNodes: PropTypes.func.isRequired, +}; diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/index.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/index.ts new file mode 100644 index 00000000000000..a8bb7177dbd24f --- /dev/null +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/edit_menu/index.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { connect } from 'react-redux'; +import { compose, withHandlers, withProps } from 'recompose'; +import { Dispatch } from 'redux'; +import { State, PositionedElement } from '../../../../types'; +import { getClipboardData } from '../../../lib/clipboard'; +// @ts-ignore Untyped local +import { flatten } from '../../../lib/aeroelastic/functional'; +// @ts-ignore Untyped local +import { globalStateUpdater } from '../../workpad_page/integration_utils'; +// @ts-ignore Untyped local +import { crawlTree } from '../../workpad_page/integration_utils'; +// @ts-ignore Untyped local +import { insertNodes, elementLayer, removeElements } from '../../../state/actions/elements'; +// @ts-ignore Untyped local +import { undoHistory, redoHistory } from '../../../state/actions/history'; +// @ts-ignore Untyped local +import { selectToplevelNodes } from '../../../state/actions/transient'; +import { + getSelectedPage, + getNodes, + getSelectedToplevelNodes, +} from '../../../state/selectors/workpad'; +import { + layerHandlerCreators, + clipboardHandlerCreators, + basicHandlerCreators, + groupHandlerCreators, + alignmentDistributionHandlerCreators, +} from '../../../lib/element_handler_creators'; +import { EditMenu as Component, Props as ComponentProps } from './edit_menu'; + +type LayoutState = any; + +type CommitFn = (type: string, payload: any) => LayoutState; + +interface OwnProps { + commit: CommitFn; +} + +const withGlobalState = ( + commit: CommitFn, + updateGlobalState: (layoutState: LayoutState) => void +) => (type: string, payload: any) => { + const newLayoutState = commit(type, payload); + if (newLayoutState.currentScene.gestureEnd) { + updateGlobalState(newLayoutState); + } +}; + +/* + * TODO: this is all copied from interactive_workpad_page and workpad_shortcuts + */ +const mapStateToProps = (state: State) => { + const pageId = getSelectedPage(state); + const nodes = getNodes(state, pageId) as PositionedElement[]; + const selectedToplevelNodes = getSelectedToplevelNodes(state); + const selectedPrimaryShapeObjects = selectedToplevelNodes + .map((id: string) => nodes.find((s: PositionedElement) => s.id === id)) + .filter((shape?: PositionedElement) => shape) as PositionedElement[]; + const selectedPersistentPrimaryNodes = flatten( + selectedPrimaryShapeObjects.map((shape: PositionedElement) => + nodes.find((n: PositionedElement) => n.id === shape.id) // is it a leaf or a persisted group? + ? [shape.id] + : nodes.filter((s: PositionedElement) => s.position.parent === shape.id).map(s => s.id) + ) + ); + const selectedNodeIds = flatten(selectedPersistentPrimaryNodes.map(crawlTree(nodes))); + + return { + pageId, + selectedToplevelNodes, + selectedNodes: selectedNodeIds.map((id: string) => nodes.find(s => s.id === id)), + state, + }; +}; + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + insertNodes: (selectedNodes: PositionedElement[], pageId: string) => + dispatch(insertNodes(selectedNodes, pageId)), + removeNodes: (nodeIds: string[], pageId: string) => dispatch(removeElements(nodeIds, pageId)), + selectToplevelNodes: (nodes: PositionedElement[]) => + dispatch( + selectToplevelNodes(nodes.filter((e: PositionedElement) => !e.position.parent).map(e => e.id)) + ), + elementLayer: (pageId: string, elementId: string, movement: number) => { + dispatch(elementLayer({ pageId, elementId, movement })); + }, + undoHistory: () => dispatch(undoHistory()), + redoHistory: () => dispatch(redoHistory()), + dispatch, +}); + +const mergeProps = ( + { state, selectedToplevelNodes, ...restStateProps }: ReturnType, + { dispatch, ...restDispatchProps }: ReturnType, + { commit }: OwnProps +) => { + const updateGlobalState = globalStateUpdater(dispatch, state); + + return { + ...restDispatchProps, + ...restStateProps, + commit: withGlobalState(commit, updateGlobalState), + groupIsSelected: + selectedToplevelNodes.length === 1 && selectedToplevelNodes[0].includes('group'), + }; +}; + +export const EditMenu = compose( + connect(mapStateToProps, mapDispatchToProps, mergeProps), + withProps(() => ({ hasPasteData: Boolean(getClipboardData()) })), + withHandlers(basicHandlerCreators), + withHandlers(clipboardHandlerCreators), + withHandlers(layerHandlerCreators), + withHandlers(groupHandlerCreators), + withHandlers(alignmentDistributionHandlerCreators) +)(Component); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/element_menu.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/element_menu.tsx index 5c420cf3a04c9c..fbb5d70dfc55c4 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/element_menu.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/element_menu/element_menu.tsx @@ -139,7 +139,6 @@ export const ElementMenu: FunctionComponent = ({ return { id: 0, - title: strings.getElementMenuLabel(), items: [ elementListToMenuItems(textElements), elementListToMenuItems(shapeElements), diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.tsx index 621077c29c3689..2ac0591a1bdd4b 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/share_menu/share_menu.tsx @@ -62,7 +62,6 @@ export const ShareMenu: FunctionComponent = ({ onCopy, onExport, getExpor const getPanelTree = (closePopover: ClosePopoverFn) => ({ id: 0, - title: strings.getShareWorkpadMessage(), items: [ { name: strings.getShareDownloadJSONTitle(), diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx index 1f8e47b556771b..4aab8280a9f249 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_header.tsx @@ -15,6 +15,7 @@ import { ToolTipShortcut } from '../tool_tip_shortcut/'; import { RefreshControl } from './refresh_control'; // @ts-ignore untyped local import { FullscreenControl } from './fullscreen_control'; +import { EditMenu } from './edit_menu'; import { ElementMenu } from './element_menu'; import { ShareMenu } from './share_menu'; import { ViewMenu } from './view_menu'; @@ -25,12 +26,14 @@ export interface Props { isWriteable: boolean; toggleWriteable: () => void; canUserWrite: boolean; + commit: (type: string, payload: any) => any; } export const WorkpadHeader: FunctionComponent = ({ isWriteable, canUserWrite, toggleWriteable, + commit, }) => { const keyHandler = (action: string) => { if (action === 'EDITING') { @@ -99,6 +102,9 @@ export const WorkpadHeader: FunctionComponent = ({ + + + diff --git a/x-pack/legacy/plugins/canvas/public/state/selectors/workpad.ts b/x-pack/legacy/plugins/canvas/public/state/selectors/workpad.ts index 1623035bd25bad..80a7c34e8bef59 100644 --- a/x-pack/legacy/plugins/canvas/public/state/selectors/workpad.ts +++ b/x-pack/legacy/plugins/canvas/public/state/selectors/workpad.ts @@ -363,7 +363,11 @@ export function getNodesForPage(page: CanvasPage, withAst: boolean): CanvasEleme } // todo unify or DRY up with `getElements` -export function getNodes(state: State, pageId: string, withAst = true): CanvasElement[] { +export function getNodes( + state: State, + pageId: string, + withAst = true +): CanvasElement[] | PositionedElement[] { const id = pageId || getSelectedPage(state); if (!id) { return []; diff --git a/x-pack/legacy/plugins/canvas/types/elements.ts b/x-pack/legacy/plugins/canvas/types/elements.ts index 86356f5bd32a92..5de6b4968545fb 100644 --- a/x-pack/legacy/plugins/canvas/types/elements.ts +++ b/x-pack/legacy/plugins/canvas/types/elements.ts @@ -75,4 +75,8 @@ export interface ElementPosition { parent: string | null; } -export type PositionedElement = CanvasElement & { ast: ExpressionAstExpression }; +export type PositionedElement = CanvasElement & { + ast: ExpressionAstExpression; +} & { + position: ElementPosition; +}; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 49a749c578caf7..d1270ea92c51ee 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -5521,26 +5521,10 @@ "xpack.canvas.sidebarContent.groupedElementSidebarTitle": "グループ化されたエレメント", "xpack.canvas.sidebarContent.multiElementSidebarTitle": "複数エレメント", "xpack.canvas.sidebarContent.singleElementSidebarTitle": "選択されたエレメント", - "xpack.canvas.sidebarHeader.alignmentMenuItemLabel": "アラインメント", - "xpack.canvas.sidebarHeader.bottomAlignMenuItemLabel": "一番下", "xpack.canvas.sidebarHeader.bringForwardArialLabel": "エレメントを 1 つ上のレイヤーに移動", "xpack.canvas.sidebarHeader.bringToFrontArialLabel": "エレメントを一番上のレイヤーに移動", - "xpack.canvas.sidebarHeader.centerAlignMenuItemLabel": "中央", - "xpack.canvas.sidebarHeader.contextMenuAriaLabel": "エレメントオプション", - "xpack.canvas.sidebarHeader.createElementModalTitle": "新規エレメントの作成", - "xpack.canvas.sidebarHeader.distributionMenutItemLabel": "分布", - "xpack.canvas.sidebarHeader.groupMenuItemLabel": "グループ", - "xpack.canvas.sidebarHeader.horizontalDistributionMenutItemLabel": "横", - "xpack.canvas.sidebarHeader.leftAlignMenuItemLabel": "左", - "xpack.canvas.sidebarHeader.middleAlignMenuItemLabel": "真ん中", - "xpack.canvas.sidebarHeader.orderMenuItemLabel": "順序", - "xpack.canvas.sidebarHeader.rightAlignMenuItemLabel": "右", - "xpack.canvas.sidebarHeader.savedElementMenuItemLabel": "新規エレメントとして保存", "xpack.canvas.sidebarHeader.sendBackwardArialLabel": "エレメントを 1 つ下のレイヤーに移動", "xpack.canvas.sidebarHeader.sendToBackArialLabel": "エレメントを一番下のレイヤーに移動", - "xpack.canvas.sidebarHeader.topAlignMenuItemLabel": "一番上", - "xpack.canvas.sidebarHeader.ungroupMenuItemLabel": "グループ解除", - "xpack.canvas.sidebarHeader.verticalDistributionMenutItemLabel": "縦", "xpack.canvas.tags.presentationTag": "プレゼンテーション", "xpack.canvas.tags.reportTag": "レポート", "xpack.canvas.templates.darkHelp": "ダークカラーテーマのプレゼンテーションデッキです", @@ -5854,6 +5838,18 @@ "xpack.canvas.workpadHeaderCustomInterval.confirmButtonLabel": "設定", "xpack.canvas.workpadHeaderCustomInterval.formDescription": "{secondsExample}、{minutesExample}、{hoursExample} のような短い表記を使用します", "xpack.canvas.workpadHeaderCustomInterval.formLabel": "カスタム間隔を設定", + "xpack.canvas.workpadHeaderEditMenu.alignmentMenuItemLabel": "アラインメント", + "xpack.canvas.workpadHeaderEditMenu.bottomAlignMenuItemLabel": "一番下", + "xpack.canvas.workpadHeaderEditMenu.centerAlignMenuItemLabel": "中央", + "xpack.canvas.workpadHeaderEditMenu.createElementModalTitle": "新規エレメントの作成", + "xpack.canvas.workpadHeaderEditMenu.distributionMenutItemLabel": "分布", + "xpack.canvas.workpadHeaderEditMenu.groupMenuItemLabel": "グループ", + "xpack.canvas.workpadHeaderEditMenu.horizontalDistributionMenutItemLabel": "横", + "xpack.canvas.workpadHeaderEditMenu.leftAlignMenuItemLabel": "左", + "xpack.canvas.workpadHeaderEditMenu.middleAlignMenuItemLabel": "真ん中", + "xpack.canvas.workpadHeaderEditMenu.orderMenuItemLabel": "順序", + "xpack.canvas.workpadHeaderEditMenu.rightAlignMenuItemLabel": "右", + "xpack.canvas.workpadHeaderEditMenu.savedElementMenuItemLabel": "新規エレメントとして保存", "xpack.canvas.workpadHeaderKioskControl.controlTitle": "全画面ページのサイクル", "xpack.canvas.workpadHeaderKioskControl.cycleFormLabel": "サイクル間隔を変更", "xpack.canvas.workpadHeaderKioskControl.cycleToggleSwitch": "スライドを自動的にサイクル", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index adb8ac1817a14e..32c91a6ef29310 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -5522,26 +5522,10 @@ "xpack.canvas.sidebarContent.groupedElementSidebarTitle": "已分组元素", "xpack.canvas.sidebarContent.multiElementSidebarTitle": "多个元素", "xpack.canvas.sidebarContent.singleElementSidebarTitle": "选定元素", - "xpack.canvas.sidebarHeader.alignmentMenuItemLabel": "对齐方式", - "xpack.canvas.sidebarHeader.bottomAlignMenuItemLabel": "下", "xpack.canvas.sidebarHeader.bringForwardArialLabel": "将元素上移一层", "xpack.canvas.sidebarHeader.bringToFrontArialLabel": "将元素移到顶层", - "xpack.canvas.sidebarHeader.centerAlignMenuItemLabel": "中", - "xpack.canvas.sidebarHeader.contextMenuAriaLabel": "元素选项", - "xpack.canvas.sidebarHeader.createElementModalTitle": "创建新元素", - "xpack.canvas.sidebarHeader.distributionMenutItemLabel": "分布", - "xpack.canvas.sidebarHeader.groupMenuItemLabel": "分组", - "xpack.canvas.sidebarHeader.horizontalDistributionMenutItemLabel": "水平", - "xpack.canvas.sidebarHeader.leftAlignMenuItemLabel": "左", - "xpack.canvas.sidebarHeader.middleAlignMenuItemLabel": "中", - "xpack.canvas.sidebarHeader.orderMenuItemLabel": "顺序", - "xpack.canvas.sidebarHeader.rightAlignMenuItemLabel": "右", - "xpack.canvas.sidebarHeader.savedElementMenuItemLabel": "另存为新元素", "xpack.canvas.sidebarHeader.sendBackwardArialLabel": "将元素下移一层", "xpack.canvas.sidebarHeader.sendToBackArialLabel": "将元素移到底层", - "xpack.canvas.sidebarHeader.topAlignMenuItemLabel": "上", - "xpack.canvas.sidebarHeader.ungroupMenuItemLabel": "取消分组", - "xpack.canvas.sidebarHeader.verticalDistributionMenutItemLabel": "垂直", "xpack.canvas.tags.presentationTag": "演示", "xpack.canvas.tags.reportTag": "报告", "xpack.canvas.templates.darkHelp": "深色主题的演示幻灯片", @@ -5856,6 +5840,21 @@ "xpack.canvas.workpadHeaderCustomInterval.confirmButtonLabel": "设置", "xpack.canvas.workpadHeaderCustomInterval.formDescription": "使用速记表示法,如 {secondsExample}、{minutesExample} 或 {hoursExample}", "xpack.canvas.workpadHeaderCustomInterval.formLabel": "设置定制时间间隔", + "xpack.canvas.workpadHeaderEditMenu.alignmentMenuItemLabel": "对齐方式", + "xpack.canvas.workpadHeaderEditMenu.bottomAlignMenuItemLabel": "下", + "xpack.canvas.workpadHeaderEditMenu.centerAlignMenuItemLabel": "中", + "xpack.canvas.workpadHeaderEditMenu.createElementModalTitle": "创建新元素", + "xpack.canvas.workpadHeaderEditMenu.distributionMenutItemLabel": "分布", + "xpack.canvas.workpadHeaderEditMenu.groupMenuItemLabel": "分组", + "xpack.canvas.workpadHeaderEditMenu.horizontalDistributionMenutItemLabel": "水平", + "xpack.canvas.workpadHeaderEditMenu.leftAlignMenuItemLabel": "左", + "xpack.canvas.workpadHeaderEditMenu.middleAlignMenuItemLabel": "中", + "xpack.canvas.workpadHeaderEditMenu.orderMenuItemLabel": "顺序", + "xpack.canvas.workpadHeaderEditMenu.rightAlignMenuItemLabel": "右", + "xpack.canvas.workpadHeaderEditMenu.savedElementMenuItemLabel": "另存为新元素", + "xpack.canvas.workpadHeaderEditMenu.topAlignMenuItemLabel": "上", + "xpack.canvas.workpadHeaderEditMenu.ungroupMenuItemLabel": "取消分组", + "xpack.canvas.workpadHeaderEditMenu.verticalDistributionMenutItemLabel": "垂直", "xpack.canvas.workpadHeaderKioskControl.controlTitle": "循环播放全屏页面", "xpack.canvas.workpadHeaderKioskControl.cycleFormLabel": "更改循环播放时间间隔", "xpack.canvas.workpadHeaderKioskControl.cycleToggleSwitch": "自动循环播放幻灯片", diff --git a/x-pack/test/functional/apps/canvas/custom_elements.ts b/x-pack/test/functional/apps/canvas/custom_elements.ts index d4e17023688791..3bd3749ec6935e 100644 --- a/x-pack/test/functional/apps/canvas/custom_elements.ts +++ b/x-pack/test/functional/apps/canvas/custom_elements.ts @@ -40,8 +40,11 @@ export default function canvasCustomElementTest({ // find the first workpad element (a markdown element) and click it to select it await testSubjects.click('canvasWorkpadPage > canvasWorkpadPageElementContent', 20000); + // click "Edit" menu + await testSubjects.click('canvasWorkpadEditMenuButton', 20000); + // click the "Save as new element" button - await testSubjects.click('canvasSidebarHeader__saveElementButton', 20000); + await testSubjects.click('canvasWorkpadEditMenu__saveElementButton', 20000); // fill out the custom element form and submit it await PageObjects.canvas.fillOutCustomElementForm( From 3cef8e6f309f8d52b7b9c5553692f52a4131e815 Mon Sep 17 00:00:00 2001 From: Phillip Burch Date: Thu, 30 Apr 2020 16:53:56 -0500 Subject: [PATCH 35/47] [Metrics UI] Add inventory metric threshold alerts (#64292) * Add new inventory metric threshold alert * Add missed file * Fix some types * Convert units on client and executor. * Move formatters to common. Properly format metrics in alert messages * Style changes * Remove unused files * fix test * Update create * Fix signature * Remove old test. Remove unecessary import * Pass in filter when clicking create alert from context menu * Fix filtering * Fix more types * Fix tests * Fix merge * Fix merge Co-authored-by: Elastic Machine --- .../utils => common}/formatters/bytes.test.ts | 4 +- .../utils => common}/formatters/bytes.ts | 3 +- .../utils => common}/formatters/datetime.ts | 0 .../formatters/high_precision.ts | 0 .../utils => common}/formatters/index.ts | 4 +- .../utils => common}/formatters/number.ts | 0 .../utils => common}/formatters/percent.ts | 0 .../formatters/snapshot_metric_formats.ts | 73 +++ .../plugins/infra/common/formatters/types.ts | 11 + .../aws_ec2/toolbar_items.tsx | 32 +- .../aws_rds/toolbar_items.tsx | 30 +- .../inventory_models/aws_s3/toolbar_items.tsx | 22 +- .../aws_sqs/toolbar_items.tsx | 21 +- .../container/toolbar_items.tsx | 23 +- .../inventory_models/host/toolbar_items.tsx | 27 +- .../inventory_models/pod/toolbar_items.tsx | 9 +- .../components/alert_dropdown.tsx | 2 +- .../components/expression.tsx | 37 +- .../alerting/inventory/alert_dropdown.tsx | 62 +++ .../alerting/inventory/alert_flyout.tsx | 52 ++ .../alerting/inventory/expression.tsx | 498 ++++++++++++++++++ .../components/alerting/inventory/metric.tsx | 150 ++++++ .../metric_inventory_threshold_alert_type.ts | 34 ++ .../alerting/inventory/node_type.tsx | 115 ++++ .../alerting/inventory/validation.tsx | 80 +++ .../log_text_stream/column_headers.tsx | 2 +- .../logging/log_text_stream/log_date_row.tsx | 2 +- .../containers/source/use_source_via_http.ts | 17 +- x-pack/plugins/infra/public/index.ts | 2 +- x-pack/plugins/infra/public/lib/lib.ts | 6 - .../infra/public/pages/metrics/index.tsx | 7 +- .../components/waffle/node_context_menu.tsx | 315 ++++++----- .../lib/create_inventory_metric_formatter.ts | 2 +- .../components/gauges_section_vis.tsx | 2 +- .../metric_detail/components/helpers.ts | 2 +- .../helpers/create_formatter_for_metric.ts | 2 +- .../metrics_explorer/components/kuery_bar.tsx | 5 + x-pack/plugins/infra/public/plugin.ts | 2 + .../infra/server/graphql/sources/resolvers.ts | 10 +- .../metrics/kibana_metrics_adapter.ts | 10 +- .../inventory_metric_threshold_executor.ts | 214 ++++++++ ...r_inventory_metric_threshold_alert_type.ts | 92 ++++ .../inventory_metric_threshold/types.ts | 35 ++ .../metric_threshold_executor.test.ts | 192 ++++--- .../metric_threshold_executor.ts | 63 +-- .../register_metric_threshold_alert_type.ts | 39 +- .../lib/alerting/register_alert_types.ts | 5 +- .../infra/server/lib/compose/kibana.ts | 2 +- .../create_timerange_with_interval.ts | 21 +- .../infra/server/lib/snapshot/snapshot.ts | 52 +- .../infra/server/lib/sources/sources.ts | 33 +- x-pack/plugins/infra/server/plugin.ts | 2 +- .../routes/log_sources/configuration.ts | 4 +- .../lib/get_dataset_for_field.ts | 13 +- .../lib/populate_series_with_tsvb_data.ts | 16 +- .../infra/server/routes/snapshot/index.ts | 9 +- .../server/utils/calculate_metric_interval.ts | 14 +- .../apis/infra/metrics_alerting.ts | 11 +- 58 files changed, 1995 insertions(+), 497 deletions(-) rename x-pack/plugins/infra/{public/utils => common}/formatters/bytes.test.ts (93%) rename x-pack/plugins/infra/{public/utils => common}/formatters/bytes.ts (96%) rename x-pack/plugins/infra/{public/utils => common}/formatters/datetime.ts (100%) rename x-pack/plugins/infra/{public/utils => common}/formatters/high_precision.ts (100%) rename x-pack/plugins/infra/{public/utils => common}/formatters/index.ts (90%) rename x-pack/plugins/infra/{public/utils => common}/formatters/number.ts (100%) rename x-pack/plugins/infra/{public/utils => common}/formatters/percent.ts (100%) create mode 100644 x-pack/plugins/infra/common/formatters/snapshot_metric_formats.ts create mode 100644 x-pack/plugins/infra/common/formatters/types.ts create mode 100644 x-pack/plugins/infra/public/components/alerting/inventory/alert_dropdown.tsx create mode 100644 x-pack/plugins/infra/public/components/alerting/inventory/alert_flyout.tsx create mode 100644 x-pack/plugins/infra/public/components/alerting/inventory/expression.tsx create mode 100644 x-pack/plugins/infra/public/components/alerting/inventory/metric.tsx create mode 100644 x-pack/plugins/infra/public/components/alerting/inventory/metric_inventory_threshold_alert_type.ts create mode 100644 x-pack/plugins/infra/public/components/alerting/inventory/node_type.tsx create mode 100644 x-pack/plugins/infra/public/components/alerting/inventory/validation.tsx create mode 100644 x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts create mode 100644 x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts create mode 100644 x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/types.ts diff --git a/x-pack/plugins/infra/public/utils/formatters/bytes.test.ts b/x-pack/plugins/infra/common/formatters/bytes.test.ts similarity index 93% rename from x-pack/plugins/infra/public/utils/formatters/bytes.test.ts rename to x-pack/plugins/infra/common/formatters/bytes.test.ts index 4c872bcee057d7..ccdeed120acca1 100644 --- a/x-pack/plugins/infra/public/utils/formatters/bytes.test.ts +++ b/x-pack/plugins/infra/common/formatters/bytes.test.ts @@ -3,9 +3,9 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { InfraWaffleMapDataFormat } from '../../lib/lib'; +import { InfraWaffleMapDataFormat } from './types'; import { createBytesFormatter } from './bytes'; + describe('createDataFormatter', () => { it('should format bytes as bytesDecimal', () => { const formatter = createBytesFormatter(InfraWaffleMapDataFormat.bytesDecimal); diff --git a/x-pack/plugins/infra/public/utils/formatters/bytes.ts b/x-pack/plugins/infra/common/formatters/bytes.ts similarity index 96% rename from x-pack/plugins/infra/public/utils/formatters/bytes.ts rename to x-pack/plugins/infra/common/formatters/bytes.ts index 80a5603ed6994b..3a45caa8b5e150 100644 --- a/x-pack/plugins/infra/public/utils/formatters/bytes.ts +++ b/x-pack/plugins/infra/common/formatters/bytes.ts @@ -3,9 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { InfraWaffleMapDataFormat } from '../../lib/lib'; import { formatNumber } from './number'; +import { InfraWaffleMapDataFormat } from './types'; /** * The labels are derived from these two Wikipedia articles. diff --git a/x-pack/plugins/infra/public/utils/formatters/datetime.ts b/x-pack/plugins/infra/common/formatters/datetime.ts similarity index 100% rename from x-pack/plugins/infra/public/utils/formatters/datetime.ts rename to x-pack/plugins/infra/common/formatters/datetime.ts diff --git a/x-pack/plugins/infra/public/utils/formatters/high_precision.ts b/x-pack/plugins/infra/common/formatters/high_precision.ts similarity index 100% rename from x-pack/plugins/infra/public/utils/formatters/high_precision.ts rename to x-pack/plugins/infra/common/formatters/high_precision.ts diff --git a/x-pack/plugins/infra/public/utils/formatters/index.ts b/x-pack/plugins/infra/common/formatters/index.ts similarity index 90% rename from x-pack/plugins/infra/public/utils/formatters/index.ts rename to x-pack/plugins/infra/common/formatters/index.ts index 3c60dba7478257..096085696bd6bf 100644 --- a/x-pack/plugins/infra/public/utils/formatters/index.ts +++ b/x-pack/plugins/infra/common/formatters/index.ts @@ -5,12 +5,12 @@ */ import Mustache from 'mustache'; -import { InfraWaffleMapDataFormat } from '../../lib/lib'; import { createBytesFormatter } from './bytes'; import { formatNumber } from './number'; import { formatPercent } from './percent'; -import { InventoryFormatterType } from '../../../common/inventory_models/types'; +import { InventoryFormatterType } from '../inventory_models/types'; import { formatHighPercision } from './high_precision'; +import { InfraWaffleMapDataFormat } from './types'; export const FORMATTERS = { number: formatNumber, diff --git a/x-pack/plugins/infra/public/utils/formatters/number.ts b/x-pack/plugins/infra/common/formatters/number.ts similarity index 100% rename from x-pack/plugins/infra/public/utils/formatters/number.ts rename to x-pack/plugins/infra/common/formatters/number.ts diff --git a/x-pack/plugins/infra/public/utils/formatters/percent.ts b/x-pack/plugins/infra/common/formatters/percent.ts similarity index 100% rename from x-pack/plugins/infra/public/utils/formatters/percent.ts rename to x-pack/plugins/infra/common/formatters/percent.ts diff --git a/x-pack/plugins/infra/common/formatters/snapshot_metric_formats.ts b/x-pack/plugins/infra/common/formatters/snapshot_metric_formats.ts new file mode 100644 index 00000000000000..8b4ae27cb30614 --- /dev/null +++ b/x-pack/plugins/infra/common/formatters/snapshot_metric_formats.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +enum InfraFormatterType { + number = 'number', + abbreviatedNumber = 'abbreviatedNumber', + bytes = 'bytes', + bits = 'bits', + percent = 'percent', +} + +interface MetricFormatter { + formatter: InfraFormatterType; + template: string; + bounds?: { min: number; max: number }; +} + +interface MetricFormatters { + [key: string]: MetricFormatter; +} + +export const METRIC_FORMATTERS: MetricFormatters = { + ['count']: { formatter: InfraFormatterType.number, template: '{{value}}' }, + ['cpu']: { + formatter: InfraFormatterType.percent, + template: '{{value}}', + }, + ['memory']: { + formatter: InfraFormatterType.percent, + template: '{{value}}', + }, + ['rx']: { formatter: InfraFormatterType.bits, template: '{{value}}/s' }, + ['tx']: { formatter: InfraFormatterType.bits, template: '{{value}}/s' }, + ['logRate']: { + formatter: InfraFormatterType.abbreviatedNumber, + template: '{{value}}/s', + }, + ['diskIOReadBytes']: { + formatter: InfraFormatterType.bytes, + template: '{{value}}/s', + }, + ['diskIOWriteBytes']: { + formatter: InfraFormatterType.bytes, + template: '{{value}}/s', + }, + ['s3BucketSize']: { + formatter: InfraFormatterType.bytes, + template: '{{value}}', + }, + ['s3TotalRequests']: { + formatter: InfraFormatterType.abbreviatedNumber, + template: '{{value}}', + }, + ['s3NumberOfObjects']: { + formatter: InfraFormatterType.abbreviatedNumber, + template: '{{value}}', + }, + ['s3UploadBytes']: { + formatter: InfraFormatterType.bytes, + template: '{{value}}', + }, + ['s3DownloadBytes']: { + formatter: InfraFormatterType.bytes, + template: '{{value}}', + }, + ['sqsOldestMessage']: { + formatter: InfraFormatterType.number, + template: '{{value}} seconds', + }, +}; diff --git a/x-pack/plugins/infra/common/formatters/types.ts b/x-pack/plugins/infra/common/formatters/types.ts new file mode 100644 index 00000000000000..c438ec2d4205d1 --- /dev/null +++ b/x-pack/plugins/infra/common/formatters/types.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; + * you may not use this file except in compliance with the Elastic License. + */ + +export enum InfraWaffleMapDataFormat { + bytesDecimal = 'bytesDecimal', + bitsDecimal = 'bitsDecimal', + abbreviatedNumber = 'abbreviatedNumber', +} diff --git a/x-pack/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx index b2da7dec3f2e08..764db2164b7118 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_ec2/toolbar_items.tsx @@ -11,27 +11,29 @@ import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_ import { CloudToolbarItems } from '../shared/components/cloud_toolbar_items'; import { SnapshotMetricType } from '../types'; +export const ec2MetricTypes: SnapshotMetricType[] = [ + 'cpu', + 'rx', + 'tx', + 'diskIOReadBytes', + 'diskIOWriteBytes', +]; + +export const ec2groupByFields = [ + 'cloud.availability_zone', + 'cloud.machine.type', + 'aws.ec2.instance.image.id', + 'aws.ec2.instance.state.name', +]; + export const AwsEC2ToolbarItems = (props: ToolbarProps) => { - const metricTypes: SnapshotMetricType[] = [ - 'cpu', - 'rx', - 'tx', - 'diskIOReadBytes', - 'diskIOWriteBytes', - ]; - const groupByFields = [ - 'cloud.availability_zone', - 'cloud.machine.type', - 'aws.ec2.instance.image.id', - 'aws.ec2.instance.state.name', - ]; return ( <> ); diff --git a/x-pack/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx index 2a8394b9dd3a41..3eebdee22b2c30 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_rds/toolbar_items.tsx @@ -11,26 +11,28 @@ import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_ import { CloudToolbarItems } from '../shared/components/cloud_toolbar_items'; import { SnapshotMetricType } from '../types'; +export const rdsMetricTypes: SnapshotMetricType[] = [ + 'cpu', + 'rdsConnections', + 'rdsQueriesExecuted', + 'rdsActiveTransactions', + 'rdsLatency', +]; + +export const rdsGroupByFields = [ + 'cloud.availability_zone', + 'aws.rds.db_instance.class', + 'aws.rds.db_instance.status', +]; + export const AwsRDSToolbarItems = (props: ToolbarProps) => { - const metricTypes: SnapshotMetricType[] = [ - 'cpu', - 'rdsConnections', - 'rdsQueriesExecuted', - 'rdsActiveTransactions', - 'rdsLatency', - ]; - const groupByFields = [ - 'cloud.availability_zone', - 'aws.rds.db_instance.class', - 'aws.rds.db_instance.status', - ]; return ( <> ); diff --git a/x-pack/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx index 324bdd05860290..ede618b1bf19d0 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_s3/toolbar_items.tsx @@ -11,22 +11,24 @@ import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_ import { CloudToolbarItems } from '../shared/components/cloud_toolbar_items'; import { SnapshotMetricType } from '../types'; +export const s3MetricTypes: SnapshotMetricType[] = [ + 's3BucketSize', + 's3NumberOfObjects', + 's3TotalRequests', + 's3DownloadBytes', + 's3UploadBytes', +]; + +export const s3GroupByFields = ['cloud.region']; + export const AwsS3ToolbarItems = (props: ToolbarProps) => { - const metricTypes: SnapshotMetricType[] = [ - 's3BucketSize', - 's3NumberOfObjects', - 's3TotalRequests', - 's3DownloadBytes', - 's3UploadBytes', - ]; - const groupByFields = ['cloud.region']; return ( <> ); diff --git a/x-pack/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx index 3229c07034772c..e77f3af5781970 100644 --- a/x-pack/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/aws_sqs/toolbar_items.tsx @@ -11,22 +11,23 @@ import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_ import { CloudToolbarItems } from '../shared/components/cloud_toolbar_items'; import { SnapshotMetricType } from '../types'; +export const sqsMetricTypes: SnapshotMetricType[] = [ + 'sqsMessagesVisible', + 'sqsMessagesDelayed', + 'sqsMessagesSent', + 'sqsMessagesEmpty', + 'sqsOldestMessage', +]; +export const sqsGroupByFields = ['cloud.region']; + export const AwsSQSToolbarItems = (props: ToolbarProps) => { - const metricTypes: SnapshotMetricType[] = [ - 'sqsMessagesVisible', - 'sqsMessagesDelayed', - 'sqsMessagesSent', - 'sqsMessagesEmpty', - 'sqsOldestMessage', - ]; - const groupByFields = ['cloud.region']; return ( <> ); diff --git a/x-pack/plugins/infra/common/inventory_models/container/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/container/toolbar_items.tsx index f6c707726d9ca1..f193adbf6aadc4 100644 --- a/x-pack/plugins/infra/common/inventory_models/container/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/container/toolbar_items.tsx @@ -10,21 +10,22 @@ import { ToolbarProps } from '../../../public/pages/metrics/inventory_view/compo import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_groupby_toolbar_items'; import { SnapshotMetricType } from '../types'; +export const containerMetricTypes: SnapshotMetricType[] = ['cpu', 'memory', 'rx', 'tx']; +export const containerGroupByFields = [ + 'host.name', + 'cloud.availability_zone', + 'cloud.machine.type', + 'cloud.project.id', + 'cloud.provider', + 'service.type', +]; + export const ContainerToolbarItems = (props: ToolbarProps) => { - const metricTypes: SnapshotMetricType[] = ['cpu', 'memory', 'rx', 'tx']; - const groupByFields = [ - 'host.name', - 'cloud.availability_zone', - 'cloud.machine.type', - 'cloud.project.id', - 'cloud.provider', - 'service.type', - ]; return ( ); }; diff --git a/x-pack/plugins/infra/common/inventory_models/host/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/host/toolbar_items.tsx index 136264c0e26f4d..8ed684b3885de2 100644 --- a/x-pack/plugins/infra/common/inventory_models/host/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/host/toolbar_items.tsx @@ -10,20 +10,27 @@ import { ToolbarProps } from '../../../public/pages/metrics/inventory_view/compo import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_groupby_toolbar_items'; import { SnapshotMetricType } from '../types'; +export const hostMetricTypes: SnapshotMetricType[] = [ + 'cpu', + 'memory', + 'load', + 'rx', + 'tx', + 'logRate', +]; +export const hostGroupByFields = [ + 'cloud.availability_zone', + 'cloud.machine.type', + 'cloud.project.id', + 'cloud.provider', + 'service.type', +]; export const HostToolbarItems = (props: ToolbarProps) => { - const metricTypes: SnapshotMetricType[] = ['cpu', 'memory', 'load', 'rx', 'tx', 'logRate']; - const groupByFields = [ - 'cloud.availability_zone', - 'cloud.machine.type', - 'cloud.project.id', - 'cloud.provider', - 'service.type', - ]; return ( ); }; diff --git a/x-pack/plugins/infra/common/inventory_models/pod/toolbar_items.tsx b/x-pack/plugins/infra/common/inventory_models/pod/toolbar_items.tsx index c1cd375ff47bf9..54a32e3e0180a1 100644 --- a/x-pack/plugins/infra/common/inventory_models/pod/toolbar_items.tsx +++ b/x-pack/plugins/infra/common/inventory_models/pod/toolbar_items.tsx @@ -10,14 +10,15 @@ import { ToolbarProps } from '../../../public/pages/metrics/inventory_view/compo import { MetricsAndGroupByToolbarItems } from '../shared/components/metrics_and_groupby_toolbar_items'; import { SnapshotMetricType } from '../types'; +export const podMetricTypes: SnapshotMetricType[] = ['cpu', 'memory', 'rx', 'tx']; +export const podGroupByFields = ['kubernetes.namespace', 'kubernetes.node.name', 'service.type']; + export const PodToolbarItems = (props: ToolbarProps) => { - const metricTypes: SnapshotMetricType[] = ['cpu', 'memory', 'rx', 'tx']; - const groupByFields = ['kubernetes.namespace', 'kubernetes.node.name', 'service.type']; return ( ); }; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx index bb664f40676625..8bcf0e9ed5be5d 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_dropdown.tsx @@ -10,7 +10,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { AlertFlyout } from './alert_flyout'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -export const AlertDropdown = () => { +export const MetricsAlertDropdown = () => { const [popoverOpen, setPopoverOpen] = useState(false); const [flyoutVisible, setFlyoutVisible] = useState(false); const kibana = useKibana(); diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx index 352ac1927479ea..5e14babddcb07a 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression.tsx @@ -34,6 +34,8 @@ import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer import { MetricsExplorerOptions } from '../../../pages/metrics/metrics_explorer/hooks/use_metrics_explorer_options'; import { MetricsExplorerGroupBy } from '../../../pages/metrics/metrics_explorer/components/group_by'; import { useSourceViaHttp } from '../../../containers/source/use_source_via_http'; +import { convertKueryToElasticSearchQuery } from '../../../utils/kuery'; + import { ExpressionRow } from './expression_row'; import { AlertContextMeta, TimeUnit, MetricExpression } from '../types'; import { ExpressionChart } from './expression_chart'; @@ -45,6 +47,7 @@ interface Props { groupBy?: string; filterQuery?: string; sourceId?: string; + filterQueryText?: string; alertOnNoData?: boolean; }; alertsContext: AlertsContextValue; @@ -111,11 +114,15 @@ export const Expressions: React.FC = props => { [setAlertParams, alertParams.criteria] ); - const onFilterQuerySubmit = useCallback( + const onFilterChange = useCallback( (filter: any) => { - setAlertParams('filterQuery', filter); + setAlertParams('filterQueryText', filter); + setAlertParams( + 'filterQuery', + convertKueryToElasticSearchQuery(filter, derivedIndexPattern) || '' + ); }, - [setAlertParams] + [setAlertParams, derivedIndexPattern] ); const onGroupByChange = useCallback( @@ -180,10 +187,19 @@ export const Expressions: React.FC = props => { if (md.currentOptions) { if (md.currentOptions.filterQuery) { - setAlertParams('filterQuery', md.currentOptions.filterQuery); + setAlertParams('filterQueryText', md.currentOptions.filterQuery); + setAlertParams( + 'filterQuery', + convertKueryToElasticSearchQuery(md.currentOptions.filterQuery, derivedIndexPattern) || + '' + ); } else if (md.currentOptions.groupBy && md.series) { const filter = `${md.currentOptions.groupBy}: "${md.series.id}"`; - setAlertParams('filterQuery', filter); + setAlertParams('filterQueryText', filter); + setAlertParams( + 'filterQuery', + convertKueryToElasticSearchQuery(filter, derivedIndexPattern) || '' + ); } setAlertParams('groupBy', md.currentOptions.groupBy); @@ -200,8 +216,8 @@ export const Expressions: React.FC = props => { }, [alertsContext.metadata, defaultExpression, source]); // eslint-disable-line react-hooks/exhaustive-deps const handleFieldSearchChange = useCallback( - (e: ChangeEvent) => onFilterQuerySubmit(e.target.value), - [onFilterQuerySubmit] + (e: ChangeEvent) => onFilterChange(e.target.value), + [onFilterChange] ); return ( @@ -304,13 +320,14 @@ export const Expressions: React.FC = props => { {(alertsContext.metadata && ( )) || ( )} diff --git a/x-pack/plugins/infra/public/components/alerting/inventory/alert_dropdown.tsx b/x-pack/plugins/infra/public/components/alerting/inventory/alert_dropdown.tsx new file mode 100644 index 00000000000000..d2904206875c79 --- /dev/null +++ b/x-pack/plugins/infra/public/components/alerting/inventory/alert_dropdown.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState, useCallback, useMemo } from 'react'; +import { EuiPopover, EuiButtonEmpty, EuiContextMenuItem, EuiContextMenuPanel } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { AlertFlyout } from './alert_flyout'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; + +export const InventoryAlertDropdown = () => { + const [popoverOpen, setPopoverOpen] = useState(false); + const [flyoutVisible, setFlyoutVisible] = useState(false); + const kibana = useKibana(); + + const closePopover = useCallback(() => { + setPopoverOpen(false); + }, [setPopoverOpen]); + + const openPopover = useCallback(() => { + setPopoverOpen(true); + }, [setPopoverOpen]); + + const menuItems = useMemo(() => { + return [ + setFlyoutVisible(true)}> + + , + + + , + ]; + }, [kibana.services]); + + return ( + <> + + + + } + isOpen={popoverOpen} + closePopover={closePopover} + > + + + + + ); +}; diff --git a/x-pack/plugins/infra/public/components/alerting/inventory/alert_flyout.tsx b/x-pack/plugins/infra/public/components/alerting/inventory/alert_flyout.tsx new file mode 100644 index 00000000000000..83298afd4fc5a5 --- /dev/null +++ b/x-pack/plugins/infra/public/components/alerting/inventory/alert_flyout.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useContext } from 'react'; +import { AlertsContextProvider, AlertAdd } from '../../../../../triggers_actions_ui/public'; +import { TriggerActionsContext } from '../../../utils/triggers_actions_context'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from '../../../../server/lib/alerting/inventory_metric_threshold/types'; +import { InfraWaffleMapOptions } from '../../../lib/lib'; +import { InventoryItemType } from '../../../../common/inventory_models/types'; + +interface Props { + visible?: boolean; + options?: Partial; + nodeType?: InventoryItemType; + filter?: string; + setVisible: React.Dispatch>; +} + +export const AlertFlyout = (props: Props) => { + const { triggersActionsUI } = useContext(TriggerActionsContext); + const { services } = useKibana(); + + return ( + <> + {triggersActionsUI && ( + + + + )} + + ); +}; diff --git a/x-pack/plugins/infra/public/components/alerting/inventory/expression.tsx b/x-pack/plugins/infra/public/components/alerting/inventory/expression.tsx new file mode 100644 index 00000000000000..15cad770836bda --- /dev/null +++ b/x-pack/plugins/infra/public/components/alerting/inventory/expression.tsx @@ -0,0 +1,498 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useCallback, useMemo, useEffect, useState, ChangeEvent } from 'react'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiButtonIcon, + EuiSpacer, + EuiText, + EuiFormRow, + EuiButtonEmpty, + EuiFieldSearch, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; +import { + Comparator, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../server/lib/alerting/metric_threshold/types'; +import { euiStyled } from '../../../../../observability/public'; +import { + ThresholdExpression, + ForLastExpression, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../triggers_actions_ui/public/common'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { IErrorObject } from '../../../../../triggers_actions_ui/public/types'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { AlertsContextValue } from '../../../../../triggers_actions_ui/public/application/context/alerts_context'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { MetricsExplorerKueryBar } from '../../../pages/metrics/metrics_explorer/components/kuery_bar'; +import { useSourceViaHttp } from '../../../containers/source/use_source_via_http'; +import { toMetricOpt } from '../../../pages/metrics/inventory_view/components/toolbars/toolbar_wrapper'; +import { sqsMetricTypes } from '../../../../common/inventory_models/aws_sqs/toolbar_items'; +import { ec2MetricTypes } from '../../../../common/inventory_models/aws_ec2/toolbar_items'; +import { s3MetricTypes } from '../../../../common/inventory_models/aws_s3/toolbar_items'; +import { rdsMetricTypes } from '../../../../common/inventory_models/aws_rds/toolbar_items'; +import { hostMetricTypes } from '../../../../common/inventory_models/host/toolbar_items'; +import { containerMetricTypes } from '../../../../common/inventory_models/container/toolbar_items'; +import { podMetricTypes } from '../../../../common/inventory_models/pod/toolbar_items'; +import { findInventoryModel } from '../../../../common/inventory_models'; +import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { InventoryMetricConditions } from '../../../../server/lib/alerting/inventory_metric_threshold/types'; +import { MetricExpression } from './metric'; +import { NodeTypeExpression } from './node_type'; +import { InfraWaffleMapOptions } from '../../../lib/lib'; +import { convertKueryToElasticSearchQuery } from '../../../utils/kuery'; + +interface AlertContextMeta { + options?: Partial; + nodeType?: InventoryItemType; + filter?: string; +} + +interface Props { + errors: IErrorObject[]; + alertParams: { + criteria: InventoryMetricConditions[]; + nodeType: InventoryItemType; + groupBy?: string; + filterQuery?: string; + filterQueryText?: string; + sourceId?: string; + }; + alertsContext: AlertsContextValue; + setAlertParams(key: string, value: any): void; + setAlertProperty(key: string, value: any): void; +} + +type TimeUnit = 's' | 'm' | 'h' | 'd'; + +const defaultExpression = { + metric: 'cpu' as SnapshotMetricType, + comparator: Comparator.GT, + threshold: [], + timeSize: 1, + timeUnit: 'm', +} as InventoryMetricConditions; + +export const Expressions: React.FC = props => { + const { setAlertParams, alertParams, errors, alertsContext } = props; + const { source, createDerivedIndexPattern } = useSourceViaHttp({ + sourceId: 'default', + type: 'metrics', + fetch: alertsContext.http.fetch, + toastWarning: alertsContext.toastNotifications.addWarning, + }); + const [timeSize, setTimeSize] = useState(1); + const [timeUnit, setTimeUnit] = useState('m'); + + const derivedIndexPattern = useMemo(() => createDerivedIndexPattern('metrics'), [ + createDerivedIndexPattern, + ]); + + const updateParams = useCallback( + (id, e: InventoryMetricConditions) => { + const exp = alertParams.criteria ? alertParams.criteria.slice() : []; + exp[id] = { ...exp[id], ...e }; + setAlertParams('criteria', exp); + }, + [setAlertParams, alertParams.criteria] + ); + + const addExpression = useCallback(() => { + const exp = alertParams.criteria.slice(); + exp.push(defaultExpression); + setAlertParams('criteria', exp); + }, [setAlertParams, alertParams.criteria]); + + const removeExpression = useCallback( + (id: number) => { + const exp = alertParams.criteria.slice(); + if (exp.length > 1) { + exp.splice(id, 1); + setAlertParams('criteria', exp); + } + }, + [setAlertParams, alertParams.criteria] + ); + + const onFilterChange = useCallback( + (filter: any) => { + setAlertParams('filterQueryText', filter || ''); + setAlertParams( + 'filterQuery', + convertKueryToElasticSearchQuery(filter, derivedIndexPattern) || '' + ); + }, + [derivedIndexPattern, setAlertParams] + ); + + const emptyError = useMemo(() => { + return { + aggField: [], + timeSizeUnit: [], + timeWindowSize: [], + }; + }, []); + + const updateTimeSize = useCallback( + (ts: number | undefined) => { + const criteria = alertParams.criteria.map(c => ({ + ...c, + timeSize: ts, + })); + setTimeSize(ts || undefined); + setAlertParams('criteria', criteria); + }, + [alertParams.criteria, setAlertParams] + ); + + const updateTimeUnit = useCallback( + (tu: string) => { + const criteria = alertParams.criteria.map(c => ({ + ...c, + timeUnit: tu, + })); + setTimeUnit(tu as TimeUnit); + setAlertParams('criteria', criteria); + }, + [alertParams.criteria, setAlertParams] + ); + + const updateNodeType = useCallback( + (nt: any) => { + setAlertParams('nodeType', nt); + }, + [setAlertParams] + ); + + const handleFieldSearchChange = useCallback( + (e: ChangeEvent) => onFilterChange(e.target.value), + [onFilterChange] + ); + + useEffect(() => { + const md = alertsContext.metadata; + if (!alertParams.nodeType) { + if (md && md.nodeType) { + setAlertParams('nodeType', md.nodeType); + } else { + setAlertParams('nodeType', 'host'); + } + } + + if (!alertParams.criteria) { + if (md && md.options) { + setAlertParams('criteria', [ + { + ...defaultExpression, + metric: md.options.metric!.type, + } as InventoryMetricConditions, + ]); + } else { + setAlertParams('criteria', [defaultExpression]); + } + } + + if (!alertParams.filterQuery) { + if (md && md.filter) { + setAlertParams('filterQueryText', md.filter); + setAlertParams( + 'filterQuery', + convertKueryToElasticSearchQuery(md.filter, derivedIndexPattern) || '' + ); + } + } + + if (!alertParams.sourceId) { + setAlertParams('sourceId', source?.id); + } + }, [alertsContext.metadata, derivedIndexPattern, defaultExpression, source]); // eslint-disable-line react-hooks/exhaustive-deps + + return ( + <> + + +

    + +

    +
    + + + + + {alertParams.criteria && + alertParams.criteria.map((e, idx) => { + return ( + 1} + remove={removeExpression} + addExpression={addExpression} + key={idx} // idx's don't usually make good key's but here the index has semantic meaning + expressionId={idx} + setAlertParams={updateParams} + errors={errors[idx] || emptyError} + expression={e || {}} + /> + ); + })} + + + +
    + + + +
    + + + + + {(alertsContext.metadata && ( + + )) || ( + + )} + + + + + ); +}; + +interface ExpressionRowProps { + nodeType: InventoryItemType; + expressionId: number; + expression: Omit & { + metric?: SnapshotMetricType; + }; + errors: IErrorObject; + canDelete: boolean; + addExpression(): void; + remove(id: number): void; + setAlertParams(id: number, params: Partial): void; +} + +const StyledExpressionRow = euiStyled(EuiFlexGroup)` + display: flex; + flex-wrap: wrap; + margin: 0 -4px; +`; + +const StyledExpression = euiStyled.div` + padding: 0 4px; +`; + +export const ExpressionRow: React.FC = props => { + const { setAlertParams, expression, errors, expressionId, remove, canDelete } = props; + const { metric, comparator = Comparator.GT, threshold = [] } = expression; + + const updateMetric = useCallback( + (m?: SnapshotMetricType) => { + setAlertParams(expressionId, { ...expression, metric: m }); + }, + [expressionId, expression, setAlertParams] + ); + + const updateComparator = useCallback( + (c?: string) => { + setAlertParams(expressionId, { ...expression, comparator: c as Comparator | undefined }); + }, + [expressionId, expression, setAlertParams] + ); + + const updateThreshold = useCallback( + t => { + if (t.join() !== expression.threshold.join()) { + setAlertParams(expressionId, { ...expression, threshold: t }); + } + }, + [expressionId, expression, setAlertParams] + ); + + const ofFields = useMemo(() => { + let myMetrics = hostMetricTypes; + + switch (props.nodeType) { + case 'awsEC2': + myMetrics = ec2MetricTypes; + break; + case 'awsRDS': + myMetrics = rdsMetricTypes; + break; + case 'awsS3': + myMetrics = s3MetricTypes; + break; + case 'awsSQS': + myMetrics = sqsMetricTypes; + break; + case 'host': + myMetrics = hostMetricTypes; + break; + case 'pod': + myMetrics = podMetricTypes; + break; + case 'container': + myMetrics = containerMetricTypes; + break; + } + return myMetrics.map(toMetricOpt); + }, [props.nodeType]); + + return ( + <> + + + + + v?.value === metric)?.text || '', + }} + metrics={ + ofFields.filter(m => m !== undefined && m.value !== undefined) as Array<{ + value: SnapshotMetricType; + text: string; + }> + } + onChange={updateMetric} + errors={errors} + /> + + + + + {metric && ( + +
    +
    {metricUnit[metric]?.label || ''}
    +
    +
    + )} +
    +
    + {canDelete && ( + + remove(expressionId)} + /> + + )} +
    + + + ); +}; + +const getDisplayNameForType = (type: InventoryItemType) => { + const inventoryModel = findInventoryModel(type); + return inventoryModel.displayName; +}; + +export const nodeTypes: { [key: string]: any } = { + host: { + text: getDisplayNameForType('host'), + value: 'host', + }, + pod: { + text: getDisplayNameForType('pod'), + value: 'pod', + }, + container: { + text: getDisplayNameForType('container'), + value: 'container', + }, + awsEC2: { + text: getDisplayNameForType('awsEC2'), + value: 'awsEC2', + }, + awsS3: { + text: getDisplayNameForType('awsS3'), + value: 'awsS3', + }, + awsRDS: { + text: getDisplayNameForType('awsRDS'), + value: 'awsRDS', + }, + awsSQS: { + text: getDisplayNameForType('awsSQS'), + value: 'awsSQS', + }, +}; + +const metricUnit: Record = { + count: { label: '' }, + cpu: { label: '%' }, + memory: { label: '%' }, + rx: { label: 'bits/s' }, + tx: { label: 'bits/s' }, + logRate: { label: '/s' }, + diskIOReadBytes: { label: 'bytes/s' }, + diskIOWriteBytes: { label: 'bytes/s' }, + s3BucketSize: { label: 'bytes' }, + s3TotalRequests: { label: '' }, + s3NumberOfObjects: { label: '' }, + s3UploadBytes: { label: 'bytes' }, + s3DownloadBytes: { label: 'bytes' }, + sqsOldestMessage: { label: 'seconds' }, +}; diff --git a/x-pack/plugins/infra/public/components/alerting/inventory/metric.tsx b/x-pack/plugins/infra/public/components/alerting/inventory/metric.tsx new file mode 100644 index 00000000000000..faafdf1b81eeda --- /dev/null +++ b/x-pack/plugins/infra/public/components/alerting/inventory/metric.tsx @@ -0,0 +1,150 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiExpression, + EuiPopover, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiComboBox, +} from '@elastic/eui'; +import { EuiPopoverTitle, EuiButtonIcon } from '@elastic/eui'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { IErrorObject } from '../../../../../triggers_actions_ui/public/types'; +import { SnapshotMetricType } from '../../../../common/inventory_models/types'; + +interface Props { + metric?: { value: SnapshotMetricType; text: string }; + metrics: Array<{ value: string; text: string }>; + errors: IErrorObject; + onChange: (metric: SnapshotMetricType) => void; + popupPosition?: + | 'upCenter' + | 'upLeft' + | 'upRight' + | 'downCenter' + | 'downLeft' + | 'downRight' + | 'leftCenter' + | 'leftUp' + | 'leftDown' + | 'rightCenter' + | 'rightUp' + | 'rightDown'; +} + +export const MetricExpression = ({ metric, metrics, errors, onChange, popupPosition }: Props) => { + const [aggFieldPopoverOpen, setAggFieldPopoverOpen] = useState(false); + const firstFieldOption = { + text: i18n.translate('xpack.infra.metrics.alertFlyout.expression.metric.selectFieldLabel', { + defaultMessage: 'Select a metric', + }), + value: '', + }; + + const availablefieldsOptions = metrics.map(m => { + return { label: m.text, value: m.value }; + }, []); + + return ( + { + setAggFieldPopoverOpen(true); + }} + color={metric ? 'secondary' : 'danger'} + /> + } + isOpen={aggFieldPopoverOpen} + closePopover={() => { + setAggFieldPopoverOpen(false); + }} + withTitle + anchorPosition={popupPosition ?? 'downRight'} + zIndex={8000} + > +
    + setAggFieldPopoverOpen(false)}> + + + + + 0 && metric !== undefined} + error={errors.metric} + > + 0 && metric !== undefined} + placeholder={firstFieldOption.text} + options={availablefieldsOptions} + noSuggestions={!availablefieldsOptions.length} + selectedOptions={ + metric ? availablefieldsOptions.filter(a => a.value === metric.value) : [] + } + renderOption={(o: any) => o.label} + onChange={selectedOptions => { + if (selectedOptions.length > 0) { + onChange(selectedOptions[0].value as SnapshotMetricType); + setAggFieldPopoverOpen(false); + } + }} + /> + + + +
    +
    + ); +}; + +interface ClosablePopoverTitleProps { + children: JSX.Element; + onClose: () => void; +} + +export const ClosablePopoverTitle = ({ children, onClose }: ClosablePopoverTitleProps) => { + return ( + + + {children} + + onClose()} + /> + + + + ); +}; diff --git a/x-pack/plugins/infra/public/components/alerting/inventory/metric_inventory_threshold_alert_type.ts b/x-pack/plugins/infra/public/components/alerting/inventory/metric_inventory_threshold_alert_type.ts new file mode 100644 index 00000000000000..b7abaf5b363737 --- /dev/null +++ b/x-pack/plugins/infra/public/components/alerting/inventory/metric_inventory_threshold_alert_type.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { AlertTypeModel } from '../../../../../triggers_actions_ui/public/types'; +import { Expressions } from './expression'; +import { validateMetricThreshold } from './validation'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from '../../../../server/lib/alerting/inventory_metric_threshold/types'; + +export function getInventoryMetricAlertType(): AlertTypeModel { + return { + id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, + name: i18n.translate('xpack.infra.metrics.inventory.alertFlyout.alertName', { + defaultMessage: 'Inventory', + }), + iconClass: 'bell', + alertParamsExpression: Expressions, + validate: validateMetricThreshold, + defaultActionMessage: i18n.translate( + 'xpack.infra.metrics.alerting.inventory.threshold.defaultActionMessage', + { + defaultMessage: `\\{\\{alertName\\}\\} - \\{\\{context.group\\}\\} + +\\{\\{context.metricOf.condition0\\}\\} has crossed a threshold of \\{\\{context.thresholdOf.condition0\\}\\} +Current value is \\{\\{context.valueOf.condition0\\}\\} +`, + } + ), + }; +} diff --git a/x-pack/plugins/infra/public/components/alerting/inventory/node_type.tsx b/x-pack/plugins/infra/public/components/alerting/inventory/node_type.tsx new file mode 100644 index 00000000000000..1623fc4e24dcb7 --- /dev/null +++ b/x-pack/plugins/infra/public/components/alerting/inventory/node_type.tsx @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiExpression, EuiPopover, EuiFlexGroup, EuiFlexItem, EuiSelect } from '@elastic/eui'; +import { EuiPopoverTitle, EuiButtonIcon } from '@elastic/eui'; +import { InventoryItemType } from '../../../../common/inventory_models/types'; + +interface WhenExpressionProps { + value: InventoryItemType; + options: { [key: string]: { text: string; value: InventoryItemType } }; + onChange: (value: InventoryItemType) => void; + popupPosition?: + | 'upCenter' + | 'upLeft' + | 'upRight' + | 'downCenter' + | 'downLeft' + | 'downRight' + | 'leftCenter' + | 'leftUp' + | 'leftDown' + | 'rightCenter' + | 'rightUp' + | 'rightDown'; +} + +export const NodeTypeExpression = ({ + value, + options, + onChange, + popupPosition, +}: WhenExpressionProps) => { + const [aggTypePopoverOpen, setAggTypePopoverOpen] = useState(false); + + return ( + { + setAggTypePopoverOpen(true); + }} + /> + } + isOpen={aggTypePopoverOpen} + closePopover={() => { + setAggTypePopoverOpen(false); + }} + ownFocus + withTitle + anchorPosition={popupPosition ?? 'downLeft'} + > +
    + setAggTypePopoverOpen(false)}> + + + { + onChange(e.target.value as InventoryItemType); + setAggTypePopoverOpen(false); + }} + options={Object.values(options).map(o => o)} + /> +
    +
    + ); +}; + +interface ClosablePopoverTitleProps { + children: JSX.Element; + onClose: () => void; +} + +export const ClosablePopoverTitle = ({ children, onClose }: ClosablePopoverTitleProps) => { + return ( + + + {children} + + onClose()} + /> + + + + ); +}; diff --git a/x-pack/plugins/infra/public/components/alerting/inventory/validation.tsx b/x-pack/plugins/infra/public/components/alerting/inventory/validation.tsx new file mode 100644 index 00000000000000..803893dd5a323f --- /dev/null +++ b/x-pack/plugins/infra/public/components/alerting/inventory/validation.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { MetricExpressionParams } from '../../../../server/lib/alerting/metric_threshold/types'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ValidationResult } from '../../../../../triggers_actions_ui/public/types'; + +export function validateMetricThreshold({ + criteria, +}: { + criteria: MetricExpressionParams[]; +}): ValidationResult { + const validationResult = { errors: {} }; + const errors: { + [id: string]: { + timeSizeUnit: string[]; + timeWindowSize: string[]; + threshold0: string[]; + threshold1: string[]; + metric: string[]; + }; + } = {}; + validationResult.errors = errors; + + if (!criteria || !criteria.length) { + return validationResult; + } + + criteria.forEach((c, idx) => { + // Create an id for each criteria, so we can map errors to specific criteria. + const id = idx.toString(); + + errors[id] = errors[id] || { + timeSizeUnit: [], + timeWindowSize: [], + threshold0: [], + threshold1: [], + metric: [], + }; + + if (!c.threshold || !c.threshold.length) { + errors[id].threshold0.push( + i18n.translate('xpack.infra.metrics.alertFlyout.error.thresholdRequired', { + defaultMessage: 'Threshold is required.', + }) + ); + } + + if (c.comparator === 'between' && (!c.threshold || c.threshold.length < 2)) { + errors[id].threshold1.push( + i18n.translate('xpack.infra.metrics.alertFlyout.error.thresholdRequired', { + defaultMessage: 'Threshold is required.', + }) + ); + } + + if (!c.timeSize) { + errors[id].timeWindowSize.push( + i18n.translate('xpack.infra.metrics.alertFlyout.error.timeRequred', { + defaultMessage: 'Time size is Required.', + }) + ); + } + + if (!c.metric && c.aggType !== 'count') { + errors[id].metric.push( + i18n.translate('xpack.infra.metrics.alertFlyout.error.metricRequired', { + defaultMessage: 'Metric is required.', + }) + ); + } + }); + + return validationResult; +} diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx index 72d6aea5ecfc6a..c713839a1bba89 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/column_headers.tsx @@ -22,7 +22,7 @@ import { } from './log_entry_column'; import { ASSUMED_SCROLLBAR_WIDTH } from './vertical_scroll_panel'; import { LogPositionState } from '../../../containers/logs/log_position'; -import { localizedDate } from '../../../utils/formatters/datetime'; +import { localizedDate } from '../../../../common/formatters/datetime'; export const LogColumnHeaders: React.FunctionComponent<{ columnConfigurations: LogColumnConfiguration[]; diff --git a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_date_row.tsx b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_date_row.tsx index fbc450950b8283..144caed744bab8 100644 --- a/x-pack/plugins/infra/public/components/logging/log_text_stream/log_date_row.tsx +++ b/x-pack/plugins/infra/public/components/logging/log_text_stream/log_date_row.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiTitle } from '@elastic/eui'; -import { localizedDate } from '../../../utils/formatters/datetime'; +import { localizedDate } from '../../../../common/formatters/datetime'; interface LogDateRowProps { timestamp: number; diff --git a/x-pack/plugins/infra/public/containers/source/use_source_via_http.ts b/x-pack/plugins/infra/public/containers/source/use_source_via_http.ts index bc6374a6538e37..aad54bd2222b77 100644 --- a/x-pack/plugins/infra/public/containers/source/use_source_via_http.ts +++ b/x-pack/plugins/infra/public/containers/source/use_source_via_http.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { useEffect, useMemo } from 'react'; +import { useEffect, useMemo, useCallback } from 'react'; import { fold } from 'fp-ts/lib/Either'; import { identity } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; @@ -69,12 +69,15 @@ export const useSourceViaHttp = ({ })(); }, [makeRequest]); - const createDerivedIndexPattern = (indexType: 'logs' | 'metrics' | 'both' = type) => { - return { - fields: response?.source ? response.status.indexFields : [], - title: pickIndexPattern(response?.source, indexType), - }; - }; + const createDerivedIndexPattern = useCallback( + (indexType: 'logs' | 'metrics' | 'both' = type) => { + return { + fields: response?.source ? response.status.indexFields : [], + title: pickIndexPattern(response?.source, indexType), + }; + }, + [response, type] + ); const source = useMemo(() => { return response ? { ...response.source, status: response.status } : null; diff --git a/x-pack/plugins/infra/public/index.ts b/x-pack/plugins/infra/public/index.ts index 4465bde377c124..1dfdf827f203b5 100644 --- a/x-pack/plugins/infra/public/index.ts +++ b/x-pack/plugins/infra/public/index.ts @@ -16,7 +16,7 @@ export const plugin: PluginInitializer< return new Plugin(context); }; -export { FORMATTERS } from './utils/formatters'; +export { FORMATTERS } from '../common/formatters'; export { InfraFormatterType } from './lib/lib'; export type InfraAppId = 'logs' | 'metrics'; diff --git a/x-pack/plugins/infra/public/lib/lib.ts b/x-pack/plugins/infra/public/lib/lib.ts index e4de0caf9bb8ba..9043b4d9f69796 100644 --- a/x-pack/plugins/infra/public/lib/lib.ts +++ b/x-pack/plugins/infra/public/lib/lib.ts @@ -186,12 +186,6 @@ export enum InfraFormatterType { percent = 'percent', } -export enum InfraWaffleMapDataFormat { - bytesDecimal = 'bytesDecimal', - bitsDecimal = 'bitsDecimal', - abbreviatedNumber = 'abbreviatedNumber', -} - export interface InfraGroupByOptions { text: string; field: string; diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index 5dc9802fefd254..dbf71665ea869a 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -28,7 +28,9 @@ import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { WaffleOptionsProvider } from './inventory_view/hooks/use_waffle_options'; import { WaffleTimeProvider } from './inventory_view/hooks/use_waffle_time'; import { WaffleFiltersProvider } from './inventory_view/hooks/use_waffle_filters'; -import { AlertDropdown } from '../../alerting/metric_threshold/components/alert_dropdown'; + +import { InventoryAlertDropdown } from '../../components/alerting/inventory/alert_dropdown'; +import { MetricsAlertDropdown } from '../../alerting/metric_threshold/components/alert_dropdown'; export const InfrastructurePage = ({ match }: RouteComponentProps) => { const uiCapabilities = useKibana().services.application?.capabilities; @@ -96,7 +98,8 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => { /> - + + diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx index c528aa885346e4..d576f08108649f 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/waffle/node_context_menu.tsx @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useMemo, useState } from 'react'; -import { AlertFlyout } from '../../../../../alerting/metric_threshold/components/alert_flyout'; +import { AlertFlyout } from '../../../../../components/alerting/inventory/alert_flyout'; import { InfraWaffleMapNode, InfraWaffleMapOptions } from '../../../../../lib/lib'; import { getNodeDetailUrl, getNodeLogsUrl } from '../../../../link_to'; import { createUptimeLink } from '../../lib/create_uptime_link'; @@ -24,6 +24,8 @@ import { SectionSubtitle, SectionLinks, SectionLink, + withTheme, + EuiTheme, } from '../../../../../../../observability/public'; import { useLinkProps } from '../../../../../hooks/use_link_props'; @@ -37,157 +39,178 @@ interface Props { popoverPosition: EuiPopoverProps['anchorPosition']; } -export const NodeContextMenu: React.FC = ({ - options, - currentTime, - children, - node, - isPopoverOpen, - closePopover, - nodeType, - popoverPosition, -}) => { - const [flyoutVisible, setFlyoutVisible] = useState(false); - const inventoryModel = findInventoryModel(nodeType); - const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000; - const uiCapabilities = useKibana().services.application?.capabilities; - // Due to the changing nature of the fields between APM and this UI, - // We need to have some exceptions until 7.0 & ECS is finalized. Reference - // #26620 for the details for these fields. - // TODO: This is tech debt, remove it after 7.0 & ECS migration. - const apmField = nodeType === 'host' ? 'host.hostname' : inventoryModel.fields.id; +export const NodeContextMenu: React.FC = withTheme( + ({ + options, + currentTime, + children, + node, + isPopoverOpen, + closePopover, + nodeType, + popoverPosition, + theme, + }) => { + const [flyoutVisible, setFlyoutVisible] = useState(false); + const inventoryModel = findInventoryModel(nodeType); + const nodeDetailFrom = currentTime - inventoryModel.metrics.defaultTimeRangeInSeconds * 1000; + const uiCapabilities = useKibana().services.application?.capabilities; + // Due to the changing nature of the fields between APM and this UI, + // We need to have some exceptions until 7.0 & ECS is finalized. Reference + // #26620 for the details for these fields. + // TODO: This is tech debt, remove it after 7.0 & ECS migration. + const apmField = nodeType === 'host' ? 'host.hostname' : inventoryModel.fields.id; - const showDetail = inventoryModel.crosslinkSupport.details; - const showLogsLink = - inventoryModel.crosslinkSupport.logs && node.id && uiCapabilities?.logs?.show; - const showAPMTraceLink = - inventoryModel.crosslinkSupport.apm && uiCapabilities?.apm && uiCapabilities?.apm.show; - const showUptimeLink = - inventoryModel.crosslinkSupport.uptime && (['pod', 'container'].includes(nodeType) || node.ip); + const showDetail = inventoryModel.crosslinkSupport.details; + const showLogsLink = + inventoryModel.crosslinkSupport.logs && node.id && uiCapabilities?.logs?.show; + const showAPMTraceLink = + inventoryModel.crosslinkSupport.apm && uiCapabilities?.apm && uiCapabilities?.apm.show; + const showUptimeLink = + inventoryModel.crosslinkSupport.uptime && + (['pod', 'container'].includes(nodeType) || node.ip); - const inventoryId = useMemo(() => { - if (nodeType === 'host') { - if (node.ip) { - return { label: host.ip, value: node.ip }; + const inventoryId = useMemo(() => { + if (nodeType === 'host') { + if (node.ip) { + return { label: host.ip, value: node.ip }; + } + } else { + if (options.fields) { + const { id } = findInventoryFields(nodeType, options.fields); + return { + label: {id}, + value: node.id, + }; + } } - } else { - if (options.fields) { - const { id } = findInventoryFields(nodeType, options.fields); - return { - label: {id}, - value: node.id, - }; - } - } - return { label: '', value: '' }; - }, [nodeType, node.ip, node.id, options.fields]); + return { label: '', value: '' }; + }, [nodeType, node.ip, node.id, options.fields]); + + const nodeLogsMenuItemLinkProps = useLinkProps({ + app: 'logs', + ...getNodeLogsUrl({ + nodeType, + nodeId: node.id, + time: currentTime, + }), + }); + const nodeDetailMenuItemLinkProps = useLinkProps({ + ...getNodeDetailUrl({ + nodeType, + nodeId: node.id, + from: nodeDetailFrom, + to: currentTime, + }), + }); + const apmTracesMenuItemLinkProps = useLinkProps({ + app: 'apm', + hash: 'traces', + search: { + kuery: `${apmField}:"${node.id}"`, + }, + }); + const uptimeMenuItemLinkProps = useLinkProps(createUptimeLink(options, nodeType, node)); - const nodeLogsMenuItemLinkProps = useLinkProps({ - app: 'logs', - ...getNodeLogsUrl({ - nodeType, - nodeId: node.id, - time: currentTime, - }), - }); - const nodeDetailMenuItemLinkProps = useLinkProps({ - ...getNodeDetailUrl({ - nodeType, - nodeId: node.id, - from: nodeDetailFrom, - to: currentTime, - }), - }); - const apmTracesMenuItemLinkProps = useLinkProps({ - app: 'apm', - hash: 'traces', - search: { - kuery: `${apmField}:"${node.id}"`, - }, - }); - const uptimeMenuItemLinkProps = useLinkProps(createUptimeLink(options, nodeType, node)); + const nodeLogsMenuItem: SectionLinkProps = { + label: i18n.translate('xpack.infra.nodeContextMenu.viewLogsName', { + defaultMessage: '{inventoryName} logs', + values: { inventoryName: inventoryModel.singularDisplayName }, + }), + ...nodeLogsMenuItemLinkProps, + 'data-test-subj': 'viewLogsContextMenuItem', + isDisabled: !showLogsLink, + }; - const nodeLogsMenuItem: SectionLinkProps = { - label: i18n.translate('xpack.infra.nodeContextMenu.viewLogsName', { - defaultMessage: '{inventoryName} logs', - values: { inventoryName: inventoryModel.singularDisplayName }, - }), - ...nodeLogsMenuItemLinkProps, - 'data-test-subj': 'viewLogsContextMenuItem', - isDisabled: !showLogsLink, - }; + const nodeDetailMenuItem: SectionLinkProps = { + label: i18n.translate('xpack.infra.nodeContextMenu.viewMetricsName', { + defaultMessage: '{inventoryName} metrics', + values: { inventoryName: inventoryModel.singularDisplayName }, + }), + ...nodeDetailMenuItemLinkProps, + isDisabled: !showDetail, + }; - const nodeDetailMenuItem: SectionLinkProps = { - label: i18n.translate('xpack.infra.nodeContextMenu.viewMetricsName', { - defaultMessage: '{inventoryName} metrics', - values: { inventoryName: inventoryModel.singularDisplayName }, - }), - ...nodeDetailMenuItemLinkProps, - isDisabled: !showDetail, - }; + const apmTracesMenuItem: SectionLinkProps = { + label: i18n.translate('xpack.infra.nodeContextMenu.viewAPMTraces', { + defaultMessage: '{inventoryName} APM traces', + values: { inventoryName: inventoryModel.singularDisplayName }, + }), + ...apmTracesMenuItemLinkProps, + 'data-test-subj': 'viewApmTracesContextMenuItem', + isDisabled: !showAPMTraceLink, + }; - const apmTracesMenuItem: SectionLinkProps = { - label: i18n.translate('xpack.infra.nodeContextMenu.viewAPMTraces', { - defaultMessage: '{inventoryName} APM traces', - values: { inventoryName: inventoryModel.singularDisplayName }, - }), - ...apmTracesMenuItemLinkProps, - 'data-test-subj': 'viewApmTracesContextMenuItem', - isDisabled: !showAPMTraceLink, - }; + const uptimeMenuItem: SectionLinkProps = { + label: i18n.translate('xpack.infra.nodeContextMenu.viewUptimeLink', { + defaultMessage: '{inventoryName} in Uptime', + values: { inventoryName: inventoryModel.singularDisplayName }, + }), + ...uptimeMenuItemLinkProps, + isDisabled: !showUptimeLink, + }; - const uptimeMenuItem: SectionLinkProps = { - label: i18n.translate('xpack.infra.nodeContextMenu.viewUptimeLink', { - defaultMessage: '{inventoryName} in Uptime', - values: { inventoryName: inventoryModel.singularDisplayName }, - }), - ...uptimeMenuItemLinkProps, - isDisabled: !showUptimeLink, - }; + const createAlertMenuItem: SectionLinkProps = { + label: i18n.translate('xpack.infra.nodeContextMenu.createAlertLink', { + defaultMessage: 'Create alert', + }), + style: { color: theme?.eui.euiLinkColor || '#006BB4', fontWeight: 500, padding: 0 }, + onClick: () => { + setFlyoutVisible(true); + }, + }; - return ( - <> - -
    -
    - - - - {inventoryId.label && ( - -
    - -
    -
    - )} - - - - - - -
    -
    -
    - - - ); -}; + return ( + <> + +
    +
    + + + + {inventoryId.label && ( + +
    + +
    +
    + )} + + + + + + + +
    +
    +
    + + + ); + } +); diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts index acd71e51376948..f8c7a10f12831d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/lib/create_inventory_metric_formatter.ts @@ -5,13 +5,13 @@ */ import { get } from 'lodash'; -import { createFormatter } from '../../../../utils/formatters'; import { InfraFormatterType } from '../../../../lib/lib'; import { SnapshotMetricInput, SnapshotCustomMetricInputRT, } from '../../../../../common/http_api/snapshot_api'; import { createFormatterForMetric } from '../../metrics_explorer/components/helpers/create_formatter_for_metric'; +import { createFormatter } from '../../../../../common/formatters'; interface MetricFormatter { formatter: InfraFormatterType; diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx index 0aab676b7d6c54..0f53ced80888b5 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/gauges_section_vis.tsx @@ -17,7 +17,7 @@ import { get, last, max } from 'lodash'; import React, { ReactText } from 'react'; import { euiStyled } from '../../../../../../observability/public'; -import { createFormatter } from '../../../../utils/formatters'; +import { createFormatter } from '../../../../../common/formatters'; import { InventoryFormatterType } from '../../../../../common/inventory_models/types'; import { SeriesOverrides, VisSectionProps } from '../types'; import { getChartName } from './helpers'; diff --git a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/helpers.ts b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/helpers.ts index bb4ad326609520..0b8773db2dddf0 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/helpers.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metric_detail/components/helpers.ts @@ -7,7 +7,7 @@ import { ReactText } from 'react'; import Color from 'color'; import { get, first, last, min, max } from 'lodash'; -import { createFormatter } from '../../../../utils/formatters'; +import { createFormatter } from '../../../../../common/formatters'; import { InfraDataSeries } from '../../../../graphql/types'; import { InventoryVisTypeRT, diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric.ts b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric.ts index d07a6b45f02bef..46bd7b006446af 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric.ts +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/helpers/create_formatter_for_metric.ts @@ -5,7 +5,7 @@ */ import { MetricsExplorerMetric } from '../../../../../../common/http_api/metrics_explorer'; -import { createFormatter } from '../../../../../utils/formatters'; +import { createFormatter } from '../../../../../../common/formatters'; import { InfraFormatterType } from '../../../../../lib/lib'; import { metricToFormat } from './metric_to_format'; export const createFormatterForMetric = (metric?: MetricsExplorerMetric) => { diff --git a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx index e9826e1ff39552..04661bbc377025 100644 --- a/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/metrics_explorer/components/kuery_bar.tsx @@ -14,6 +14,7 @@ import { esKuery, IIndexPattern } from '../../../../../../../../src/plugins/data interface Props { derivedIndexPattern: IIndexPattern; onSubmit: (query: string) => void; + onChange?: (query: string) => void; value?: string | null; placeholder?: string; } @@ -30,6 +31,7 @@ function validateQuery(query: string) { export const MetricsExplorerKueryBar = ({ derivedIndexPattern, onSubmit, + onChange, value, placeholder, }: Props) => { @@ -46,6 +48,9 @@ export const MetricsExplorerKueryBar = ({ const handleChange = (query: string) => { setValidation(validateQuery(query)); setDraftQuery(query); + if (onChange) { + onChange(query); + } }; const filteredDerivedIndexPattern = { diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts index 8cdfc4f381f436..d61ef7fc4a631c 100644 --- a/x-pack/plugins/infra/public/plugin.ts +++ b/x-pack/plugins/infra/public/plugin.ts @@ -22,6 +22,7 @@ import { DataEnhancedSetup, DataEnhancedStart } from '../../data_enhanced/public import { TriggersAndActionsUIPublicPluginSetup } from '../../../plugins/triggers_actions_ui/public'; import { getAlertType as getLogsAlertType } from './components/alerting/logs/log_threshold_alert_type'; +import { getInventoryMetricAlertType } from './components/alerting/inventory/metric_inventory_threshold_alert_type'; import { createMetricThresholdAlertType } from './alerting/metric_threshold'; export type ClientSetup = void; @@ -53,6 +54,7 @@ export class Plugin setup(core: CoreSetup, pluginsSetup: ClientPluginsSetup) { registerFeatures(pluginsSetup.home); + pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(getInventoryMetricAlertType()); pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(getLogsAlertType()); pluginsSetup.triggers_actions_ui.alertTypeRegistry.register(createMetricThresholdAlertType()); diff --git a/x-pack/plugins/infra/server/graphql/sources/resolvers.ts b/x-pack/plugins/infra/server/graphql/sources/resolvers.ts index f880eca933241c..cffab4ba4f6f00 100644 --- a/x-pack/plugins/infra/server/graphql/sources/resolvers.ts +++ b/x-pack/plugins/infra/server/graphql/sources/resolvers.ts @@ -101,7 +101,9 @@ export const createSourcesResolvers = ( return requestedSourceConfiguration; }, async allSources(root, args, { req }) { - const sourceConfigurations = await libs.sources.getAllSourceConfigurations(req); + const sourceConfigurations = await libs.sources.getAllSourceConfigurations( + req.core.savedObjects.client + ); return sourceConfigurations; }, @@ -131,7 +133,7 @@ export const createSourcesResolvers = ( Mutation: { async createSource(root, args, { req }) { const sourceConfiguration = await libs.sources.createSourceConfiguration( - req, + req.core.savedObjects.client, args.id, compactObject({ ...args.sourceProperties, @@ -147,7 +149,7 @@ export const createSourcesResolvers = ( }; }, async deleteSource(root, args, { req }) { - await libs.sources.deleteSourceConfiguration(req, args.id); + await libs.sources.deleteSourceConfiguration(req.core.savedObjects.client, args.id); return { id: args.id, @@ -155,7 +157,7 @@ export const createSourcesResolvers = ( }, async updateSource(root, args, { req }) { const updatedSourceConfiguration = await libs.sources.updateSourceConfiguration( - req, + req.core.savedObjects.client, args.id, compactObject({ ...args.sourceProperties, diff --git a/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts index 5a5f9d0f8f5293..62f324e01f8d99 100644 --- a/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts +++ b/x-pack/plugins/infra/server/lib/adapters/metrics/kibana_metrics_adapter.ts @@ -18,6 +18,7 @@ import { InventoryMetricRT, } from '../../../../common/inventory_models/types'; import { calculateMetricInterval } from '../../../utils/calculate_metric_interval'; +import { CallWithRequestParams, InfraDatabaseSearchResponse } from '../framework'; export class KibanaMetricsAdapter implements InfraMetricsAdapter { private framework: KibanaFramework; @@ -120,9 +121,14 @@ export class KibanaMetricsAdapter implements InfraMetricsAdapter { indexPattern, options.timerange.interval ); + + const client = ( + opts: CallWithRequestParams + ): Promise> => + this.framework.callWithRequest(requestContext, 'search', opts); + const calculatedInterval = await calculateMetricInterval( - this.framework, - requestContext, + client, { indexPattern: `${options.sourceConfiguration.logAlias},${options.sourceConfiguration.metricAlias}`, timestampField: options.sourceConfiguration.fields.timestamp, diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts new file mode 100644 index 00000000000000..cc8a35f6e47a15 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/inventory_metric_threshold_executor.ts @@ -0,0 +1,214 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { mapValues, last, get } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; +import { + InfraDatabaseSearchResponse, + CallWithRequestParams, +} from '../../adapters/framework/adapter_types'; +import { Comparator, AlertStates, InventoryMetricConditions } from './types'; +import { AlertServices, AlertExecutorOptions } from '../../../../../alerting/server'; +import { InfraSnapshot } from '../../snapshot'; +import { parseFilterQuery } from '../../../utils/serialized_query'; +import { InventoryItemType, SnapshotMetricType } from '../../../../common/inventory_models/types'; +import { InfraTimerangeInput } from '../../../../common/http_api/snapshot_api'; +import { InfraSourceConfiguration } from '../../sources'; +import { InfraBackendLibs } from '../../infra_types'; +import { METRIC_FORMATTERS } from '../../../../common/formatters/snapshot_metric_formats'; +import { createFormatter } from '../../../../common/formatters'; + +interface InventoryMetricThresholdParams { + criteria: InventoryMetricConditions[]; + groupBy: string | undefined; + filterQuery: string | undefined; + nodeType: InventoryItemType; + sourceId?: string; +} + +export const createInventoryMetricThresholdExecutor = ( + libs: InfraBackendLibs, + alertId: string +) => async ({ services, params }: AlertExecutorOptions) => { + const { criteria, filterQuery, sourceId, nodeType } = params as InventoryMetricThresholdParams; + + const source = await libs.sources.getSourceConfiguration( + services.savedObjectsClient, + sourceId || 'default' + ); + + const results = await Promise.all( + criteria.map(c => evaluateCondtion(c, nodeType, source.configuration, services, filterQuery)) + ); + + const invenotryItems = Object.keys(results[0]); + for (const item of invenotryItems) { + const alertInstance = services.alertInstanceFactory(`${alertId}-${item}`); + // AND logic; all criteria must be across the threshold + const shouldAlertFire = results.every(result => result[item].shouldFire); + + // AND logic; because we need to evaluate all criteria, if one of them reports no data then the + // whole alert is in a No Data/Error state + const isNoData = results.some(result => result[item].isNoData); + const isError = results.some(result => result[item].isError); + + if (shouldAlertFire) { + alertInstance.scheduleActions(FIRED_ACTIONS.id, { + group: item, + item, + valueOf: mapToConditionsLookup(results, result => + formatMetric(result[item].metric, result[item].currentValue) + ), + thresholdOf: mapToConditionsLookup(criteria, c => c.threshold), + metricOf: mapToConditionsLookup(criteria, c => c.metric), + }); + } + + alertInstance.replaceState({ + alertState: isError + ? AlertStates.ERROR + : isNoData + ? AlertStates.NO_DATA + : shouldAlertFire + ? AlertStates.ALERT + : AlertStates.OK, + }); + } +}; + +interface ConditionResult { + shouldFire: boolean; + currentValue?: number | null; + isNoData: boolean; + isError: boolean; +} + +const evaluateCondtion = async ( + condition: InventoryMetricConditions, + nodeType: InventoryItemType, + sourceConfiguration: InfraSourceConfiguration, + services: AlertServices, + filterQuery?: string +): Promise> => { + const { comparator, metric } = condition; + let { threshold } = condition; + + const currentValues = await getData( + services, + nodeType, + metric, + { + to: Date.now(), + from: moment() + .subtract(condition.timeSize, condition.timeUnit) + .toDate() + .getTime(), + interval: condition.timeUnit, + }, + sourceConfiguration, + filterQuery + ); + + threshold = threshold.map(n => convertMetricValue(metric, n)); + + const comparisonFunction = comparatorMap[comparator]; + + return mapValues(currentValues, value => ({ + shouldFire: value !== undefined && value !== null && comparisonFunction(value, threshold), + metric, + currentValue: value, + isNoData: value === null, + isError: value === undefined, + })); +}; + +const getData = async ( + services: AlertServices, + nodeType: InventoryItemType, + metric: SnapshotMetricType, + timerange: InfraTimerangeInput, + sourceConfiguration: InfraSourceConfiguration, + filterQuery?: string +) => { + const snapshot = new InfraSnapshot(); + const esClient = ( + options: CallWithRequestParams + ): Promise> => + services.callCluster('search', options); + + const options = { + filterQuery: parseFilterQuery(filterQuery), + nodeType, + groupBy: [], + sourceConfiguration, + metric: { type: metric }, + timerange, + }; + + const { nodes } = await snapshot.getNodes(esClient, options); + + return nodes.reduce((acc, n) => { + const nodePathItem = last(n.path); + acc[nodePathItem.label] = n.metric && n.metric.value; + return acc; + }, {} as Record); +}; + +const comparatorMap = { + [Comparator.BETWEEN]: (value: number, [a, b]: number[]) => + value >= Math.min(a, b) && value <= Math.max(a, b), + // `threshold` is always an array of numbers in case the BETWEEN comparator is + // used; all other compartors will just destructure the first value in the array + [Comparator.GT]: (a: number, [b]: number[]) => a > b, + [Comparator.LT]: (a: number, [b]: number[]) => a < b, + [Comparator.OUTSIDE_RANGE]: (value: number, [a, b]: number[]) => value < a || value > b, + [Comparator.GT_OR_EQ]: (a: number, [b]: number[]) => a >= b, + [Comparator.LT_OR_EQ]: (a: number, [b]: number[]) => a <= b, +}; + +const mapToConditionsLookup = ( + list: any[], + mapFn: (value: any, index: number, array: any[]) => unknown +) => + list + .map(mapFn) + .reduce( + (result: Record, value, i) => ({ ...result, [`condition${i}`]: value }), + {} + ); + +export const FIRED_ACTIONS = { + id: 'metrics.invenotry_threshold.fired', + name: i18n.translate('xpack.infra.metrics.alerting.inventory.threshold.fired', { + defaultMessage: 'Fired', + }), +}; + +// Some metrics in the UI are in a different unit that what we store in ES. +const convertMetricValue = (metric: SnapshotMetricType, value: number) => { + if (converters[metric]) { + return converters[metric](value); + } else { + return value; + } +}; +const converters: Record number> = { + cpu: n => Number(n) / 100, + memory: n => Number(n) / 100, +}; + +const formatMetric = (metric: SnapshotMetricType, value: number) => { + // if (SnapshotCustomMetricInputRT.is(metric)) { + // const formatter = createFormatterForMetric(metric); + // return formatter(val); + // } + const metricFormatter = get(METRIC_FORMATTERS, metric, METRIC_FORMATTERS.count); + if (value == null) { + return ''; + } + const formatter = createFormatter(metricFormatter.formatter, metricFormatter.template); + return formatter(value); +}; diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts new file mode 100644 index 00000000000000..3b6a1b5557bc69 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/register_inventory_metric_threshold_alert_type.ts @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; +import { schema } from '@kbn/config-schema'; +import { curry } from 'lodash'; +import uuid from 'uuid'; +import { + createInventoryMetricThresholdExecutor, + FIRED_ACTIONS, +} from './inventory_metric_threshold_executor'; +import { METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID } from './types'; +import { InfraBackendLibs } from '../../infra_types'; + +const condition = schema.object({ + threshold: schema.arrayOf(schema.number()), + comparator: schema.oneOf([ + schema.literal('>'), + schema.literal('<'), + schema.literal('>='), + schema.literal('<='), + schema.literal('between'), + schema.literal('outside'), + ]), + timeUnit: schema.string(), + timeSize: schema.number(), + metric: schema.string(), +}); + +export const registerMetricInventoryThresholdAlertType = (libs: InfraBackendLibs) => ({ + id: METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, + name: 'Inventory', + validate: { + params: schema.object( + { + criteria: schema.arrayOf(condition), + nodeType: schema.string(), + filterQuery: schema.maybe(schema.string()), + sourceId: schema.string(), + }, + { unknowns: 'allow' } + ), + }, + defaultActionGroupId: FIRED_ACTIONS.id, + actionGroups: [FIRED_ACTIONS], + executor: curry(createInventoryMetricThresholdExecutor)(libs, uuid.v4()), + actionVariables: { + context: [ + { + name: 'group', + description: i18n.translate( + 'xpack.infra.metrics.alerting.threshold.alerting.groupActionVariableDescription', + { + defaultMessage: 'Name of the group reporting data', + } + ), + }, + { + name: 'valueOf', + description: i18n.translate( + 'xpack.infra.metrics.alerting.threshold.alerting.valueOfActionVariableDescription', + { + defaultMessage: + 'Record of the current value of the watched metric; grouped by condition, i.e valueOf.condition0, valueOf.condition1, etc.', + } + ), + }, + { + name: 'thresholdOf', + description: i18n.translate( + 'xpack.infra.metrics.alerting.threshold.alerting.thresholdOfActionVariableDescription', + { + defaultMessage: + 'Record of the alerting threshold; grouped by condition, i.e thresholdOf.condition0, thresholdOf.condition1, etc.', + } + ), + }, + { + name: 'metricOf', + description: i18n.translate( + 'xpack.infra.metrics.alerting.threshold.alerting.metricOfActionVariableDescription', + { + defaultMessage: + 'Record of the watched metric; grouped by condition, i.e metricOf.condition0, metricOf.condition1, etc.', + } + ), + }, + ], + }, +}); diff --git a/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/types.ts b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/types.ts new file mode 100644 index 00000000000000..73ee1ab6b76159 --- /dev/null +++ b/x-pack/plugins/infra/server/lib/alerting/inventory_metric_threshold/types.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { SnapshotMetricType } from '../../../../common/inventory_models/types'; + +export const METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID = 'metrics.alert.inventory.threshold'; + +export enum Comparator { + GT = '>', + LT = '<', + GT_OR_EQ = '>=', + LT_OR_EQ = '<=', + BETWEEN = 'between', + OUTSIDE_RANGE = 'outside', +} + +export enum AlertStates { + OK, + ALERT, + NO_DATA, + ERROR, +} + +export type TimeUnit = 's' | 'm' | 'h' | 'd'; + +export interface InventoryMetricConditions { + metric: SnapshotMetricType; + timeSize: number; + timeUnit: TimeUnit; + sourceId?: string; + threshold: number[]; + comparator: Comparator; +} diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index 0007b8bd719f44..2531e939792af2 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -3,7 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold_executor'; import { Comparator, AlertStates } from './types'; import * as mocks from './test_mocks'; @@ -13,81 +12,14 @@ import { AlertServicesMock, AlertInstanceMock, } from '../../../../../alerting/server/mocks'; - -const executor = createMetricThresholdExecutor('test') as (opts: { - params: AlertExecutorOptions['params']; - services: { callCluster: AlertExecutorOptions['params']['callCluster'] }; -}) => Promise; - -const services: AlertServicesMock = alertsMock.createAlertServices(); -services.callCluster.mockImplementation(async (_: string, { body, index }: any) => { - if (index === 'alternatebeat-*') return mocks.changedSourceIdResponse; - const metric = body.query.bool.filter[1]?.exists.field; - if (body.aggs.groupings) { - if (body.aggs.groupings.composite.after) { - return mocks.compositeEndResponse; - } - if (metric === 'test.metric.2') { - return mocks.alternateCompositeResponse; - } - return mocks.basicCompositeResponse; - } - if (metric === 'test.metric.2') { - return mocks.alternateMetricResponse; - } else if (metric === 'test.metric.3') { - return mocks.emptyMetricResponse; - } - return mocks.basicMetricResponse; -}); -services.savedObjectsClient.get.mockImplementation(async (type: string, sourceId: string) => { - if (sourceId === 'alternate') - return { - id: 'alternate', - attributes: { metricAlias: 'alternatebeat-*' }, - type, - references: [], - }; - return { id: 'default', attributes: { metricAlias: 'metricbeat-*' }, type, references: [] }; -}); +import { InfraSources } from '../../sources'; interface AlertTestInstance { instance: AlertInstanceMock; actionQueue: any[]; state: any; } -const alertInstances = new Map(); -services.alertInstanceFactory.mockImplementation((instanceID: string) => { - const alertInstance: AlertTestInstance = { - instance: alertsMock.createAlertInstanceFactory(), - actionQueue: [], - state: {}, - }; - alertInstances.set(instanceID, alertInstance); - alertInstance.instance.replaceState.mockImplementation((newState: any) => { - alertInstance.state = newState; - return alertInstance.instance; - }); - alertInstance.instance.scheduleActions.mockImplementation((id: string, action: any) => { - alertInstance.actionQueue.push({ id, action }); - return alertInstance.instance; - }); - return alertInstance.instance; -}); - -function mostRecentAction(id: string) { - return alertInstances.get(id)!.actionQueue.pop(); -} -function getState(id: string) { - return alertInstances.get(id)!.state; -} - -const baseCriterion = { - aggType: 'avg', - metric: 'test.metric.1', - timeSize: 1, - timeUnit: 'm', -}; describe('The metric threshold alert type', () => { describe('querying the entire infrastructure', () => { const instanceID = 'test-*'; @@ -167,14 +99,6 @@ describe('The metric threshold alert type', () => { expect(action.reason).toContain('threshold of 0.75'); expect(action.reason).toContain('test.metric.1'); }); - test('fetches the index pattern dynamically', async () => { - await execute(Comparator.LT, [17], 'alternate'); - expect(mostRecentAction(instanceID).id).toBe(FIRED_ACTIONS.id); - expect(getState(instanceID).alertState).toBe(AlertStates.ALERT); - await execute(Comparator.LT, [1.5], 'alternate'); - expect(mostRecentAction(instanceID)).toBe(undefined); - expect(getState(instanceID).alertState).toBe(AlertStates.OK); - }); }); describe('querying with a groupBy parameter', () => { @@ -338,3 +262,117 @@ describe('The metric threshold alert type', () => { }); }); }); + +const createMockStaticConfiguration = (sources: any) => ({ + enabled: true, + query: { + partitionSize: 1, + partitionFactor: 1, + }, + sources, +}); + +const mockLibs: any = { + sources: new InfraSources({ + config: createMockStaticConfiguration({}), + }), + configuration: createMockStaticConfiguration({}), +}; + +const executor = createMetricThresholdExecutor(mockLibs, 'test') as (opts: { + params: AlertExecutorOptions['params']; + services: { callCluster: AlertExecutorOptions['params']['callCluster'] }; +}) => Promise; + +const services: AlertServicesMock = alertsMock.createAlertServices(); +services.callCluster.mockImplementation(async (_: string, { body, index }: any) => { + if (index === 'alternatebeat-*') return mocks.changedSourceIdResponse; + const metric = body.query.bool.filter[1]?.exists.field; + if (body.aggs.groupings) { + if (body.aggs.groupings.composite.after) { + return mocks.compositeEndResponse; + } + if (metric === 'test.metric.2') { + return mocks.alternateCompositeResponse; + } + return mocks.basicCompositeResponse; + } + if (metric === 'test.metric.2') { + return mocks.alternateMetricResponse; + } + return mocks.basicMetricResponse; +}); +services.savedObjectsClient.get.mockImplementation(async (type: string, sourceId: string) => { + if (sourceId === 'alternate') + return { + id: 'alternate', + attributes: { metricAlias: 'alternatebeat-*' }, + type, + references: [], + }; + return { id: 'default', attributes: { metricAlias: 'metricbeat-*' }, type, references: [] }; +}); + +services.callCluster.mockImplementation(async (_: string, { body, index }: any) => { + if (index === 'alternatebeat-*') return mocks.changedSourceIdResponse; + const metric = body.query.bool.filter[1]?.exists.field; + if (body.aggs.groupings) { + if (body.aggs.groupings.composite.after) { + return mocks.compositeEndResponse; + } + if (metric === 'test.metric.2') { + return mocks.alternateCompositeResponse; + } + return mocks.basicCompositeResponse; + } + if (metric === 'test.metric.2') { + return mocks.alternateMetricResponse; + } else if (metric === 'test.metric.3') { + return mocks.emptyMetricResponse; + } + return mocks.basicMetricResponse; +}); +services.savedObjectsClient.get.mockImplementation(async (type: string, sourceId: string) => { + if (sourceId === 'alternate') + return { + id: 'alternate', + attributes: { metricAlias: 'alternatebeat-*' }, + type, + references: [], + }; + return { id: 'default', attributes: { metricAlias: 'metricbeat-*' }, type, references: [] }; +}); + +const alertInstances = new Map(); +services.alertInstanceFactory.mockImplementation((instanceID: string) => { + const alertInstance: AlertTestInstance = { + instance: alertsMock.createAlertInstanceFactory(), + actionQueue: [], + state: {}, + }; + alertInstances.set(instanceID, alertInstance); + alertInstance.instance.replaceState.mockImplementation((newState: any) => { + alertInstance.state = newState; + return alertInstance.instance; + }); + alertInstance.instance.scheduleActions.mockImplementation((id: string, action: any) => { + alertInstance.actionQueue.push({ id, action }); + return alertInstance.instance; + }); + return alertInstance.instance; +}); + +function mostRecentAction(id: string) { + return alertInstances.get(id)!.actionQueue.pop(); +} + +function getState(id: string) { + return alertInstances.get(id)!.state; +} + +const baseCriterion = { + aggType: 'avg', + metric: 'test.metric.1', + timeSize: 1, + timeUnit: 'm', +}; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index bd77e5e2daf42b..5c34a058577a1f 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -5,8 +5,6 @@ */ import { mapValues } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { convertSavedObjectToSavedSourceConfiguration } from '../../sources/sources'; -import { infraSourceConfigurationSavedObjectType } from '../../sources/saved_object_mappings'; import { InfraDatabaseSearchResponse } from '../../adapters/framework/adapter_types'; import { createAfterKeyHandler } from '../../../utils/create_afterkey_handler'; import { getAllCompositeData } from '../../../utils/get_all_composite_data'; @@ -22,9 +20,9 @@ import { import { AlertServices, AlertExecutorOptions } from '../../../../../alerting/server'; import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds'; import { getDateHistogramOffset } from '../../snapshot/query_helpers'; +import { InfraBackendLibs } from '../../infra_types'; const TOTAL_BUCKETS = 5; -const DEFAULT_INDEX_PATTERN = 'metricbeat-*'; interface Aggregation { aggregatedIntervals: { @@ -76,6 +74,7 @@ const getParsedFilterQuery: ( export const getElasticsearchMetricQuery = ( { metric, aggType, timeUnit, timeSize }: MetricExpressionParams, + timefield: string, groupBy?: string, filterQuery?: string ) => { @@ -109,7 +108,7 @@ export const getElasticsearchMetricQuery = ( const baseAggs = { aggregatedIntervals: { date_histogram: { - field: '@timestamp', + field: timefield, fixed_interval: interval, offset, extended_bounds: { @@ -181,43 +180,23 @@ export const getElasticsearchMetricQuery = ( }; }; -const getIndexPattern: ( - services: AlertServices, - sourceId?: string -) => Promise = async function({ savedObjectsClient }, sourceId = 'default') { - try { - const sourceConfiguration = await savedObjectsClient.get( - infraSourceConfigurationSavedObjectType, - sourceId - ); - const { metricAlias } = convertSavedObjectToSavedSourceConfiguration( - sourceConfiguration - ).configuration; - return metricAlias || DEFAULT_INDEX_PATTERN; - } catch (e) { - if (e.output.statusCode === 404) { - return DEFAULT_INDEX_PATTERN; - } else { - throw e; - } - } -}; - const getMetric: ( services: AlertServices, params: MetricExpressionParams, index: string, + timefield: string, groupBy: string | undefined, filterQuery: string | undefined ) => Promise> = async function( - { savedObjectsClient, callCluster }, + { callCluster }, params, index, + timefield, groupBy, filterQuery ) { const { aggType } = params; - const searchBody = getElasticsearchMetricQuery(params, groupBy, filterQuery); + const searchBody = getElasticsearchMetricQuery(params, timefield, groupBy, filterQuery); try { if (groupBy) { @@ -265,7 +244,7 @@ const comparatorMap = { [Comparator.LT_OR_EQ]: (a: number, [b]: number[]) => a <= b, }; -export const createMetricThresholdExecutor = (alertUUID: string) => +export const createMetricThresholdExecutor = (libs: InfraBackendLibs, alertId: string) => async function({ services, params }: AlertExecutorOptions) { const { criteria, groupBy, filterQuery, sourceId, alertOnNoData } = params as { criteria: MetricExpressionParams[]; @@ -275,11 +254,22 @@ export const createMetricThresholdExecutor = (alertUUID: string) => alertOnNoData: boolean; }; + const source = await libs.sources.getSourceConfiguration( + services.savedObjectsClient, + sourceId || 'default' + ); + const config = source.configuration; const alertResults = await Promise.all( - criteria.map(criterion => - (async () => { - const index = await getIndexPattern(services, sourceId); - const currentValues = await getMetric(services, criterion, index, groupBy, filterQuery); + criteria.map(criterion => { + return (async () => { + const currentValues = await getMetric( + services, + criterion, + config.fields.timestamp, + config.metricAlias, + groupBy, + filterQuery + ); const { threshold, comparator } = criterion; const comparisonFunction = comparatorMap[comparator]; return mapValues(currentValues, value => ({ @@ -291,13 +281,14 @@ export const createMetricThresholdExecutor = (alertUUID: string) => isNoData: value === null, isError: value === undefined, })); - })() - ) + })(); + }) ); + // Because each alert result has the same group definitions, just grap the groups from the first one. const groups = Object.keys(alertResults[0]); for (const group of groups) { - const alertInstance = services.alertInstanceFactory(`${alertUUID}-${group}`); + const alertInstance = services.alertInstanceFactory(`${alertId}-${group}`); // AND logic; all criteria must be across the threshold const shouldAlertFire = alertResults.every(result => result[group].shouldFire); diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts index 029491c1168cfb..23611559a184f5 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts @@ -6,11 +6,11 @@ import { i18n } from '@kbn/i18n'; import uuid from 'uuid'; import { schema } from '@kbn/config-schema'; -import { PluginSetupContract } from '../../../../../alerting/server'; +import { curry } from 'lodash'; import { METRIC_EXPLORER_AGGREGATIONS } from '../../../../common/http_api/metrics_explorer'; import { createMetricThresholdExecutor, FIRED_ACTIONS } from './metric_threshold_executor'; -import { InfraBackendLibs } from '../../infra_types'; import { METRIC_THRESHOLD_ALERT_TYPE_ID, Comparator } from './types'; +import { InfraBackendLibs } from '../../infra_types'; const oneOfLiterals = (arrayOfLiterals: Readonly) => schema.string({ @@ -18,17 +18,7 @@ const oneOfLiterals = (arrayOfLiterals: Readonly) => arrayOfLiterals.includes(value) ? undefined : `must be one of ${arrayOfLiterals.join(' | ')}`, }); -export async function registerMetricThresholdAlertType( - alertingPlugin: PluginSetupContract, - libs: InfraBackendLibs -) { - if (!alertingPlugin) { - throw new Error( - 'Cannot register metric threshold alert type. Both the actions and alerting plugins need to be enabled.' - ); - } - const alertUUID = uuid.v4(); - +export function registerMetricThresholdAlertType(libs: InfraBackendLibs) { const baseCriterion = { threshold: schema.arrayOf(schema.number()), comparator: oneOfLiterals(Object.values(Comparator)), @@ -70,21 +60,24 @@ export async function registerMetricThresholdAlertType( } ); - alertingPlugin.registerType({ + return { id: METRIC_THRESHOLD_ALERT_TYPE_ID, name: 'Metric threshold', validate: { - params: schema.object({ - criteria: schema.arrayOf(schema.oneOf([countCriterion, nonCountCriterion])), - groupBy: schema.maybe(schema.string()), - filterQuery: schema.maybe(schema.string()), - sourceId: schema.string(), - alertOnNoData: schema.maybe(schema.boolean()), - }), + params: schema.object( + { + criteria: schema.arrayOf(schema.oneOf([countCriterion, nonCountCriterion])), + groupBy: schema.maybe(schema.string()), + filterQuery: schema.maybe(schema.string()), + sourceId: schema.string(), + alertOnNoData: schema.maybe(schema.boolean()), + }, + { unknowns: 'allow' } + ), }, defaultActionGroupId: FIRED_ACTIONS.id, actionGroups: [FIRED_ACTIONS], - executor: createMetricThresholdExecutor(alertUUID), + executor: curry(createMetricThresholdExecutor)(libs, uuid.v4()), actionVariables: { context: [ { name: 'group', description: groupActionVariableDescription }, @@ -92,5 +85,5 @@ export async function registerMetricThresholdAlertType( { name: 'reason', description: reasonActionVariableDescription }, ], }, - }); + }; } diff --git a/x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts b/x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts index 9760873ff74781..44d30d7281f20d 100644 --- a/x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts +++ b/x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts @@ -6,13 +6,16 @@ import { PluginSetupContract } from '../../../../alerting/server'; import { registerMetricThresholdAlertType } from './metric_threshold/register_metric_threshold_alert_type'; +import { registerMetricInventoryThresholdAlertType } from './inventory_metric_threshold/register_inventory_metric_threshold_alert_type'; import { registerLogThresholdAlertType } from './log_threshold/register_log_threshold_alert_type'; import { InfraBackendLibs } from '../infra_types'; const registerAlertTypes = (alertingPlugin: PluginSetupContract, libs: InfraBackendLibs) => { if (alertingPlugin) { - const registerFns = [registerMetricThresholdAlertType, registerLogThresholdAlertType]; + alertingPlugin.registerType(registerMetricThresholdAlertType(libs)); + alertingPlugin.registerType(registerMetricInventoryThresholdAlertType(libs)); + const registerFns = [registerLogThresholdAlertType]; registerFns.forEach(fn => { fn(alertingPlugin, libs); }); diff --git a/x-pack/plugins/infra/server/lib/compose/kibana.ts b/x-pack/plugins/infra/server/lib/compose/kibana.ts index f100726b5b92e7..d22ca2961cfa53 100644 --- a/x-pack/plugins/infra/server/lib/compose/kibana.ts +++ b/x-pack/plugins/infra/server/lib/compose/kibana.ts @@ -28,7 +28,7 @@ export function compose(core: CoreSetup, config: InfraConfig, plugins: InfraServ const sourceStatus = new InfraSourceStatus(new InfraElasticsearchSourceStatusAdapter(framework), { sources, }); - const snapshot = new InfraSnapshot({ sources, framework }); + const snapshot = new InfraSnapshot(); const logEntryCategoriesAnalysis = new LogEntryCategoriesAnalysis({ framework }); const logEntryRateAnalysis = new LogEntryRateAnalysis({ framework }); diff --git a/x-pack/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts b/x-pack/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts index cf2b1e59b2a225..c75ee6d6440442 100644 --- a/x-pack/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts +++ b/x-pack/plugins/infra/server/lib/snapshot/create_timerange_with_interval.ts @@ -5,26 +5,23 @@ */ import { uniq } from 'lodash'; -import { RequestHandlerContext } from 'kibana/server'; import { InfraSnapshotRequestOptions } from './types'; import { getMetricsAggregations } from './query_helpers'; import { calculateMetricInterval } from '../../utils/calculate_metric_interval'; import { SnapshotModel, SnapshotModelMetricAggRT } from '../../../common/inventory_models/types'; -import { KibanaFramework } from '../adapters/framework/kibana_framework_adapter'; import { getDatasetForField } from '../../routes/metrics_explorer/lib/get_dataset_for_field'; import { InfraTimerangeInput } from '../../../common/http_api/snapshot_api'; +import { ESSearchClient } from '.'; export const createTimeRangeWithInterval = async ( - framework: KibanaFramework, - requestContext: RequestHandlerContext, + client: ESSearchClient, options: InfraSnapshotRequestOptions ): Promise => { const aggregations = getMetricsAggregations(options); - const modules = await aggregationsToModules(framework, requestContext, aggregations, options); + const modules = await aggregationsToModules(client, aggregations, options); const interval = Math.max( (await calculateMetricInterval( - framework, - requestContext, + client, { indexPattern: options.sourceConfiguration.metricAlias, timestampField: options.sourceConfiguration.fields.timestamp, @@ -43,8 +40,7 @@ export const createTimeRangeWithInterval = async ( }; const aggregationsToModules = async ( - framework: KibanaFramework, - requestContext: RequestHandlerContext, + client: ESSearchClient, aggregations: SnapshotModel, options: InfraSnapshotRequestOptions ): Promise => { @@ -59,12 +55,7 @@ const aggregationsToModules = async ( const fields = await Promise.all( uniqueFields.map( async field => - await getDatasetForField( - framework, - requestContext, - field as string, - options.sourceConfiguration.metricAlias - ) + await getDatasetForField(client, field as string, options.sourceConfiguration.metricAlias) ) ); return fields.filter(f => f) as string[]; diff --git a/x-pack/plugins/infra/server/lib/snapshot/snapshot.ts b/x-pack/plugins/infra/server/lib/snapshot/snapshot.ts index 07abfa5fd474a9..4057ed246ccaf0 100644 --- a/x-pack/plugins/infra/server/lib/snapshot/snapshot.ts +++ b/x-pack/plugins/infra/server/lib/snapshot/snapshot.ts @@ -3,11 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - -import { RequestHandlerContext } from 'src/core/server'; -import { InfraDatabaseSearchResponse } from '../adapters/framework'; -import { KibanaFramework } from '../adapters/framework/kibana_framework_adapter'; -import { InfraSources } from '../sources'; +import { InfraDatabaseSearchResponse, CallWithRequestParams } from '../adapters/framework'; import { JsonObject } from '../../../common/typed_json'; import { SNAPSHOT_COMPOSITE_REQUEST_SIZE } from './constants'; @@ -31,36 +27,26 @@ import { InfraSnapshotRequestOptions } from './types'; import { createTimeRangeWithInterval } from './create_timerange_with_interval'; import { SnapshotNode } from '../../../common/http_api/snapshot_api'; +export type ESSearchClient = ( + options: CallWithRequestParams +) => Promise>; export class InfraSnapshot { - constructor(private readonly libs: { sources: InfraSources; framework: KibanaFramework }) {} - public async getNodes( - requestContext: RequestHandlerContext, + client: ESSearchClient, options: InfraSnapshotRequestOptions ): Promise<{ nodes: SnapshotNode[]; interval: string }> { // Both requestGroupedNodes and requestNodeMetrics may send several requests to elasticsearch // in order to page through the results of their respective composite aggregations. // Both chains of requests are supposed to run in parallel, and their results be merged // when they have both been completed. - const timeRangeWithIntervalApplied = await createTimeRangeWithInterval( - this.libs.framework, - requestContext, - options - ); + const timeRangeWithIntervalApplied = await createTimeRangeWithInterval(client, options); const optionsWithTimerange = { ...options, timerange: timeRangeWithIntervalApplied }; - const groupedNodesPromise = requestGroupedNodes( - requestContext, - optionsWithTimerange, - this.libs.framework - ); - const nodeMetricsPromise = requestNodeMetrics( - requestContext, - optionsWithTimerange, - this.libs.framework - ); + const groupedNodesPromise = requestGroupedNodes(client, optionsWithTimerange); + const nodeMetricsPromise = requestNodeMetrics(client, optionsWithTimerange); const groupedNodeBuckets = await groupedNodesPromise; const nodeMetricBuckets = await nodeMetricsPromise; + return { nodes: mergeNodeBuckets(groupedNodeBuckets, nodeMetricBuckets, options), interval: timeRangeWithIntervalApplied.interval, @@ -77,15 +63,12 @@ const handleAfterKey = createAfterKeyHandler( input => input?.aggregations?.nodes?.after_key ); -const callClusterFactory = (framework: KibanaFramework, requestContext: RequestHandlerContext) => ( - opts: any -) => - framework.callWithRequest<{}, InfraSnapshotAggregationResponse>(requestContext, 'search', opts); +const callClusterFactory = (search: ESSearchClient) => (opts: any) => + search<{}, InfraSnapshotAggregationResponse>(opts); const requestGroupedNodes = async ( - requestContext: RequestHandlerContext, - options: InfraSnapshotRequestOptions, - framework: KibanaFramework + client: ESSearchClient, + options: InfraSnapshotRequestOptions ): Promise => { const inventoryModel = findInventoryModel(options.nodeType); const query = { @@ -124,13 +107,12 @@ const requestGroupedNodes = async ( return await getAllCompositeData< InfraSnapshotAggregationResponse, InfraSnapshotNodeGroupByBucket - >(callClusterFactory(framework, requestContext), query, bucketSelector, handleAfterKey); + >(callClusterFactory(client), query, bucketSelector, handleAfterKey); }; const requestNodeMetrics = async ( - requestContext: RequestHandlerContext, - options: InfraSnapshotRequestOptions, - framework: KibanaFramework + client: ESSearchClient, + options: InfraSnapshotRequestOptions ): Promise => { const index = options.metric.type === 'logRate' @@ -175,7 +157,7 @@ const requestNodeMetrics = async ( return await getAllCompositeData< InfraSnapshotAggregationResponse, InfraSnapshotNodeMetricsBucket - >(callClusterFactory(framework, requestContext), query, bucketSelector, handleAfterKey); + >(callClusterFactory(client), query, bucketSelector, handleAfterKey); }; // buckets can be InfraSnapshotNodeGroupByBucket[] or InfraSnapshotNodeMetricsBucket[] diff --git a/x-pack/plugins/infra/server/lib/sources/sources.ts b/x-pack/plugins/infra/server/lib/sources/sources.ts index 0368c7bfd6db81..71682c9e798a6f 100644 --- a/x-pack/plugins/infra/server/lib/sources/sources.ts +++ b/x-pack/plugins/infra/server/lib/sources/sources.ts @@ -9,7 +9,7 @@ import { failure } from 'io-ts/lib/PathReporter'; import { identity, constant } from 'fp-ts/lib/function'; import { pipe } from 'fp-ts/lib/pipeable'; import { map, fold } from 'fp-ts/lib/Either'; -import { RequestHandlerContext, SavedObjectsClientContract } from 'src/core/server'; +import { SavedObjectsClientContract } from 'src/core/server'; import { defaultSourceConfiguration } from './defaults'; import { NotFoundError } from './errors'; import { infraSourceConfigurationSavedObjectType } from './saved_object_mappings'; @@ -41,7 +41,6 @@ export class InfraSources { sourceId: string ): Promise { const staticDefaultSourceConfiguration = await this.getStaticDefaultSourceConfiguration(); - const savedSourceConfiguration = await this.getInternalSourceConfiguration(sourceId) .then(internalSourceConfiguration => ({ id: sourceId, @@ -79,10 +78,12 @@ export class InfraSources { return savedSourceConfiguration; } - public async getAllSourceConfigurations(requestContext: RequestHandlerContext) { + public async getAllSourceConfigurations(savedObjectsClient: SavedObjectsClientContract) { const staticDefaultSourceConfiguration = await this.getStaticDefaultSourceConfiguration(); - const savedSourceConfigurations = await this.getAllSavedSourceConfigurations(requestContext); + const savedSourceConfigurations = await this.getAllSavedSourceConfigurations( + savedObjectsClient + ); return savedSourceConfigurations.map(savedSourceConfiguration => ({ ...savedSourceConfiguration, @@ -94,7 +95,7 @@ export class InfraSources { } public async createSourceConfiguration( - requestContext: RequestHandlerContext, + savedObjectsClient: SavedObjectsClientContract, sourceId: string, source: InfraSavedSourceConfiguration ) { @@ -106,7 +107,7 @@ export class InfraSources { ); const createdSourceConfiguration = convertSavedObjectToSavedSourceConfiguration( - await requestContext.core.savedObjects.client.create( + await savedObjectsClient.create( infraSourceConfigurationSavedObjectType, pickSavedSourceConfiguration(newSourceConfiguration) as any, { id: sourceId } @@ -122,22 +123,22 @@ export class InfraSources { }; } - public async deleteSourceConfiguration(requestContext: RequestHandlerContext, sourceId: string) { - await requestContext.core.savedObjects.client.delete( - infraSourceConfigurationSavedObjectType, - sourceId - ); + public async deleteSourceConfiguration( + savedObjectsClient: SavedObjectsClientContract, + sourceId: string + ) { + await savedObjectsClient.delete(infraSourceConfigurationSavedObjectType, sourceId); } public async updateSourceConfiguration( - requestContext: RequestHandlerContext, + savedObjectsClient: SavedObjectsClientContract, sourceId: string, sourceProperties: InfraSavedSourceConfiguration ) { const staticDefaultSourceConfiguration = await this.getStaticDefaultSourceConfiguration(); const { configuration, version } = await this.getSourceConfiguration( - requestContext.core.savedObjects.client, + savedObjectsClient, sourceId ); @@ -147,7 +148,7 @@ export class InfraSources { ); const updatedSourceConfiguration = convertSavedObjectToSavedSourceConfiguration( - await requestContext.core.savedObjects.client.update( + await savedObjectsClient.update( infraSourceConfigurationSavedObjectType, sourceId, pickSavedSourceConfiguration(updatedSourceConfigurationAttributes) as any, @@ -213,8 +214,8 @@ export class InfraSources { return convertSavedObjectToSavedSourceConfiguration(savedObject); } - private async getAllSavedSourceConfigurations(requestContext: RequestHandlerContext) { - const savedObjects = await requestContext.core.savedObjects.client.find({ + private async getAllSavedSourceConfigurations(savedObjectsClient: SavedObjectsClientContract) { + const savedObjects = await savedObjectsClient.find({ type: infraSourceConfigurationSavedObjectType, }); diff --git a/x-pack/plugins/infra/server/plugin.ts b/x-pack/plugins/infra/server/plugin.ts index d4dfa60ac67a0d..db34033c1d4f8d 100644 --- a/x-pack/plugins/infra/server/plugin.ts +++ b/x-pack/plugins/infra/server/plugin.ts @@ -109,7 +109,7 @@ export class InfraServerPlugin { sources, } ); - const snapshot = new InfraSnapshot({ sources, framework }); + const snapshot = new InfraSnapshot(); const logEntryCategoriesAnalysis = new LogEntryCategoriesAnalysis({ framework }); const logEntryRateAnalysis = new LogEntryRateAnalysis({ framework }); diff --git a/x-pack/plugins/infra/server/routes/log_sources/configuration.ts b/x-pack/plugins/infra/server/routes/log_sources/configuration.ts index 0ce594675773c2..46929954431f51 100644 --- a/x-pack/plugins/infra/server/routes/log_sources/configuration.ts +++ b/x-pack/plugins/infra/server/routes/log_sources/configuration.ts @@ -82,12 +82,12 @@ export const initLogSourceConfigurationRoutes = ({ framework, sources }: InfraBa const sourceConfigurationExists = sourceConfiguration.origin === 'stored'; const patchedSourceConfiguration = await (sourceConfigurationExists ? sources.updateSourceConfiguration( - requestContext, + requestContext.core.savedObjects.client, sourceId, patchedSourceConfigurationProperties ) : sources.createSourceConfiguration( - requestContext, + requestContext.core.savedObjects.client, sourceId, patchedSourceConfigurationProperties )); diff --git a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts index 66f0ca8fc706a3..94e91d32b14bb5 100644 --- a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts +++ b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/get_dataset_for_field.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'kibana/server'; -import { KibanaFramework } from '../../../lib/adapters/framework/kibana_framework_adapter'; +import { ESSearchClient } from '../../../lib/snapshot'; interface EventDatasetHit { _source: { @@ -16,8 +15,7 @@ interface EventDatasetHit { } export const getDatasetForField = async ( - framework: KibanaFramework, - requestContext: RequestHandlerContext, + client: ESSearchClient, field: string, indexPattern: string ) => { @@ -33,11 +31,8 @@ export const getDatasetForField = async ( }, }; - const response = await framework.callWithRequest( - requestContext, - 'search', - params - ); + const response = await client(params); + if (response.hits.total.value === 0) { return null; } diff --git a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts index e735a26d96a91d..a709cbdeeb680f 100644 --- a/x-pack/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts +++ b/x-pack/plugins/infra/server/routes/metrics_explorer/lib/populate_series_with_tsvb_data.ts @@ -17,6 +17,10 @@ import { createMetricModel } from './create_metrics_model'; import { JsonObject } from '../../../../common/typed_json'; import { calculateMetricInterval } from '../../../utils/calculate_metric_interval'; import { getDatasetForField } from './get_dataset_for_field'; +import { + CallWithRequestParams, + InfraDatabaseSearchResponse, +} from '../../../lib/adapters/framework'; export const populateSeriesWithTSVBData = ( request: KibanaRequest, @@ -52,17 +56,21 @@ export const populateSeriesWithTSVBData = ( } const timerange = { min: options.timerange.from, max: options.timerange.to }; + const client = ( + opts: CallWithRequestParams + ): Promise> => + framework.callWithRequest(requestContext, 'search', opts); + // Create the TSVB model based on the request options const model = createMetricModel(options); const modules = await Promise.all( uniq(options.metrics.filter(m => m.field)).map( - async m => - await getDatasetForField(framework, requestContext, m.field as string, options.indexPattern) + async m => await getDatasetForField(client, m.field as string, options.indexPattern) ) ); + const calculatedInterval = await calculateMetricInterval( - framework, - requestContext, + client, { indexPattern: options.indexPattern, timestampField: options.timerange.field, diff --git a/x-pack/plugins/infra/server/routes/snapshot/index.ts b/x-pack/plugins/infra/server/routes/snapshot/index.ts index d1dc03893a0d9b..2d951d426b03a0 100644 --- a/x-pack/plugins/infra/server/routes/snapshot/index.ts +++ b/x-pack/plugins/infra/server/routes/snapshot/index.ts @@ -13,6 +13,7 @@ import { UsageCollector } from '../../usage/usage_collector'; import { parseFilterQuery } from '../../utils/serialized_query'; import { SnapshotRequestRT, SnapshotNodeResponseRT } from '../../../common/http_api/snapshot_api'; import { throwErrors } from '../../../common/runtime_types'; +import { CallWithRequestParams, InfraDatabaseSearchResponse } from '../../lib/adapters/framework'; const escapeHatch = schema.object({}, { unknowns: 'allow' }); @@ -57,7 +58,13 @@ export const initSnapshotRoute = (libs: InfraBackendLibs) => { metric, timerange, }; - const nodesWithInterval = await libs.snapshot.getNodes(requestContext, options); + + const searchES = ( + opts: CallWithRequestParams + ): Promise> => + framework.callWithRequest(requestContext, 'search', opts); + + const nodesWithInterval = await libs.snapshot.getNodes(searchES, options); return response.ok({ body: SnapshotNodeResponseRT.encode(nodesWithInterval), }); diff --git a/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts b/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts index 7cbbdc0f2145be..43e109b009f48e 100644 --- a/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts +++ b/x-pack/plugins/infra/server/utils/calculate_metric_interval.ts @@ -4,10 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { RequestHandlerContext } from 'src/core/server'; +// import { RequestHandlerContext } from 'src/core/server'; import { findInventoryModel } from '../../common/inventory_models'; -import { KibanaFramework } from '../lib/adapters/framework/kibana_framework_adapter'; +// import { KibanaFramework } from '../lib/adapters/framework/kibana_framework_adapter'; import { InventoryItemType } from '../../common/inventory_models/types'; +import { ESSearchClient } from '../lib/snapshot'; interface Options { indexPattern: string; @@ -23,8 +24,7 @@ interface Options { * This is useful for visualizing metric modules like s3 that only send metrics once per day. */ export const calculateMetricInterval = async ( - framework: KibanaFramework, - requestContext: RequestHandlerContext, + client: ESSearchClient, options: Options, modules?: string[], nodeType?: InventoryItemType // TODO: check that this type still makes sense @@ -73,11 +73,7 @@ export const calculateMetricInterval = async ( }, }; - const resp = await framework.callWithRequest<{}, PeriodAggregationData>( - requestContext, - 'search', - query - ); + const resp = await client<{}, PeriodAggregationData>(query); // if ES doesn't return an aggregations key, something went seriously wrong. if (!resp.aggregations) { diff --git a/x-pack/test/api_integration/apis/infra/metrics_alerting.ts b/x-pack/test/api_integration/apis/infra/metrics_alerting.ts index 19879f5761ab2c..5c43e8938a8c12 100644 --- a/x-pack/test/api_integration/apis/infra/metrics_alerting.ts +++ b/x-pack/test/api_integration/apis/infra/metrics_alerting.ts @@ -32,7 +32,7 @@ export default function({ getService }: FtrProviderContext) { describe('querying the entire infrastructure', () => { for (const aggType of aggs) { it(`should work with the ${aggType} aggregator`, async () => { - const searchBody = getElasticsearchMetricQuery(getSearchParams(aggType)); + const searchBody = getElasticsearchMetricQuery(getSearchParams(aggType), '@timestamp'); const result = await client.search({ index, body: searchBody, @@ -45,6 +45,7 @@ export default function({ getService }: FtrProviderContext) { it('should work with a filterQuery', async () => { const searchBody = getElasticsearchMetricQuery( getSearchParams('avg'), + '@timestamp', undefined, '{"bool":{"should":[{"match_phrase":{"agent.hostname":"foo"}}],"minimum_should_match":1}}' ); @@ -59,6 +60,7 @@ export default function({ getService }: FtrProviderContext) { it('should work with a filterQuery in KQL format', async () => { const searchBody = getElasticsearchMetricQuery( getSearchParams('avg'), + '@timestamp', undefined, '"agent.hostname":"foo"' ); @@ -74,7 +76,11 @@ export default function({ getService }: FtrProviderContext) { describe('querying with a groupBy parameter', () => { for (const aggType of aggs) { it(`should work with the ${aggType} aggregator`, async () => { - const searchBody = getElasticsearchMetricQuery(getSearchParams(aggType), 'agent.id'); + const searchBody = getElasticsearchMetricQuery( + getSearchParams(aggType), + '@timestamp', + 'agent.id' + ); const result = await client.search({ index, body: searchBody, @@ -87,6 +93,7 @@ export default function({ getService }: FtrProviderContext) { it('should work with a filterQuery', async () => { const searchBody = getElasticsearchMetricQuery( getSearchParams('avg'), + '@timestamp', 'agent.id', '{"bool":{"should":[{"match_phrase":{"agent.hostname":"foo"}}],"minimum_should_match":1}}' ); From ffe5166023a8e4d82d5b4fead3718d69a0dc62bc Mon Sep 17 00:00:00 2001 From: Sandra Gonzales Date: Thu, 30 Apr 2020 17:54:37 -0400 Subject: [PATCH 36/47] [EPM] restrict package install endpoint from installing/updating to old packages (#64932) * restrict installing or updating to out-of-date package * throw bad requests in remove handler * remove accidental commit * remove space --- .../ingest_manager/server/routes/epm/handlers.ts | 12 ++++++++++++ .../server/services/epm/packages/install.ts | 12 ++++++++++-- .../server/services/epm/packages/remove.ts | 5 +++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts index ad16e1dde456bc..fd3a9c520b90a4 100644 --- a/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/epm/handlers.ts @@ -136,6 +136,12 @@ export const installPackageHandler: RequestHandler Date: Thu, 30 Apr 2020 18:46:07 -0400 Subject: [PATCH 37/47] [Discover] Show doc viewer action buttons on focus (#64912) --- .../public/components/doc_viewer/_doc_viewer.scss | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/plugins/discover/public/components/doc_viewer/_doc_viewer.scss b/src/plugins/discover/public/components/doc_viewer/_doc_viewer.scss index 25aa5309767199..ec2beca15a5468 100644 --- a/src/plugins/discover/public/components/doc_viewer/_doc_viewer.scss +++ b/src/plugins/discover/public/components/doc_viewer/_doc_viewer.scss @@ -43,6 +43,14 @@ } .kbnDocViewer__buttons { width: 60px; + + // Show all icons if one is focused, + // IE doesn't support, but the fallback is just the focused button becomes visible + &:focus-within { + .kbnDocViewer__actionButton { + opacity: 1; + } + } } .kbnDocViewer__field { @@ -51,7 +59,12 @@ .kbnDocViewer__actionButton { opacity: 0; + + &:focus { + opacity: 1; + } } + .kbnDocViewer__warning { margin-right: $euiSizeS; } From 7e5be981d8ac4deacea9cd03e21e168f279413be Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Thu, 30 Apr 2020 18:07:23 -0700 Subject: [PATCH 38/47] Fixed `AddAlert` flyout does not immediately update state to reflect new props (#64927) * Fixed `AddAlert` flyout does not immediately update state to reflect new props * fixed add form * Fixed type check --- .../builtin_action_types/es_index.tsx | 4 +- .../sections/alert_form/alert_add.tsx | 9 +++- .../sections/alert_form/alert_edit.tsx | 5 ++ .../apps/triggers_actions_ui/details.ts | 47 +++++++++++++++++++ 4 files changed, 63 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx index 55a219ca94aead..861d6ad7284c2b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx @@ -86,7 +86,9 @@ const IndexActionConnectorFields: React.FunctionComponent([]); - const [timeFieldOptions, setTimeFieldOptions] = useState([firstFieldOption]); + const [timeFieldOptions, setTimeFieldOptions] = useState>([ + firstFieldOption, + ]); const [isIndiciesLoading, setIsIndiciesLoading] = useState(false); useEffect(() => { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx index 0620ced6365a96..651f2cdba34af7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback, useReducer, useState } from 'react'; +import React, { useCallback, useReducer, useState, useEffect } from 'react'; import { isObject } from 'lodash'; import { FormattedMessage } from '@kbn/i18n/react'; import { @@ -60,6 +60,9 @@ export const AlertAdd = ({ const setAlert = (value: any) => { dispatch({ command: { type: 'setAlert' }, payload: { key: 'alert', value } }); }; + const setAlertProperty = (key: string, value: any) => { + dispatch({ command: { type: 'setProperty' }, payload: { key, value } }); + }; const { reloadAlerts, @@ -70,6 +73,10 @@ export const AlertAdd = ({ docLinks, } = useAlertsContext(); + useEffect(() => { + setAlertProperty('alertTypeId', alertTypeId); + }, [alertTypeId]); + const closeFlyout = useCallback(() => { setAddFlyoutVisibility(false); setAlert(initialAlert); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index 4255eca83be473..c9cf59d87414fe 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -43,6 +43,9 @@ export const AlertEdit = ({ const [{ alert }, dispatch] = useReducer(alertReducer, { alert: initialAlert }); const [isSaving, setIsSaving] = useState(false); const [hasActionsDisabled, setHasActionsDisabled] = useState(false); + const setAlert = (key: string, value: any) => { + dispatch({ command: { type: 'setAlert' }, payload: { key, value } }); + }; const { reloadAlerts, @@ -55,6 +58,8 @@ export const AlertEdit = ({ const closeFlyout = useCallback(() => { setEditFlyoutVisibility(false); + setAlert('alert', initialAlert); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [setEditFlyoutVisibility]); if (!editFlyoutVisible) { diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index d0ce18bbc1c54c..6ff065c1f4ab24 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -197,6 +197,53 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const headingText = await pageObjects.alertDetailsUI.getHeadingText(); expect(headingText).to.be(updatedAlertName); }); + + it('should reset alert when canceling an edit', async () => { + await pageObjects.common.navigateToApp('triggersActions'); + const params = { + aggType: 'count', + termSize: 5, + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + groupBy: 'all', + threshold: [1000, 5000], + index: ['.kibana_1'], + timeField: 'alert', + }; + const alert = await alerting.alerts.createAlertWithActions( + testRunUuid, + '.index-threshold', + params + ); + // refresh to see alert + await browser.refresh(); + + await pageObjects.header.waitUntilLoadingHasFinished(); + + // Verify content + await testSubjects.existOrFail('alertsList'); + + // click on first alert + await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name); + + const editButton = await testSubjects.find('openEditAlertFlyoutButton'); + await editButton.click(); + + const updatedAlertName = `Changed Alert Name ${uuid.v4()}`; + await testSubjects.setValue('alertNameInput', updatedAlertName, { + clearWithKeyboard: true, + }); + + await testSubjects.click('cancelSaveEditedAlertButton'); + await find.waitForDeletedByCssSelector('[data-test-subj="cancelSaveEditedAlertButton"]'); + + await editButton.click(); + + const nameInputAfterCancel = await testSubjects.find('alertNameInput'); + const textAfterCancel = await nameInputAfterCancel.getAttribute('value'); + expect(textAfterCancel).to.eql(alert.name); + }); }); describe('View In App', function() { From 7f8f765541cf31f3efb59d76ff587069a3dc95d3 Mon Sep 17 00:00:00 2001 From: Luke Elmers Date: Thu, 30 Apr 2020 19:40:01 -0600 Subject: [PATCH 39/47] [data.search.aggs] Remove legacy aggs APIs. (#64719) --- ...ugin-plugins-data-public.agggrouplabels.md | 15 +++ ...plugin-plugins-data-public.agggroupname.md | 11 ++ ...ta-public.aggtypefieldfilters.addfilter.md | 24 ---- ...-data-public.aggtypefieldfilters.filter.md | 25 ---- ...plugins-data-public.aggtypefieldfilters.md | 21 ---- ...ns-data-public.aggtypefilters.addfilter.md | 24 ---- ...ugins-data-public.aggtypefilters.filter.md | 27 ----- ...ugin-plugins-data-public.aggtypefilters.md | 21 ---- ...n-plugins-data-public.daterangekey.from.md | 11 -- ...plugin-plugins-data-public.daterangekey.md | 19 --- ...gin-plugins-data-public.daterangekey.to.md | 11 -- ...ugin-plugins-data-public.iagggroupnames.md | 11 -- ...a-plugin-plugins-data-public.iprangekey.md | 18 --- .../kibana-plugin-plugins-data-public.md | 8 +- ...ublic.optionedparameditorprops.aggparam.md | 13 -- ...ns-data-public.optionedparameditorprops.md | 18 --- ...ibana-plugin-plugins-data-public.search.md | 5 - .../new_platform/new_platform.karma_mock.js | 10 -- src/plugins/data/public/index.ts | 30 ++--- src/plugins/data/public/public.api.md | 112 +++++------------- .../data/public/search/aggs/agg_groups.ts | 14 ++- .../aggs/filter/agg_type_filters.test.ts | 62 ---------- .../search/aggs/filter/agg_type_filters.ts | 74 ------------ .../data/public/search/aggs/filter/index.ts | 21 ---- src/plugins/data/public/search/aggs/index.ts | 1 - .../public/search/aggs/param_types/field.ts | 2 +- .../param_types/filter/field_filters.test.ts | 61 ---------- .../aggs/param_types/filter/field_filters.ts | 60 ---------- .../public/search/aggs/param_types/index.ts | 1 - .../search/aggs/param_types/optioned.ts | 6 - src/plugins/data/public/search/aggs/types.ts | 20 +--- .../data/public/search/aggs/utils/index.ts | 1 + .../{filter => utils}/prop_filter.test.ts | 0 .../aggs/{filter => utils}/prop_filter.ts | 4 +- src/plugins/data/public/search/mocks.ts | 8 -- .../data/public/search/search_service.ts | 14 --- src/plugins/data/public/search/types.ts | 4 +- .../saved_objects_table.test.tsx.snap | 9 -- .../agg_filters/agg_type_field_filters.ts | 49 ++++++++ .../public/agg_filters/agg_type_filters.ts | 75 ++++++++++++ .../public/agg_filters}/index.ts | 3 +- .../public/components/agg_common_props.ts | 4 +- .../public/components/agg_group.tsx | 4 +- .../public/components/agg_param_props.ts | 13 +- .../public/components/agg_params.tsx | 16 +-- .../components/agg_params_helper.test.ts | 34 +----- .../public/components/agg_params_helper.ts | 24 ++-- .../public/components/controls/order.tsx | 4 +- .../components/controls/top_aggregate.tsx | 3 +- .../public/default_editor.tsx | 1 - .../vis_default_editor/public/schemas.ts | 4 +- .../public/vis_type_agg_filter.ts | 33 ------ x-pack/plugins/rollup/kibana.json | 3 +- x-pack/plugins/rollup/public/plugin.ts | 18 +-- .../public/visualize/agg_type_field_filter.js | 22 ---- .../public/visualize/agg_type_filter.js | 23 ---- 56 files changed, 246 insertions(+), 883 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.agggrouplabels.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.agggroupname.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefieldfilters.addfilter.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefieldfilters.filter.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefieldfilters.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefilters.addfilter.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefilters.filter.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefilters.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.daterangekey.from.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.daterangekey.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.daterangekey.to.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iagggroupnames.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iprangekey.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.optionedparameditorprops.aggparam.md delete mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.optionedparameditorprops.md delete mode 100644 src/plugins/data/public/search/aggs/filter/agg_type_filters.test.ts delete mode 100644 src/plugins/data/public/search/aggs/filter/agg_type_filters.ts delete mode 100644 src/plugins/data/public/search/aggs/filter/index.ts delete mode 100644 src/plugins/data/public/search/aggs/param_types/filter/field_filters.test.ts delete mode 100644 src/plugins/data/public/search/aggs/param_types/filter/field_filters.ts rename src/plugins/data/public/search/aggs/{filter => utils}/prop_filter.test.ts (100%) rename src/plugins/data/public/search/aggs/{filter => utils}/prop_filter.ts (97%) create mode 100644 src/plugins/vis_default_editor/public/agg_filters/agg_type_field_filters.ts create mode 100644 src/plugins/vis_default_editor/public/agg_filters/agg_type_filters.ts rename src/plugins/{data/public/search/aggs/param_types/filter => vis_default_editor/public/agg_filters}/index.ts (91%) delete mode 100644 src/plugins/vis_default_editor/public/vis_type_agg_filter.ts delete mode 100644 x-pack/plugins/rollup/public/visualize/agg_type_field_filter.js delete mode 100644 x-pack/plugins/rollup/public/visualize/agg_type_filter.js diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.agggrouplabels.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.agggrouplabels.md new file mode 100644 index 00000000000000..6684ba8546f858 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.agggrouplabels.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggGroupLabels](./kibana-plugin-plugins-data-public.agggrouplabels.md) + +## AggGroupLabels variable + +Signature: + +```typescript +AggGroupLabels: { + [AggGroupNames.Buckets]: string; + [AggGroupNames.Metrics]: string; + [AggGroupNames.None]: string; +} +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.agggroupname.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.agggroupname.md new file mode 100644 index 00000000000000..d4476398680a8e --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.agggroupname.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggGroupName](./kibana-plugin-plugins-data-public.agggroupname.md) + +## AggGroupName type + +Signature: + +```typescript +export declare type AggGroupName = $Values; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefieldfilters.addfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefieldfilters.addfilter.md deleted file mode 100644 index c9d6772a13b8d5..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefieldfilters.addfilter.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggTypeFieldFilters](./kibana-plugin-plugins-data-public.aggtypefieldfilters.md) > [addFilter](./kibana-plugin-plugins-data-public.aggtypefieldfilters.addfilter.md) - -## AggTypeFieldFilters.addFilter() method - -Register a new with this registry. This will be used by the . - -Signature: - -```typescript -addFilter(filter: AggTypeFieldFilter): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| filter | AggTypeFieldFilter | | - -Returns: - -`void` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefieldfilters.filter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefieldfilters.filter.md deleted file mode 100644 index 038c339bf67745..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefieldfilters.filter.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggTypeFieldFilters](./kibana-plugin-plugins-data-public.aggtypefieldfilters.md) > [filter](./kibana-plugin-plugins-data-public.aggtypefieldfilters.filter.md) - -## AggTypeFieldFilters.filter() method - -Returns the filtered by all registered filters. - -Signature: - -```typescript -filter(fields: IndexPatternField[], aggConfig: IAggConfig): IndexPatternField[]; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| fields | IndexPatternField[] | | -| aggConfig | IAggConfig | | - -Returns: - -`IndexPatternField[]` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefieldfilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefieldfilters.md deleted file mode 100644 index c0b386efbf9c71..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefieldfilters.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggTypeFieldFilters](./kibana-plugin-plugins-data-public.aggtypefieldfilters.md) - -## AggTypeFieldFilters class - -A registry to store which are used to filter down available fields for a specific visualization and . - -Signature: - -```typescript -declare class AggTypeFieldFilters -``` - -## Methods - -| Method | Modifiers | Description | -| --- | --- | --- | -| [addFilter(filter)](./kibana-plugin-plugins-data-public.aggtypefieldfilters.addfilter.md) | | Register a new with this registry. This will be used by the . | -| [filter(fields, aggConfig)](./kibana-plugin-plugins-data-public.aggtypefieldfilters.filter.md) | | Returns the filtered by all registered filters. | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefilters.addfilter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefilters.addfilter.md deleted file mode 100644 index 9df003377c4a10..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefilters.addfilter.md +++ /dev/null @@ -1,24 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggTypeFilters](./kibana-plugin-plugins-data-public.aggtypefilters.md) > [addFilter](./kibana-plugin-plugins-data-public.aggtypefilters.addfilter.md) - -## AggTypeFilters.addFilter() method - -Register a new with this registry. - -Signature: - -```typescript -addFilter(filter: AggTypeFilter): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| filter | AggTypeFilter | | - -Returns: - -`void` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefilters.filter.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefilters.filter.md deleted file mode 100644 index 81e6e9b95d6559..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefilters.filter.md +++ /dev/null @@ -1,27 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggTypeFilters](./kibana-plugin-plugins-data-public.aggtypefilters.md) > [filter](./kibana-plugin-plugins-data-public.aggtypefilters.filter.md) - -## AggTypeFilters.filter() method - -Returns the filtered by all registered filters. - -Signature: - -```typescript -filter(aggTypes: IAggType[], indexPattern: IndexPattern, aggConfig: IAggConfig, aggFilter: string[]): IAggType[]; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| aggTypes | IAggType[] | | -| indexPattern | IndexPattern | | -| aggConfig | IAggConfig | | -| aggFilter | string[] | | - -Returns: - -`IAggType[]` - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefilters.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefilters.md deleted file mode 100644 index c5e24bc0a78a06..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.aggtypefilters.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [AggTypeFilters](./kibana-plugin-plugins-data-public.aggtypefilters.md) - -## AggTypeFilters class - -A registry to store which are used to filter down available aggregations for a specific visualization and . - -Signature: - -```typescript -declare class AggTypeFilters -``` - -## Methods - -| Method | Modifiers | Description | -| --- | --- | --- | -| [addFilter(filter)](./kibana-plugin-plugins-data-public.aggtypefilters.addfilter.md) | | Register a new with this registry. | -| [filter(aggTypes, indexPattern, aggConfig, aggFilter)](./kibana-plugin-plugins-data-public.aggtypefilters.filter.md) | | Returns the filtered by all registered filters. | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.daterangekey.from.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.daterangekey.from.md deleted file mode 100644 index 245269af366bc0..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.daterangekey.from.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DateRangeKey](./kibana-plugin-plugins-data-public.daterangekey.md) > [from](./kibana-plugin-plugins-data-public.daterangekey.from.md) - -## DateRangeKey.from property - -Signature: - -```typescript -from: number; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.daterangekey.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.daterangekey.md deleted file mode 100644 index 540d429dced48e..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.daterangekey.md +++ /dev/null @@ -1,19 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DateRangeKey](./kibana-plugin-plugins-data-public.daterangekey.md) - -## DateRangeKey interface - -Signature: - -```typescript -export interface DateRangeKey -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [from](./kibana-plugin-plugins-data-public.daterangekey.from.md) | number | | -| [to](./kibana-plugin-plugins-data-public.daterangekey.to.md) | number | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.daterangekey.to.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.daterangekey.to.md deleted file mode 100644 index 024a6c2105427d..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.daterangekey.to.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [DateRangeKey](./kibana-plugin-plugins-data-public.daterangekey.md) > [to](./kibana-plugin-plugins-data-public.daterangekey.to.md) - -## DateRangeKey.to property - -Signature: - -```typescript -to: number; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iagggroupnames.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iagggroupnames.md deleted file mode 100644 index 07310a4219359a..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iagggroupnames.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IAggGroupNames](./kibana-plugin-plugins-data-public.iagggroupnames.md) - -## IAggGroupNames type - -Signature: - -```typescript -export declare type IAggGroupNames = $Values; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iprangekey.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iprangekey.md deleted file mode 100644 index 96903a5df98441..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.iprangekey.md +++ /dev/null @@ -1,18 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IpRangeKey](./kibana-plugin-plugins-data-public.iprangekey.md) - -## IpRangeKey type - -Signature: - -```typescript -export declare type IpRangeKey = { - type: 'mask'; - mask: string; -} | { - type: 'range'; - from: string; - to: string; -}; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md index e1df493143b736..13e38ba5e6e5dc 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md @@ -9,8 +9,6 @@ | Class | Description | | --- | --- | | [AggParamType](./kibana-plugin-plugins-data-public.aggparamtype.md) | | -| [AggTypeFieldFilters](./kibana-plugin-plugins-data-public.aggtypefieldfilters.md) | A registry to store which are used to filter down available fields for a specific visualization and . | -| [AggTypeFilters](./kibana-plugin-plugins-data-public.aggtypefilters.md) | A registry to store which are used to filter down available aggregations for a specific visualization and . | | [Field](./kibana-plugin-plugins-data-public.field.md) | | | [FieldFormat](./kibana-plugin-plugins-data-public.fieldformat.md) | | | [FilterManager](./kibana-plugin-plugins-data-public.filtermanager.md) | | @@ -53,7 +51,6 @@ | [AggParamOption](./kibana-plugin-plugins-data-public.aggparamoption.md) | | | [DataPublicPluginSetup](./kibana-plugin-plugins-data-public.datapublicpluginsetup.md) | | | [DataPublicPluginStart](./kibana-plugin-plugins-data-public.datapublicpluginstart.md) | | -| [DateRangeKey](./kibana-plugin-plugins-data-public.daterangekey.md) | | | [EsQueryConfig](./kibana-plugin-plugins-data-public.esqueryconfig.md) | | | [FetchOptions](./kibana-plugin-plugins-data-public.fetchoptions.md) | | | [FieldFormatConfig](./kibana-plugin-plugins-data-public.fieldformatconfig.md) | | @@ -75,7 +72,6 @@ | [ISearchStrategy](./kibana-plugin-plugins-data-public.isearchstrategy.md) | Search strategy interface contains a search method that takes in a request and returns a promise that resolves to a response. | | [ISyncSearchRequest](./kibana-plugin-plugins-data-public.isyncsearchrequest.md) | | | [KueryNode](./kibana-plugin-plugins-data-public.kuerynode.md) | | -| [OptionedParamEditorProps](./kibana-plugin-plugins-data-public.optionedparameditorprops.md) | | | [OptionedValueProp](./kibana-plugin-plugins-data-public.optionedvalueprop.md) | | | [Query](./kibana-plugin-plugins-data-public.query.md) | | | [QueryState](./kibana-plugin-plugins-data-public.querystate.md) | All query state service state | @@ -95,6 +91,7 @@ | Variable | Description | | --- | --- | +| [AggGroupLabels](./kibana-plugin-plugins-data-public.agggrouplabels.md) | | | [AggGroupNames](./kibana-plugin-plugins-data-public.agggroupnames.md) | | | [baseFormattersPublic](./kibana-plugin-plugins-data-public.baseformatterspublic.md) | | | [castEsToKbnFieldTypeName](./kibana-plugin-plugins-data-public.castestokbnfieldtypename.md) | Get the KbnFieldType name for an esType string | @@ -119,6 +116,7 @@ | Type Alias | Description | | --- | --- | | [AggConfigOptions](./kibana-plugin-plugins-data-public.aggconfigoptions.md) | | +| [AggGroupName](./kibana-plugin-plugins-data-public.agggroupname.md) | | | [AggParam](./kibana-plugin-plugins-data-public.aggparam.md) | | | [CustomFilter](./kibana-plugin-plugins-data-public.customfilter.md) | | | [EsQuerySortValue](./kibana-plugin-plugins-data-public.esquerysortvalue.md) | | @@ -127,7 +125,6 @@ | [FieldFormatsContentType](./kibana-plugin-plugins-data-public.fieldformatscontenttype.md) | \* | | [FieldFormatsGetConfigFn](./kibana-plugin-plugins-data-public.fieldformatsgetconfigfn.md) | | | [IAggConfig](./kibana-plugin-plugins-data-public.iaggconfig.md) | AggConfig This class represents an aggregation, which is displayed in the left-hand nav of the Visualize app. | -| [IAggGroupNames](./kibana-plugin-plugins-data-public.iagggroupnames.md) | | | [IAggType](./kibana-plugin-plugins-data-public.iaggtype.md) | | | [IFieldFormat](./kibana-plugin-plugins-data-public.ifieldformat.md) | | | [IFieldFormatsRegistry](./kibana-plugin-plugins-data-public.ifieldformatsregistry.md) | | @@ -136,7 +133,6 @@ | [IndexPatternAggRestrictions](./kibana-plugin-plugins-data-public.indexpatternaggrestrictions.md) | | | [IndexPatternsContract](./kibana-plugin-plugins-data-public.indexpatternscontract.md) | | | [InputTimeRange](./kibana-plugin-plugins-data-public.inputtimerange.md) | | -| [IpRangeKey](./kibana-plugin-plugins-data-public.iprangekey.md) | | | [ISearch](./kibana-plugin-plugins-data-public.isearch.md) | | | [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) | | | [ISearchSource](./kibana-plugin-plugins-data-public.isearchsource.md) | \* | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.optionedparameditorprops.aggparam.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.optionedparameditorprops.aggparam.md deleted file mode 100644 index 68e4371acc2f36..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.optionedparameditorprops.aggparam.md +++ /dev/null @@ -1,13 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [OptionedParamEditorProps](./kibana-plugin-plugins-data-public.optionedparameditorprops.md) > [aggParam](./kibana-plugin-plugins-data-public.optionedparameditorprops.aggparam.md) - -## OptionedParamEditorProps.aggParam property - -Signature: - -```typescript -aggParam: { - options: T[]; - }; -``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.optionedparameditorprops.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.optionedparameditorprops.md deleted file mode 100644 index 00a440a0a775a6..00000000000000 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.optionedparameditorprops.md +++ /dev/null @@ -1,18 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [OptionedParamEditorProps](./kibana-plugin-plugins-data-public.optionedparameditorprops.md) - -## OptionedParamEditorProps interface - -Signature: - -```typescript -export interface OptionedParamEditorProps -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [aggParam](./kibana-plugin-plugins-data-public.optionedparameditorprops.aggparam.md) | {
    options: T[];
    } | | - diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md index 9a22339fd05307..67c4eac67a9e61 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md @@ -9,12 +9,7 @@ ```typescript search: { aggs: { - AggConfigs: typeof AggConfigs; - aggGroupNamesMap: () => Record<"metrics" | "buckets", string>; - aggTypeFilters: import("./search/aggs/filter/agg_type_filters").AggTypeFilters; CidrMask: typeof CidrMask; - convertDateRangeToString: typeof convertDateRangeToString; - convertIPRangeToString: (range: import("./search").IpRangeKey, format: (val: any) => string) => string; dateHistogramInterval: typeof dateHistogramInterval; intervalOptions: ({ display: string; diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index 271586bb8c5827..3caba24748bfa7 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -462,16 +462,6 @@ export const npStart = { types: aggTypesRegistry.start(), }, __LEGACY: { - AggConfig: sinon.fake(), - AggType: sinon.fake(), - aggTypeFieldFilters: { - addFilter: sinon.fake(), - filter: sinon.fake(), - }, - FieldParamType: sinon.fake(), - MetricAggType: sinon.fake(), - parentPipelineAggHelper: sinon.fake(), - siblingPipelineAggHelper: sinon.fake(), esClient: { search: sinon.fake(), msearch: sinon.fake(), diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 60d8079b22347e..75deff23ce20d6 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -288,13 +288,8 @@ export { import { // aggs - AggConfigs, - aggTypeFilters, - aggGroupNamesMap, CidrMask, - convertDateRangeToString, - convertIPRangeToString, - intervalOptions, // only used in Discover + intervalOptions, isDateHistogramBucketAggConfig, isNumberType, isStringType, @@ -326,26 +321,22 @@ export { ParsedInterval } from '../common'; export { // aggs + AggGroupLabels, + AggGroupName, AggGroupNames, - AggParam, // only the type is used externally, only in vis editor - AggParamOption, // only the type is used externally + AggParam, + AggParamOption, AggParamType, - AggTypeFieldFilters, // TODO convert to interface - AggTypeFilters, // TODO convert to interface AggConfigOptions, BUCKET_TYPES, - DateRangeKey, // only used in field formatter deserialization, which will live in data IAggConfig, IAggConfigs, - IAggGroupNames, IAggType, IFieldParamType, IMetricAggType, - IpRangeKey, // only used in field formatter deserialization, which will live in data METRIC_TYPES, - OptionedParamEditorProps, // only type is used externally OptionedParamType, - OptionedValueProp, // only type is used externally + OptionedValueProp, // search ES_SEARCH_STRATEGY, SYNC_SEARCH_STRATEGY, @@ -383,17 +374,12 @@ export { // Search namespace export const search = { aggs: { - AggConfigs, - aggGroupNamesMap, - aggTypeFilters, CidrMask, - convertDateRangeToString, - convertIPRangeToString, dateHistogramInterval, - intervalOptions, // only used in Discover + intervalOptions, InvalidEsCalendarIntervalError, InvalidEsIntervalFormatError, - isDateHistogramBucketAggConfig, + isDateHistogramBucketAggConfig, // TODO: remove in build_pipeline refactor isNumberType, isStringType, isType, diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 91dea66f06a94b..b0bb911dca9e51 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -26,7 +26,6 @@ import { History } from 'history'; import { HttpSetup } from 'src/core/public'; import { HttpStart } from 'src/core/public'; import { IconType } from '@elastic/eui'; -import { IndexPatternField as IndexPatternField_2 } from 'src/plugins/data/public'; import { InjectedIntl } from '@kbn/i18n/react'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { IUiSettingsClient } from 'src/core/public'; @@ -68,6 +67,20 @@ export type AggConfigOptions = Assign; +// Warning: (ae-missing-release-tag) "AggGroupLabels" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const AggGroupLabels: { + [AggGroupNames.Buckets]: string; + [AggGroupNames.Metrics]: string; + [AggGroupNames.None]: string; +}; + +// Warning: (ae-missing-release-tag) "AggGroupName" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AggGroupName = $Values; + // Warning: (ae-missing-release-tag) "AggGroupNames" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -108,32 +121,6 @@ export class AggParamType extends Ba makeAgg: (agg: TAggConfig, state?: AggConfigSerialized) => TAggConfig; } -// Warning: (ae-missing-release-tag) "AggTypeFieldFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "AggTypeFieldFilter" -// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "AggType" -// -// @public -export class AggTypeFieldFilters { - // Warning: (ae-forgotten-export) The symbol "AggTypeFieldFilter" needs to be exported by the entry point index.d.ts - // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "AggTypeFieldFilter" - addFilter(filter: AggTypeFieldFilter): void; - // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "any" - filter(fields: IndexPatternField_2[], aggConfig: IAggConfig): IndexPatternField_2[]; - } - -// Warning: (ae-missing-release-tag) "AggTypeFilters" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "AggTypeFilter" -// Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "AggConfig" -// -// @public -export class AggTypeFilters { - // Warning: (ae-forgotten-export) The symbol "AggTypeFilter" needs to be exported by the entry point index.d.ts - // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "AggTypeFilter" - addFilter(filter: AggTypeFilter): void; - // Warning: (ae-unresolved-link) The @link reference could not be resolved: The package "kibana" does not have an export "AggType" - filter(aggTypes: IAggType[], indexPattern: IndexPattern, aggConfig: IAggConfig, aggFilter: string[]): IAggType[]; - } - // Warning: (ae-forgotten-export) The symbol "DateFormat" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "baseFormattersPublic" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -266,16 +253,6 @@ export interface DataPublicPluginStart { }; } -// Warning: (ae-missing-release-tag) "DateRangeKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface DateRangeKey { - // (undocumented) - from: number; - // (undocumented) - to: number; -} - // @public (undocumented) export enum ES_FIELD_TYPES { // (undocumented) @@ -714,11 +691,6 @@ export type IAggConfig = AggConfig; // @internal export type IAggConfigs = AggConfigs; -// Warning: (ae-missing-release-tag) "IAggGroupNames" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type IAggGroupNames = $Values; - // Warning: (ae-forgotten-export) The symbol "AggType" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "IAggType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1101,18 +1073,6 @@ export type InputTimeRange = TimeRange | { to: Moment; }; -// Warning: (ae-missing-release-tag) "IpRangeKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type IpRangeKey = { - type: 'mask'; - mask: string; -} | { - type: 'range'; - from: string; - to: string; -}; - // Warning: (ae-missing-release-tag) "IRequestTypesMap" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1290,16 +1250,6 @@ export enum METRIC_TYPES { TOP_HITS = "top_hits" } -// Warning: (ae-missing-release-tag) "OptionedParamEditorProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface OptionedParamEditorProps { - // (undocumented) - aggParam: { - options: T[]; - }; -} - // Warning: (ae-missing-release-tag) "OptionedParamType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1578,12 +1528,7 @@ export type SavedQueryTimeFilter = TimeRange & { // @public (undocumented) export const search: { aggs: { - AggConfigs: typeof AggConfigs; - aggGroupNamesMap: () => Record<"metrics" | "buckets", string>; - aggTypeFilters: import("./search/aggs/filter/agg_type_filters").AggTypeFilters; CidrMask: typeof CidrMask; - convertDateRangeToString: typeof convertDateRangeToString; - convertIPRangeToString: (range: import("./search").IpRangeKey, format: (val: any) => string) => string; dateHistogramInterval: typeof dateHistogramInterval; intervalOptions: ({ display: string; @@ -1870,21 +1815,20 @@ export type TSearchStrategyProvider = (context: ISearc // src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "getRoutes" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:236:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:384:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:384:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:384:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:384:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:392:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:408:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:411:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:415:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:375:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:377:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:378:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:393:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:394:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:397:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:33:33 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:37:1 - (ae-forgotten-export) The symbol "QueryStateChange" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromValueClickAction" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/search/aggs/agg_groups.ts b/src/plugins/data/public/search/aggs/agg_groups.ts index 9cebff76c96843..dec3397126e67d 100644 --- a/src/plugins/data/public/search/aggs/agg_groups.ts +++ b/src/plugins/data/public/search/aggs/agg_groups.ts @@ -25,15 +25,17 @@ export const AggGroupNames = Object.freeze({ Metrics: 'metrics' as 'metrics', None: 'none' as 'none', }); -export type IAggGroupNames = $Values; -type IAggGroupNamesMap = () => Record<'buckets' | 'metrics', string>; +export type AggGroupName = $Values; -export const aggGroupNamesMap: IAggGroupNamesMap = () => ({ +export const AggGroupLabels = { + [AggGroupNames.Buckets]: i18n.translate('data.search.aggs.aggGroups.bucketsText', { + defaultMessage: 'Buckets', + }), [AggGroupNames.Metrics]: i18n.translate('data.search.aggs.aggGroups.metricsText', { defaultMessage: 'Metrics', }), - [AggGroupNames.Buckets]: i18n.translate('data.search.aggs.aggGroups.bucketsText', { - defaultMessage: 'Buckets', + [AggGroupNames.None]: i18n.translate('data.search.aggs.aggGroups.noneText', { + defaultMessage: 'None', }), -}); +}; diff --git a/src/plugins/data/public/search/aggs/filter/agg_type_filters.test.ts b/src/plugins/data/public/search/aggs/filter/agg_type_filters.test.ts deleted file mode 100644 index 58f5aef0b9dfd4..00000000000000 --- a/src/plugins/data/public/search/aggs/filter/agg_type_filters.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IndexPattern } from '../../../index_patterns'; -import { AggTypeFilters } from './agg_type_filters'; -import { IAggConfig, IAggType } from '../types'; - -describe('AggTypeFilters', () => { - let registry: AggTypeFilters; - const indexPattern = ({ id: '1234', fields: [], title: 'foo' } as unknown) as IndexPattern; - const aggConfig = {} as IAggConfig; - - beforeEach(() => { - registry = new AggTypeFilters(); - }); - - it('should filter nothing without registered filters', async () => { - const aggTypes = [{ name: 'count' }, { name: 'sum' }] as IAggType[]; - const filtered = registry.filter(aggTypes, indexPattern, aggConfig, []); - expect(filtered).toEqual(aggTypes); - }); - - it('should pass all aggTypes to the registered filter', async () => { - const aggTypes = [{ name: 'count' }, { name: 'sum' }] as IAggType[]; - const filter = jest.fn(); - registry.addFilter(filter); - registry.filter(aggTypes, indexPattern, aggConfig, []); - expect(filter).toHaveBeenCalledWith(aggTypes[0], indexPattern, aggConfig, []); - expect(filter).toHaveBeenCalledWith(aggTypes[1], indexPattern, aggConfig, []); - }); - - it('should allow registered filters to filter out aggTypes', async () => { - const aggTypes = [{ name: 'count' }, { name: 'sum' }, { name: 'avg' }] as IAggType[]; - let filtered = registry.filter(aggTypes, indexPattern, aggConfig, []); - expect(filtered).toEqual(aggTypes); - - registry.addFilter(() => true); - registry.addFilter(aggType => aggType.name !== 'count'); - filtered = registry.filter(aggTypes, indexPattern, aggConfig, []); - expect(filtered).toEqual([aggTypes[1], aggTypes[2]]); - - registry.addFilter(aggType => aggType.name !== 'avg'); - filtered = registry.filter(aggTypes, indexPattern, aggConfig, []); - expect(filtered).toEqual([aggTypes[1]]); - }); -}); diff --git a/src/plugins/data/public/search/aggs/filter/agg_type_filters.ts b/src/plugins/data/public/search/aggs/filter/agg_type_filters.ts deleted file mode 100644 index b8d192cd66b5ac..00000000000000 --- a/src/plugins/data/public/search/aggs/filter/agg_type_filters.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IndexPattern } from '../../../index_patterns'; -import { IAggConfig, IAggType } from '../types'; - -type AggTypeFilter = ( - aggType: IAggType, - indexPattern: IndexPattern, - aggConfig: IAggConfig, - aggFilter: string[] -) => boolean; - -/** - * A registry to store {@link AggTypeFilter} which are used to filter down - * available aggregations for a specific visualization and {@link AggConfig}. - */ -class AggTypeFilters { - private filters = new Set(); - - /** - * Register a new {@link AggTypeFilter} with this registry. - * - * @param filter The filter to register. - */ - public addFilter(filter: AggTypeFilter): void { - this.filters.add(filter); - } - - /** - * Returns the {@link AggType|aggTypes} filtered by all registered filters. - * - * @param aggTypes A list of aggTypes that will be filtered down by this registry. - * @param indexPattern The indexPattern for which this list should be filtered down. - * @param aggConfig The aggConfig for which the returning list will be used. - * @param schema - * @return A filtered list of the passed aggTypes. - */ - public filter( - aggTypes: IAggType[], - indexPattern: IndexPattern, - aggConfig: IAggConfig, - aggFilter: string[] - ) { - const allFilters = Array.from(this.filters); - const allowedAggTypes = aggTypes.filter(aggType => { - const isAggTypeAllowed = allFilters.every(filter => - filter(aggType, indexPattern, aggConfig, aggFilter) - ); - return isAggTypeAllowed; - }); - return allowedAggTypes; - } -} - -const aggTypeFilters = new AggTypeFilters(); - -export { aggTypeFilters, AggTypeFilters }; diff --git a/src/plugins/data/public/search/aggs/filter/index.ts b/src/plugins/data/public/search/aggs/filter/index.ts deleted file mode 100644 index 35d06807d0ec24..00000000000000 --- a/src/plugins/data/public/search/aggs/filter/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { aggTypeFilters, AggTypeFilters } from './agg_type_filters'; -export { propFilter } from './prop_filter'; diff --git a/src/plugins/data/public/search/aggs/index.ts b/src/plugins/data/public/search/aggs/index.ts index 5dfb6aeff8d14f..1139d9c7ff7228 100644 --- a/src/plugins/data/public/search/aggs/index.ts +++ b/src/plugins/data/public/search/aggs/index.ts @@ -24,7 +24,6 @@ export * from './agg_type'; export * from './agg_types'; export * from './agg_types_registry'; export * from './buckets'; -export * from './filter'; export * from './metrics'; export * from './param_types'; export * from './types'; diff --git a/src/plugins/data/public/search/aggs/param_types/field.ts b/src/plugins/data/public/search/aggs/param_types/field.ts index 4d67f41905c5a4..63dbed9cec6125 100644 --- a/src/plugins/data/public/search/aggs/param_types/field.ts +++ b/src/plugins/data/public/search/aggs/param_types/field.ts @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; import { IAggConfig } from '../agg_config'; import { SavedObjectNotFound } from '../../../../../../plugins/kibana_utils/public'; import { BaseParamType } from './base'; -import { propFilter } from '../filter'; +import { propFilter } from '../utils'; import { isNestedField, KBN_FIELD_TYPES } from '../../../../common'; import { Field as IndexPatternField } from '../../../index_patterns'; import { GetInternalStartServicesFn } from '../../../types'; diff --git a/src/plugins/data/public/search/aggs/param_types/filter/field_filters.test.ts b/src/plugins/data/public/search/aggs/param_types/filter/field_filters.test.ts deleted file mode 100644 index f776a3deb23a15..00000000000000 --- a/src/plugins/data/public/search/aggs/param_types/filter/field_filters.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { AggTypeFieldFilters } from './field_filters'; -import { IAggConfig } from '../../agg_config'; -import { Field as IndexPatternField } from '../../../../index_patterns'; - -describe('AggTypeFieldFilters', () => { - let registry: AggTypeFieldFilters; - const aggConfig = {} as IAggConfig; - - beforeEach(() => { - registry = new AggTypeFieldFilters(); - }); - - it('should filter nothing without registered filters', async () => { - const fields = [{ name: 'foo' }, { name: 'bar' }] as IndexPatternField[]; - const filtered = registry.filter(fields, aggConfig); - expect(filtered).toEqual(fields); - }); - - it('should pass all fields to the registered filter', async () => { - const fields = [{ name: 'foo' }, { name: 'bar' }] as IndexPatternField[]; - const filter = jest.fn(); - registry.addFilter(filter); - registry.filter(fields, aggConfig); - expect(filter).toHaveBeenCalledWith(fields[0], aggConfig); - expect(filter).toHaveBeenCalledWith(fields[1], aggConfig); - }); - - it('should allow registered filters to filter out fields', async () => { - const fields = [{ name: 'foo' }, { name: 'bar' }] as IndexPatternField[]; - let filtered = registry.filter(fields, aggConfig); - expect(filtered).toEqual(fields); - - registry.addFilter(() => true); - registry.addFilter(field => field.name !== 'foo'); - filtered = registry.filter(fields, aggConfig); - expect(filtered).toEqual([fields[1]]); - - registry.addFilter(field => field.name !== 'bar'); - filtered = registry.filter(fields, aggConfig); - expect(filtered).toEqual([]); - }); -}); diff --git a/src/plugins/data/public/search/aggs/param_types/filter/field_filters.ts b/src/plugins/data/public/search/aggs/param_types/filter/field_filters.ts deleted file mode 100644 index 1cbf0c9ae36245..00000000000000 --- a/src/plugins/data/public/search/aggs/param_types/filter/field_filters.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { IndexPatternField } from 'src/plugins/data/public'; -import { IAggConfig } from '../../agg_config'; - -type AggTypeFieldFilter = (field: IndexPatternField, aggConfig: IAggConfig) => boolean; - -/** - * A registry to store {@link AggTypeFieldFilter} which are used to filter down - * available fields for a specific visualization and {@link AggType}. - */ -class AggTypeFieldFilters { - private filters = new Set(); - - /** - * Register a new {@link AggTypeFieldFilter} with this registry. - * This will be used by the {@link #filter|filter method}. - * - * @param filter The filter to register. - */ - public addFilter(filter: AggTypeFieldFilter): void { - this.filters.add(filter); - } - - /** - * Returns the {@link any|fields} filtered by all registered filters. - * - * @param fields An array of fields that will be filtered down by this registry. - * @param aggConfig The aggConfig for which the returning list will be used. - * @return A filtered list of the passed fields. - */ - public filter(fields: IndexPatternField[], aggConfig: IAggConfig) { - const allFilters = Array.from(this.filters); - const allowedAggTypeFields = fields.filter(field => { - const isAggTypeFieldAllowed = allFilters.every(filter => filter(field, aggConfig)); - return isAggTypeFieldAllowed; - }); - return allowedAggTypeFields; - } -} - -const aggTypeFieldFilters = new AggTypeFieldFilters(); - -export { aggTypeFieldFilters, AggTypeFieldFilters }; diff --git a/src/plugins/data/public/search/aggs/param_types/index.ts b/src/plugins/data/public/search/aggs/param_types/index.ts index c9e8a9879f427c..e25dd55dbd3f27 100644 --- a/src/plugins/data/public/search/aggs/param_types/index.ts +++ b/src/plugins/data/public/search/aggs/param_types/index.ts @@ -20,7 +20,6 @@ export * from './agg'; export * from './base'; export * from './field'; -export * from './filter'; export * from './json'; export * from './optioned'; export * from './string'; diff --git a/src/plugins/data/public/search/aggs/param_types/optioned.ts b/src/plugins/data/public/search/aggs/param_types/optioned.ts index 9eb7ceda607117..45d0a65f69170e 100644 --- a/src/plugins/data/public/search/aggs/param_types/optioned.ts +++ b/src/plugins/data/public/search/aggs/param_types/optioned.ts @@ -27,12 +27,6 @@ export interface OptionedValueProp { isCompatible: (agg: IAggConfig) => boolean; } -export interface OptionedParamEditorProps { - aggParam: { - options: T[]; - }; -} - export class OptionedParamType extends BaseParamType { options: OptionedValueProp[]; diff --git a/src/plugins/data/public/search/aggs/types.ts b/src/plugins/data/public/search/aggs/types.ts index 95a7a450135670..1c5b5b458ce907 100644 --- a/src/plugins/data/public/search/aggs/types.ts +++ b/src/plugins/data/public/search/aggs/types.ts @@ -19,20 +19,13 @@ import { IndexPattern } from '../../index_patterns'; import { - AggConfig, AggConfigSerialized, AggConfigs, AggParamsTerms, - AggType, - aggTypeFieldFilters, AggTypesRegistrySetup, AggTypesRegistryStart, CreateAggConfigParams, - FieldParamType, getCalculateAutoTimeExpression, - MetricAggType, - parentPipelineAggHelper, - siblingPipelineAggHelper, } from './'; export { IAggConfig, AggConfigSerialized } from './agg_config'; @@ -43,7 +36,7 @@ export { IFieldParamType } from './param_types'; export { IMetricAggType } from './metrics/metric_agg_type'; export { DateRangeKey } from './buckets/lib/date_range'; export { IpRangeKey } from './buckets/lib/ip_range'; -export { OptionedValueProp, OptionedParamEditorProps } from './param_types/optioned'; +export { OptionedValueProp } from './param_types/optioned'; /** @internal */ export interface SearchAggsSetup { @@ -51,17 +44,6 @@ export interface SearchAggsSetup { types: AggTypesRegistrySetup; } -/** @internal */ -export interface SearchAggsStartLegacy { - AggConfig: typeof AggConfig; - AggType: typeof AggType; - aggTypeFieldFilters: typeof aggTypeFieldFilters; - FieldParamType: typeof FieldParamType; - MetricAggType: typeof MetricAggType; - parentPipelineAggHelper: typeof parentPipelineAggHelper; - siblingPipelineAggHelper: typeof siblingPipelineAggHelper; -} - /** @internal */ export interface SearchAggsStart { calculateAutoTimeExpression: ReturnType; diff --git a/src/plugins/data/public/search/aggs/utils/index.ts b/src/plugins/data/public/search/aggs/utils/index.ts index 23606bd1093424..169d872b17d3aa 100644 --- a/src/plugins/data/public/search/aggs/utils/index.ts +++ b/src/plugins/data/public/search/aggs/utils/index.ts @@ -18,4 +18,5 @@ */ export * from './calculate_auto_time_expression'; +export * from './prop_filter'; export * from './to_angular_json'; diff --git a/src/plugins/data/public/search/aggs/filter/prop_filter.test.ts b/src/plugins/data/public/search/aggs/utils/prop_filter.test.ts similarity index 100% rename from src/plugins/data/public/search/aggs/filter/prop_filter.test.ts rename to src/plugins/data/public/search/aggs/utils/prop_filter.test.ts diff --git a/src/plugins/data/public/search/aggs/filter/prop_filter.ts b/src/plugins/data/public/search/aggs/utils/prop_filter.ts similarity index 97% rename from src/plugins/data/public/search/aggs/filter/prop_filter.ts rename to src/plugins/data/public/search/aggs/utils/prop_filter.ts index e6b5f3831e65d9..01e98a68d3949e 100644 --- a/src/plugins/data/public/search/aggs/filter/prop_filter.ts +++ b/src/plugins/data/public/search/aggs/utils/prop_filter.ts @@ -28,7 +28,7 @@ type FilterFunc

    = (item: T[P]) => boolean; * * @returns the filter function which can be registered with angular */ -function propFilter

    (prop: P) { +export function propFilter

    (prop: P) { /** * List filtering function which accepts an array or list of values that a property * must contain @@ -92,5 +92,3 @@ function propFilter

    (prop: P) { }); }; } - -export { propFilter }; diff --git a/src/plugins/data/public/search/mocks.ts b/src/plugins/data/public/search/mocks.ts index dd196074c81736..44082040b5b0bc 100644 --- a/src/plugins/data/public/search/mocks.ts +++ b/src/plugins/data/public/search/mocks.ts @@ -18,7 +18,6 @@ */ import { searchAggsSetupMock, searchAggsStartMock } from './aggs/mocks'; -import { AggTypeFieldFilters } from './aggs/param_types/filter'; import { ISearchStart } from './types'; import { searchSourceMock, createSearchSourceMock } from './search_source/mocks'; @@ -34,13 +33,6 @@ const searchStartMock: jest.Mocked = { search: jest.fn(), searchSource: searchSourceMock, __LEGACY: { - AggConfig: jest.fn() as any, - AggType: jest.fn(), - aggTypeFieldFilters: new AggTypeFieldFilters(), - FieldParamType: jest.fn(), - MetricAggType: jest.fn(), - parentPipelineAggHelper: jest.fn() as any, - siblingPipelineAggHelper: jest.fn() as any, esClient: { search: jest.fn(), msearch: jest.fn(), diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index b59524baa9fa79..4d183797dfef06 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -39,16 +39,9 @@ import { SearchInterceptor } from './search_interceptor'; import { getAggTypes, getAggTypesFunctions, - AggType, AggTypesRegistry, - AggConfig, AggConfigs, - FieldParamType, getCalculateAutoTimeExpression, - MetricAggType, - aggTypeFieldFilters, - parentPipelineAggHelper, - siblingPipelineAggHelper, } from './aggs'; import { FieldFormatsStart } from '../field_formats'; import { ISearchGeneric } from './i_search'; @@ -156,13 +149,6 @@ export class SearchService implements Plugin { const legacySearch = { esClient: this.esClient!, - AggConfig, - AggType, - aggTypeFieldFilters, - FieldParamType, - MetricAggType, - parentPipelineAggHelper, - siblingPipelineAggHelper, }; const searchSourceDependencies: SearchSourceDependencies = { diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts index 99d111ce1574e5..1687c8f9833930 100644 --- a/src/plugins/data/public/search/types.ts +++ b/src/plugins/data/public/search/types.ts @@ -18,7 +18,7 @@ */ import { CoreStart, SavedObjectReference } from 'kibana/public'; -import { SearchAggsSetup, SearchAggsStart, SearchAggsStartLegacy } from './aggs'; +import { SearchAggsSetup, SearchAggsStart } from './aggs'; import { ISearch, ISearchGeneric } from './i_search'; import { TStrategyTypes } from './strategy_types'; import { LegacyApiCaller } from './legacy/es_client'; @@ -88,5 +88,5 @@ export interface ISearchStart { references: SavedObjectReference[] ) => Promise; }; - __LEGACY: ISearchStartLegacy & SearchAggsStartLegacy; + __LEGACY: ISearchStartLegacy; } diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap index fb3d6efa638268..1860a4625afc0a 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap @@ -260,19 +260,10 @@ exports[`SavedObjectsTable import should show the flyout 1`] = ` search={ Object { "__LEGACY": Object { - "AggConfig": [MockFunction], - "AggType": [MockFunction], - "FieldParamType": [MockFunction], - "MetricAggType": [MockFunction], - "aggTypeFieldFilters": AggTypeFieldFilters { - "filters": Set {}, - }, "esClient": Object { "msearch": [MockFunction], "search": [MockFunction], }, - "parentPipelineAggHelper": [MockFunction], - "siblingPipelineAggHelper": [MockFunction], }, "aggs": Object { "calculateAutoTimeExpression": [Function], diff --git a/src/plugins/vis_default_editor/public/agg_filters/agg_type_field_filters.ts b/src/plugins/vis_default_editor/public/agg_filters/agg_type_field_filters.ts new file mode 100644 index 00000000000000..15df2f0acccd18 --- /dev/null +++ b/src/plugins/vis_default_editor/public/agg_filters/agg_type_field_filters.ts @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IAggConfig, IndexPatternField } from '../../../data/public'; + +type AggTypeFieldFilter = (field: IndexPatternField, aggConfig: IAggConfig) => boolean; + +const filters: AggTypeFieldFilter[] = [ + /** + * Check index pattern aggregation restrictions + * and limit available fields for a given aggType based on that. + */ + (field, aggConfig) => { + const indexPattern = aggConfig.getIndexPattern(); + const aggRestrictions = indexPattern.getAggregationRestrictions(); + + if (!aggRestrictions) { + return true; + } + + const aggName = aggConfig.type && aggConfig.type.name; + const aggFields = aggRestrictions[aggName]; + return !!aggFields && !!aggFields[field.name]; + }, +]; + +export function filterAggTypeFields(fields: IndexPatternField[], aggConfig: IAggConfig) { + const allowedAggTypeFields = fields.filter(field => { + const isAggTypeFieldAllowed = filters.every(filter => filter(field, aggConfig)); + return isAggTypeFieldAllowed; + }); + return allowedAggTypeFields; +} diff --git a/src/plugins/vis_default_editor/public/agg_filters/agg_type_filters.ts b/src/plugins/vis_default_editor/public/agg_filters/agg_type_filters.ts new file mode 100644 index 00000000000000..2cf1acba4d228f --- /dev/null +++ b/src/plugins/vis_default_editor/public/agg_filters/agg_type_filters.ts @@ -0,0 +1,75 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IAggType, IAggConfig, IndexPattern, search } from '../../../data/public'; + +const { propFilter } = search.aggs; +const filterByName = propFilter('name'); + +type AggTypeFilter = ( + aggType: IAggType, + indexPattern: IndexPattern, + aggConfig: IAggConfig, + aggFilter: string[] +) => boolean; + +const filters: AggTypeFilter[] = [ + /** + * This filter checks the defined aggFilter in the schemas of that visualization + * and limits available aggregations based on that. + */ + (aggType, indexPattern, aggConfig, aggFilter) => { + const doesSchemaAllowAggType = filterByName([aggType], aggFilter).length !== 0; + return doesSchemaAllowAggType; + }, + /** + * Check index pattern aggregation restrictions and limit available aggTypes. + */ + (aggType, indexPattern, aggConfig, aggFilter) => { + const aggRestrictions = indexPattern.getAggregationRestrictions(); + + if (!aggRestrictions) { + return true; + } + + const aggName = aggType.name; + // Only return agg types which are specified in the agg restrictions, + // except for `count` which should always be returned. + return ( + aggName === 'count' || + (!!aggRestrictions && Object.keys(aggRestrictions).includes(aggName)) || + false + ); + }, +]; + +export function filterAggTypes( + aggTypes: IAggType[], + indexPattern: IndexPattern, + aggConfig: IAggConfig, + aggFilter: string[] +) { + const allowedAggTypes = aggTypes.filter(aggType => { + const isAggTypeAllowed = filters.every(filter => + filter(aggType, indexPattern, aggConfig, aggFilter) + ); + return isAggTypeAllowed; + }); + return allowedAggTypes; +} diff --git a/src/plugins/data/public/search/aggs/param_types/filter/index.ts b/src/plugins/vis_default_editor/public/agg_filters/index.ts similarity index 91% rename from src/plugins/data/public/search/aggs/param_types/filter/index.ts rename to src/plugins/vis_default_editor/public/agg_filters/index.ts index 2e0039c96a1921..2b08449fb31613 100644 --- a/src/plugins/data/public/search/aggs/param_types/filter/index.ts +++ b/src/plugins/vis_default_editor/public/agg_filters/index.ts @@ -17,4 +17,5 @@ * under the License. */ -export { aggTypeFieldFilters, AggTypeFieldFilters } from './field_filters'; +export * from './agg_type_filters'; +export * from './agg_type_field_filters'; diff --git a/src/plugins/vis_default_editor/public/components/agg_common_props.ts b/src/plugins/vis_default_editor/public/components/agg_common_props.ts index 0c130a96230b4a..40d7b79bfbefc9 100644 --- a/src/plugins/vis_default_editor/public/components/agg_common_props.ts +++ b/src/plugins/vis_default_editor/public/components/agg_common_props.ts @@ -18,7 +18,7 @@ */ import { VisParams } from 'src/plugins/visualizations/public'; -import { IAggType, IAggConfig, IAggGroupNames } from 'src/plugins/data/public'; +import { IAggType, IAggConfig, AggGroupName } from 'src/plugins/data/public'; import { Schema } from '../schemas'; import { EditorVisState } from './sidebar/state/reducers'; @@ -30,7 +30,7 @@ export type ReorderAggs = (sourceAgg: IAggConfig, destinationAgg: IAggConfig) => export interface DefaultEditorCommonProps { formIsTouched: boolean; - groupName: IAggGroupNames; + groupName: AggGroupName; metricAggs: IAggConfig[]; state: EditorVisState; setAggParamValue: ( diff --git a/src/plugins/vis_default_editor/public/components/agg_group.tsx b/src/plugins/vis_default_editor/public/components/agg_group.tsx index 72515d0845926e..fae9de6959ef1d 100644 --- a/src/plugins/vis_default_editor/public/components/agg_group.tsx +++ b/src/plugins/vis_default_editor/public/components/agg_group.tsx @@ -30,7 +30,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { AggGroupNames, search, IAggConfig, TimeRange } from '../../../data/public'; +import { AggGroupNames, AggGroupLabels, IAggConfig, TimeRange } from '../../../data/public'; import { DefaultEditorAgg } from './agg'; import { DefaultEditorAggAdd } from './agg_add'; import { AddSchema, ReorderAggs, DefaultEditorAggCommonProps } from './agg_common_props'; @@ -70,7 +70,7 @@ function DefaultEditorAggGroup({ setValidity, timeRange, }: DefaultEditorAggGroupProps) { - const groupNameLabel = (search.aggs.aggGroupNamesMap() as any)[groupName]; + const groupNameLabel = AggGroupLabels[groupName]; // e.g. buckets can have no aggs const schemaNames = schemas.map(s => s.name); const group: IAggConfig[] = useMemo( diff --git a/src/plugins/vis_default_editor/public/components/agg_param_props.ts b/src/plugins/vis_default_editor/public/components/agg_param_props.ts index aec332e8674d74..076bddc9551ea8 100644 --- a/src/plugins/vis_default_editor/public/components/agg_param_props.ts +++ b/src/plugins/vis_default_editor/public/components/agg_param_props.ts @@ -17,7 +17,12 @@ * under the License. */ -import { IAggConfig, AggParam, IndexPatternField } from 'src/plugins/data/public'; +import { + IAggConfig, + AggParam, + IndexPatternField, + OptionedValueProp, +} from 'src/plugins/data/public'; import { ComboBoxGroupedOptions } from '../utils'; import { EditorConfig } from './utils'; import { Schema } from '../schemas'; @@ -46,3 +51,9 @@ export interface AggParamEditorProps extends AggParamCommonProp setValidity(isValid: boolean): void; setTouched(): void; } + +export interface OptionedParamEditorProps { + aggParam: { + options: T[]; + }; +} diff --git a/src/plugins/vis_default_editor/public/components/agg_params.tsx b/src/plugins/vis_default_editor/public/components/agg_params.tsx index 3674e39b558d26..d36c2d0e7625b2 100644 --- a/src/plugins/vis_default_editor/public/components/agg_params.tsx +++ b/src/plugins/vis_default_editor/public/components/agg_params.tsx @@ -112,20 +112,8 @@ function DefaultEditorAggParams({ fieldName, ]); const params = useMemo( - () => - getAggParamsToRender( - { agg, editorConfig, metricAggs, state, schemas, hideCustomLabel }, - services.data.search.__LEGACY.aggTypeFieldFilters - ), - [ - agg, - editorConfig, - metricAggs, - state, - schemas, - hideCustomLabel, - services.data.search.__LEGACY.aggTypeFieldFilters, - ] + () => getAggParamsToRender({ agg, editorConfig, metricAggs, state, schemas, hideCustomLabel }), + [agg, editorConfig, metricAggs, state, schemas, hideCustomLabel] ); const allParams = [...params.basic, ...params.advanced]; const [paramsState, onChangeParamsState] = useReducer( diff --git a/src/plugins/vis_default_editor/public/components/agg_params_helper.test.ts b/src/plugins/vis_default_editor/public/components/agg_params_helper.test.ts index bed2561341737b..834ad8b70ad0db 100644 --- a/src/plugins/vis_default_editor/public/components/agg_params_helper.test.ts +++ b/src/plugins/vis_default_editor/public/components/agg_params_helper.test.ts @@ -23,7 +23,6 @@ import { IAggConfig, IAggType, IndexPattern, - IndexPatternField, } from 'src/plugins/data/public'; import { getAggParamsToRender, @@ -39,12 +38,6 @@ jest.mock('../utils', () => ({ groupAndSortBy: jest.fn(() => ['indexedFields']), })); -const mockFilter: any = { - filter(fields: IndexPatternField[]): IndexPatternField[] { - return fields; - }, -}; - describe('DefaultEditorAggParams helpers', () => { describe('getAggParamsToRender', () => { let agg: IAggConfig; @@ -72,20 +65,14 @@ describe('DefaultEditorAggParams helpers', () => { }, schema: 'metric', } as IAggConfig; - const params = getAggParamsToRender( - { agg, editorConfig, metricAggs, state, schemas }, - mockFilter - ); + const params = getAggParamsToRender({ agg, editorConfig, metricAggs, state, schemas }); expect(params).toEqual(emptyParams); }); it('should not create any param if there is no agg type', () => { agg = { schema: 'metric' } as IAggConfig; - const params = getAggParamsToRender( - { agg, editorConfig, metricAggs, state, schemas }, - mockFilter - ); + const params = getAggParamsToRender({ agg, editorConfig, metricAggs, state, schemas }); expect(params).toEqual(emptyParams); }); @@ -101,10 +88,7 @@ describe('DefaultEditorAggParams helpers', () => { hidden: true, }, }; - const params = getAggParamsToRender( - { agg, editorConfig, metricAggs, state, schemas }, - mockFilter - ); + const params = getAggParamsToRender({ agg, editorConfig, metricAggs, state, schemas }); expect(params).toEqual(emptyParams); }); @@ -116,10 +100,7 @@ describe('DefaultEditorAggParams helpers', () => { }, schema: 'metric2', } as any) as IAggConfig; - const params = getAggParamsToRender( - { agg, editorConfig, metricAggs, state, schemas }, - mockFilter - ); + const params = getAggParamsToRender({ agg, editorConfig, metricAggs, state, schemas }); expect(params).toEqual(emptyParams); }); @@ -152,16 +133,14 @@ describe('DefaultEditorAggParams helpers', () => { { name: '@timestamp', type: 'date' }, { name: 'geo_desc', type: 'string' }, ], + getAggregationRestrictions: jest.fn(), })), params: { orderBy: 'orderBy', field: 'field', }, } as any) as IAggConfig; - const params = getAggParamsToRender( - { agg, editorConfig, metricAggs, state, schemas }, - mockFilter - ); + const params = getAggParamsToRender({ agg, editorConfig, metricAggs, state, schemas }); expect(params).toEqual({ basic: [ @@ -190,7 +169,6 @@ describe('DefaultEditorAggParams helpers', () => { ], advanced: [], }); - expect(agg.getIndexPattern).toBeCalledTimes(1); }); }); diff --git a/src/plugins/vis_default_editor/public/components/agg_params_helper.ts b/src/plugins/vis_default_editor/public/components/agg_params_helper.ts index a32bd76bafa5ab..9977f1e5e71fca 100644 --- a/src/plugins/vis_default_editor/public/components/agg_params_helper.ts +++ b/src/plugins/vis_default_editor/public/components/agg_params_helper.ts @@ -20,7 +20,6 @@ import { get, isEmpty } from 'lodash'; import { - AggTypeFieldFilters, IAggConfig, AggParam, IFieldParamType, @@ -28,13 +27,13 @@ import { IndexPattern, IndexPatternField, } from 'src/plugins/data/public'; +import { filterAggTypes, filterAggTypeFields } from '../agg_filters'; import { groupAndSortBy, ComboBoxGroupedOptions } from '../utils'; import { AggTypeState, AggParamsState } from './agg_params_state'; import { AggParamEditorProps } from './agg_param_props'; import { aggParamsMap } from './agg_params_map'; import { EditorConfig } from './utils'; import { Schema, getSchemaByName } from '../schemas'; -import { search } from '../../../data/public'; import { EditorVisState } from './sidebar/state/reducers'; interface ParamInstanceBase { @@ -53,10 +52,14 @@ export interface ParamInstance extends ParamInstanceBase { value: unknown; } -function getAggParamsToRender( - { agg, editorConfig, metricAggs, state, schemas, hideCustomLabel }: ParamInstanceBase, - aggTypeFieldFilters: AggTypeFieldFilters -) { +function getAggParamsToRender({ + agg, + editorConfig, + metricAggs, + state, + schemas, + hideCustomLabel, +}: ParamInstanceBase) { const params = { basic: [] as ParamInstance[], advanced: [] as ParamInstance[], @@ -89,7 +92,7 @@ function getAggParamsToRender( availableFields = availableFields.filter(field => field.type === 'number'); } } - fields = aggTypeFieldFilters.filter(availableFields, agg); + fields = filterAggTypeFields(availableFields, agg); indexedFields = groupAndSortBy(fields, 'type', 'name'); if (fields && !indexedFields.length && index > 0) { @@ -138,12 +141,7 @@ function getAggTypeOptions( groupName: string, allowedAggs: string[] ): ComboBoxGroupedOptions { - const aggTypeOptions = search.aggs.aggTypeFilters.filter( - aggTypes[groupName], - indexPattern, - agg, - allowedAggs - ); + const aggTypeOptions = filterAggTypes(aggTypes[groupName], indexPattern, agg, allowedAggs); return groupAndSortBy(aggTypeOptions as any[], 'subtype', 'title'); } diff --git a/src/plugins/vis_default_editor/public/components/controls/order.tsx b/src/plugins/vis_default_editor/public/components/controls/order.tsx index e609bf9adf790e..3c0224564300a3 100644 --- a/src/plugins/vis_default_editor/public/components/controls/order.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/order.tsx @@ -21,8 +21,8 @@ import React, { useEffect } from 'react'; import { EuiFormRow, EuiSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { OptionedValueProp, OptionedParamEditorProps } from 'src/plugins/data/public'; -import { AggParamEditorProps } from '../agg_param_props'; +import { OptionedValueProp } from 'src/plugins/data/public'; +import { AggParamEditorProps, OptionedParamEditorProps } from '../agg_param_props'; function OrderParamEditor({ aggParam, diff --git a/src/plugins/vis_default_editor/public/components/controls/top_aggregate.tsx b/src/plugins/vis_default_editor/public/components/controls/top_aggregate.tsx index ad23cec87800fe..66abb88b97d29a 100644 --- a/src/plugins/vis_default_editor/public/components/controls/top_aggregate.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/top_aggregate.tsx @@ -26,10 +26,9 @@ import { IAggConfig, AggParam, OptionedValueProp, - OptionedParamEditorProps, OptionedParamType, } from 'src/plugins/data/public'; -import { AggParamEditorProps } from '../agg_param_props'; +import { AggParamEditorProps, OptionedParamEditorProps } from '../agg_param_props'; export interface AggregateValueProp extends OptionedValueProp { isCompatible(aggConfig: IAggConfig): boolean; diff --git a/src/plugins/vis_default_editor/public/default_editor.tsx b/src/plugins/vis_default_editor/public/default_editor.tsx index 1c2ddbc314f991..8088921ba7fda6 100644 --- a/src/plugins/vis_default_editor/public/default_editor.tsx +++ b/src/plugins/vis_default_editor/public/default_editor.tsx @@ -22,7 +22,6 @@ import React, { useEffect, useRef, useState, useCallback } from 'react'; import { EditorRenderProps } from 'src/plugins/visualize/public'; import { PanelsContainer, Panel } from '../../kibana_react/public'; -import './vis_type_agg_filter'; import { DefaultEditorSideBar } from './components/sidebar'; import { DefaultEditorControllerState } from './default_editor_controller'; import { getInitialWidth } from './editor_size'; diff --git a/src/plugins/vis_default_editor/public/schemas.ts b/src/plugins/vis_default_editor/public/schemas.ts index 05ba5fa9c94198..26d1cbe91b996d 100644 --- a/src/plugins/vis_default_editor/public/schemas.ts +++ b/src/plugins/vis_default_editor/public/schemas.ts @@ -21,7 +21,7 @@ import _, { defaults } from 'lodash'; import { Optional } from '@kbn/utility-types'; -import { AggGroupNames, AggParam, IAggGroupNames } from '../../data/public'; +import { AggGroupNames, AggParam, AggGroupName } from '../../data/public'; export interface ISchemas { [AggGroupNames.Buckets]: Schema[]; @@ -32,7 +32,7 @@ export interface ISchemas { export interface Schema { aggFilter: string[]; editor: boolean | string; - group: IAggGroupNames; + group: AggGroupName; max: number; min: number; name: string; diff --git a/src/plugins/vis_default_editor/public/vis_type_agg_filter.ts b/src/plugins/vis_default_editor/public/vis_type_agg_filter.ts deleted file mode 100644 index bf5661f42a9f5f..00000000000000 --- a/src/plugins/vis_default_editor/public/vis_type_agg_filter.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { IAggType, IAggConfig, IndexPattern, search } from '../../data/public'; - -const { aggTypeFilters, propFilter } = search.aggs; -const filterByName = propFilter('name'); - -/** - * This filter checks the defined aggFilter in the schemas of that visualization - * and limits available aggregations based on that. - */ -aggTypeFilters.addFilter( - (aggType: IAggType, indexPatterns: IndexPattern, aggConfig: IAggConfig, aggFilter: string[]) => { - const doesSchemaAllowAggType = filterByName([aggType], aggFilter).length !== 0; - return doesSchemaAllowAggType; - } -); diff --git a/x-pack/plugins/rollup/kibana.json b/x-pack/plugins/rollup/kibana.json index 4c7dcb48a4d3f0..f897051d3ed8a6 100644 --- a/x-pack/plugins/rollup/kibana.json +++ b/x-pack/plugins/rollup/kibana.json @@ -7,8 +7,7 @@ "requiredPlugins": [ "indexPatternManagement", "management", - "licensing", - "data" + "licensing" ], "optionalPlugins": [ "home", diff --git a/x-pack/plugins/rollup/public/plugin.ts b/x-pack/plugins/rollup/public/plugin.ts index fd1b90fbc98557..5bb678ac35d06b 100644 --- a/x-pack/plugins/rollup/public/plugin.ts +++ b/x-pack/plugins/rollup/public/plugin.ts @@ -11,10 +11,6 @@ import { rollupBadgeExtension, rollupToggleExtension } from './extend_index_mana import { RollupIndexPatternCreationConfig } from './index_pattern_creation/rollup_index_pattern_creation_config'; // @ts-ignore import { RollupIndexPatternListConfig } from './index_pattern_list/rollup_index_pattern_list_config'; -// @ts-ignore -import { initAggTypeFilter } from './visualize/agg_type_filter'; -// @ts-ignore -import { initAggTypeFieldFilter } from './visualize/agg_type_field_filter'; import { CONFIG_ROLLUPS, UIM_APP_NAME } from '../common'; import { FeatureCatalogueCategory, @@ -25,7 +21,6 @@ import { CRUD_APP_BASE_PATH } from './crud_app/constants'; import { ManagementSetup } from '../../../../src/plugins/management/public'; import { IndexManagementPluginSetup } from '../../index_management/public'; import { IndexPatternManagementSetup } from '../../../../src/plugins/index_pattern_management/public'; -import { DataPublicPluginStart, search } from '../../../../src/plugins/data/public'; // @ts-ignore import { setEsBaseAndXPackBase, setHttp } from './crud_app/services/index'; import { setNotifications, setFatalErrors, setUiStatsReporter } from './kibana_services'; @@ -39,10 +34,6 @@ export interface RollupPluginSetupDependencies { usageCollection?: UsageCollectionSetup; } -export interface RollupPluginStartDependencies { - data: DataPublicPluginStart; -} - export class RollupPlugin implements Plugin { setup( core: CoreSetup, @@ -108,16 +99,9 @@ export class RollupPlugin implements Plugin { } } - start(core: CoreStart, plugins: RollupPluginStartDependencies) { + start(core: CoreStart) { setHttp(core.http); setNotifications(core.notifications); setEsBaseAndXPackBase(core.docLinks.ELASTIC_WEBSITE_URL, core.docLinks.DOC_LINK_VERSION); - - const isRollupIndexPatternsEnabled = core.uiSettings.get(CONFIG_ROLLUPS); - - if (isRollupIndexPatternsEnabled) { - initAggTypeFilter(search.aggs.aggTypeFilters); - initAggTypeFieldFilter(plugins.data.search.__LEGACY.aggTypeFieldFilters); - } } } diff --git a/x-pack/plugins/rollup/public/visualize/agg_type_field_filter.js b/x-pack/plugins/rollup/public/visualize/agg_type_field_filter.js deleted file mode 100644 index 6f44e0ef90efd9..00000000000000 --- a/x-pack/plugins/rollup/public/visualize/agg_type_field_filter.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export function initAggTypeFieldFilter(aggTypeFieldFilters) { - /** - * If rollup index pattern, check its capabilities - * and limit available fields for a given aggType based on that. - */ - aggTypeFieldFilters.addFilter((field, aggConfig) => { - const indexPattern = aggConfig.getIndexPattern(); - if (!indexPattern || indexPattern.type !== 'rollup') { - return true; - } - const aggName = aggConfig.type && aggConfig.type.name; - const aggFields = - indexPattern.typeMeta && indexPattern.typeMeta.aggs && indexPattern.typeMeta.aggs[aggName]; - return aggFields && aggFields[field.name]; - }); -} diff --git a/x-pack/plugins/rollup/public/visualize/agg_type_filter.js b/x-pack/plugins/rollup/public/visualize/agg_type_filter.js deleted file mode 100644 index 5f9fab3061a194..00000000000000 --- a/x-pack/plugins/rollup/public/visualize/agg_type_filter.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export function initAggTypeFilter(aggTypeFilters) { - /** - * If rollup index pattern, check its capabilities - * and limit available aggregations based on that. - */ - aggTypeFilters.addFilter((aggType, indexPattern) => { - if (indexPattern.type !== 'rollup') { - return true; - } - const aggName = aggType.name; - const aggs = indexPattern.typeMeta && indexPattern.typeMeta.aggs; - - // Return doc_count (which is collected by default for rollup date histogram, histogram, and terms) - // and the rest of the defined metrics from capabilities. - return aggName === 'count' || Object.keys(aggs).includes(aggName); - }); -} From f4db1c2b92841f36d87643d9a80754d1b7f66895 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Thu, 30 Apr 2020 21:26:08 -0700 Subject: [PATCH 40/47] Fixed alert Edit flyout shows the error message when one of this actions has a preconfigured action type (#64742) * Fixed alert Edit flyout shows the error message when one of this actions has a preconfigured action type * Added tests * fixed config * fixed tests * Fixed browser error about memory * Fixed type check * Fixed func tests * fixed more tests * fixed tests --- .../action_connector_form/action_form.tsx | 28 +++++++-- .../components/alert_instances_route.tsx | 3 +- .../sections/alert_form/alert_edit.tsx | 1 + .../apps/triggers_actions_ui/connectors.ts | 59 +++++++++--------- .../apps/triggers_actions_ui/details.ts | 60 ++++++++++++------- .../apps/triggers_actions_ui/home_page.ts | 8 ++- x-pack/test/functional_with_es_ssl/config.ts | 23 ++++++- 7 files changed, 121 insertions(+), 61 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 0027837c913d16..531e9e1926ff45 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -106,10 +106,6 @@ export const ActionForm = ({ index[actionTypeItem.id] = actionTypeItem; } setActionTypesIndex(index); - const hasActionsDisabled = actions.some(action => !index[action.actionTypeId].enabled); - if (setHasActionsDisabled) { - setHasActionsDisabled(hasActionsDisabled); - } } catch (e) { toastNotifications.addDanger({ title: i18n.translate( @@ -129,7 +125,8 @@ export const ActionForm = ({ (async () => { try { setIsLoadingConnectors(true); - setConnectors(await loadConnectors({ http })); + const loadedConnectors = await loadConnectors({ http }); + setConnectors(loadedConnectors); } catch (e) { toastNotifications.addDanger({ title: i18n.translate( @@ -146,6 +143,27 @@ export const ActionForm = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + const setActionTypesAvalilability = () => { + const preconfiguredConnectors = connectors.filter(connector => connector.isPreconfigured); + const hasActionsDisabled = actions.some( + action => + !actionTypesIndex![action.actionTypeId].enabled && + !checkActionFormActionTypeEnabled( + actionTypesIndex![action.actionTypeId], + preconfiguredConnectors + ).isEnabled + ); + if (setHasActionsDisabled) { + setHasActionsDisabled(hasActionsDisabled); + } + }; + if (connectors.length > 0 && actionTypesIndex) { + setActionTypesAvalilability(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [connectors, actionTypesIndex]); + const preconfiguredMessage = i18n.translate( 'xpack.triggersActionsUI.sections.actionForm.preconfiguredTitleMessage', { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.tsx index b9d08abae16842..a02b44523e26c1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.tsx @@ -32,7 +32,8 @@ export const AlertInstancesRoute: React.FunctionComponent = useEffect(() => { getAlertState(alert.id, loadAlertState, setAlertState, toastNotifications); - }, [alert, loadAlertState, toastNotifications]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [alert]); return alertState ? ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index c9cf59d87414fe..00bc9874face19 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -150,6 +150,7 @@ export const AlertEdit = ({ size="s" color="danger" iconType="alert" + data-test-subj="hasActionsDisabled" title={i18n.translate( 'xpack.triggersActionsUI.sections.alertEdit.disabledActionsWarningTitle', { defaultMessage: 'This alert has actions that are disabled' } diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts index 562f64656319e4..1facc05bc186d2 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts @@ -21,10 +21,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('Connectors', function() { before(async () => { await alerting.actions.createAction({ - name: `server-log-${Date.now()}`, - actionTypeId: '.server-log', + name: `slack-${Date.now()}`, + actionTypeId: '.slack', config: {}, - secrets: {}, + secrets: { + webhookUrl: 'https://test', + }, }); await pageObjects.common.navigateToApp('triggersActions'); @@ -36,12 +38,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.triggersActionsUI.clickCreateConnectorButton(); - await testSubjects.click('.server-log-card'); + await testSubjects.click('.slack-card'); + + await testSubjects.setValue('nameInput', connectorName); - const nameInput = await testSubjects.find('nameInput'); - await nameInput.click(); - await nameInput.clearValue(); - await nameInput.type(connectorName); + await testSubjects.setValue('slackWebhookUrlInput', 'https://test'); await find.clickByCssSelector('[data-test-subj="saveNewActionButton"]:not(disabled)'); @@ -54,7 +55,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(searchResults).to.eql([ { name: connectorName, - actionType: 'Server log', + actionType: 'Slack', referencedByCount: '0', }, ]); @@ -66,12 +67,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.triggersActionsUI.clickCreateConnectorButton(); - await testSubjects.click('.server-log-card'); + await testSubjects.click('.slack-card'); + + await testSubjects.setValue('nameInput', connectorName); - const nameInput = await testSubjects.find('nameInput'); - await nameInput.click(); - await nameInput.clearValue(); - await nameInput.type(connectorName); + await testSubjects.setValue('slackWebhookUrlInput', 'https://test'); await find.clickByCssSelector('[data-test-subj="saveNewActionButton"]:not(disabled)'); @@ -84,10 +84,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await find.clickByCssSelector('[data-test-subj="connectorsTableCell-name"] button'); - const nameInputToUpdate = await testSubjects.find('nameInput'); - await nameInputToUpdate.click(); - await nameInputToUpdate.clearValue(); - await nameInputToUpdate.type(updatedConnectorName); + await testSubjects.setValue('nameInput', updatedConnectorName); + + await testSubjects.setValue('slackWebhookUrlInput', 'https://test'); await find.clickByCssSelector('[data-test-subj="saveEditedActionButton"]:not(disabled)'); @@ -100,7 +99,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(searchResultsAfterEdit).to.eql([ { name: updatedConnectorName, - actionType: 'Server log', + actionType: 'Slack', referencedByCount: '0', }, ]); @@ -110,12 +109,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { async function createConnector(connectorName: string) { await pageObjects.triggersActionsUI.clickCreateConnectorButton(); - await testSubjects.click('.server-log-card'); + await testSubjects.click('.slack-card'); + + await testSubjects.setValue('nameInput', connectorName); - const nameInput = await testSubjects.find('nameInput'); - await nameInput.click(); - await nameInput.clearValue(); - await nameInput.type(connectorName); + await testSubjects.setValue('slackWebhookUrlInput', 'https://test'); await find.clickByCssSelector('[data-test-subj="saveNewActionButton"]:not(disabled)'); await pageObjects.common.closeToast(); @@ -148,12 +146,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { async function createConnector(connectorName: string) { await pageObjects.triggersActionsUI.clickCreateConnectorButton(); - await testSubjects.click('.server-log-card'); + await testSubjects.click('.slack-card'); + + await testSubjects.setValue('nameInput', connectorName); - const nameInput = await testSubjects.find('nameInput'); - await nameInput.click(); - await nameInput.clearValue(); - await nameInput.type(connectorName); + await testSubjects.setValue('slackWebhookUrlInput', 'https://test'); await find.clickByCssSelector('[data-test-subj="saveNewActionButton"]:not(disabled)'); await pageObjects.common.closeToast(); @@ -186,7 +183,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should not be able to delete a preconfigured connector', async () => { - const preconfiguredConnectorName = 'xyz'; + const preconfiguredConnectorName = 'Serverlog'; await pageObjects.triggersActionsUI.searchConnectors(preconfiguredConnectorName); const searchResults = await pageObjects.triggersActionsUI.getConnectorsList(); @@ -197,7 +194,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should not be able to edit a preconfigured connector', async () => { - const preconfiguredConnectorName = 'xyz'; + const preconfiguredConnectorName = 'xyztest'; await pageObjects.triggersActionsUI.searchConnectors(preconfiguredConnectorName); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index 6ff065c1f4ab24..7970c9b24427e1 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -27,16 +27,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const actions = await Promise.all([ alerting.actions.createAction({ - name: `server-log-${testRunUuid}-${0}`, - actionTypeId: '.server-log', + name: `slack-${testRunUuid}-${0}`, + actionTypeId: '.slack', config: {}, - secrets: {}, + secrets: { + webhookUrl: 'https://test', + }, }), alerting.actions.createAction({ - name: `server-log-${testRunUuid}-${1}`, - actionTypeId: '.server-log', + name: `slack-${testRunUuid}-${1}`, + actionTypeId: '.slack', config: {}, - secrets: {}, + secrets: { + webhookUrl: 'https://test', + }, }), ]); @@ -72,7 +76,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(alertType).to.be(`Always Firing`); const { actionType, actionCount } = await pageObjects.alertDetailsUI.getActionsLabels(); - expect(actionType).to.be(`Server log`); + expect(actionType).to.be(`Slack`); expect(actionCount).to.be(`+1`); }); @@ -168,7 +172,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const alert = await alerting.alerts.createAlertWithActions( testRunUuid, '.index-threshold', - params + params, + [ + { + group: 'threshold met', + id: 'my-server-log', + params: { level: 'info', message: ' {{context.message}}' }, + }, + ] ); // refresh to see alert await browser.refresh(); @@ -183,6 +194,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const editButton = await testSubjects.find('openEditAlertFlyoutButton'); await editButton.click(); + expect(await testSubjects.exists('hasActionsDisabled')).to.eql(false); const updatedAlertName = `Changed Alert Name ${uuid.v4()}`; await testSubjects.setValue('alertNameInput', updatedAlertName, { @@ -304,16 +316,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const actions = await Promise.all([ alerting.actions.createAction({ - name: `server-log-${testRunUuid}-${0}`, - actionTypeId: '.server-log', + name: `slack-${testRunUuid}-${0}`, + actionTypeId: '.slack', config: {}, - secrets: {}, + secrets: { + webhookUrl: 'https://test', + }, }), alerting.actions.createAction({ - name: `server-log-${testRunUuid}-${1}`, - actionTypeId: '.server-log', + name: `slack-${testRunUuid}-${1}`, + actionTypeId: '.slack', config: {}, - secrets: {}, + secrets: { + webhookUrl: 'https://test', + }, }), ]); @@ -516,16 +532,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const actions = await Promise.all([ alerting.actions.createAction({ - name: `server-log-${testRunUuid}-${0}`, - actionTypeId: '.server-log', + name: `slack-${testRunUuid}-${0}`, + actionTypeId: '.slack', config: {}, - secrets: {}, + secrets: { + webhookUrl: 'https://test', + }, }), alerting.actions.createAction({ - name: `server-log-${testRunUuid}-${1}`, - actionTypeId: '.server-log', + name: `slack-${testRunUuid}-${1}`, + actionTypeId: '.slack', config: {}, - secrets: {}, + secrets: { + webhookUrl: 'https://test', + }, }), ]); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts index f049406b639c79..2edab1b164a1bb 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts @@ -59,10 +59,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('navigates to an alert details page', async () => { const action = await alerting.actions.createAction({ - name: `server-log-${Date.now()}`, - actionTypeId: '.server-log', + name: `Slack-${Date.now()}`, + actionTypeId: '.slack', config: {}, - secrets: {}, + secrets: { + webhookUrl: 'https://test', + }, }); const alert = await alerting.alerts.createAlwaysFiringWithAction( diff --git a/x-pack/test/functional_with_es_ssl/config.ts b/x-pack/test/functional_with_es_ssl/config.ts index 71b22a336f6b92..ef2270fb97745d 100644 --- a/x-pack/test/functional_with_es_ssl/config.ts +++ b/x-pack/test/functional_with_es_ssl/config.ts @@ -10,6 +10,21 @@ import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { services } from './services'; import { pageObjects } from './page_objects'; +// .server-log is specifically not enabled +const enabledActionTypes = [ + '.email', + '.index', + '.pagerduty', + '.servicenow', + '.slack', + '.webhook', + 'test.authorization', + 'test.failing', + 'test.index-record', + 'test.noop', + 'test.rate-limit', +]; + // eslint-disable-next-line import/no-default-export export default async function({ readConfigFile }: FtrConfigProviderContext) { const xpackFunctionalConfig = await readConfigFile(require.resolve('../functional/config.js')); @@ -50,15 +65,21 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { `--elasticsearch.hosts=https://${servers.elasticsearch.hostname}:${servers.elasticsearch.port}`, `--elasticsearch.ssl.certificateAuthorities=${CA_CERT_PATH}`, `--plugin-path=${join(__dirname, 'fixtures', 'plugins', 'alerts')}`, + `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, `--xpack.actions.preconfigured=${JSON.stringify([ { id: 'my-slack1', actionTypeId: '.slack', - name: 'Slack#xyz', + name: 'Slack#xyztest', config: { webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', }, }, + { + id: 'my-server-log', + actionTypeId: '.server-log', + name: 'Serverlog#xyz', + }, ])}`, ], }, From 728c34fc3c6a5d605b48b9c8ac56e18533c67c96 Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 30 Apr 2020 23:58:46 -0700 Subject: [PATCH 41/47] [legacy/server/config] remove unnecessary deps for simple helper (#64954) * [legacy/server/config] remove unnecessary deps for simple helper * remove unnecessary change * expand test coverage a smidge * explode dot-separated keys * add a test for really deep keys Co-authored-by: spalger --- src/legacy/server/config/config.js | 2 +- src/legacy/server/config/explode_by.js | 37 ------ src/legacy/server/config/explode_by.test.js | 48 -------- src/legacy/server/config/override.js | 28 ----- src/legacy/server/config/override.test.js | 44 ------- src/legacy/server/config/override.test.ts | 130 ++++++++++++++++++++ src/legacy/server/config/override.ts | 52 ++++++++ 7 files changed, 183 insertions(+), 158 deletions(-) delete mode 100644 src/legacy/server/config/explode_by.js delete mode 100644 src/legacy/server/config/explode_by.test.js delete mode 100644 src/legacy/server/config/override.js delete mode 100644 src/legacy/server/config/override.test.js create mode 100644 src/legacy/server/config/override.test.ts create mode 100644 src/legacy/server/config/override.ts diff --git a/src/legacy/server/config/config.js b/src/legacy/server/config/config.js index c31ded608dd317..b186071edeaf7f 100644 --- a/src/legacy/server/config/config.js +++ b/src/legacy/server/config/config.js @@ -19,7 +19,7 @@ import Joi from 'joi'; import _ from 'lodash'; -import override from './override'; +import { override } from './override'; import createDefaultSchema from './schema'; import { unset, deepCloneWithBuffers as clone, IS_KIBANA_DISTRIBUTABLE } from '../../utils'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths diff --git a/src/legacy/server/config/explode_by.js b/src/legacy/server/config/explode_by.js deleted file mode 100644 index 46347feca550d8..00000000000000 --- a/src/legacy/server/config/explode_by.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; - -export default function(dot, flatObject) { - const fullObject = {}; - _.each(flatObject, function(value, key) { - const keys = key.split(dot); - (function walk(memo, keys, value) { - const _key = keys.shift(); - if (keys.length === 0) { - memo[_key] = value; - } else { - if (!memo[_key]) memo[_key] = {}; - walk(memo[_key], keys, value); - } - })(fullObject, keys, value); - }); - return fullObject; -} diff --git a/src/legacy/server/config/explode_by.test.js b/src/legacy/server/config/explode_by.test.js deleted file mode 100644 index 741edba27d325e..00000000000000 --- a/src/legacy/server/config/explode_by.test.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import explodeBy from './explode_by'; - -describe('explode_by(dot, flatObject)', function() { - it('should explode a flatten object with dots', function() { - const flatObject = { - 'test.enable': true, - 'test.hosts': ['host-01', 'host-02'], - }; - expect(explodeBy('.', flatObject)).toEqual({ - test: { - enable: true, - hosts: ['host-01', 'host-02'], - }, - }); - }); - - it('should explode a flatten object with slashes', function() { - const flatObject = { - 'test/enable': true, - 'test/hosts': ['host-01', 'host-02'], - }; - expect(explodeBy('/', flatObject)).toEqual({ - test: { - enable: true, - hosts: ['host-01', 'host-02'], - }, - }); - }); -}); diff --git a/src/legacy/server/config/override.js b/src/legacy/server/config/override.js deleted file mode 100644 index bab9387ac006fa..00000000000000 --- a/src/legacy/server/config/override.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import explodeBy from './explode_by'; -import { getFlattenedObject } from '../../../core/utils'; - -export default function(target, source) { - const _target = getFlattenedObject(target); - const _source = getFlattenedObject(source); - return explodeBy('.', _.defaults(_source, _target)); -} diff --git a/src/legacy/server/config/override.test.js b/src/legacy/server/config/override.test.js deleted file mode 100644 index 331c586e28a87a..00000000000000 --- a/src/legacy/server/config/override.test.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import override from './override'; - -describe('override(target, source)', function() { - it('should override the values form source to target', function() { - const target = { - test: { - enable: true, - host: ['host-01', 'host-02'], - client: { - type: 'sql', - }, - }, - }; - const source = { test: { client: { type: 'nosql' } } }; - expect(override(target, source)).toEqual({ - test: { - enable: true, - host: ['host-01', 'host-02'], - client: { - type: 'nosql', - }, - }, - }); - }); -}); diff --git a/src/legacy/server/config/override.test.ts b/src/legacy/server/config/override.test.ts new file mode 100644 index 00000000000000..4e21a88e79e61d --- /dev/null +++ b/src/legacy/server/config/override.test.ts @@ -0,0 +1,130 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { override } from './override'; + +describe('override(target, source)', function() { + it('should override the values form source to target', function() { + const target = { + test: { + enable: true, + host: ['something else'], + client: { + type: 'sql', + }, + }, + }; + + const source = { + test: { + host: ['host-01', 'host-02'], + client: { + type: 'nosql', + }, + foo: { + bar: { + baz: 1, + }, + }, + }, + }; + + expect(override(target, source)).toMatchInlineSnapshot(` + Object { + "test": Object { + "client": Object { + "type": "nosql", + }, + "enable": true, + "foo": Object { + "bar": Object { + "baz": 1, + }, + }, + "host": Array [ + "host-01", + "host-02", + ], + }, + } + `); + }); + + it('does not mutate arguments', () => { + const target = { + foo: { + bar: 1, + baz: 1, + }, + }; + + const source = { + foo: { + bar: 2, + }, + box: 2, + }; + + expect(override(target, source)).toMatchInlineSnapshot(` + Object { + "box": 2, + "foo": Object { + "bar": 2, + "baz": 1, + }, + } + `); + expect(target).not.toHaveProperty('box'); + expect(source.foo).not.toHaveProperty('baz'); + }); + + it('explodes keys with dots in them', () => { + const target = { + foo: { + bar: 1, + }, + 'baz.box.boot.bar.bar': 20, + }; + + const source = { + 'foo.bar': 2, + 'baz.box.boot': { + 'bar.foo': 10, + }, + }; + + expect(override(target, source)).toMatchInlineSnapshot(` + Object { + "baz": Object { + "box": Object { + "boot": Object { + "bar": Object { + "bar": 20, + "foo": 10, + }, + }, + }, + }, + "foo": Object { + "bar": 2, + }, + } + `); + }); +}); diff --git a/src/legacy/server/config/override.ts b/src/legacy/server/config/override.ts new file mode 100644 index 00000000000000..3dd7d62016004d --- /dev/null +++ b/src/legacy/server/config/override.ts @@ -0,0 +1,52 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const isObject = (v: any): v is Record => + typeof v === 'object' && v !== null && !Array.isArray(v); + +const assignDeep = (target: Record, source: Record) => { + for (let [key, value] of Object.entries(source)) { + // unwrap dot-separated keys + if (key.includes('.')) { + const [first, ...others] = key.split('.'); + key = first; + value = { [others.join('.')]: value }; + } + + if (isObject(value)) { + if (!target.hasOwnProperty(key)) { + target[key] = {}; + } + + assignDeep(target[key], value); + } else { + target[key] = value; + } + } +}; + +export const override = (...sources: Array>): Record => { + const result = {}; + + for (const object of sources) { + assignDeep(result, object); + } + + return result; +}; From c8ddb6b8edefaaf88f028f7ab46d3ace16218122 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Fri, 1 May 2020 12:57:29 +0200 Subject: [PATCH 42/47] load lens app lazily (#64769) --- .../lens/public/app_plugin/mounter.tsx | 118 ++++++++++ x-pack/plugins/lens/public/helpers/index.ts | 1 + .../lens/public/helpers/is_rison_object.ts | 12 + x-pack/plugins/lens/public/plugin.ts | 103 +++++++++ x-pack/plugins/lens/public/plugin.tsx | 209 ------------------ 5 files changed, 234 insertions(+), 209 deletions(-) create mode 100644 x-pack/plugins/lens/public/app_plugin/mounter.tsx create mode 100644 x-pack/plugins/lens/public/helpers/is_rison_object.ts create mode 100644 x-pack/plugins/lens/public/plugin.ts delete mode 100644 x-pack/plugins/lens/public/plugin.tsx diff --git a/x-pack/plugins/lens/public/app_plugin/mounter.tsx b/x-pack/plugins/lens/public/app_plugin/mounter.tsx new file mode 100644 index 00000000000000..f295f88a58e5f9 --- /dev/null +++ b/x-pack/plugins/lens/public/app_plugin/mounter.tsx @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; + +import { AppMountParameters, CoreSetup } from 'kibana/public'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; +import { HashRouter, Route, RouteComponentProps, Switch } from 'react-router-dom'; +import { render, unmountComponentAtNode } from 'react-dom'; + +import rison from 'rison-node'; +import { DashboardConstants } from '../../../../../src/plugins/dashboard/public'; +import { Storage } from '../../../../../src/plugins/kibana_utils/public'; + +import { LensReportManager, setReportManager, trackUiEvent } from '../lens_ui_telemetry'; + +import { App } from './app'; +import { EditorFrameStart } from '../types'; +import { addEmbeddableToDashboardUrl, getUrlVars, isRisonObject } from '../helpers'; +import { addHelpMenuToAppChrome } from '../help_menu_util'; +import { SavedObjectIndexStore } from '../persistence'; +import { LensPluginStartDependencies } from '../plugin'; + +export async function mountApp( + core: CoreSetup, + params: AppMountParameters, + createEditorFrame: EditorFrameStart['createInstance'] +) { + const [coreStart, startDependencies] = await core.getStartServices(); + const { data: dataStart, navigation } = startDependencies; + const savedObjectsClient = coreStart.savedObjects.client; + addHelpMenuToAppChrome(coreStart.chrome, coreStart.docLinks); + + const instance = await createEditorFrame(); + + setReportManager( + new LensReportManager({ + storage: new Storage(localStorage), + http: core.http, + }) + ); + const updateUrlTime = (urlVars: Record): void => { + const decoded = rison.decode(urlVars._g); + if (!isRisonObject(decoded)) { + return; + } + // @ts-ignore + decoded.time = dataStart.query.timefilter.timefilter.getTime(); + urlVars._g = rison.encode(decoded); + }; + const redirectTo = ( + routeProps: RouteComponentProps<{ id?: string }>, + addToDashboardMode: boolean, + id?: string + ) => { + if (!id) { + routeProps.history.push('/lens'); + } else if (!addToDashboardMode) { + routeProps.history.push(`/lens/edit/${id}`); + } else if (addToDashboardMode && id) { + routeProps.history.push(`/lens/edit/${id}`); + const lastDashboardLink = coreStart.chrome.navLinks.get('kibana:dashboard'); + if (!lastDashboardLink || !lastDashboardLink.url) { + throw new Error('Cannot get last dashboard url'); + } + const urlVars = getUrlVars(lastDashboardLink.url); + updateUrlTime(urlVars); // we need to pass in timerange in query params directly + const dashboardUrl = addEmbeddableToDashboardUrl(lastDashboardLink.url, id, urlVars); + window.history.pushState({}, '', dashboardUrl); + } + }; + + const renderEditor = (routeProps: RouteComponentProps<{ id?: string }>) => { + trackUiEvent('loaded'); + const addToDashboardMode = + !!routeProps.location.search && + routeProps.location.search.includes( + DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM + ); + return ( + redirectTo(routeProps, addToDashboardMode, id)} + addToDashboardMode={addToDashboardMode} + /> + ); + }; + + function NotFound() { + trackUiEvent('loaded_404'); + return ; + } + + render( + + + + + + + + + , + params.element + ); + return () => { + instance.unmount(); + unmountComponentAtNode(params.element); + }; +} diff --git a/x-pack/plugins/lens/public/helpers/index.ts b/x-pack/plugins/lens/public/helpers/index.ts index f464b5dcc97a32..69a22d19ffbef8 100644 --- a/x-pack/plugins/lens/public/helpers/index.ts +++ b/x-pack/plugins/lens/public/helpers/index.ts @@ -5,3 +5,4 @@ */ export { addEmbeddableToDashboardUrl, getUrlVars } from './url_helper'; +export { isRisonObject } from './is_rison_object'; diff --git a/x-pack/plugins/lens/public/helpers/is_rison_object.ts b/x-pack/plugins/lens/public/helpers/is_rison_object.ts new file mode 100644 index 00000000000000..81976c9a83320a --- /dev/null +++ b/x-pack/plugins/lens/public/helpers/is_rison_object.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { RisonObject, RisonValue } from 'rison-node'; +import { isObject } from 'lodash'; + +export const isRisonObject = (value: RisonValue): value is RisonObject => { + return isObject(value); +}; diff --git a/x-pack/plugins/lens/public/plugin.ts b/x-pack/plugins/lens/public/plugin.ts new file mode 100644 index 00000000000000..a6acc61922177c --- /dev/null +++ b/x-pack/plugins/lens/public/plugin.ts @@ -0,0 +1,103 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AppMountParameters, CoreSetup, CoreStart } from 'kibana/public'; +import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public'; +import { EmbeddableSetup, EmbeddableStart } from 'src/plugins/embeddable/public'; +import { ExpressionsSetup, ExpressionsStart } from 'src/plugins/expressions/public'; +import { VisualizationsSetup } from 'src/plugins/visualizations/public'; +import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; +import { KibanaLegacySetup } from 'src/plugins/kibana_legacy/public'; +import { EditorFrameService } from './editor_frame_service'; +import { IndexPatternDatasource } from './indexpattern_datasource'; +import { XyVisualization } from './xy_visualization'; +import { MetricVisualization } from './metric_visualization'; +import { DatatableVisualization } from './datatable_visualization'; +import { stopReportManager } from './lens_ui_telemetry'; + +import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; +import { NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../common'; +import { EditorFrameStart } from './types'; +import { getLensAliasConfig } from './vis_type_alias'; + +import './index.scss'; + +export interface LensPluginSetupDependencies { + kibanaLegacy: KibanaLegacySetup; + expressions: ExpressionsSetup; + data: DataPublicPluginSetup; + embeddable?: EmbeddableSetup; + visualizations: VisualizationsSetup; +} + +export interface LensPluginStartDependencies { + data: DataPublicPluginStart; + embeddable: EmbeddableStart; + expressions: ExpressionsStart; + navigation: NavigationPublicPluginStart; + uiActions: UiActionsStart; +} + +export class LensPlugin { + private datatableVisualization: DatatableVisualization; + private editorFrameService: EditorFrameService; + private createEditorFrame: EditorFrameStart['createInstance'] | null = null; + private indexpatternDatasource: IndexPatternDatasource; + private xyVisualization: XyVisualization; + private metricVisualization: MetricVisualization; + + constructor() { + this.datatableVisualization = new DatatableVisualization(); + this.editorFrameService = new EditorFrameService(); + this.indexpatternDatasource = new IndexPatternDatasource(); + this.xyVisualization = new XyVisualization(); + this.metricVisualization = new MetricVisualization(); + } + + setup( + core: CoreSetup, + { kibanaLegacy, expressions, data, embeddable, visualizations }: LensPluginSetupDependencies + ) { + const editorFrameSetupInterface = this.editorFrameService.setup(core, { + data, + embeddable, + expressions, + }); + const dependencies = { + expressions, + data, + editorFrame: editorFrameSetupInterface, + formatFactory: core + .getStartServices() + .then(([_, { data: dataStart }]) => dataStart.fieldFormats.deserialize), + }; + this.indexpatternDatasource.setup(core, dependencies); + this.xyVisualization.setup(core, dependencies); + this.datatableVisualization.setup(core, dependencies); + this.metricVisualization.setup(core, dependencies); + + visualizations.registerAlias(getLensAliasConfig()); + + kibanaLegacy.registerLegacyApp({ + id: 'lens', + title: NOT_INTERNATIONALIZED_PRODUCT_NAME, + mount: async (params: AppMountParameters) => { + const { mountApp } = await import('./app_plugin/mounter'); + return mountApp(core, params, this.createEditorFrame!); + }, + }); + } + + start(core: CoreStart, startDependencies: LensPluginStartDependencies) { + this.createEditorFrame = this.editorFrameService.start(core, startDependencies).createInstance; + this.xyVisualization.start(core, startDependencies); + this.datatableVisualization.start(core, startDependencies); + } + + stop() { + stopReportManager(); + } +} diff --git a/x-pack/plugins/lens/public/plugin.tsx b/x-pack/plugins/lens/public/plugin.tsx deleted file mode 100644 index fe0e81177e2599..00000000000000 --- a/x-pack/plugins/lens/public/plugin.tsx +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; -import { HashRouter, Route, RouteComponentProps, Switch } from 'react-router-dom'; -import { render, unmountComponentAtNode } from 'react-dom'; -import rison, { RisonObject, RisonValue } from 'rison-node'; -import { isObject } from 'lodash'; - -import { AppMountParameters, CoreSetup, CoreStart } from 'kibana/public'; -import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public'; -import { EmbeddableSetup, EmbeddableStart } from 'src/plugins/embeddable/public'; -import { ExpressionsSetup, ExpressionsStart } from 'src/plugins/expressions/public'; -import { VisualizationsSetup } from 'src/plugins/visualizations/public'; -import { NavigationPublicPluginStart } from 'src/plugins/navigation/public'; -import { KibanaLegacySetup } from 'src/plugins/kibana_legacy/public'; -import { DashboardConstants } from '../../../../src/plugins/dashboard/public'; -import { Storage } from '../../../../src/plugins/kibana_utils/public'; -import { EditorFrameService } from './editor_frame_service'; -import { IndexPatternDatasource } from './indexpattern_datasource'; -import { addHelpMenuToAppChrome } from './help_menu_util'; -import { SavedObjectIndexStore } from './persistence'; -import { XyVisualization } from './xy_visualization'; -import { MetricVisualization } from './metric_visualization'; -import { DatatableVisualization } from './datatable_visualization'; -import { App } from './app_plugin'; -import { - LensReportManager, - setReportManager, - stopReportManager, - trackUiEvent, -} from './lens_ui_telemetry'; - -import { UiActionsStart } from '../../../../src/plugins/ui_actions/public'; -import { NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../common'; -import { addEmbeddableToDashboardUrl, getUrlVars } from './helpers'; -import { EditorFrameStart } from './types'; -import { getLensAliasConfig } from './vis_type_alias'; - -import './index.scss'; - -export interface LensPluginSetupDependencies { - kibanaLegacy: KibanaLegacySetup; - expressions: ExpressionsSetup; - data: DataPublicPluginSetup; - embeddable?: EmbeddableSetup; - visualizations: VisualizationsSetup; -} - -export interface LensPluginStartDependencies { - data: DataPublicPluginStart; - embeddable: EmbeddableStart; - expressions: ExpressionsStart; - navigation: NavigationPublicPluginStart; - uiActions: UiActionsStart; -} - -export const isRisonObject = (value: RisonValue): value is RisonObject => { - return isObject(value); -}; -export class LensPlugin { - private datatableVisualization: DatatableVisualization; - private editorFrameService: EditorFrameService; - private createEditorFrame: EditorFrameStart['createInstance'] | null = null; - private indexpatternDatasource: IndexPatternDatasource; - private xyVisualization: XyVisualization; - private metricVisualization: MetricVisualization; - - constructor() { - this.datatableVisualization = new DatatableVisualization(); - this.editorFrameService = new EditorFrameService(); - this.indexpatternDatasource = new IndexPatternDatasource(); - this.xyVisualization = new XyVisualization(); - this.metricVisualization = new MetricVisualization(); - } - - setup( - core: CoreSetup, - { kibanaLegacy, expressions, data, embeddable, visualizations }: LensPluginSetupDependencies - ) { - const editorFrameSetupInterface = this.editorFrameService.setup(core, { - data, - embeddable, - expressions, - }); - const dependencies = { - expressions, - data, - editorFrame: editorFrameSetupInterface, - formatFactory: core - .getStartServices() - .then(([_, { data: dataStart }]) => dataStart.fieldFormats.deserialize), - }; - this.indexpatternDatasource.setup(core, dependencies); - this.xyVisualization.setup(core, dependencies); - this.datatableVisualization.setup(core, dependencies); - this.metricVisualization.setup(core, dependencies); - - visualizations.registerAlias(getLensAliasConfig()); - - kibanaLegacy.registerLegacyApp({ - id: 'lens', - title: NOT_INTERNATIONALIZED_PRODUCT_NAME, - mount: async (params: AppMountParameters) => { - const [coreStart, startDependencies] = await core.getStartServices(); - const { data: dataStart, navigation } = startDependencies; - const savedObjectsClient = coreStart.savedObjects.client; - addHelpMenuToAppChrome(coreStart.chrome, coreStart.docLinks); - - const instance = await this.createEditorFrame!(); - - setReportManager( - new LensReportManager({ - storage: new Storage(localStorage), - http: core.http, - }) - ); - const updateUrlTime = (urlVars: Record): void => { - const decoded = rison.decode(urlVars._g); - if (!isRisonObject(decoded)) { - return; - } - // @ts-ignore - decoded.time = dataStart.query.timefilter.timefilter.getTime(); - urlVars._g = rison.encode(decoded); - }; - const redirectTo = ( - routeProps: RouteComponentProps<{ id?: string }>, - addToDashboardMode: boolean, - id?: string - ) => { - if (!id) { - routeProps.history.push('/lens'); - } else if (!addToDashboardMode) { - routeProps.history.push(`/lens/edit/${id}`); - } else if (addToDashboardMode && id) { - routeProps.history.push(`/lens/edit/${id}`); - const lastDashboardLink = coreStart.chrome.navLinks.get('kibana:dashboard'); - if (!lastDashboardLink || !lastDashboardLink.url) { - throw new Error('Cannot get last dashboard url'); - } - const urlVars = getUrlVars(lastDashboardLink.url); - updateUrlTime(urlVars); // we need to pass in timerange in query params directly - const dashboardUrl = addEmbeddableToDashboardUrl(lastDashboardLink.url, id, urlVars); - window.history.pushState({}, '', dashboardUrl); - } - }; - - const renderEditor = (routeProps: RouteComponentProps<{ id?: string }>) => { - trackUiEvent('loaded'); - const addToDashboardMode = - !!routeProps.location.search && - routeProps.location.search.includes( - DashboardConstants.ADD_VISUALIZATION_TO_DASHBOARD_MODE_PARAM - ); - return ( - redirectTo(routeProps, addToDashboardMode, id)} - addToDashboardMode={addToDashboardMode} - /> - ); - }; - - function NotFound() { - trackUiEvent('loaded_404'); - return ; - } - - render( - - - - - - - - - , - params.element - ); - return () => { - instance.unmount(); - unmountComponentAtNode(params.element); - }; - }, - }); - } - - start(core: CoreStart, startDependencies: LensPluginStartDependencies) { - this.createEditorFrame = this.editorFrameService.start(core, startDependencies).createInstance; - this.xyVisualization.start(core, startDependencies); - this.datatableVisualization.start(core, startDependencies); - } - - stop() { - stopReportManager(); - } -} From 523926f0a49a5bd951ed0e056610b049482adbca Mon Sep 17 00:00:00 2001 From: Shahzad Date: Fri, 1 May 2020 16:04:19 +0200 Subject: [PATCH 43/47] [Uptime] Certificates page (#64059) --- x-pack/plugins/uptime/common/constants/ui.ts | 9 + .../uptime/common/runtime_types/certs.ts | 30 +- .../uptime/common/runtime_types/ping/ping.ts | 4 +- .../__snapshots__/cert_monitors.test.tsx.snap | 134 + .../__snapshots__/cert_search.test.tsx.snap | 93 + .../__snapshots__/cert_status.test.tsx.snap | 100 + .../certificates_list.test.tsx.snap | 70 + .../fingerprint_col.test.tsx.snap | 171 ++ .../__tests__/cert_monitors.test.tsx | 24 + .../__tests__/cert_search.test.tsx | 18 + .../__tests__/cert_status.test.tsx | 42 + .../__tests__/certificates_list.test.tsx | 26 + .../__tests__/fingerprint_col.test.tsx | 34 + .../components/certificates/cert_monitors.tsx | 31 + .../components/certificates/cert_search.tsx | 34 + .../components/certificates/cert_status.tsx | 49 + .../certificates/certificates_list.tsx | 113 + .../certificates/fingerprint_col.tsx | 46 + .../public/components/certificates/index.ts | 11 + .../components/certificates/translations.ts | 63 + .../monitor_page_link.test.tsx.snap | 0 .../__tests__/monitor_page_link.test.tsx | 2 +- .../monitor_page_link.tsx | 4 +- .../monitor_ssl_certificate.test.tsx.snap | 31 - .../ssl_certificate.test.tsx.snap | 119 + .../__test__/monitor_status.bar.test.tsx | 7 + ...cate.test.tsx => ssl_certificate.test.tsx} | 46 +- .../monitor_status_bar/ssl_certificate.tsx | 102 +- .../__snapshots__/monitor_list.test.tsx.snap | 84 +- .../__tests__/monitor_list.test.tsx | 7 + .../monitor_list/cert_status_column.tsx | 51 + .../overview/monitor_list/monitor_list.tsx | 46 +- .../monitor_list_drawer/most_recent_error.tsx | 2 +- .../overview/monitor_list/translations.ts | 4 + x-pack/plugins/uptime/public/hooks/index.ts | 1 + .../uptime/public/hooks/use_cert_status.ts | 42 + .../uptime/public/hooks/use_telemetry.ts | 1 + .../__snapshots__/certificates.test.tsx.snap | 56 + .../pages/__tests__/certificates.test.tsx | 15 + .../uptime/public/pages/certificates.tsx | 136 + .../plugins/uptime/public/pages/monitor.tsx | 11 +- .../uptime/public/pages/page_header.tsx | 2 +- .../uptime/public/pages/translations.ts | 15 + x-pack/plugins/uptime/public/routes.tsx | 13 +- .../uptime/public/state/api/certificates.ts | 13 + .../public/state/certificates/certificates.ts | 43 + .../uptime/public/state/effects/index.ts | 2 + .../uptime/public/state/reducers/index.ts | 2 + .../public/state/reducers/ml_anomaly.ts | 13 +- .../public/state/reducers/monitor_list.ts | 2 +- .../uptime/public/state/reducers/utils.ts | 4 +- .../state/selectors/__tests__/index.test.ts | 6 + .../lib/requests/__tests__/get_certs.test.ts | 104 +- .../__tests__/get_latest_monitor.test.ts | 13 +- .../uptime/server/lib/requests/get_certs.ts | 110 +- .../server/lib/requests/get_latest_monitor.ts | 18 +- .../requests/search/enrich_monitor_groups.ts | 8 +- .../server/lib/requests/uptime_requests.ts | 15 +- .../server/rest_api/{ => certs}/certs.ts | 39 +- .../plugins/uptime/server/rest_api/index.ts | 3 +- .../api_integration/apis/uptime/rest/certs.ts | 15 +- .../rest/fixtures/monitor_latest_status.json | 5 +- .../apis/uptime/rest/helper/make_checks.ts | 154 +- .../apis/uptime/rest/helper/make_ping.ts | 118 + .../apis/uptime/rest/helper/make_tls.ts | 70 + .../functional/apps/uptime/certificates.ts | 62 + x-pack/test/functional/apps/uptime/index.ts | 1 + .../es_archives/uptime/blank/mappings.json | 44 +- .../uptime/full_heartbeat/mappings.json | 2664 +++++++++++++---- .../functional/page_objects/uptime_page.ts | 5 +- .../services/uptime/certificates.ts | 50 + .../functional/services/uptime/navigation.ts | 13 +- .../test/functional/services/uptime/uptime.ts | 3 + 73 files changed, 4489 insertions(+), 1009 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/cert_monitors.test.tsx.snap create mode 100644 x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/cert_search.test.tsx.snap create mode 100644 x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/cert_status.test.tsx.snap create mode 100644 x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/certificates_list.test.tsx.snap create mode 100644 x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/fingerprint_col.test.tsx.snap create mode 100644 x-pack/plugins/uptime/public/components/certificates/__tests__/cert_monitors.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/certificates/__tests__/cert_search.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/certificates/__tests__/cert_status.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/certificates/__tests__/certificates_list.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/certificates/__tests__/fingerprint_col.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/certificates/cert_monitors.tsx create mode 100644 x-pack/plugins/uptime/public/components/certificates/cert_search.tsx create mode 100644 x-pack/plugins/uptime/public/components/certificates/cert_status.tsx create mode 100644 x-pack/plugins/uptime/public/components/certificates/certificates_list.tsx create mode 100644 x-pack/plugins/uptime/public/components/certificates/fingerprint_col.tsx create mode 100644 x-pack/plugins/uptime/public/components/certificates/index.ts create mode 100644 x-pack/plugins/uptime/public/components/certificates/translations.ts rename x-pack/plugins/uptime/public/components/{overview/monitor_list => common}/__tests__/__snapshots__/monitor_page_link.test.tsx.snap (100%) rename x-pack/plugins/uptime/public/components/{overview/monitor_list => common}/__tests__/monitor_page_link.test.tsx (100%) rename x-pack/plugins/uptime/public/components/{overview/monitor_list => common}/monitor_page_link.tsx (89%) delete mode 100644 x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_ssl_certificate.test.tsx.snap create mode 100644 x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/ssl_certificate.test.tsx.snap rename x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/{monitor_ssl_certificate.test.tsx => ssl_certificate.test.tsx} (55%) create mode 100644 x-pack/plugins/uptime/public/components/overview/monitor_list/cert_status_column.tsx create mode 100644 x-pack/plugins/uptime/public/hooks/use_cert_status.ts create mode 100644 x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/certificates.test.tsx.snap create mode 100644 x-pack/plugins/uptime/public/pages/__tests__/certificates.test.tsx create mode 100644 x-pack/plugins/uptime/public/pages/certificates.tsx create mode 100644 x-pack/plugins/uptime/public/state/api/certificates.ts create mode 100644 x-pack/plugins/uptime/public/state/certificates/certificates.ts rename x-pack/plugins/uptime/server/rest_api/{ => certs}/certs.ts (63%) create mode 100644 x-pack/test/api_integration/apis/uptime/rest/helper/make_ping.ts create mode 100644 x-pack/test/api_integration/apis/uptime/rest/helper/make_tls.ts create mode 100644 x-pack/test/functional/apps/uptime/certificates.ts create mode 100644 x-pack/test/functional/services/uptime/certificates.ts diff --git a/x-pack/plugins/uptime/common/constants/ui.ts b/x-pack/plugins/uptime/common/constants/ui.ts index 29e8dabf53f920..3bf3e3cc0a2cc3 100644 --- a/x-pack/plugins/uptime/common/constants/ui.ts +++ b/x-pack/plugins/uptime/common/constants/ui.ts @@ -10,6 +10,8 @@ export const OVERVIEW_ROUTE = '/'; export const SETTINGS_ROUTE = '/settings'; +export const CERTIFICATES_ROUTE = '/certificates'; + export enum STATUS { UP = 'up', DOWN = 'down', @@ -41,3 +43,10 @@ export const SHORT_TIMESPAN_LOCALE = { yy: '%d Yr', }, }; + +export enum CERT_STATUS { + OK = 'OK', + EXPIRING_SOON = 'EXPIRING_SOON', + EXPIRED = 'EXPIRED', + TOO_OLD = 'TOO_OLD', +} diff --git a/x-pack/plugins/uptime/common/runtime_types/certs.ts b/x-pack/plugins/uptime/common/runtime_types/certs.ts index e8be67abf3a443..e9071e76b6d75f 100644 --- a/x-pack/plugins/uptime/common/runtime_types/certs.ts +++ b/x-pack/plugins/uptime/common/runtime_types/certs.ts @@ -8,35 +8,45 @@ import * as t from 'io-ts'; export const GetCertsParamsType = t.intersection([ t.type({ - from: t.string, - to: t.string, index: t.number, size: t.number, + sortBy: t.string, + direction: t.string, }), t.partial({ search: t.string, + from: t.string, + to: t.string, }), ]); export type GetCertsParams = t.TypeOf; +export const CertMonitorType = t.partial({ + name: t.string, + id: t.string, + url: t.string, +}); + export const CertType = t.intersection([ t.type({ - monitors: t.array( - t.partial({ - name: t.string, - id: t.string, - }) - ), + monitors: t.array(CertMonitorType), sha256: t.string, }), t.partial({ - certificate_not_valid_after: t.string, - certificate_not_valid_before: t.string, + not_after: t.string, + not_before: t.string, common_name: t.string, issuer: t.string, sha1: t.string, }), ]); +export const CertResultType = t.type({ + certs: t.array(CertType), + total: t.number, +}); + export type Cert = t.TypeOf; +export type CertMonitor = t.TypeOf; +export type CertResult = t.TypeOf; diff --git a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts index ee14b298f38104..d8dc7fc89d94b6 100644 --- a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts @@ -17,8 +17,8 @@ export const HttpResponseBodyType = t.partial({ export type HttpResponseBody = t.TypeOf; export const TlsType = t.partial({ - certificate_not_valid_after: t.string, - certificate_not_valid_before: t.string, + not_after: t.string, + not_before: t.string, }); export type Tls = t.TypeOf; diff --git a/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/cert_monitors.test.tsx.snap b/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/cert_monitors.test.tsx.snap new file mode 100644 index 00000000000000..a79fb0f0d3debe --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/cert_monitors.test.tsx.snap @@ -0,0 +1,134 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CertMonitors renders expected elements for valid props 1`] = ` + + + + + + + + , + + + + + + , + + + + + +`; + +exports[`CertMonitors shallow renders expected elements for valid props 1`] = ` + + + +`; diff --git a/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/cert_search.test.tsx.snap b/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/cert_search.test.tsx.snap new file mode 100644 index 00000000000000..0706198a099a5c --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/cert_search.test.tsx.snap @@ -0,0 +1,93 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CertificatesSearch renders expected elements for valid props 1`] = ` +.c0 { + min-width: 700px; +} + +

    +
    + +
    + + +
    +
    +`; + +exports[`CertificatesSearch shallow renders expected elements for valid props 1`] = ` + + + +`; diff --git a/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/cert_status.test.tsx.snap b/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/cert_status.test.tsx.snap new file mode 100644 index 00000000000000..089d272a075c68 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/cert_status.test.tsx.snap @@ -0,0 +1,100 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CertStatus renders expected elements for valid props 1`] = ` +
    +
    +
    +
    +
    +
    + + OK + +
    +
    +
    +`; + +exports[`CertStatus shallow renders expected elements for valid props 1`] = ` + + + +`; diff --git a/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/certificates_list.test.tsx.snap b/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/certificates_list.test.tsx.snap new file mode 100644 index 00000000000000..fd90db793b26e0 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/certificates_list.test.tsx.snap @@ -0,0 +1,70 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CertificateList shallow renders expected elements for valid props 1`] = ` + + + +`; diff --git a/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/fingerprint_col.test.tsx.snap b/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/fingerprint_col.test.tsx.snap new file mode 100644 index 00000000000000..c9b17db5532f4a --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/__tests__/__snapshots__/fingerprint_col.test.tsx.snap @@ -0,0 +1,171 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FingerprintCol renders expected elements for valid props 1`] = ` +Array [ + .c1 .euiButtonEmpty__content { + padding-right: 0px; +} + +.c0 { + margin-right: 8px; +} + + + + + + + + + , + .c1 .euiButtonEmpty__content { + padding-right: 0px; +} + +.c0 { + margin-right: 8px; +} + + + + + + + + + , +] +`; + +exports[`FingerprintCol shallow renders expected elements for valid props 1`] = ` + + + +`; diff --git a/x-pack/plugins/uptime/public/components/certificates/__tests__/cert_monitors.test.tsx b/x-pack/plugins/uptime/public/components/certificates/__tests__/cert_monitors.test.tsx new file mode 100644 index 00000000000000..bc4c770d5cd243 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/__tests__/cert_monitors.test.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { CertMonitors } from '../cert_monitors'; +import { renderWithRouter, shallowWithRouter } from '../../../lib'; + +describe('CertMonitors', () => { + const certMons = [ + { name: '', id: 'bad-ssl-dashboard', url: 'https://badssl.com/dashboard/' }, + { name: 'elastic', id: 'elastic-co', url: 'https://www.elastic.co/' }, + { name: '', id: 'extended-validation', url: 'https://extended-validation.badssl.com/' }, + ]; + it('shallow renders expected elements for valid props', () => { + expect(shallowWithRouter()).toMatchSnapshot(); + }); + + it('renders expected elements for valid props', () => { + expect(renderWithRouter()).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/certificates/__tests__/cert_search.test.tsx b/x-pack/plugins/uptime/public/components/certificates/__tests__/cert_search.test.tsx new file mode 100644 index 00000000000000..27d3bb18f17c2b --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/__tests__/cert_search.test.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { renderWithRouter, shallowWithRouter } from '../../../lib'; +import { CertificateSearch } from '../cert_search'; + +describe('CertificatesSearch', () => { + it('shallow renders expected elements for valid props', () => { + expect(shallowWithRouter()).toMatchSnapshot(); + }); + it('renders expected elements for valid props', () => { + expect(renderWithRouter()).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/certificates/__tests__/cert_status.test.tsx b/x-pack/plugins/uptime/public/components/certificates/__tests__/cert_status.test.tsx new file mode 100644 index 00000000000000..6f91994fb89c43 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/__tests__/cert_status.test.tsx @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { renderWithRouter, shallowWithRouter } from '../../../lib'; +import { CertStatus } from '../cert_status'; +import * as redux from 'react-redux'; +import moment from 'moment'; + +describe('CertStatus', () => { + beforeEach(() => { + const spy = jest.spyOn(redux, 'useDispatch'); + spy.mockReturnValue(jest.fn()); + + const spy1 = jest.spyOn(redux, 'useSelector'); + spy1.mockReturnValue(true); + }); + + const cert = { + monitors: [{ name: '', id: 'github', url: 'https://github.com/' }], + not_after: '2020-05-08T00:00:00.000Z', + not_before: '2018-05-08T00:00:00.000Z', + issuer: 'DigiCert SHA2 Extended Validation Server CA', + sha1: 'ca06f56b258b7a0d4f2b05470939478651151984', + sha256: '3111500c4a66012cdae333ec3fca1c9dde45c954440e7ee413716bff3663c074', + common_name: 'github.com', + }; + + it('shallow renders expected elements for valid props', () => { + expect(shallowWithRouter()).toMatchSnapshot(); + }); + + it('renders expected elements for valid props', () => { + cert.not_after = moment() + .add('4', 'months') + .toISOString(); + expect(renderWithRouter()).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/certificates/__tests__/certificates_list.test.tsx b/x-pack/plugins/uptime/public/components/certificates/__tests__/certificates_list.test.tsx new file mode 100644 index 00000000000000..a8b60900ec65c0 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/__tests__/certificates_list.test.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallowWithRouter } from '../../../lib'; +import { CertificateList, CertSort } from '../certificates_list'; + +describe('CertificateList', () => { + it('shallow renders expected elements for valid props', () => { + const page = { + index: 0, + size: 10, + }; + const sort: CertSort = { + field: 'not_after', + direction: 'asc', + }; + + expect( + shallowWithRouter() + ).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/certificates/__tests__/fingerprint_col.test.tsx b/x-pack/plugins/uptime/public/components/certificates/__tests__/fingerprint_col.test.tsx new file mode 100644 index 00000000000000..609b876e24849f --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/__tests__/fingerprint_col.test.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { renderWithRouter, shallowWithRouter } from '../../../lib'; +import { FingerprintCol } from '../fingerprint_col'; +import moment from 'moment'; + +describe('FingerprintCol', () => { + const cert = { + monitors: [{ name: '', id: 'github', url: 'https://github.com/' }], + not_after: '2020-05-08T00:00:00.000Z', + not_before: '2018-05-08T00:00:00.000Z', + issuer: 'DigiCert SHA2 Extended Validation Server CA', + sha1: 'ca06f56b258b7a0d4f2b05470939478651151984', + sha256: '3111500c4a66012cdae333ec3fca1c9dde45c954440e7ee413716bff3663c074', + common_name: 'github.com', + }; + + it('shallow renders expected elements for valid props', () => { + expect(shallowWithRouter()).toMatchSnapshot(); + }); + + it('renders expected elements for valid props', () => { + cert.not_after = moment() + .add('4', 'months') + .toISOString(); + + expect(renderWithRouter()).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/certificates/cert_monitors.tsx b/x-pack/plugins/uptime/public/components/certificates/cert_monitors.tsx new file mode 100644 index 00000000000000..bfd309e59d0132 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/cert_monitors.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiToolTip } from '@elastic/eui'; +import { CertMonitor } from '../../../common/runtime_types'; +import { MonitorPageLink } from '../common/monitor_page_link'; + +interface Props { + monitors: CertMonitor[]; +} + +export const CertMonitors: React.FC = ({ monitors }) => { + return ( + + {monitors.map((mon: CertMonitor, ind: number) => ( + + {ind > 0 && ', '} + + + {mon.name || mon.id} + + + + ))} + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/certificates/cert_search.tsx b/x-pack/plugins/uptime/public/components/certificates/cert_search.tsx new file mode 100644 index 00000000000000..282b623f0f6627 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/cert_search.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { ChangeEvent } from 'react'; +import { EuiFieldSearch } from '@elastic/eui'; +import styled from 'styled-components'; +import * as labels from './translations'; + +const WrapFieldSearch = styled(EuiFieldSearch)` + min-width: 700px; +`; + +interface Props { + setSearch: (val: string) => void; +} + +export const CertificateSearch: React.FC = ({ setSearch }) => { + const onChange = (e: ChangeEvent) => { + setSearch(e.target.value); + }; + + return ( + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/certificates/cert_status.tsx b/x-pack/plugins/uptime/public/components/certificates/cert_status.tsx new file mode 100644 index 00000000000000..e7a86ce98fa3c1 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/cert_status.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiHealth } from '@elastic/eui'; +import { Cert } from '../../../common/runtime_types'; +import { useCertStatus } from '../../hooks'; +import * as labels from './translations'; +import { CERT_STATUS } from '../../../common/constants'; + +interface Props { + cert: Cert; +} + +export const CertStatus: React.FC = ({ cert }) => { + const certStatus = useCertStatus(cert?.not_after, cert?.not_before); + + if (certStatus === CERT_STATUS.EXPIRING_SOON) { + return ( + + {labels.EXPIRES_SOON} + + ); + } + if (certStatus === CERT_STATUS.EXPIRED) { + return ( + + {labels.EXPIRED} + + ); + } + + if (certStatus === CERT_STATUS.TOO_OLD) { + return ( + + {labels.TOO_OLD} + + ); + } + + return ( + + {labels.OK} + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/certificates/certificates_list.tsx b/x-pack/plugins/uptime/public/components/certificates/certificates_list.tsx new file mode 100644 index 00000000000000..595aa03c99c730 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/certificates_list.tsx @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import moment from 'moment'; +import { useSelector } from 'react-redux'; +import { Direction, EuiBasicTable } from '@elastic/eui'; +import { certificatesSelector } from '../../state/certificates/certificates'; +import { CertStatus } from './cert_status'; +import { CertMonitors } from './cert_monitors'; +import * as labels from './translations'; +import { Cert, CertMonitor } from '../../../common/runtime_types'; +import { FingerprintCol } from './fingerprint_col'; + +interface Page { + index: number; + size: number; +} + +export type CertFields = + | 'sha256' + | 'sha1' + | 'issuer' + | 'common_name' + | 'monitors' + | 'not_after' + | 'not_before'; + +export interface CertSort { + field: CertFields; + direction: Direction; +} + +interface Props { + page: Page; + sort: CertSort; + onChange: (page: Page, sort: CertSort) => void; +} + +export const CertificateList: React.FC = ({ page, sort, onChange }) => { + const certificates = useSelector(certificatesSelector); + + const onTableChange = (newVal: Partial) => { + onChange(newVal.page as Page, newVal.sort as CertSort); + }; + + const pagination = { + pageIndex: page.index, + pageSize: page.size, + totalItemCount: certificates?.total ?? 0, + pageSizeOptions: [10, 25, 50, 100], + hidePerPageOptions: false, + }; + + const columns = [ + { + field: 'not_after', + name: labels.STATUS_COL, + sortable: true, + render: (val: string, item: Cert) => , + }, + { + name: labels.COMMON_NAME_COL, + field: 'common_name', + sortable: true, + }, + { + name: labels.MONITORS_COL, + field: 'monitors', + render: (monitors: CertMonitor[]) => , + }, + { + name: labels.ISSUED_BY_COL, + field: 'issuer', + sortable: true, + }, + { + name: labels.VALID_UNTIL_COL, + field: 'not_after', + sortable: true, + render: (value: string) => moment(value).format('L LT'), + }, + { + name: labels.AGE_COL, + field: 'not_before', + sortable: true, + render: (value: string) => moment().diff(moment(value), 'days') + ' ' + labels.DAYS, + }, + { + name: labels.FINGERPRINTS_COL, + field: 'sha256', + render: (val: string, item: Cert) => , + }, + ]; + + return ( + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/certificates/fingerprint_col.tsx b/x-pack/plugins/uptime/public/components/certificates/fingerprint_col.tsx new file mode 100644 index 00000000000000..41015739079246 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/fingerprint_col.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiButtonEmpty, EuiButtonIcon, EuiCopy, EuiToolTip } from '@elastic/eui'; +import styled from 'styled-components'; +import { Cert } from '../../../common/runtime_types'; +import { COPY_FINGERPRINT } from './translations'; + +const EmptyButton = styled(EuiButtonEmpty)` + .euiButtonEmpty__content { + padding-right: 0px; + } +`; + +const Span = styled.span` + margin-right: 8px; +`; + +interface Props { + cert: Cert; +} + +export const FingerprintCol: React.FC = ({ cert }) => { + const ShaComponent = ({ text, val }: { text: string; val: string }) => { + return ( + + + {text} + + + {copy => } + + + ); + }; + return ( + <> + + + + ); +}; diff --git a/x-pack/plugins/uptime/public/components/certificates/index.ts b/x-pack/plugins/uptime/public/components/certificates/index.ts new file mode 100644 index 00000000000000..82f3f7ab67c918 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/index.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; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './cert_monitors'; +export * from './cert_search'; +export * from './cert_status'; +export * from './certificates_list'; +export * from './fingerprint_col'; diff --git a/x-pack/plugins/uptime/public/components/certificates/translations.ts b/x-pack/plugins/uptime/public/components/certificates/translations.ts new file mode 100644 index 00000000000000..518eddf1211a4f --- /dev/null +++ b/x-pack/plugins/uptime/public/components/certificates/translations.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const OK = i18n.translate('xpack.uptime.certs.ok', { + defaultMessage: 'OK', +}); + +export const EXPIRED = i18n.translate('xpack.uptime.certs.expired', { + defaultMessage: 'Expired', +}); + +export const EXPIRES_SOON = i18n.translate('xpack.uptime.certs.expireSoon', { + defaultMessage: 'Expires soon', +}); + +export const SEARCH_CERTS = i18n.translate('xpack.uptime.certs.searchCerts', { + defaultMessage: 'Search certificates', +}); + +export const STATUS_COL = i18n.translate('xpack.uptime.certs.list.status', { + defaultMessage: 'Status', +}); + +export const TOO_OLD = i18n.translate('xpack.uptime.certs.list.status.old', { + defaultMessage: 'Too old', +}); + +export const COMMON_NAME_COL = i18n.translate('xpack.uptime.certs.list.commonName', { + defaultMessage: 'Common name', +}); + +export const MONITORS_COL = i18n.translate('xpack.uptime.certs.list.monitors', { + defaultMessage: 'Monitors', +}); + +export const ISSUED_BY_COL = i18n.translate('xpack.uptime.certs.list.issuedBy', { + defaultMessage: 'Issued by', +}); + +export const VALID_UNTIL_COL = i18n.translate('xpack.uptime.certs.list.validUntil', { + defaultMessage: 'Valid until', +}); + +export const AGE_COL = i18n.translate('xpack.uptime.certs.list.ageCol', { + defaultMessage: 'Age', +}); + +export const DAYS = i18n.translate('xpack.uptime.certs.list.days', { + defaultMessage: 'days', +}); + +export const FINGERPRINTS_COL = i18n.translate('xpack.uptime.certs.list.expirationDate', { + defaultMessage: 'Fingerprints', +}); + +export const COPY_FINGERPRINT = i18n.translate('xpack.uptime.certs.list.copyFingerprint', { + defaultMessage: 'Click to copy fingerprint value', +}); diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap b/x-pack/plugins/uptime/public/components/common/__tests__/__snapshots__/monitor_page_link.test.tsx.snap similarity index 100% rename from x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_page_link.test.tsx.snap rename to x-pack/plugins/uptime/public/components/common/__tests__/__snapshots__/monitor_page_link.test.tsx.snap diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_page_link.test.tsx b/x-pack/plugins/uptime/public/components/common/__tests__/monitor_page_link.test.tsx similarity index 100% rename from x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_page_link.test.tsx rename to x-pack/plugins/uptime/public/components/common/__tests__/monitor_page_link.test.tsx index dd6e9c66d395bd..36ebeb66156481 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_page_link.test.tsx +++ b/x-pack/plugins/uptime/public/components/common/__tests__/monitor_page_link.test.tsx @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import React from 'react'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; import { MonitorPageLink } from '../monitor_page_link'; describe('MonitorPageLink component', () => { diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_page_link.tsx b/x-pack/plugins/uptime/public/components/common/monitor_page_link.tsx similarity index 89% rename from x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_page_link.tsx rename to x-pack/plugins/uptime/public/components/common/monitor_page_link.tsx index 803b399810508b..77faa8edfc5c83 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_page_link.tsx +++ b/x-pack/plugins/uptime/public/components/common/monitor_page_link.tsx @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import React, { FC } from 'react'; import { EuiLink } from '@elastic/eui'; import { Link } from 'react-router-dom'; -import React, { FunctionComponent } from 'react'; interface DetailPageLinkProps { /** @@ -19,7 +19,7 @@ interface DetailPageLinkProps { linkParameters: string | undefined; } -export const MonitorPageLink: FunctionComponent = ({ +export const MonitorPageLink: FC = ({ children, monitorId, linkParameters, diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_ssl_certificate.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_ssl_certificate.test.tsx.snap deleted file mode 100644 index 605fc3cdb6b38e..00000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/monitor_ssl_certificate.test.tsx.snap +++ /dev/null @@ -1,31 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`MonitorStatusBar component renders 1`] = ` -Array [ -
    , -
    - SSL certificate expires - - - - in 2 months - - - -
    , -] -`; - -exports[`MonitorStatusBar component renders null if invalid date 1`] = `null`; diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/ssl_certificate.test.tsx.snap b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/ssl_certificate.test.tsx.snap new file mode 100644 index 00000000000000..2f4473ba54cf9f --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/__snapshots__/ssl_certificate.test.tsx.snap @@ -0,0 +1,119 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SSL Certificate component renders 1`] = ` +Array [ +
    + Certificate +
    , +
    , +
    +
    +
    + Expires + + + + in 2 months + + + +
    +
    + +
    , +] +`; + +exports[`SSL Certificate component renders null if invalid date 1`] = `null`; + +exports[`SSL Certificate component shallow renders 1`] = ` + + + +`; diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_status.bar.test.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_status.bar.test.tsx index 5fd32c808da42c..b39a1cb537583f 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_status.bar.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_status.bar.test.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { renderWithIntl } from 'test_utils/enzyme_helpers'; import { MonitorStatusBarComponent } from '../monitor_status_bar'; import { Ping } from '../../../../../common/runtime_types'; +import * as redux from 'react-redux'; describe('MonitorStatusBar component', () => { let monitorStatus: Ping; @@ -46,6 +47,12 @@ describe('MonitorStatusBar component', () => { }, ], }; + + const spy = jest.spyOn(redux, 'useDispatch'); + spy.mockReturnValue(jest.fn()); + + const spy1 = jest.spyOn(redux, 'useSelector'); + spy1.mockReturnValue(true); }); it('renders duration in ms, not us', () => { diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_ssl_certificate.test.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/ssl_certificate.test.tsx similarity index 55% rename from x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_ssl_certificate.test.tsx rename to x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/ssl_certificate.test.tsx index 57ed09cc30ef15..70a161a2394ec1 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/monitor_ssl_certificate.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/__test__/ssl_certificate.test.tsx @@ -6,13 +6,14 @@ import React from 'react'; import moment from 'moment'; -import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { EuiBadge } from '@elastic/eui'; -import { renderWithIntl } from 'test_utils/enzyme_helpers'; import { Tls } from '../../../../../common/runtime_types'; import { MonitorSSLCertificate } from '../monitor_status_bar'; +import * as redux from 'react-redux'; +import { mountWithRouter, renderWithRouter, shallowWithRouter } from '../../../../lib'; +import { DYNAMIC_SETTINGS_DEFAULTS } from '../../../../../common/constants'; -describe('MonitorStatusBar component', () => { +describe('SSL Certificate component', () => { let monitorTls: Tls; beforeEach(() => { @@ -21,37 +22,52 @@ describe('MonitorStatusBar component', () => { .toString(); monitorTls = { - certificate_not_valid_after: dateInTwoMonths, + not_after: dateInTwoMonths, }; + + const useDispatchSpy = jest.spyOn(redux, 'useDispatch'); + useDispatchSpy.mockReturnValue(jest.fn()); + + const useSelectorSpy = jest.spyOn(redux, 'useSelector'); + useSelectorSpy.mockReturnValue({ settings: DYNAMIC_SETTINGS_DEFAULTS }); + }); + + it('shallow renders', () => { + const monitorTls1 = { + not_after: '2020-04-24T11:41:38.200Z', + }; + const component = shallowWithRouter(); + expect(component).toMatchSnapshot(); }); it('renders', () => { - const component = renderWithIntl(); + const component = renderWithRouter(); expect(component).toMatchSnapshot(); }); it('renders null if invalid date', () => { monitorTls = { - certificate_not_valid_after: 'i am so invalid date', + not_after: 'i am so invalid date', }; - const component = renderWithIntl(); + const component = renderWithRouter(); expect(component).toMatchSnapshot(); }); - it('renders expiration date with a warning state if ssl expiry date is less than 30 days', () => { - const dateIn15Days = moment() - .add(15, 'day') + it('renders expiration date with a warning state if ssl expiry date is less than 5 days', () => { + const dateIn5Days = moment() + .add(5, 'day') .toString(); monitorTls = { - certificate_not_valid_after: dateIn15Days, + not_after: dateIn5Days, }; - const component = mountWithIntl(); + const component = mountWithRouter(); const badgeComponent = component.find(EuiBadge); + expect(badgeComponent.props().color).toBe('warning'); const badgeComponentText = component.find('.euiBadge__text'); - expect(badgeComponentText.text()).toBe(moment(dateIn15Days).fromNow()); + expect(badgeComponentText.text()).toBe(moment(dateIn5Days).fromNow()); expect(badgeComponent.find('span.euiBadge--warning')).toBeTruthy(); }); @@ -61,9 +77,9 @@ describe('MonitorStatusBar component', () => { .add(40, 'day') .toString(); monitorTls = { - certificate_not_valid_after: dateIn40Days, + not_after: dateIn40Days, }; - const component = mountWithIntl(); + const component = mountWithRouter(); const badgeComponent = component.find(EuiBadge); expect(badgeComponent.props().color).toBe('default'); diff --git a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx index d92534aecd1754..734a68f00f7de6 100644 --- a/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/monitor_status_details/monitor_status_bar/ssl_certificate.tsx @@ -6,10 +6,13 @@ import React from 'react'; import moment from 'moment'; -import { EuiSpacer, EuiText, EuiBadge } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import { Link } from 'react-router-dom'; +import { EuiSpacer, EuiText, EuiBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { Tls } from '../../../../../common/runtime_types'; +import { useCertStatus } from '../../../../hooks'; +import { CERT_STATUS, CERTIFICATES_ROUTE } from '../../../../../common/constants'; interface Props { /** @@ -19,40 +22,79 @@ interface Props { } export const MonitorSSLCertificate = ({ tls }: Props) => { - const certValidityDate = new Date(tls?.certificate_not_valid_after ?? ''); + const certStatus = useCertStatus(tls?.not_after); - const isValidDate = !isNaN(certValidityDate.valueOf()); + const isExpiringSoon = certStatus === CERT_STATUS.EXPIRING_SOON; - const dateIn30Days = moment().add('30', 'days'); + const isExpired = certStatus === CERT_STATUS.EXPIRED; - const isExpiringInMonth = isValidDate && dateIn30Days > moment(certValidityDate); + const relativeDate = moment(tls?.not_after).fromNow(); - return isValidDate ? ( + return certStatus ? ( <> - - - - {moment(certValidityDate).fromNow()} - - ), - }} - /> + + {i18n.translate('xpack.uptime.monitorStatusBar.sslCertificate.title', { + defaultMessage: 'Certificate', + })} + + + + + {isExpired ? ( + {relativeDate}, + }} + /> + ) : ( + + {relativeDate} + + ), + }} + /> + )} + + + + + + {i18n.translate('xpack.uptime.monitorStatusBar.sslCertificate.overview', { + defaultMessage: 'Certificate overview', + })} + + + + ) : null; }; diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap index ed5602323d254f..0d6638e7070d64 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/__snapshots__/monitor_list.test.tsx.snap @@ -556,13 +556,35 @@ exports[`MonitorList component renders the monitor list 1`] = `
    -
    - Monitor status -
    +
    +
    + Monitor status +
    +
    + +
    + +
    + + TLS Certificate + +
    + @@ -657,7 +695,7 @@ exports[`MonitorList component renders the monitor list 1`] = `
    + +
    + TLS Certificate +
    +
    + + - + +
    + @@ -951,6 +1005,22 @@ exports[`MonitorList component renders the monitor list 1`] = `
    + +
    + TLS Certificate +
    +
    + + - + +
    + diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list.test.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list.test.tsx index 9b1d799a23e379..9dd44f51766641 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list.test.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/__tests__/monitor_list.test.tsx @@ -12,12 +12,19 @@ import { } from '../../../../../common/runtime_types'; import { MonitorListComponent } from '../monitor_list'; import { renderWithRouter, shallowWithRouter } from '../../../../lib'; +import * as redux from 'react-redux'; describe('MonitorList component', () => { let result: MonitorSummaryResult; let localStorageMock: any; beforeEach(() => { + const useDispatchSpy = jest.spyOn(redux, 'useDispatch'); + useDispatchSpy.mockReturnValue(jest.fn()); + + const useSelectorSpy = jest.spyOn(redux, 'useSelector'); + useSelectorSpy.mockReturnValue(true); + localStorageMock = { getItem: jest.fn().mockImplementation(() => '25'), setItem: jest.fn(), diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/cert_status_column.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/cert_status_column.tsx new file mode 100644 index 00000000000000..d9380476eaf454 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/cert_status_column.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import moment from 'moment'; +import styled from 'styled-components'; +import { EuiIcon, EuiText, EuiToolTip } from '@elastic/eui'; +import { Cert } from '../../../../common/runtime_types'; +import { useCertStatus } from '../../../hooks'; +import { EXPIRED, EXPIRES_SOON } from '../../certificates/translations'; +import { CERT_STATUS } from '../../../../common/constants'; + +interface Props { + cert: Cert; +} + +const Span = styled.span` + margin-left: 5px; + vertical-align: middle; +`; + +export const CertStatusColumn: React.FC = ({ cert }) => { + const certStatus = useCertStatus(cert?.not_after); + + const relativeDate = moment(cert?.not_after).fromNow(); + + const CertStatus = ({ color, text }: { color: string; text: string }) => { + return ( + + + + + {text} {relativeDate} + + + + ); + }; + + if (certStatus === CERT_STATUS.EXPIRING_SOON) { + return ; + } + if (certStatus === CERT_STATUS.EXPIRED) { + return ; + } + + return certStatus ? : -; +}; diff --git a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx index 7e9536689470e6..616d8fbd76043f 100644 --- a/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx +++ b/x-pack/plugins/uptime/public/components/overview/monitor_list/monitor_list.tsx @@ -18,12 +18,13 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; +import { Link } from 'react-router-dom'; import { HistogramPoint, FetchMonitorStatesQueryArgs } from '../../../../common/runtime_types'; import { MonitorSummary } from '../../../../common/runtime_types'; import { MonitorListStatusColumn } from './monitor_list_status_column'; import { ExpandedRowMap } from './types'; import { MonitorBarSeries } from '../../common/charts'; -import { MonitorPageLink } from './monitor_page_link'; +import { MonitorPageLink } from '../../common/monitor_page_link'; import { OverviewPageLink } from './overview_page_link'; import * as labels from './translations'; import { MonitorListPageSizeSelect } from './monitor_list_page_size_select'; @@ -31,6 +32,8 @@ import { MonitorListDrawer } from './monitor_list_drawer/list_drawer_container'; import { MonitorListProps } from './monitor_list_container'; import { MonitorList } from '../../../state/reducers/monitor_list'; import { useUrlParams } from '../../../hooks'; +import { CERTIFICATES_ROUTE } from '../../../../common/constants'; +import { CertStatusColumn } from './cert_status_column'; interface Props extends MonitorListProps { lastRefresh: number; @@ -143,6 +146,12 @@ export const MonitorListComponent: React.FC = ({ ), }, + { + align: 'center' as const, + field: 'state.tls', + name: labels.TLS_COLUMN_LABEL, + render: (tls: any) => , + }, { align: 'center' as const, field: 'histogram.points', @@ -181,15 +190,32 @@ export const MonitorListComponent: React.FC = ({ return ( - -
    - -
    -
    - + + + +
    + +
    +
    +
    + + +
    + + + +
    +
    +
    +
    + + { return i18n.translate('xpack.uptime.monitorList.expandDrawerButton.ariaLabel', { defaultMessage: 'Expand row for monitor with ID {id}', diff --git a/x-pack/plugins/uptime/public/hooks/index.ts b/x-pack/plugins/uptime/public/hooks/index.ts index 1f50e995eda495..b92d2d4cf7df58 100644 --- a/x-pack/plugins/uptime/public/hooks/index.ts +++ b/x-pack/plugins/uptime/public/hooks/index.ts @@ -8,3 +8,4 @@ export * from './use_monitor'; export * from './use_url_params'; export * from './use_telemetry'; export * from './update_kuery_string'; +export * from './use_cert_status'; diff --git a/x-pack/plugins/uptime/public/hooks/use_cert_status.ts b/x-pack/plugins/uptime/public/hooks/use_cert_status.ts new file mode 100644 index 00000000000000..cb54b05af9dd17 --- /dev/null +++ b/x-pack/plugins/uptime/public/hooks/use_cert_status.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import moment from 'moment'; +import { useSelector } from 'react-redux'; +import { selectDynamicSettings } from '../state/selectors'; +import { CERT_STATUS } from '../../common/constants'; + +export const useCertStatus = (expiryDate?: string, issueDate?: string) => { + const dss = useSelector(selectDynamicSettings); + + const expiryThreshold = dss.settings?.certThresholds?.expiration; + + const ageThreshold = dss.settings?.certThresholds?.age; + + const certValidityDate = new Date(expiryDate ?? ''); + + const isValidDate = !isNaN(certValidityDate.valueOf()); + + if (!isValidDate) { + return false; + } + + const isExpiringSoon = moment(certValidityDate).diff(moment(), 'days') < expiryThreshold!; + + const isTooOld = moment().diff(moment(issueDate), 'days') > ageThreshold!; + + const isExpired = moment(certValidityDate) < moment(); + + if (isExpired) { + return CERT_STATUS.EXPIRED; + } + + return isExpiringSoon + ? CERT_STATUS.EXPIRING_SOON + : isTooOld + ? CERT_STATUS.TOO_OLD + : CERT_STATUS.OK; +}; diff --git a/x-pack/plugins/uptime/public/hooks/use_telemetry.ts b/x-pack/plugins/uptime/public/hooks/use_telemetry.ts index a2012b8ac56365..9b4a441fe5adeb 100644 --- a/x-pack/plugins/uptime/public/hooks/use_telemetry.ts +++ b/x-pack/plugins/uptime/public/hooks/use_telemetry.ts @@ -13,6 +13,7 @@ export enum UptimePage { Overview = 'Overview', Monitor = 'Monitor', Settings = 'Settings', + Certificates = 'Certificates', NotFound = '__not-found__', } diff --git a/x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/certificates.test.tsx.snap b/x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/certificates.test.tsx.snap new file mode 100644 index 00000000000000..53b2ea27864bc0 --- /dev/null +++ b/x-pack/plugins/uptime/public/pages/__tests__/__snapshots__/certificates.test.tsx.snap @@ -0,0 +1,56 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CertificatesPage shallow renders expected elements for valid props 1`] = ` + + + +`; diff --git a/x-pack/plugins/uptime/public/pages/__tests__/certificates.test.tsx b/x-pack/plugins/uptime/public/pages/__tests__/certificates.test.tsx new file mode 100644 index 00000000000000..8dfb6fba3d6beb --- /dev/null +++ b/x-pack/plugins/uptime/public/pages/__tests__/certificates.test.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallowWithRouter } from '../../lib'; +import { CertificatesPage } from '../certificates'; + +describe('CertificatesPage', () => { + it('shallow renders expected elements for valid props', () => { + expect(shallowWithRouter()).toMatchSnapshot(); + }); +}); diff --git a/x-pack/plugins/uptime/public/pages/certificates.tsx b/x-pack/plugins/uptime/public/pages/certificates.tsx new file mode 100644 index 00000000000000..d6c1b8e2b4568c --- /dev/null +++ b/x-pack/plugins/uptime/public/pages/certificates.tsx @@ -0,0 +1,136 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useDispatch, useSelector } from 'react-redux'; +import { Link } from 'react-router-dom'; +import { + EuiButton, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiSpacer, +} from '@elastic/eui'; +import React, { useContext, useEffect, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { useTrackPageview } from '../../../observability/public'; +import { PageHeader } from './page_header'; +import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; +import { OVERVIEW_ROUTE, SETTINGS_ROUTE } from '../../common/constants'; +import { getDynamicSettings } from '../state/actions/dynamic_settings'; +import { UptimeRefreshContext } from '../contexts'; +import * as labels from './translations'; +import { UptimePage, useUptimeTelemetry } from '../hooks'; +import { certificatesSelector, getCertificatesAction } from '../state/certificates/certificates'; +import { CertificateList, CertificateSearch, CertSort } from '../components/certificates'; + +const DEFAULT_PAGE_SIZE = 10; +const LOCAL_STORAGE_KEY = 'xpack.uptime.certList.pageSize'; +const getPageSizeValue = () => { + const value = parseInt(localStorage.getItem(LOCAL_STORAGE_KEY) ?? '', 10); + if (isNaN(value)) { + return DEFAULT_PAGE_SIZE; + } + return value; +}; + +export const CertificatesPage: React.FC = () => { + useUptimeTelemetry(UptimePage.Certificates); + + useTrackPageview({ app: 'uptime', path: 'certificates' }); + useTrackPageview({ app: 'uptime', path: 'certificates', delay: 15000 }); + + useBreadcrumbs([{ text: 'Certificates' }]); + + const [page, setPage] = useState({ index: 0, size: getPageSizeValue() }); + const [sort, setSort] = useState({ + field: 'not_after', + direction: 'asc', + }); + const [search, setSearch] = useState(''); + + const dispatch = useDispatch(); + + const { lastRefresh, refreshApp } = useContext(UptimeRefreshContext); + + useEffect(() => { + dispatch(getDynamicSettings()); + }, [dispatch]); + + useEffect(() => { + dispatch( + getCertificatesAction.get({ + search, + ...page, + sortBy: sort.field, + direction: sort.direction, + }) + ); + }, [dispatch, page, search, sort.direction, sort.field, lastRefresh]); + + const certificates = useSelector(certificatesSelector); + + return ( + <> + + + + + {labels.RETURN_TO_OVERVIEW} + + + + + + + {labels.SETTINGS_ON_CERT} + + + + + { + refreshApp(); + }} + data-test-subj="superDatePickerApplyTimeButton" + > + {labels.REFRESH_CERT} + + + + + + + {certificates?.total ?? 0}, + }} + /> + } + datePicker={false} + /> + + + + { + setPage(pageVal); + setSort(sortVal); + localStorage.setItem(LOCAL_STORAGE_KEY, pageVal.size.toString()); + }} + sort={sort} + /> + + + ); +}; diff --git a/x-pack/plugins/uptime/public/pages/monitor.tsx b/x-pack/plugins/uptime/public/pages/monitor.tsx index 8a309db75acd23..fc796e679a2f65 100644 --- a/x-pack/plugins/uptime/public/pages/monitor.tsx +++ b/x-pack/plugins/uptime/public/pages/monitor.tsx @@ -5,8 +5,8 @@ */ import { EuiSpacer } from '@elastic/eui'; -import React from 'react'; -import { useSelector } from 'react-redux'; +import React, { useEffect } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; import { monitorStatusSelector } from '../state/selectors'; import { PageHeader } from './page_header'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; @@ -14,8 +14,15 @@ import { useTrackPageview } from '../../../observability/public'; import { useMonitorId, useUptimeTelemetry, UptimePage } from '../hooks'; import { MonitorCharts } from '../components/monitor'; import { MonitorStatusDetails, PingList } from '../components/monitor'; +import { getDynamicSettings } from '../state/actions/dynamic_settings'; export const MonitorPage: React.FC = () => { + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(getDynamicSettings()); + }, [dispatch]); + const monitorId = useMonitorId(); const selectedMonitor = useSelector(monitorStatusSelector); diff --git a/x-pack/plugins/uptime/public/pages/page_header.tsx b/x-pack/plugins/uptime/public/pages/page_header.tsx index b10bc6ba44f8a6..b6791e6a93445c 100644 --- a/x-pack/plugins/uptime/public/pages/page_header.tsx +++ b/x-pack/plugins/uptime/public/pages/page_header.tsx @@ -13,7 +13,7 @@ import { SETTINGS_ROUTE } from '../../common/constants'; import { ToggleAlertFlyoutButton } from '../components/overview/alerts/alerts_containers'; interface PageHeaderProps { - headingText: string; + headingText: string | JSX.Element; extraLinks?: boolean; datePicker?: boolean; } diff --git a/x-pack/plugins/uptime/public/pages/translations.ts b/x-pack/plugins/uptime/public/pages/translations.ts index 85e4e3f931c46f..74fb2eeb1416bd 100644 --- a/x-pack/plugins/uptime/public/pages/translations.ts +++ b/x-pack/plugins/uptime/public/pages/translations.ts @@ -6,6 +6,21 @@ import { i18n } from '@kbn/i18n'; +export const SETTINGS_ON_CERT = i18n.translate('xpack.uptime.certificates.settingsLinkLabel', { + defaultMessage: 'Settings', +}); + +export const RETURN_TO_OVERVIEW = i18n.translate( + 'xpack.uptime.certificates.returnToOverviewLinkLabel', + { + defaultMessage: 'Return to overview', + } +); + +export const REFRESH_CERT = i18n.translate('xpack.uptime.certificates.refresh', { + defaultMessage: 'Refresh', +}); + export const settings = { breadcrumbText: i18n.translate('xpack.uptime.settingsBreadcrumbText', { defaultMessage: 'Settings', diff --git a/x-pack/plugins/uptime/public/routes.tsx b/x-pack/plugins/uptime/public/routes.tsx index eb0587c0417a2b..ca97858998df7a 100644 --- a/x-pack/plugins/uptime/public/routes.tsx +++ b/x-pack/plugins/uptime/public/routes.tsx @@ -8,8 +8,14 @@ import React, { FC } from 'react'; import { Route, Switch } from 'react-router-dom'; import { DataPublicPluginSetup } from '../../../../src/plugins/data/public'; import { OverviewPage } from './components/overview/overview_container'; -import { MONITOR_ROUTE, OVERVIEW_ROUTE, SETTINGS_ROUTE } from '../common/constants'; +import { + CERTIFICATES_ROUTE, + MONITOR_ROUTE, + OVERVIEW_ROUTE, + SETTINGS_ROUTE, +} from '../common/constants'; import { MonitorPage, NotFoundPage, SettingsPage } from './pages'; +import { CertificatesPage } from './pages/certificates'; interface RouterProps { autocomplete: DataPublicPluginSetup['autocomplete']; @@ -27,6 +33,11 @@ export const PageRouter: FC = ({ autocomplete }) => (
    + +
    + +
    +
    diff --git a/x-pack/plugins/uptime/public/state/api/certificates.ts b/x-pack/plugins/uptime/public/state/api/certificates.ts new file mode 100644 index 00000000000000..78267e659d233c --- /dev/null +++ b/x-pack/plugins/uptime/public/state/api/certificates.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { API_URLS } from '../../../common/constants'; +import { apiService } from './utils'; +import { CertResultType, GetCertsParams } from '../../../common/runtime_types'; + +export const fetchCertificates = async (params: GetCertsParams) => { + return await apiService.get(API_URLS.CERTS, params, CertResultType); +}; diff --git a/x-pack/plugins/uptime/public/state/certificates/certificates.ts b/x-pack/plugins/uptime/public/state/certificates/certificates.ts new file mode 100644 index 00000000000000..18cbcf6bcb614b --- /dev/null +++ b/x-pack/plugins/uptime/public/state/certificates/certificates.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { handleActions } from 'redux-actions'; +import { takeLatest } from 'redux-saga/effects'; +import { createAsyncAction } from '../actions/utils'; +import { getAsyncInitialState, handleAsyncAction } from '../reducers/utils'; +import { CertResult, GetCertsParams } from '../../../common/runtime_types'; +import { AppState } from '../index'; +import { AsyncInitialState } from '../reducers/types'; +import { fetchEffectFactory } from '../effects/fetch_effect'; +import { fetchCertificates } from '../api/certificates'; + +export const getCertificatesAction = createAsyncAction( + 'GET_CERTIFICATES' +); + +interface CertificatesState { + certs: AsyncInitialState; +} + +const initialState = { + certs: getAsyncInitialState(), +}; + +export const certificatesReducer = handleActions( + { + ...handleAsyncAction('certs', getCertificatesAction), + }, + initialState +); + +export function* fetchCertificatesEffect() { + yield takeLatest( + getCertificatesAction.get, + fetchEffectFactory(fetchCertificates, getCertificatesAction.success, getCertificatesAction.fail) + ); +} + +export const certificatesSelector = ({ certificates }: AppState) => certificates.certs.data; diff --git a/x-pack/plugins/uptime/public/state/effects/index.ts b/x-pack/plugins/uptime/public/state/effects/index.ts index 739179c5bbeae6..211067c840d54b 100644 --- a/x-pack/plugins/uptime/public/state/effects/index.ts +++ b/x-pack/plugins/uptime/public/state/effects/index.ts @@ -16,6 +16,7 @@ import { fetchPingsEffect, fetchPingHistogramEffect } from './ping'; import { fetchMonitorDurationEffect } from './monitor_duration'; import { fetchMLJobEffect } from './ml_anomaly'; import { fetchIndexStatusEffect } from './index_status'; +import { fetchCertificatesEffect } from '../certificates/certificates'; export function* rootEffect() { yield fork(fetchMonitorDetailsEffect); @@ -31,4 +32,5 @@ export function* rootEffect() { yield fork(fetchMLJobEffect); yield fork(fetchMonitorDurationEffect); yield fork(fetchIndexStatusEffect); + yield fork(fetchCertificatesEffect); } diff --git a/x-pack/plugins/uptime/public/state/reducers/index.ts b/x-pack/plugins/uptime/public/state/reducers/index.ts index 294bde2f277ec5..ead7f5b46431bc 100644 --- a/x-pack/plugins/uptime/public/state/reducers/index.ts +++ b/x-pack/plugins/uptime/public/state/reducers/index.ts @@ -18,6 +18,7 @@ import { pingListReducer } from './ping_list'; import { monitorDurationReducer } from './monitor_duration'; import { indexStatusReducer } from './index_status'; import { mlJobsReducer } from './ml_anomaly'; +import { certificatesReducer } from '../certificates/certificates'; export const rootReducer = combineReducers({ monitor: monitorReducer, @@ -33,4 +34,5 @@ export const rootReducer = combineReducers({ ml: mlJobsReducer, monitorDuration: monitorDurationReducer, indexStatus: indexStatusReducer, + certificates: certificatesReducer, }); diff --git a/x-pack/plugins/uptime/public/state/reducers/ml_anomaly.ts b/x-pack/plugins/uptime/public/state/reducers/ml_anomaly.ts index 61e03a95929211..9a4a949ac4ede4 100644 --- a/x-pack/plugins/uptime/public/state/reducers/ml_anomaly.ts +++ b/x-pack/plugins/uptime/public/state/reducers/ml_anomaly.ts @@ -15,7 +15,6 @@ import { getMLCapabilitiesAction, } from '../actions'; import { getAsyncInitialState, handleAsyncAction } from './utils'; -import { IHttpFetchError } from '../../../../../../target/types/core/public/http'; import { AsyncInitialState } from './types'; import { MlCapabilitiesResponse } from '../../../../../plugins/ml/common/types/capabilities'; import { CreateMLJobSuccess, DeleteJobResults } from '../actions/types'; @@ -37,15 +36,13 @@ const initialState: MLJobState = { mlCapabilities: getAsyncInitialState(), }; -type Payload = IHttpFetchError; - export const mlJobsReducer = handleActions( { - ...handleAsyncAction('mlJob', getExistingMLJobAction), - ...handleAsyncAction('mlCapabilities', getMLCapabilitiesAction), - ...handleAsyncAction('createJob', createMLJobAction), - ...handleAsyncAction('deleteJob', deleteMLJobAction), - ...handleAsyncAction('anomalies', getAnomalyRecordsAction), + ...handleAsyncAction('mlJob', getExistingMLJobAction), + ...handleAsyncAction('mlCapabilities', getMLCapabilitiesAction), + ...handleAsyncAction('createJob', createMLJobAction), + ...handleAsyncAction('deleteJob', deleteMLJobAction), + ...handleAsyncAction('anomalies', getAnomalyRecordsAction), ...{ [String(resetMLState)]: state => ({ ...state, diff --git a/x-pack/plugins/uptime/public/state/reducers/monitor_list.ts b/x-pack/plugins/uptime/public/state/reducers/monitor_list.ts index cf895aebeb7558..59a794a549d576 100644 --- a/x-pack/plugins/uptime/public/state/reducers/monitor_list.ts +++ b/x-pack/plugins/uptime/public/state/reducers/monitor_list.ts @@ -9,9 +9,9 @@ import { getMonitorList, getMonitorListSuccess, getMonitorListFailure } from '.. import { MonitorSummaryResult } from '../../../common/runtime_types'; export interface MonitorList { - list: MonitorSummaryResult; error?: Error; loading: boolean; + list: MonitorSummaryResult; } export const initialState: MonitorList = { diff --git a/x-pack/plugins/uptime/public/state/reducers/utils.ts b/x-pack/plugins/uptime/public/state/reducers/utils.ts index d7a7f237c1154b..15e49e7f6de8bc 100644 --- a/x-pack/plugins/uptime/public/state/reducers/utils.ts +++ b/x-pack/plugins/uptime/public/state/reducers/utils.ts @@ -7,7 +7,7 @@ import { Action } from 'redux-actions'; import { AsyncAction } from '../actions/types'; -export function handleAsyncAction( +export function handleAsyncAction( storeKey: string, asyncAction: AsyncAction ) { @@ -24,7 +24,7 @@ export function handleAsyncAction( ...state, [storeKey]: { ...(state as any)[storeKey], - data: action.payload === null ? action.payload : { ...action.payload }, + data: action.payload, loading: false, }, }), diff --git a/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts b/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts index ba5e5abf588b8d..1c4c12f5f52d2f 100644 --- a/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts +++ b/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts @@ -101,6 +101,12 @@ describe('state selectors', () => { loading: false, }, }, + certificates: { + certs: { + data: null, + loading: false, + }, + }, }; it('selects base path from state', () => { diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_certs.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_certs.test.ts index 894e2316dc927f..4aec376ceadf02 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_certs.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_certs.test.ts @@ -19,9 +19,10 @@ describe('getCerts', () => { _score: 0, _source: { tls: { - certificate_not_valid_before: '2019-08-16T01:40:25.000Z', server: { x509: { + not_before: '2019-08-16T01:40:25.000Z', + not_after: '2020-07-16T03:15:39.000Z', subject: { common_name: 'r2.shared.global.fastly.net', }, @@ -34,12 +35,14 @@ describe('getCerts', () => { sha256: '12b00d04db0db8caa302bfde043e88f95baceb91e86ac143e93830b4bbec726d', }, }, - certificate_not_valid_after: '2020-07-16T03:15:39.000Z', }, monitor: { name: 'Real World Test', id: 'real-world-test', }, + url: { + full: 'https://fullurl.com', + }, }, fields: { 'tls.server.hash.sha256': [ @@ -96,24 +99,30 @@ describe('getCerts', () => { to: 'now+1h', search: 'my_common_name', size: 30, + sortBy: 'not_after', + direction: 'desc', }); expect(result).toMatchInlineSnapshot(` - Array [ - Object { - "certificate_not_valid_after": "2020-07-16T03:15:39.000Z", - "certificate_not_valid_before": "2019-08-16T01:40:25.000Z", - "common_name": "r2.shared.global.fastly.net", - "issuer": "GlobalSign CloudSSL CA - SHA256 - G3", - "monitors": Array [ - Object { - "id": "real-world-test", - "name": "Real World Test", - }, - ], - "sha1": "b7b4b89ef0d0caf39d223736f0fdbb03c7b426f1", - "sha256": "12b00d04db0db8caa302bfde043e88f95baceb91e86ac143e93830b4bbec726d", - }, - ] + Object { + "certs": Array [ + Object { + "common_name": "r2.shared.global.fastly.net", + "issuer": "GlobalSign CloudSSL CA - SHA256 - G3", + "monitors": Array [ + Object { + "id": "real-world-test", + "name": "Real World Test", + "url": undefined, + }, + ], + "not_after": "2020-07-16T03:15:39.000Z", + "not_before": "2019-08-16T01:40:25.000Z", + "sha1": "b7b4b89ef0d0caf39d223736f0fdbb03c7b426f1", + "sha256": "12b00d04db0db8caa302bfde043e88f95baceb91e86ac143e93830b4bbec726d", + }, + ], + "total": 0, + } `); expect(mockCallES.mock.calls).toMatchInlineSnapshot(` Array [ @@ -128,9 +137,16 @@ describe('getCerts', () => { "tls.server.x509.subject.common_name", "tls.server.hash.sha1", "tls.server.hash.sha256", - "tls.certificate_not_valid_before", - "tls.certificate_not_valid_after", + "tls.server.x509.not_after", + "tls.server.x509.not_before", ], + "aggs": Object { + "total": Object { + "cardinality": Object { + "field": "tls.server.hash.sha256", + }, + }, + }, "collapse": Object { "field": "tls.server.hash.sha256", "inner_hits": Object { @@ -138,6 +154,7 @@ describe('getCerts', () => { "includes": Array [ "monitor.id", "monitor.name", + "url.full", ], }, "collapse": Object { @@ -151,13 +168,13 @@ describe('getCerts', () => { ], }, }, - "from": 1, + "from": 30, "query": Object { "bool": Object { "filter": Array [ Object { "exists": Object { - "field": "tls", + "field": "tls.server", }, }, Object { @@ -169,39 +186,32 @@ describe('getCerts', () => { }, }, ], + "minimum_should_match": 1, "should": Array [ Object { - "wildcard": Object { - "tls.server.issuer": Object { - "value": "*my_common_name*", - }, - }, - }, - Object { - "wildcard": Object { - "tls.common_name": Object { - "value": "*my_common_name*", - }, - }, - }, - Object { - "wildcard": Object { - "monitor.id": Object { - "value": "*my_common_name*", - }, - }, - }, - Object { - "wildcard": Object { - "monitor.name": Object { - "value": "*my_common_name*", - }, + "multi_match": Object { + "fields": Array [ + "monitor.id.text", + "monitor.name.text", + "url.full.text", + "tls.server.x509.subject.common_name.text", + "tls.server.x509.issuer.common_name.text", + ], + "query": "my_common_name", + "type": "phrase_prefix", }, }, ], }, }, "size": 30, + "sort": Array [ + Object { + "tls.server.x509.not_after": Object { + "order": "desc", + }, + }, + ], }, "index": "heartbeat*", }, diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts index 75bf5096bd9978..f8a335c387f2e9 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts @@ -32,7 +32,14 @@ describe('getLatestMonitor', () => { }, }, size: 1, - _source: ['url', 'monitor', 'observer', 'tls', '@timestamp'], + _source: [ + 'url', + 'monitor', + 'observer', + '@timestamp', + 'tls.server.x509.not_after', + 'tls.server.x509.not_before', + ], sort: { '@timestamp': { order: 'desc' }, }, @@ -83,6 +90,10 @@ describe('getLatestMonitor', () => { "type": "http", }, "timestamp": "123456", + "tls": Object { + "not_after": undefined, + "not_before": undefined, + }, } `); expect(result.timestamp).toBe('123456'); diff --git a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts index b427e7cae1a7e8..6820cd69376d18 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_certs.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_certs.ts @@ -5,9 +5,16 @@ */ import { UMElasticsearchQueryFn } from '../adapters'; -import { Cert, GetCertsParams } from '../../../common/runtime_types'; +import { CertResult, GetCertsParams } from '../../../common/runtime_types'; -export const getCerts: UMElasticsearchQueryFn = async ({ +enum SortFields { + 'issuer' = 'tls.server.x509.issuer.common_name', + 'not_after' = 'tls.server.x509.not_after', + 'not_before' = 'tls.server.x509.not_before', + 'common_name' = 'tls.server.x509.subject.common_name', +} + +export const getCerts: UMElasticsearchQueryFn = async ({ callES, dynamicSettings, index, @@ -15,19 +22,29 @@ export const getCerts: UMElasticsearchQueryFn = async ({ to, search, size, + sortBy, + direction, }) => { - const searchWrapper = `*${search}*`; + const sort = SortFields[sortBy as keyof typeof SortFields]; + const params: any = { index: dynamicSettings.heartbeatIndices, body: { - from: index, + from: index * size, size, + sort: [ + { + [sort]: { + order: direction, + }, + }, + ], query: { bool: { filter: [ { exists: { - field: 'tls', + field: 'tls.server', }, }, { @@ -48,14 +65,14 @@ export const getCerts: UMElasticsearchQueryFn = async ({ 'tls.server.x509.subject.common_name', 'tls.server.hash.sha1', 'tls.server.hash.sha256', - 'tls.certificate_not_valid_before', - 'tls.certificate_not_valid_after', + 'tls.server.x509.not_after', + 'tls.server.x509.not_before', ], collapse: { field: 'tls.server.hash.sha256', inner_hits: { _source: { - includes: ['monitor.id', 'monitor.name'], + includes: ['monitor.id', 'monitor.name', 'url.full'], }, collapse: { field: 'monitor.id', @@ -64,72 +81,67 @@ export const getCerts: UMElasticsearchQueryFn = async ({ sort: [{ 'monitor.id': 'asc' }], }, }, + aggs: { + total: { + cardinality: { + field: 'tls.server.hash.sha256', + }, + }, + }, }, }; if (search) { + params.body.query.bool.minimum_should_match = 1; params.body.query.bool.should = [ { - wildcard: { - 'tls.server.issuer': { - value: searchWrapper, - }, - }, - }, - { - wildcard: { - 'tls.common_name': { - value: searchWrapper, - }, - }, - }, - { - wildcard: { - 'monitor.id': { - value: searchWrapper, - }, - }, - }, - { - wildcard: { - 'monitor.name': { - value: searchWrapper, - }, + multi_match: { + query: escape(search), + type: 'phrase_prefix', + fields: [ + 'monitor.id.text', + 'monitor.name.text', + 'url.full.text', + 'tls.server.x509.subject.common_name.text', + 'tls.server.x509.issuer.common_name.text', + ], }, }, ]; } const result = await callES('search', params); - const formatted = (result?.hits?.hits ?? []).map((hit: any) => { + + const certs = (result?.hits?.hits ?? []).map((hit: any) => { const { _source: { - tls: { - server: { - x509: { - issuer: { common_name: issuer }, - subject: { common_name }, - }, - hash: { sha1, sha256 }, - }, - certificate_not_valid_after, - certificate_not_valid_before, - }, + tls: { server }, }, } = hit; + + const notAfter = server?.x509?.not_after; + const notBefore = server?.x509?.not_before; + const issuer = server?.x509?.issuer?.common_name; + const commonName = server?.x509?.subject?.common_name; + const sha1 = server?.hash?.sha1; + const sha256 = server?.hash?.sha256; + const monitors = hit.inner_hits.monitors.hits.hits.map((monitor: any) => ({ name: monitor._source?.monitor.name, id: monitor._source?.monitor.id, + url: monitor._source?.url?.full, })); + return { monitors, - certificate_not_valid_after, - certificate_not_valid_before, issuer, sha1, sha256, - common_name, + not_after: notAfter, + not_before: notBefore, + common_name: commonName, }; }); - return formatted; + const total = result?.aggregations?.total?.value ?? 0; + return { certs, total }; }; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts b/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts index 98ce449002f217..db34de51592135 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts @@ -45,7 +45,14 @@ export const getLatestMonitor: UMElasticsearchQueryFn = UMElasticsearchQueryFn; export interface UptimeRequests { - getCerts: ESQ; + getCerts: ESQ; getFilterBar: ESQ; getIndexPattern: ESQ<{}, {}>; getLatestMonitor: ESQ; diff --git a/x-pack/plugins/uptime/server/rest_api/certs.ts b/x-pack/plugins/uptime/server/rest_api/certs/certs.ts similarity index 63% rename from x-pack/plugins/uptime/server/rest_api/certs.ts rename to x-pack/plugins/uptime/server/rest_api/certs/certs.ts index f2e1700b23e7d8..a5ca6e264d299f 100644 --- a/x-pack/plugins/uptime/server/rest_api/certs.ts +++ b/x-pack/plugins/uptime/server/rest_api/certs/certs.ts @@ -5,14 +5,16 @@ */ import { schema } from '@kbn/config-schema'; -import { UMServerLibs } from '../lib/lib'; -import { UMRestApiRouteFactory } from '.'; -import { API_URLS } from '../../common/constants'; +import { API_URLS } from '../../../common/constants'; +import { UMServerLibs } from '../../lib/lib'; +import { UMRestApiRouteFactory } from '../types'; const DEFAULT_INDEX = 0; const DEFAULT_SIZE = 25; const DEFAULT_FROM = 'now-1d'; const DEFAULT_TO = 'now'; +const DEFAULT_SORT = 'not_after'; +const DEFAULT_DIRECTION = 'asc'; export const createGetCertsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', @@ -24,30 +26,33 @@ export const createGetCertsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) = search: schema.maybe(schema.string()), index: schema.maybe(schema.number()), size: schema.maybe(schema.number()), + sortBy: schema.maybe(schema.string()), + direction: schema.maybe(schema.string()), }), }, - writeAccess: false, - options: { - tags: ['access:uptime-read'], - }, handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { const index = request.query?.index ?? DEFAULT_INDEX; const size = request.query?.size ?? DEFAULT_SIZE; const from = request.query?.from ?? DEFAULT_FROM; const to = request.query?.to ?? DEFAULT_TO; + const sortBy = request.query?.sortBy ?? DEFAULT_SORT; + const direction = request.query?.direction ?? DEFAULT_DIRECTION; const { search } = request.query; - + const result = await libs.requests.getCerts({ + callES, + dynamicSettings, + index, + search, + size, + from, + to, + sortBy, + direction, + }); return response.ok({ body: { - certs: await libs.requests.getCerts({ - callES, - dynamicSettings, - index, - search, - size, - from, - to, - }), + certs: result.certs, + total: result.total, }, }); }, diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts index a7a63342d11d46..2b598be284e1cb 100644 --- a/x-pack/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createGetCertsRoute } from './certs'; +import { createGetCertsRoute } from './certs/certs'; import { createGetOverviewFilters } from './overview_filters'; import { createGetPingHistogramRoute, createGetPingsRoute } from './pings'; import { createGetDynamicSettingsRoute, createPostDynamicSettingsRoute } from './dynamic_settings'; @@ -19,6 +19,7 @@ import { } from './monitors'; import { createGetMonitorDurationRoute } from './monitors/monitors_durations'; import { createGetIndexPatternRoute, createGetIndexStatusRoute } from './index_state'; + export * from './types'; export { createRouteWithAuth } from './create_route_with_auth'; export { uptimeRouteWrapper } from './uptime_route_wrapper'; diff --git a/x-pack/test/api_integration/apis/uptime/rest/certs.ts b/x-pack/test/api_integration/apis/uptime/rest/certs.ts index a3a15d8f8b0143..4917917fdd6bc1 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/certs.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/certs.ts @@ -21,7 +21,7 @@ export default function({ getService }: FtrProviderContext) { describe('empty index', async () => { it('returns empty array for no data', async () => { const apiResponse = await supertest.get(API_URLS.CERTS); - expect(JSON.stringify(apiResponse.body)).to.eql('{"certs":[]}'); + expect(JSON.stringify(apiResponse.body)).to.eql('{"certs":[],"total":0}'); }); }); @@ -39,10 +39,10 @@ export default function({ getService }: FtrProviderContext) { 10000, { tls: { - certificate_not_valid_after: cnva, - certificate_not_valid_before: cnvb, server: { x509: { + not_after: cnva, + not_before: cnvb, issuer: { common_name: 'issuer-common-name', }, @@ -78,9 +78,12 @@ export default function({ getService }: FtrProviderContext) { const cert = body.certs[0]; expect(Array.isArray(cert.monitors)).to.be(true); - expect(cert.monitors[0]).to.eql({ id: monitorId }); - expect(cert.certificate_not_valid_after).to.eql(cnva); - expect(cert.certificate_not_valid_before).to.eql(cnvb); + expect(cert.monitors[0]).to.eql({ + id: monitorId, + url: 'http://localhost:5678/pattern?r=200x5,500x1', + }); + expect(cert.not_after).to.eql(cnva); + expect(cert.not_before).to.eql(cnvb); expect(cert.common_name).to.eql('subject-common-name'); expect(cert.issuer).to.eql('issuer-common-name'); }); diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/monitor_latest_status.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/monitor_latest_status.json index 9a33be807670ea..1baff443bd97fc 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/monitor_latest_status.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/monitor_latest_status.json @@ -27,5 +27,6 @@ "full": "http://localhost:5678/pattern?r=200x1" }, "docId": "h5toHm0B0I9WX_CznN_V", - "timestamp": "2019-09-11T03:40:34.371Z" -} \ No newline at end of file + "timestamp": "2019-09-11T03:40:34.371Z", + "tls": {} +} diff --git a/x-pack/test/api_integration/apis/uptime/rest/helper/make_checks.ts b/x-pack/test/api_integration/apis/uptime/rest/helper/make_checks.ts index ae326c8b2aee0c..5f62a3c55a2eb0 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/helper/make_checks.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/helper/make_checks.ts @@ -6,119 +6,36 @@ import uuid from 'uuid'; import { merge, flattenDeep } from 'lodash'; - -const INDEX_NAME = 'heartbeat-8-generated-test'; - -export const makePing = async ( - es: any, - monitorId: string, - fields: { [key: string]: any }, - mogrify: (doc: any) => any, - refresh: boolean = true -) => { - const baseDoc = { - tcp: { - rtt: { - connect: { - us: 14687, - }, - }, - }, - observer: { - geo: { - name: 'mpls', - location: '37.926868, -78.024902', - }, - hostname: 'avc-x1e', - }, - agent: { - hostname: 'avc-x1e', - id: '10730a1a-4cb7-45ce-8524-80c4820476ab', - type: 'heartbeat', - ephemeral_id: '0d9a8dc6-f604-49e3-86a0-d8f9d6f2cbad', - version: '8.0.0', - }, - '@timestamp': new Date().toISOString(), - resolve: { - rtt: { - us: 350, - }, - ip: '127.0.0.1', - }, - ecs: { - version: '1.1.0', - }, - host: { - name: 'avc-x1e', - }, - http: { - rtt: { - response_header: { - us: 19349, - }, - total: { - us: 48954, - }, - write_request: { - us: 33, - }, - content: { - us: 51, - }, - validate: { - us: 19400, - }, - }, - response: { - status_code: 200, - body: { - bytes: 3, - hash: '27badc983df1780b60c2b3fa9d3a19a00e46aac798451f0febdca52920faaddf', - }, - }, - }, - monitor: { - duration: { - us: 49347, - }, - ip: '127.0.0.1', - id: monitorId, - check_group: uuid.v4(), - type: 'http', - status: 'up', - }, - event: { - dataset: 'uptime', - }, - url: { - path: '/pattern', - scheme: 'http', - port: 5678, - domain: 'localhost', - query: 'r=200x5,500x1', - full: 'http://localhost:5678/pattern?r=200x5,500x1', - }, - }; - - const doc = mogrify(merge(baseDoc, fields)); - - await es.index({ - index: INDEX_NAME, - refresh, - body: doc, - }); - - return doc; +import { makePing } from './make_ping'; +import { TlsProps } from './make_tls'; + +interface CheckProps { + es: any; + monitorId?: string; + numIps?: number; + fields?: { [key: string]: any }; + mogrify?: (doc: any) => any; + refresh?: boolean; + tls?: boolean | TlsProps; +} + +const getRandomMonitorId = () => { + return ( + 'monitor-' + + Math.random() + .toString(36) + .substring(7) + ); }; - -export const makeCheck = async ( - es: any, - monitorId: string, - numIps: number, - fields: { [key: string]: any }, - mogrify: (doc: any) => any, - refresh: boolean = true -) => { +export const makeCheck = async ({ + es, + monitorId = getRandomMonitorId(), + numIps = 1, + fields = {}, + mogrify = d => d, + refresh = true, + tls = false, +}: CheckProps): Promise<{ monitorId: string; docs: any }> => { const cgFields = { monitor: { check_group: uuid.v4(), @@ -139,7 +56,7 @@ export const makeCheck = async ( if (i === numIps - 1) { pingFields.summary = summary; } - const doc = await makePing(es, monitorId, pingFields, mogrify, false); + const doc = await makePing(es, monitorId, pingFields, mogrify, false, tls as any); docs.push(doc); // @ts-ignore summary[doc.monitor.status]++; @@ -149,15 +66,15 @@ export const makeCheck = async ( await es.indices.refresh(); } - return docs; + return { monitorId, docs }; }; export const makeChecks = async ( es: any, monitorId: string, - numChecks: number, - numIps: number, - every: number, // number of millis between checks + numChecks: number = 1, + numIps: number = 1, + every: number = 10000, // number of millis between checks fields: { [key: string]: any } = {}, mogrify: (doc: any) => any = d => d, refresh: boolean = true @@ -177,7 +94,8 @@ export const makeChecks = async ( }, }, }); - checks.push(await makeCheck(es, monitorId, numIps, fields, mogrify, false)); + const { docs } = await makeCheck({ es, monitorId, numIps, fields, mogrify, refresh: false }); + checks.push(docs); } if (refresh) { diff --git a/x-pack/test/api_integration/apis/uptime/rest/helper/make_ping.ts b/x-pack/test/api_integration/apis/uptime/rest/helper/make_ping.ts new file mode 100644 index 00000000000000..908c571e07e067 --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/helper/make_ping.ts @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import uuid from 'uuid'; +import { merge } from 'lodash'; +import { makeTls, TlsProps } from './make_tls'; + +const INDEX_NAME = 'heartbeat-8-generated-test'; + +export const makePing = async ( + es: any, + monitorId: string, + fields: { [key: string]: any }, + mogrify: (doc: any) => any, + refresh: boolean = true, + tls: boolean | TlsProps = false +) => { + const baseDoc: any = { + tcp: { + rtt: { + connect: { + us: 14687, + }, + }, + }, + observer: { + geo: { + name: 'mpls', + location: '37.926868, -78.024902', + }, + hostname: 'avc-x1e', + }, + agent: { + hostname: 'avc-x1e', + id: '10730a1a-4cb7-45ce-8524-80c4820476ab', + type: 'heartbeat', + ephemeral_id: '0d9a8dc6-f604-49e3-86a0-d8f9d6f2cbad', + version: '8.0.0', + }, + '@timestamp': new Date().toISOString(), + resolve: { + rtt: { + us: 350, + }, + ip: '127.0.0.1', + }, + ecs: { + version: '1.1.0', + }, + host: { + name: 'avc-x1e', + }, + http: { + rtt: { + response_header: { + us: 19349, + }, + total: { + us: 48954, + }, + write_request: { + us: 33, + }, + content: { + us: 51, + }, + validate: { + us: 19400, + }, + }, + response: { + status_code: 200, + body: { + bytes: 3, + hash: '27badc983df1780b60c2b3fa9d3a19a00e46aac798451f0febdca52920faaddf', + }, + }, + }, + monitor: { + duration: { + us: 49347, + }, + ip: '127.0.0.1', + id: monitorId, + check_group: uuid.v4(), + type: 'http', + status: 'up', + }, + event: { + dataset: 'uptime', + }, + url: { + path: '/pattern', + scheme: 'http', + port: 5678, + domain: 'localhost', + query: 'r=200x5,500x1', + full: 'http://localhost:5678/pattern?r=200x5,500x1', + }, + }; + + if (tls) { + baseDoc.tls = makeTls(tls as any); + } + + const doc = mogrify(merge(baseDoc, fields)); + + await es.index({ + index: INDEX_NAME, + refresh, + body: doc, + }); + + return doc; +}; diff --git a/x-pack/test/api_integration/apis/uptime/rest/helper/make_tls.ts b/x-pack/test/api_integration/apis/uptime/rest/helper/make_tls.ts new file mode 100644 index 00000000000000..36064625220245 --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/helper/make_tls.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import moment from 'moment'; +import crypto from 'crypto'; + +export interface TlsProps { + valid?: boolean; + commonName?: string; + expiry?: string; + sha256?: string; +} + +type Props = TlsProps & boolean; + +// Note This is just a mock sha256 value, this doesn't actually generate actually sha 256 val +export const getSha256 = () => { + return crypto + .randomBytes(64) + .toString('hex') + .toUpperCase(); +}; + +export const makeTls = ({ valid = true, commonName = '*.elastic.co', expiry, sha256 }: Props) => { + const expiryDate = + expiry ?? + moment() + .add(valid ? 2 : -2, 'months') + .toISOString(); + + return { + version: '1.3', + cipher: 'TLS-AES-128-GCM-SHA256', + certificate_not_valid_before: '2020-03-01T00:00:00.000Z', + certificate_not_valid_after: expiryDate, + server: { + x509: { + not_before: '2020-03-01T00:00:00.000Z', + not_after: '2020-05-30T12:00:00.000Z', + issuer: { + distinguished_name: + 'CN=DigiCert SHA2 High Assurance Server CA,OU=www.digicert.com,O=DigiCert Inc,C=US', + common_name: 'DigiCert SHA2 High Assurance Server CA', + }, + subject: { + common_name: commonName, + distinguished_name: 'CN=*.facebook.com,O=Facebook Inc.,L=Menlo Park,ST=California,C=US', + }, + serial_number: '10043199409725537507026285099403602396', + signature_algorithm: 'SHA256-RSA', + public_key_algorithm: 'ECDSA', + public_key_curve: 'P-256', + }, + hash: { + sha256: sha256 ?? '1a48f1db13c3bd1482ba1073441e74a1bb1308dc445c88749e0dc4f1889a88a4', + sha1: '23291c758d925b9f4bb3584de3763317e94c6ce9', + }, + }, + established: true, + rtt: { + handshake: { + us: 33103, + }, + }, + version_protocol: 'tls', + }; +}; diff --git a/x-pack/test/functional/apps/uptime/certificates.ts b/x-pack/test/functional/apps/uptime/certificates.ts new file mode 100644 index 00000000000000..05967e0f3acaf0 --- /dev/null +++ b/x-pack/test/functional/apps/uptime/certificates.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; +import { makeCheck } from '../../../api_integration/apis/uptime/rest/helper/make_checks'; +import { getSha256 } from '../../../api_integration/apis/uptime/rest/helper/make_tls'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const { uptime } = getPageObjects(['uptime']); + const uptimeService = getService('uptime'); + + const es = getService('es'); + + describe('certificate page', function() { + before(async () => { + await uptime.goToRoot(true); + }); + + beforeEach(async () => { + await makeCheck({ es, tls: true }); + await uptimeService.navigation.refreshApp(); + }); + + it('can navigate to cert page', async () => { + await uptimeService.navigation.refreshApp(); + await uptimeService.cert.hasViewCertButton(); + await uptimeService.navigation.goToCertificates(); + }); + + it('displays certificates', async () => { + await uptimeService.cert.hasCertificates(); + }); + + it('displays specific certificates', async () => { + const certId = getSha256(); + const { monitorId } = await makeCheck({ + es, + tls: { + sha256: certId, + }, + }); + + await uptimeService.navigation.refreshApp(); + await uptimeService.cert.certificateExists({ certId, monitorId }); + }); + + it('performs search against monitor id', async () => { + const certId = getSha256(); + const { monitorId } = await makeCheck({ + es, + tls: { + sha256: certId, + }, + }); + await uptimeService.navigation.refreshApp(); + await uptimeService.cert.searchIsWorking(monitorId); + }); + }); +}; diff --git a/x-pack/test/functional/apps/uptime/index.ts b/x-pack/test/functional/apps/uptime/index.ts index f47214dc2ad2f8..6ecd39f696312c 100644 --- a/x-pack/test/functional/apps/uptime/index.ts +++ b/x-pack/test/functional/apps/uptime/index.ts @@ -53,6 +53,7 @@ export default ({ loadTestFile, getService }: FtrProviderContext) => { loadTestFile(require.resolve('./locations')); loadTestFile(require.resolve('./settings')); + loadTestFile(require.resolve('./certificates')); }); describe('with real-world data', () => { before(async () => { diff --git a/x-pack/test/functional/es_archives/uptime/blank/mappings.json b/x-pack/test/functional/es_archives/uptime/blank/mappings.json index fff4ef47bce0cf..dd7f5cb9aa7789 100644 --- a/x-pack/test/functional/es_archives/uptime/blank/mappings.json +++ b/x-pack/test/functional/es_archives/uptime/blank/mappings.json @@ -1665,6 +1665,13 @@ }, "id": { "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false, + "analyzer": "simple" + } + }, "ignore_above": 1024 }, "ip": { @@ -1672,6 +1679,13 @@ }, "name": { "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false, + "analyzer": "simple" + } + }, "ignore_above": 1024 }, "status": { @@ -3079,10 +3093,21 @@ }, "x509": { "properties": { + "alternative_names": { + "type": "keyword", + "ignore_above": 1024 + }, "issuer": { "properties": { "common_name": { "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false, + "analyzer": "simple" + } + }, "ignore_above": 1024 }, "distinguished_name": { @@ -3092,14 +3117,16 @@ } }, "not_after": { - "type": "keyword", - "ignore_above": 1024 + "type": "date" }, "not_before": { + "type": "date" + }, + "public_key_algorithm": { "type": "keyword", "ignore_above": 1024 }, - "public_key_algorithm": { + "public_key_curve": { "type": "keyword", "ignore_above": 1024 }, @@ -3121,6 +3148,13 @@ "properties": { "common_name": { "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false, + "analyzer": "simple" + } + }, "ignore_above": 1024 }, "distinguished_name": { @@ -3128,6 +3162,10 @@ "ignore_above": 1024 } } + }, + "version_number": { + "type": "keyword", + "ignore_above": 1024 } } } diff --git a/x-pack/test/functional/es_archives/uptime/full_heartbeat/mappings.json b/x-pack/test/functional/es_archives/uptime/full_heartbeat/mappings.json index 2b6002ddb3fab1..97b72510da286b 100644 --- a/x-pack/test/functional/es_archives/uptime/full_heartbeat/mappings.json +++ b/x-pack/test/functional/es_archives/uptime/full_heartbeat/mappings.json @@ -12,79 +12,115 @@ "beat": "heartbeat", "version": "8.0.0" }, - "date_detection": false, "dynamic_templates": [ { "labels": { + "path_match": "labels.*", + "match_mapping_type": "string", "mapping": { "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "labels.*" + } } }, { "container.labels": { + "path_match": "container.labels.*", + "match_mapping_type": "string", "mapping": { "type": "keyword" - }, - "match_mapping_type": "string", - "path_match": "container.labels.*" + } } }, { "dns.answers": { + "path_match": "dns.answers.*", + "match_mapping_type": "string", "mapping": { "type": "keyword" - }, + } + } + }, + { + "log.syslog": { + "path_match": "log.syslog.*", "match_mapping_type": "string", - "path_match": "dns.answers.*" + "mapping": { + "type": "keyword" + } } }, { - "fields": { + "network.inner": { + "path_match": "network.inner.*", + "match_mapping_type": "string", "mapping": { "type": "keyword" - }, + } + } + }, + { + "observer.egress": { + "path_match": "observer.egress.*", "match_mapping_type": "string", - "path_match": "fields.*" + "mapping": { + "type": "keyword" + } } }, { - "docker.container.labels": { + "observer.ingress": { + "path_match": "observer.ingress.*", + "match_mapping_type": "string", "mapping": { "type": "keyword" - }, + } + } + }, + { + "fields": { + "path_match": "fields.*", + "match_mapping_type": "string", + "mapping": { + "type": "keyword" + } + } + }, + { + "docker.container.labels": { + "path_match": "docker.container.labels.*", "match_mapping_type": "string", - "path_match": "docker.container.labels.*" + "mapping": { + "type": "keyword" + } } }, { "kubernetes.labels.*": { + "path_match": "kubernetes.labels.*", "mapping": { "type": "keyword" - }, - "path_match": "kubernetes.labels.*" + } } }, { "kubernetes.annotations.*": { + "path_match": "kubernetes.annotations.*", "mapping": { "type": "keyword" - }, - "path_match": "kubernetes.annotations.*" + } } }, { "strings_as_keyword": { + "match_mapping_type": "string", "mapping": { "ignore_above": 1024, "type": "keyword" - }, - "match_mapping_type": "string" + } } } ], + "date_detection": false, "properties": { "@timestamp": { "type": "date" @@ -92,28 +128,28 @@ "agent": { "properties": { "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "hostname": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "version": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -125,8 +161,14 @@ "organization": { "properties": { "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 } } } @@ -135,8 +177,8 @@ "client": { "properties": { "address": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "as": { "properties": { @@ -146,8 +188,14 @@ "organization": { "properties": { "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 } } } @@ -157,41 +205,41 @@ "type": "long" }, "domain": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "geo": { "properties": { "city_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "continent_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "location": { "type": "geo_point" }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -199,8 +247,8 @@ "type": "ip" }, "mac": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "nat": { "properties": { @@ -218,43 +266,67 @@ "port": { "type": "long" }, + "registered_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 + }, "user": { "properties": { "domain": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "email": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "full_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "group": { "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "hash": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 } } } @@ -265,76 +337,97 @@ "account": { "properties": { "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "availability_zone": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "image": { "properties": { "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "instance": { "properties": { "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "machine": { "properties": { "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "project": { "properties": { "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "provider": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" } } }, "container": { "properties": { "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "image": { "properties": { "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "tag": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -342,20 +435,20 @@ "type": "object" }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "runtime": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "destination": { "properties": { "address": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "as": { "properties": { @@ -365,8 +458,14 @@ "organization": { "properties": { "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 } } } @@ -376,41 +475,41 @@ "type": "long" }, "domain": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "geo": { "properties": { "city_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "continent_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "location": { "type": "geo_point" }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -418,8 +517,8 @@ "type": "ip" }, "mac": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "nat": { "properties": { @@ -437,43 +536,144 @@ "port": { "type": "long" }, + "registered_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 + }, "user": { "properties": { "domain": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "email": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "full_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "group": { "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "hash": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + } + } + } + } + }, + "dll": { + "properties": { + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha512": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "pe": { + "properties": { + "company": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "file_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "original_file_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "product": { + "type": "keyword", + "ignore_above": 1024 } } } @@ -484,55 +684,63 @@ "answers": { "properties": { "class": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "data": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "ttl": { "type": "long" }, "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "header_flags": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "op_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "question": { "properties": { "class": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "registered_domain": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "subdomain": { + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 }, "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -540,12 +748,12 @@ "type": "ip" }, "response_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -563,51 +771,61 @@ "ecs": { "properties": { "version": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "error": { "properties": { "code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "message": { - "norms": false, - "type": "text" + "type": "text", + "norms": false + }, + "stack_trace": { + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "event": { "properties": { "action": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "category": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "created": { "type": "date" }, "dataset": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "duration": { "type": "long" @@ -616,32 +834,39 @@ "type": "date" }, "hash": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "ingested": { + "type": "date" }, "kind": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "module": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "original": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "outcome": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "provider": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 }, "risk_score": { "type": "float" @@ -659,12 +884,16 @@ "type": "date" }, "timezone": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "url": { + "type": "keyword", + "ignore_above": 1024 } } }, @@ -676,6 +905,31 @@ "accessed": { "type": "date" }, + "attributes": { + "type": "keyword", + "ignore_above": 1024 + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, "created": { "type": "date" }, @@ -683,254 +937,318 @@ "type": "date" }, "device": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "directory": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "drive_letter": { + "type": "keyword", + "ignore_above": 1 }, "extension": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "gid": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "group": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "hash": { "properties": { "md5": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "sha1": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "sha256": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "sha512": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "inode": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "mime_type": { + "type": "keyword", + "ignore_above": 1024 }, "mode": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "mtime": { "type": "date" }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "owner": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "path": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + }, + "pe": { + "properties": { + "company": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "file_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "original_file_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "product": { + "type": "keyword", + "ignore_above": 1024 + } + } }, "size": { "type": "long" }, "target_path": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "uid": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "geo": { "properties": { "city_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "continent_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "location": { "type": "geo_point" }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "group": { "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "hash": { "properties": { "md5": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "sha1": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "sha256": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "sha512": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "host": { "properties": { "architecture": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "containerized": { "type": "boolean" }, + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, "geo": { "properties": { "city_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "continent_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "location": { "type": "geo_point" }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "hostname": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "ip": { "type": "ip" }, "mac": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "os": { "properties": { "build": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "codename": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "family": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "full": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "kernel": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "platform": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "version": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "uptime": { "type": "long" @@ -938,40 +1256,56 @@ "user": { "properties": { "domain": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "email": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "full_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "group": { "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "hash": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 } } } @@ -987,8 +1321,14 @@ "type": "long" }, "content": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 } } }, @@ -996,12 +1336,12 @@ "type": "long" }, "method": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "referrer": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -1013,18 +1353,28 @@ "type": "long" }, "content": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "hash": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "bytes": { "type": "long" }, + "redirects": { + "type": "keyword", + "ignore_above": 1024 + }, "status_code": { "type": "long" } @@ -1077,8 +1427,8 @@ } }, "version": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -1096,17 +1446,33 @@ } } }, + "interface": { + "properties": { + "alias": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, "jolokia": { "properties": { "agent": { "properties": { "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "version": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -1116,22 +1482,22 @@ "server": { "properties": { "product": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "vendor": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "version": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "url": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -1147,20 +1513,20 @@ "container": { "properties": { "image": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "deployment": { "properties": { "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -1172,42 +1538,42 @@ } }, "namespace": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "node": { "properties": { "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "pod": { "properties": { "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "uid": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "replicaset": { "properties": { "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "statefulset": { "properties": { "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } } @@ -1219,28 +1585,76 @@ "log": { "properties": { "level": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "logger": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "long" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "function": { + "type": "keyword", + "ignore_above": 1024 + } + } }, "original": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } } } }, "message": { - "norms": false, - "type": "text" + "type": "text", + "norms": false }, "monitor": { "properties": { "check_group": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "duration": { "properties": { @@ -1250,238 +1664,683 @@ } }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false, + "analyzer": "simple" + } + }, + "ignore_above": 1024 }, "ip": { "type": "ip" }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false, + "analyzer": "simple" + } + }, + "ignore_above": 1024 }, "status": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "timespan": { + "type": "date_range" }, "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "network": { "properties": { "application": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "bytes": { "type": "long" }, "community_id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "direction": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "forwarded_ip": { "type": "ip" }, "iana_number": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "inner": { + "properties": { + "vlan": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "packets": { "type": "long" }, "protocol": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "transport": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "vlan": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } } } }, "observer": { "properties": { + "egress": { + "properties": { + "interface": { + "properties": { + "alias": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "vlan": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "zone": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, "geo": { "properties": { "city_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "continent_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "location": { "type": "geo_point" }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "hostname": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "ingress": { + "properties": { + "interface": { + "properties": { + "alias": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "vlan": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "zone": { + "type": "keyword", + "ignore_above": 1024 + } + } }, "ip": { "type": "ip" }, "mac": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 }, "os": { "properties": { "family": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "full": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "kernel": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "platform": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "version": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, + "product": { + "type": "keyword", + "ignore_above": 1024 + }, "serial_number": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "vendor": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "version": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "organization": { "properties": { "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 } } }, "os": { "properties": { "family": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "full": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "kernel": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "platform": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "version": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "package": { + "properties": { + "architecture": { + "type": "keyword", + "ignore_above": 1024 + }, + "build_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "checksum": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "install_scope": { + "type": "keyword", + "ignore_above": 1024 + }, + "installed": { + "type": "date" + }, + "license": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + }, + "size": { + "type": "long" + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "pe": { + "properties": { + "company": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "file_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "original_file_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "product": { + "type": "keyword", + "ignore_above": 1024 } } }, "process": { "properties": { "args": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, - "executable": { - "ignore_above": 1024, - "type": "keyword" + "args_count": { + "type": "long" }, - "hash": { + "code_signature": { "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" + "exists": { + "type": "boolean" }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" + "status": { + "type": "keyword", + "ignore_above": 1024 }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" + "subject_name": { + "type": "keyword", + "ignore_above": 1024 }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + }, + "entity_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "executable": { + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha512": { + "type": "keyword", + "ignore_above": 1024 } } }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + }, + "parent": { + "properties": { + "args": { + "type": "keyword", + "ignore_above": 1024 + }, + "args_count": { + "type": "long" + }, + "code_signature": { + "properties": { + "exists": { + "type": "boolean" + }, + "status": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "trusted": { + "type": "boolean" + }, + "valid": { + "type": "boolean" + } + } + }, + "command_line": { + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + }, + "entity_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "executable": { + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha512": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "name": { + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "title": { + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + } + } + }, + "pe": { + "properties": { + "company": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "file_version": { + "type": "keyword", + "ignore_above": 1024 + }, + "original_file_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "product": { + "type": "keyword", + "ignore_above": 1024 + } + } }, "pgid": { "type": "long" @@ -1501,28 +2360,84 @@ "type": "long" }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "title": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "uptime": { "type": "long" }, "working_directory": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "type": "keyword", + "ignore_above": 1024 + }, + "strings": { + "type": "keyword", + "ignore_above": 1024 + }, + "type": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "hive": { + "type": "keyword", + "ignore_above": 1024 + }, + "key": { + "type": "keyword", + "ignore_above": 1024 + }, + "path": { + "type": "keyword", + "ignore_above": 1024 + }, + "value": { + "type": "keyword", + "ignore_above": 1024 } } }, "related": { "properties": { + "hash": { + "type": "keyword", + "ignore_above": 1024 + }, "ip": { "type": "ip" + }, + "user": { + "type": "keyword", + "ignore_above": 1024 } } }, @@ -1540,11 +2455,55 @@ } } }, + "rule": { + "properties": { + "author": { + "type": "keyword", + "ignore_above": 1024 + }, + "category": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "license": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + }, + "ruleset": { + "type": "keyword", + "ignore_above": 1024 + }, + "uuid": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, "server": { "properties": { "address": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "as": { "properties": { @@ -1554,8 +2513,14 @@ "organization": { "properties": { "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 } } } @@ -1565,41 +2530,41 @@ "type": "long" }, "domain": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "geo": { "properties": { "city_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "continent_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "location": { "type": "geo_point" }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -1607,8 +2572,8 @@ "type": "ip" }, "mac": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "nat": { "properties": { @@ -1626,43 +2591,67 @@ "port": { "type": "long" }, + "registered_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 + }, "user": { "properties": { "domain": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "email": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "full_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "group": { "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "hash": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 } } } @@ -1671,28 +2660,36 @@ "service": { "properties": { "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "node": { + "properties": { + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } }, "state": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "type": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "version": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -1714,8 +2711,8 @@ "source": { "properties": { "address": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "as": { "properties": { @@ -1725,8 +2722,14 @@ "organization": { "properties": { "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 } } } @@ -1736,41 +2739,41 @@ "type": "long" }, "domain": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "geo": { "properties": { "city_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "continent_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "country_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "location": { "type": "geo_point" }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "region_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -1778,8 +2781,8 @@ "type": "ip" }, "mac": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "nat": { "properties": { @@ -1797,43 +2800,67 @@ "port": { "type": "long" }, + "registered_domain": { + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 + }, "user": { "properties": { "domain": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "email": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "full_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "group": { "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "hash": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 } } } @@ -1850,8 +2877,8 @@ } }, "tags": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "tcp": { "properties": { @@ -1875,11 +2902,57 @@ } } }, + "threat": { + "properties": { + "framework": { + "type": "keyword", + "ignore_above": 1024 + }, + "tactic": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "technique": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, "timeseries": { "properties": { "instance": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, @@ -1891,6 +2964,78 @@ "certificate_not_valid_before": { "type": "date" }, + "cipher": { + "type": "keyword", + "ignore_above": 1024 + }, + "client": { + "properties": { + "certificate": { + "type": "keyword", + "ignore_above": 1024 + }, + "certificate_chain": { + "type": "keyword", + "ignore_above": 1024 + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "issuer": { + "type": "keyword", + "ignore_above": 1024 + }, + "ja3": { + "type": "keyword", + "ignore_above": 1024 + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject": { + "type": "keyword", + "ignore_above": 1024 + }, + "supported_ciphers": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "curve": { + "type": "keyword", + "ignore_above": 1024 + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "type": "keyword", + "ignore_above": 1024 + }, + "resumed": { + "type": "boolean" + }, "rtt": { "properties": { "handshake": { @@ -1901,6 +3046,138 @@ } } } + }, + "server": { + "properties": { + "certificate": { + "type": "keyword", + "ignore_above": 1024 + }, + "certificate_chain": { + "type": "keyword", + "ignore_above": 1024 + }, + "hash": { + "properties": { + "md5": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha1": { + "type": "keyword", + "ignore_above": 1024 + }, + "sha256": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "issuer": { + "type": "keyword", + "ignore_above": 1024 + }, + "ja3s": { + "type": "keyword", + "ignore_above": 1024 + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "type": "keyword", + "ignore_above": 1024 + }, + "x509": { + "properties": { + "alternative_names": { + "type": "keyword", + "ignore_above": 1024 + }, + "issuer": { + "properties": { + "common_name": { + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false, + "analyzer": "simple" + } + }, + "ignore_above": 1024 + }, + "distinguished_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "public_key_algorithm": { + "type": "keyword", + "ignore_above": 1024 + }, + "public_key_curve": { + "type": "keyword", + "ignore_above": 1024 + }, + "public_key_exponent": { + "type": "long" + }, + "public_key_size": { + "type": "long" + }, + "serial_number": { + "type": "keyword", + "ignore_above": 1024 + }, + "signature_algorithm": { + "type": "keyword", + "ignore_above": 1024 + }, + "subject": { + "properties": { + "common_name": { + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false, + "analyzer": "simple" + } + }, + "ignore_above": 1024 + }, + "distinguished_name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "version_number": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + }, + "version_protocol": { + "type": "keyword", + "ignore_above": 1024 } } }, @@ -1909,16 +3186,16 @@ "trace": { "properties": { "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "transaction": { "properties": { "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } } @@ -1927,83 +3204,123 @@ "url": { "properties": { "domain": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "extension": { + "type": "keyword", + "ignore_above": 1024 }, "fragment": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "full": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "original": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "password": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "path": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "port": { "type": "long" }, "query": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "registered_domain": { + "type": "keyword", + "ignore_above": 1024 }, "scheme": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + }, + "top_level_domain": { + "type": "keyword", + "ignore_above": 1024 }, "username": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "user": { "properties": { "domain": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "email": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "full_name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "group": { "properties": { + "domain": { + "type": "keyword", + "ignore_above": 1024 + }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "hash": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "id": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 } } }, @@ -2012,50 +3329,147 @@ "device": { "properties": { "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "original": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "os": { "properties": { "family": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "full": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "kernel": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "name": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 }, "platform": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 }, "version": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 } } }, "version": { - "ignore_above": 1024, - "type": "keyword" + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "vlan": { + "properties": { + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "vulnerability": { + "properties": { + "category": { + "type": "keyword", + "ignore_above": 1024 + }, + "classification": { + "type": "keyword", + "ignore_above": 1024 + }, + "description": { + "type": "keyword", + "fields": { + "text": { + "type": "text", + "norms": false + } + }, + "ignore_above": 1024 + }, + "enumeration": { + "type": "keyword", + "ignore_above": 1024 + }, + "id": { + "type": "keyword", + "ignore_above": 1024 + }, + "reference": { + "type": "keyword", + "ignore_above": 1024 + }, + "report_id": { + "type": "keyword", + "ignore_above": 1024 + }, + "scanner": { + "properties": { + "vendor": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "severity": { + "type": "keyword", + "ignore_above": 1024 } } } diff --git a/x-pack/test/functional/page_objects/uptime_page.ts b/x-pack/test/functional/page_objects/uptime_page.ts index 0ebcb5c87deeec..53c89eadeced74 100644 --- a/x-pack/test/functional/page_objects/uptime_page.ts +++ b/x-pack/test/functional/page_objects/uptime_page.ts @@ -13,8 +13,11 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo const retry = getService('retry'); return new (class UptimePage { - public async goToRoot() { + public async goToRoot(refresh?: boolean) { await navigation.goToUptime(); + if (refresh) { + await navigation.refreshApp(); + } } public async setDateRange(start: string, end: string) { diff --git a/x-pack/test/functional/services/uptime/certificates.ts b/x-pack/test/functional/services/uptime/certificates.ts new file mode 100644 index 00000000000000..fb7cb6191b0ae8 --- /dev/null +++ b/x-pack/test/functional/services/uptime/certificates.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function UptimeCertProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + + const changeSearchField = async (text: string) => { + const input = await testSubjects.find('uptimeCertSearch'); + await input.clearValueWithKeyboard(); + await input.type(text); + }; + + return { + async hasViewCertButton() { + return retry.tryForTime(15000, async () => { + await testSubjects.existOrFail('uptimeCertificatesLink'); + }); + }, + async certificateExists(cert: { certId: string; monitorId: string }) { + return retry.tryForTime(15000, async () => { + await testSubjects.existOrFail(cert.certId); + await testSubjects.existOrFail('monitor-page-link-' + cert.monitorId); + }); + }, + async hasCertificates(expectedTotal?: number) { + return retry.tryForTime(15000, async () => { + const totalCerts = await testSubjects.getVisibleText('uptimeCertTotal'); + if (expectedTotal) { + expect(Number(totalCerts) === expectedTotal).to.eql(true); + } else { + expect(Number(totalCerts) > 0).to.eql(true); + } + }); + }, + async searchIsWorking(monId: string) { + const self = this; + return retry.tryForTime(15000, async () => { + await changeSearchField(monId); + await self.hasCertificates(1); + }); + }, + }; +} diff --git a/x-pack/test/functional/services/uptime/navigation.ts b/x-pack/test/functional/services/uptime/navigation.ts index 36a5d7c9702f86..c17fa3a5f63395 100644 --- a/x-pack/test/functional/services/uptime/navigation.ts +++ b/x-pack/test/functional/services/uptime/navigation.ts @@ -25,9 +25,13 @@ export function UptimeNavigationProvider({ getService, getPageObjects }: FtrProv }); }; + const refreshApp = async () => { + await testSubjects.click('superDatePickerApplyTimeButton'); + }; + return { async refreshApp() { - await testSubjects.click('superDatePickerApplyTimeButton'); + await refreshApp(); }, async goToUptime() { @@ -60,6 +64,13 @@ export function UptimeNavigationProvider({ getService, getPageObjects }: FtrProv } }, + goToCertificates: async () => { + return retry.tryForTime(30 * 1000, async () => { + await testSubjects.click('uptimeCertificatesLink'); + await testSubjects.existOrFail('uptimeCertificatesPage'); + }); + }, + async loadDataAndGoToMonitorPage(dateStart: string, dateEnd: string, monitorId: string) { await PageObjects.timePicker.setAbsoluteRange(dateStart, dateEnd); await this.goToMonitor(monitorId); diff --git a/x-pack/test/functional/services/uptime/uptime.ts b/x-pack/test/functional/services/uptime/uptime.ts index 601feb6b0646e1..8d36ba4bf6cfd6 100644 --- a/x-pack/test/functional/services/uptime/uptime.ts +++ b/x-pack/test/functional/services/uptime/uptime.ts @@ -12,6 +12,7 @@ import { UptimeMonitorProvider } from './monitor'; import { UptimeNavigationProvider } from './navigation'; import { UptimeAlertsProvider } from './alerts'; import { UptimeMLAnomalyProvider } from './ml_anomaly'; +import { UptimeCertProvider } from './certificates'; export function UptimeProvider(context: FtrProviderContext) { const common = UptimeCommonProvider(context); @@ -20,6 +21,7 @@ export function UptimeProvider(context: FtrProviderContext) { const navigation = UptimeNavigationProvider(context); const alerts = UptimeAlertsProvider(context); const ml = UptimeMLAnomalyProvider(context); + const cert = UptimeCertProvider(context); return { common, @@ -28,5 +30,6 @@ export function UptimeProvider(context: FtrProviderContext) { navigation, alerts, ml, + cert, }; } From 856a82046ddd612c48551c9bcde2d3de0a3f3b5b Mon Sep 17 00:00:00 2001 From: Sandra Gonzales Date: Fri, 1 May 2020 10:42:05 -0400 Subject: [PATCH 44/47] [EPM] fix updates available filter (#64957) * fix filter * remove unneeded installationVersion from package * use filter instead of reduce * fix type error --- .../ingest_manager/common/types/models/epm.ts | 1 - .../sections/epm/screens/detail/header.tsx | 7 ++++++- .../sections/epm/screens/detail/index.tsx | 5 ++++- .../sections/epm/screens/home/index.tsx | 20 +++++++++++-------- .../ingest_manager/types/index.ts | 1 + .../server/services/epm/packages/index.ts | 1 - 6 files changed, 23 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/ingest_manager/common/types/models/epm.ts b/x-pack/plugins/ingest_manager/common/types/models/epm.ts index f8779a879a049f..82de90e4735f26 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/epm.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/epm.ts @@ -205,7 +205,6 @@ export interface RegistryVarsEntry { interface PackageAdditions { title: string; latestVersion: string; - installedVersion?: string; assets: AssetsGroupedByServiceByType; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx index d20350c5db631e..cf51296d468a99 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx @@ -29,7 +29,12 @@ const Text = styled.span` type HeaderProps = PackageInfo & { iconType?: IconType }; export function Header(props: HeaderProps) { - const { iconType, name, title, version, installedVersion, latestVersion } = props; + const { iconType, name, title, version, latestVersion } = props; + + let installedVersion; + if ('savedObject' in props) { + installedVersion = props.savedObject.attributes.version; + } const hasWriteCapabilites = useCapabilities().write; const { toListView } = useLinks(); const ADD_DATASOURCE_URI = useLink(`${EPM_PATH}/${name}-${version}/add-datasource`); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx index 1f3eb2cc9362e5..848d278819d1d2 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/index.tsx @@ -32,7 +32,10 @@ export function Detail() { const packageInfo = response.data?.response; const title = packageInfo?.title; const name = packageInfo?.name; - const installedVersion = packageInfo?.installedVersion; + let installedVersion; + if (packageInfo && 'savedObject' in packageInfo) { + installedVersion = packageInfo.savedObject.attributes.version; + } const status: InstallStatus = packageInfo?.status as any; // track install status state diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx index bf785147502b53..983a322de1088c 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx @@ -67,29 +67,34 @@ export function EPMHomePage() { function InstalledPackages() { const { data: allPackages, isLoading: isLoadingPackages } = useGetPackages(); const [selectedCategory, setSelectedCategory] = useState(''); - const packages = - allPackages && allPackages.response && selectedCategory === '' - ? allPackages.response.filter(pkg => pkg.status === 'installed') - : []; const title = i18n.translate('xpack.ingestManager.epmList.installedTitle', { defaultMessage: 'Installed integrations', }); + const allInstalledPackages = + allPackages && allPackages.response + ? allPackages.response.filter(pkg => pkg.status === 'installed') + : []; + + const updatablePackages = allInstalledPackages.filter( + item => 'savedObject' in item && item.version > item.savedObject.attributes.version + ); + const categories = [ { id: '', title: i18n.translate('xpack.ingestManager.epmList.allFilterLinkText', { defaultMessage: 'All', }), - count: packages.length, + count: allInstalledPackages.length, }, { id: 'updates_available', title: i18n.translate('xpack.ingestManager.epmList.updatesAvailableFilterLinkText', { defaultMessage: 'Updates available', }), - count: 0, // TODO: Update with real count when available + count: updatablePackages.length, }, ]; @@ -106,7 +111,7 @@ function InstalledPackages() { isLoading={isLoadingPackages} controls={controls} title={title} - list={packages} + list={selectedCategory === 'updates_available' ? updatablePackages : allInstalledPackages} /> ); } @@ -134,7 +139,6 @@ function AvailablePackages() { }, ...(categoriesRes ? categoriesRes.response : []), ]; - const controls = categories ? ( ( ? { ...from, status: InstallationStatus.installed, - installedVersion: savedObject.attributes.version, savedObject, } : { From d314e4624e49e264ea32fd1585b4edb1ca5df779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Sat, 2 May 2020 22:12:13 +0200 Subject: [PATCH 45/47] Use HDR for percentiles (#64758) --- .../__snapshots__/fetcher.test.ts.snap | 6 ++++++ .../__snapshots__/queries.test.ts.snap | 6 ++++++ .../plugins/apm/server/lib/transaction_groups/fetcher.ts | 6 +++++- .../lib/transactions/__snapshots__/queries.test.ts.snap | 9 +++++++++ .../__snapshots__/fetcher.test.ts.snap | 3 +++ .../transactions/charts/get_timeseries_data/fetcher.ts | 6 +++++- x-pack/plugins/apm/typings/elasticsearch/aggregations.ts | 1 + 7 files changed, 35 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap index 580cafff95e0cd..64f06ad0a81cd0 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/fetcher.test.ts.snap @@ -16,6 +16,9 @@ Array [ "p95": Object { "percentiles": Object { "field": "transaction.duration.us", + "hdr": Object { + "number_of_significant_value_digits": 2, + }, "percents": Array [ 95, ], @@ -126,6 +129,9 @@ Array [ "p95": Object { "percentiles": Object { "field": "transaction.duration.us", + "hdr": Object { + "number_of_significant_value_digits": 2, + }, "percents": Array [ 95, ], diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap index 1096c1638f3f22..b93f842b878cb0 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap @@ -14,6 +14,9 @@ Object { "p95": Object { "percentiles": Object { "field": "transaction.duration.us", + "hdr": Object { + "number_of_significant_value_digits": 2, + }, "percents": Array [ 95, ], @@ -120,6 +123,9 @@ Object { "p95": Object { "percentiles": Object { "field": "transaction.duration.us", + "hdr": Object { + "number_of_significant_value_digits": 2, + }, "percents": Array [ 95, ], diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts index 39f2be551ab6ed..fb1aafc2d6c958 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts @@ -83,7 +83,11 @@ export function transactionGroupsFetcher( sample: { top_hits: { size: 1, sort } }, avg: { avg: { field: TRANSACTION_DURATION } }, p95: { - percentiles: { field: TRANSACTION_DURATION, percents: [95] } + percentiles: { + field: TRANSACTION_DURATION, + percents: [95], + hdr: { number_of_significant_value_digits: 2 } + } }, sum: { sum: { field: TRANSACTION_DURATION } } } diff --git a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap index 49e0e0669c2412..cc5900919f8297 100644 --- a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap @@ -333,6 +333,9 @@ Object { "pct": Object { "percentiles": Object { "field": "transaction.duration.us", + "hdr": Object { + "number_of_significant_value_digits": 2, + }, "percents": Array [ 95, 99, @@ -425,6 +428,9 @@ Object { "pct": Object { "percentiles": Object { "field": "transaction.duration.us", + "hdr": Object { + "number_of_significant_value_digits": 2, + }, "percents": Array [ 95, 99, @@ -522,6 +528,9 @@ Object { "pct": Object { "percentiles": Object { "field": "transaction.duration.us", + "hdr": Object { + "number_of_significant_value_digits": 2, + }, "percents": Array [ 95, 99, diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/fetcher.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/fetcher.test.ts.snap index 6c8430a3e71cf2..25ebb15fd73e8d 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/fetcher.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/__snapshots__/fetcher.test.ts.snap @@ -21,6 +21,9 @@ Array [ "pct": Object { "percentiles": Object { "field": "transaction.duration.us", + "hdr": Object { + "number_of_significant_value_digits": 2, + }, "percents": Array [ 95, 99, diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts index 8a2e01c9a78911..e33b98592da2d2 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts @@ -69,7 +69,11 @@ export function timeseriesFetcher({ aggs: { avg: { avg: { field: TRANSACTION_DURATION } }, pct: { - percentiles: { field: TRANSACTION_DURATION, percents: [95, 99] } + percentiles: { + field: TRANSACTION_DURATION, + percents: [95, 99], + hdr: { number_of_significant_value_digits: 2 } + } } } }, diff --git a/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts b/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts index 8a8d256cf42732..0739e8e6120bf7 100644 --- a/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts +++ b/x-pack/plugins/apm/typings/elasticsearch/aggregations.ts @@ -86,6 +86,7 @@ export interface AggregationOptionsByType { percentiles: { field: string; percents?: number[]; + hdr?: { number_of_significant_value_digits: number }; }; extended_stats: { field: string; From 8eeaf96cf57f910c0944ba0d5a0609bfd0c11e1c Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Sat, 2 May 2020 23:36:26 +0200 Subject: [PATCH 46/47] [APM] Fix paths for ts optimization script (#65012) --- x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js b/x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js index cab55a25262028..aeccd403c5ce63 100644 --- a/x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js +++ b/x-pack/plugins/apm/scripts/optimize-tsconfig/paths.js @@ -5,7 +5,7 @@ */ const path = require('path'); -const xpackRoot = path.resolve(__dirname, '../../../../..'); +const xpackRoot = path.resolve(__dirname, '../../../..'); const kibanaRoot = path.resolve(xpackRoot, '..'); const tsconfigTpl = path.resolve(__dirname, './tsconfig.json'); From c995a333de4384f14233dcd61031267fbb6b50bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Sun, 3 May 2020 09:19:16 +0200 Subject: [PATCH 47/47] [APM] Fix failing `ApmIndices` test (#64965) * [APM] Fix failing Indicies Settings test * Cleanup Co-authored-by: Elastic Machine --- .../components/app/Settings/ApmIndices/index.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.test.tsx b/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.test.tsx index 272c4b3add415c..b03960861e0ada 100644 --- a/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.test.tsx +++ b/x-pack/plugins/apm/public/components/app/Settings/ApmIndices/index.test.tsx @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { render, wait } from '@testing-library/react'; +import { render } from '@testing-library/react'; import React from 'react'; import { ApmIndices } from '.'; import * as hooks from '../../../../hooks/useFetcher'; import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext'; describe('ApmIndices', () => { - it('should not get stuck in infinite loop', async () => { - spyOn(hooks, 'useFetcher').and.returnValue({ + it('should not get stuck in infinite loop', () => { + const spy = spyOn(hooks, 'useFetcher').and.returnValue({ data: undefined, status: 'loading' }); @@ -30,6 +30,6 @@ describe('ApmIndices', () => { `); - await wait(); + expect(spy).toHaveBeenCalledTimes(2); }); });