From e0bb8605b420f6202bd9bc5dd813706dbf34f153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Tue, 13 Oct 2020 09:32:49 -0400 Subject: [PATCH 001/128] Apply back pressure in Task Manager whenever Elasticsearch responds with a 429 (#75666) * Make task manager maxWorkers and pollInterval observables (#75293) * WIP step 1 * WIP step 2 * Cleanup * Make maxWorkers an observable for the task pool * Cleanup * Fix test failures * Use BehaviorSubject * Add some tests * Make the task manager store emit error events (#75679) * Add errors$ observable to the task store * Add unit tests * Temporarily apply back pressure to maxWorkers and pollInterval when 429 errors occur (#77096) * WIP * Cleanup * Add error count to message * Reset observable values on stop * Add comments * Fix issues when changing configurations * Cleanup code * Cleanup pt2 * Some renames * Fix typecheck * Use observables to manage throughput * Rename class * Switch to createManagedConfiguration * Add some comments * Start unit tests * Add logs * Fix log level * Attempt at adding integration tests * Fix test failures * Fix timer * Revert "Fix timer" This reverts commit 0817e5e6a5ef9bdfe9329a559f4a5674ebbbef24. * Use Symbol * Fix merge scan * replace startsWith with a timer that is scheduled to 0 * typo Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Gidi Meir Morris --- .../managed_configuration.test.ts | 105 ++++++++ .../lib/create_managed_configuration.test.ts | 213 ++++++++++++++++ .../lib/create_managed_configuration.ts | 160 ++++++++++++ .../server/polling/observable_monitor.ts | 6 +- .../server/polling/task_poller.test.ts | 85 ++++++- .../server/polling/task_poller.ts | 21 +- .../task_manager/server/task_manager.ts | 13 +- .../task_manager/server/task_pool.test.ts | 41 ++-- .../plugins/task_manager/server/task_pool.ts | 10 +- .../task_manager/server/task_store.test.ts | 229 ++++++++++++++---- .../plugins/task_manager/server/task_store.ts | 119 ++++++--- 11 files changed, 882 insertions(+), 120 deletions(-) create mode 100644 x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts create mode 100644 x-pack/plugins/task_manager/server/lib/create_managed_configuration.test.ts create mode 100644 x-pack/plugins/task_manager/server/lib/create_managed_configuration.ts diff --git a/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts b/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts new file mode 100644 index 0000000000000..443c811469002 --- /dev/null +++ b/x-pack/plugins/task_manager/server/integration_tests/managed_configuration.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import sinon from 'sinon'; +import { mockLogger } from '../test_utils'; +import { TaskManager } from '../task_manager'; +import { savedObjectsRepositoryMock } from '../../../../../src/core/server/mocks'; +import { + SavedObjectsSerializer, + SavedObjectTypeRegistry, + SavedObjectsErrorHelpers, +} from '../../../../../src/core/server'; +import { ADJUST_THROUGHPUT_INTERVAL } from '../lib/create_managed_configuration'; + +describe('managed configuration', () => { + let taskManager: TaskManager; + let clock: sinon.SinonFakeTimers; + const callAsInternalUser = jest.fn(); + const logger = mockLogger(); + const serializer = new SavedObjectsSerializer(new SavedObjectTypeRegistry()); + const savedObjectsClient = savedObjectsRepositoryMock.create(); + const config = { + enabled: true, + max_workers: 10, + index: 'foo', + max_attempts: 9, + poll_interval: 3000, + max_poll_inactivity_cycles: 10, + request_capacity: 1000, + }; + + beforeEach(() => { + jest.resetAllMocks(); + callAsInternalUser.mockResolvedValue({ total: 0, updated: 0, version_conflicts: 0 }); + clock = sinon.useFakeTimers(); + taskManager = new TaskManager({ + config, + logger, + serializer, + callAsInternalUser, + taskManagerId: 'some-uuid', + savedObjectsRepository: savedObjectsClient, + }); + taskManager.registerTaskDefinitions({ + foo: { + type: 'foo', + title: 'Foo', + createTaskRunner: jest.fn(), + }, + }); + taskManager.start(); + // force rxjs timers to fire when they are scheduled for setTimeout(0) as the + // sinon fake timers cause them to stall + clock.tick(0); + }); + + afterEach(() => clock.restore()); + + test('should lower max workers when Elasticsearch returns 429 error', async () => { + savedObjectsClient.create.mockRejectedValueOnce( + SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b') + ); + // Cause "too many requests" error to be thrown + await expect( + taskManager.schedule({ + taskType: 'foo', + state: {}, + params: {}, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Too Many Requests"`); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + expect(logger.warn).toHaveBeenCalledWith( + 'Max workers configuration is temporarily reduced after Elasticsearch returned 1 "too many request" error(s).' + ); + expect(logger.debug).toHaveBeenCalledWith( + 'Max workers configuration changing from 10 to 8 after seeing 1 error(s)' + ); + expect(logger.debug).toHaveBeenCalledWith('Task pool now using 10 as the max worker value'); + }); + + test('should increase poll interval when Elasticsearch returns 429 error', async () => { + savedObjectsClient.create.mockRejectedValueOnce( + SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b') + ); + // Cause "too many requests" error to be thrown + await expect( + taskManager.schedule({ + taskType: 'foo', + state: {}, + params: {}, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Too Many Requests"`); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + expect(logger.warn).toHaveBeenCalledWith( + 'Poll interval configuration is temporarily increased after Elasticsearch returned 1 "too many request" error(s).' + ); + expect(logger.debug).toHaveBeenCalledWith( + 'Poll interval configuration changing from 3000 to 3600 after seeing 1 error(s)' + ); + expect(logger.debug).toHaveBeenCalledWith('Task poller now using interval of 3600ms'); + }); +}); diff --git a/x-pack/plugins/task_manager/server/lib/create_managed_configuration.test.ts b/x-pack/plugins/task_manager/server/lib/create_managed_configuration.test.ts new file mode 100644 index 0000000000000..b6b5cd003c5d4 --- /dev/null +++ b/x-pack/plugins/task_manager/server/lib/create_managed_configuration.test.ts @@ -0,0 +1,213 @@ +/* + * 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 sinon from 'sinon'; +import { Subject } from 'rxjs'; +import { mockLogger } from '../test_utils'; +import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; +import { + createManagedConfiguration, + ADJUST_THROUGHPUT_INTERVAL, +} from './create_managed_configuration'; + +describe('createManagedConfiguration()', () => { + let clock: sinon.SinonFakeTimers; + const logger = mockLogger(); + + beforeEach(() => { + jest.resetAllMocks(); + clock = sinon.useFakeTimers(); + }); + + afterEach(() => clock.restore()); + + test('returns observables with initialized values', async () => { + const maxWorkersSubscription = jest.fn(); + const pollIntervalSubscription = jest.fn(); + const { maxWorkersConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ + logger, + errors$: new Subject(), + startingMaxWorkers: 1, + startingPollInterval: 2, + }); + maxWorkersConfiguration$.subscribe(maxWorkersSubscription); + pollIntervalConfiguration$.subscribe(pollIntervalSubscription); + expect(maxWorkersSubscription).toHaveBeenCalledTimes(1); + expect(maxWorkersSubscription).toHaveBeenNthCalledWith(1, 1); + expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); + expect(pollIntervalSubscription).toHaveBeenNthCalledWith(1, 2); + }); + + test(`skips errors that aren't about too many requests`, async () => { + const maxWorkersSubscription = jest.fn(); + const pollIntervalSubscription = jest.fn(); + const errors$ = new Subject(); + const { maxWorkersConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ + errors$, + logger, + startingMaxWorkers: 100, + startingPollInterval: 100, + }); + maxWorkersConfiguration$.subscribe(maxWorkersSubscription); + pollIntervalConfiguration$.subscribe(pollIntervalSubscription); + errors$.next(new Error('foo')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + expect(maxWorkersSubscription).toHaveBeenCalledTimes(1); + expect(pollIntervalSubscription).toHaveBeenCalledTimes(1); + }); + + describe('maxWorker configuration', () => { + function setupScenario(startingMaxWorkers: number) { + const errors$ = new Subject(); + const subscription = jest.fn(); + const { maxWorkersConfiguration$ } = createManagedConfiguration({ + errors$, + startingMaxWorkers, + logger, + startingPollInterval: 1, + }); + maxWorkersConfiguration$.subscribe(subscription); + return { subscription, errors$ }; + } + + beforeEach(() => { + jest.resetAllMocks(); + clock = sinon.useFakeTimers(); + }); + + afterEach(() => clock.restore()); + + test('should decrease configuration at the next interval when an error is emitted', async () => { + const { subscription, errors$ } = setupScenario(100); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL - 1); + expect(subscription).toHaveBeenCalledTimes(1); + clock.tick(1); + expect(subscription).toHaveBeenCalledTimes(2); + expect(subscription).toHaveBeenNthCalledWith(2, 80); + }); + + test('should log a warning when the configuration changes from the starting value', async () => { + const { errors$ } = setupScenario(100); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + expect(logger.warn).toHaveBeenCalledWith( + 'Max workers configuration is temporarily reduced after Elasticsearch returned 1 "too many request" error(s).' + ); + }); + + test('should increase configuration back to normal incrementally after an error is emitted', async () => { + const { subscription, errors$ } = setupScenario(100); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL * 10); + expect(subscription).toHaveBeenNthCalledWith(2, 80); + expect(subscription).toHaveBeenNthCalledWith(3, 84); + // 88.2- > 89 from Math.ceil + expect(subscription).toHaveBeenNthCalledWith(4, 89); + expect(subscription).toHaveBeenNthCalledWith(5, 94); + expect(subscription).toHaveBeenNthCalledWith(6, 99); + // 103.95 -> 100 from Math.min with starting value + expect(subscription).toHaveBeenNthCalledWith(7, 100); + // No new calls due to value not changing and usage of distinctUntilChanged() + expect(subscription).toHaveBeenCalledTimes(7); + }); + + test('should keep reducing configuration when errors keep emitting', async () => { + const { subscription, errors$ } = setupScenario(100); + for (let i = 0; i < 20; i++) { + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + } + expect(subscription).toHaveBeenNthCalledWith(2, 80); + expect(subscription).toHaveBeenNthCalledWith(3, 64); + // 51.2 -> 51 from Math.floor + expect(subscription).toHaveBeenNthCalledWith(4, 51); + expect(subscription).toHaveBeenNthCalledWith(5, 40); + expect(subscription).toHaveBeenNthCalledWith(6, 32); + expect(subscription).toHaveBeenNthCalledWith(7, 25); + expect(subscription).toHaveBeenNthCalledWith(8, 20); + expect(subscription).toHaveBeenNthCalledWith(9, 16); + expect(subscription).toHaveBeenNthCalledWith(10, 12); + expect(subscription).toHaveBeenNthCalledWith(11, 9); + expect(subscription).toHaveBeenNthCalledWith(12, 7); + expect(subscription).toHaveBeenNthCalledWith(13, 5); + expect(subscription).toHaveBeenNthCalledWith(14, 4); + expect(subscription).toHaveBeenNthCalledWith(15, 3); + expect(subscription).toHaveBeenNthCalledWith(16, 2); + expect(subscription).toHaveBeenNthCalledWith(17, 1); + // No new calls due to value not changing and usage of distinctUntilChanged() + expect(subscription).toHaveBeenCalledTimes(17); + }); + }); + + describe('pollInterval configuration', () => { + function setupScenario(startingPollInterval: number) { + const errors$ = new Subject(); + const subscription = jest.fn(); + const { pollIntervalConfiguration$ } = createManagedConfiguration({ + logger, + errors$, + startingPollInterval, + startingMaxWorkers: 1, + }); + pollIntervalConfiguration$.subscribe(subscription); + return { subscription, errors$ }; + } + + beforeEach(() => { + jest.resetAllMocks(); + clock = sinon.useFakeTimers(); + }); + + afterEach(() => clock.restore()); + + test('should increase configuration at the next interval when an error is emitted', async () => { + const { subscription, errors$ } = setupScenario(100); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL - 1); + expect(subscription).toHaveBeenCalledTimes(1); + clock.tick(1); + expect(subscription).toHaveBeenCalledTimes(2); + expect(subscription).toHaveBeenNthCalledWith(2, 120); + }); + + test('should log a warning when the configuration changes from the starting value', async () => { + const { errors$ } = setupScenario(100); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + expect(logger.warn).toHaveBeenCalledWith( + 'Poll interval configuration is temporarily increased after Elasticsearch returned 1 "too many request" error(s).' + ); + }); + + test('should decrease configuration back to normal incrementally after an error is emitted', async () => { + const { subscription, errors$ } = setupScenario(100); + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL * 10); + expect(subscription).toHaveBeenNthCalledWith(2, 120); + expect(subscription).toHaveBeenNthCalledWith(3, 114); + // 108.3 -> 108 from Math.floor + expect(subscription).toHaveBeenNthCalledWith(4, 108); + expect(subscription).toHaveBeenNthCalledWith(5, 102); + // 96.9 -> 100 from Math.max with the starting value + expect(subscription).toHaveBeenNthCalledWith(6, 100); + // No new calls due to value not changing and usage of distinctUntilChanged() + expect(subscription).toHaveBeenCalledTimes(6); + }); + + test('should increase configuration when errors keep emitting', async () => { + const { subscription, errors$ } = setupScenario(100); + for (let i = 0; i < 3; i++) { + errors$.next(SavedObjectsErrorHelpers.createTooManyRequestsError('a', 'b')); + clock.tick(ADJUST_THROUGHPUT_INTERVAL); + } + expect(subscription).toHaveBeenNthCalledWith(2, 120); + expect(subscription).toHaveBeenNthCalledWith(3, 144); + // 172.8 -> 173 from Math.ceil + expect(subscription).toHaveBeenNthCalledWith(4, 173); + }); + }); +}); diff --git a/x-pack/plugins/task_manager/server/lib/create_managed_configuration.ts b/x-pack/plugins/task_manager/server/lib/create_managed_configuration.ts new file mode 100644 index 0000000000000..3dc5fd50d3ca4 --- /dev/null +++ b/x-pack/plugins/task_manager/server/lib/create_managed_configuration.ts @@ -0,0 +1,160 @@ +/* + * 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 { interval, merge, of, Observable } from 'rxjs'; +import { filter, mergeScan, map, scan, distinctUntilChanged, startWith } from 'rxjs/operators'; +import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; +import { Logger } from '../types'; + +const FLUSH_MARKER = Symbol('flush'); +export const ADJUST_THROUGHPUT_INTERVAL = 10 * 1000; + +// When errors occur, reduce maxWorkers by MAX_WORKERS_DECREASE_PERCENTAGE +// When errors no longer occur, start increasing maxWorkers by MAX_WORKERS_INCREASE_PERCENTAGE +// until starting value is reached +const MAX_WORKERS_DECREASE_PERCENTAGE = 0.8; +const MAX_WORKERS_INCREASE_PERCENTAGE = 1.05; + +// When errors occur, increase pollInterval by POLL_INTERVAL_INCREASE_PERCENTAGE +// When errors no longer occur, start decreasing pollInterval by POLL_INTERVAL_DECREASE_PERCENTAGE +// until starting value is reached +const POLL_INTERVAL_DECREASE_PERCENTAGE = 0.95; +const POLL_INTERVAL_INCREASE_PERCENTAGE = 1.2; + +interface ManagedConfigurationOpts { + logger: Logger; + startingMaxWorkers: number; + startingPollInterval: number; + errors$: Observable; +} + +interface ManagedConfiguration { + maxWorkersConfiguration$: Observable; + pollIntervalConfiguration$: Observable; +} + +export function createManagedConfiguration({ + logger, + startingMaxWorkers, + startingPollInterval, + errors$, +}: ManagedConfigurationOpts): ManagedConfiguration { + const errorCheck$ = countErrors(errors$, ADJUST_THROUGHPUT_INTERVAL); + return { + maxWorkersConfiguration$: errorCheck$.pipe( + createMaxWorkersScan(logger, startingMaxWorkers), + startWith(startingMaxWorkers), + distinctUntilChanged() + ), + pollIntervalConfiguration$: errorCheck$.pipe( + createPollIntervalScan(logger, startingPollInterval), + startWith(startingPollInterval), + distinctUntilChanged() + ), + }; +} + +function createMaxWorkersScan(logger: Logger, startingMaxWorkers: number) { + return scan((previousMaxWorkers: number, errorCount: number) => { + let newMaxWorkers: number; + if (errorCount > 0) { + // Decrease max workers by MAX_WORKERS_DECREASE_PERCENTAGE while making sure it doesn't go lower than 1. + // Using Math.floor to make sure the number is different than previous while not being a decimal value. + newMaxWorkers = Math.max(Math.floor(previousMaxWorkers * MAX_WORKERS_DECREASE_PERCENTAGE), 1); + } else { + // Increase max workers by MAX_WORKERS_INCREASE_PERCENTAGE while making sure it doesn't go + // higher than the starting value. Using Math.ceil to make sure the number is different than + // previous while not being a decimal value + newMaxWorkers = Math.min( + startingMaxWorkers, + Math.ceil(previousMaxWorkers * MAX_WORKERS_INCREASE_PERCENTAGE) + ); + } + if (newMaxWorkers !== previousMaxWorkers) { + logger.debug( + `Max workers configuration changing from ${previousMaxWorkers} to ${newMaxWorkers} after seeing ${errorCount} error(s)` + ); + if (previousMaxWorkers === startingMaxWorkers) { + logger.warn( + `Max workers configuration is temporarily reduced after Elasticsearch returned ${errorCount} "too many request" error(s).` + ); + } + } + return newMaxWorkers; + }, startingMaxWorkers); +} + +function createPollIntervalScan(logger: Logger, startingPollInterval: number) { + return scan((previousPollInterval: number, errorCount: number) => { + let newPollInterval: number; + if (errorCount > 0) { + // Increase poll interval by POLL_INTERVAL_INCREASE_PERCENTAGE and use Math.ceil to + // make sure the number is different than previous while not being a decimal value. + newPollInterval = Math.ceil(previousPollInterval * POLL_INTERVAL_INCREASE_PERCENTAGE); + } else { + // Decrease poll interval by POLL_INTERVAL_DECREASE_PERCENTAGE and use Math.floor to + // make sure the number is different than previous while not being a decimal value. + newPollInterval = Math.max( + startingPollInterval, + Math.floor(previousPollInterval * POLL_INTERVAL_DECREASE_PERCENTAGE) + ); + } + if (newPollInterval !== previousPollInterval) { + logger.debug( + `Poll interval configuration changing from ${previousPollInterval} to ${newPollInterval} after seeing ${errorCount} error(s)` + ); + if (previousPollInterval === startingPollInterval) { + logger.warn( + `Poll interval configuration is temporarily increased after Elasticsearch returned ${errorCount} "too many request" error(s).` + ); + } + } + return newPollInterval; + }, startingPollInterval); +} + +function countErrors(errors$: Observable, countInterval: number): Observable { + return merge( + // Flush error count at fixed interval + interval(countInterval).pipe(map(() => FLUSH_MARKER)), + errors$.pipe(filter((e) => SavedObjectsErrorHelpers.isTooManyRequestsError(e))) + ).pipe( + // When tag is "flush", reset the error counter + // Otherwise increment the error counter + mergeScan(({ count }, next) => { + return next === FLUSH_MARKER + ? of(emitErrorCount(count), resetErrorCount()) + : of(incementErrorCount(count)); + }, emitErrorCount(0)), + filter(isEmitEvent), + map(({ count }) => count) + ); +} + +function emitErrorCount(count: number) { + return { + tag: 'emit', + count, + }; +} + +function isEmitEvent(event: { tag: string; count: number }) { + return event.tag === 'emit'; +} + +function incementErrorCount(count: number) { + return { + tag: 'inc', + count: count + 1, + }; +} + +function resetErrorCount() { + return { + tag: 'initial', + count: 0, + }; +} diff --git a/x-pack/plugins/task_manager/server/polling/observable_monitor.ts b/x-pack/plugins/task_manager/server/polling/observable_monitor.ts index 7b06117ef59d1..b07bb6661163b 100644 --- a/x-pack/plugins/task_manager/server/polling/observable_monitor.ts +++ b/x-pack/plugins/task_manager/server/polling/observable_monitor.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Subject, Observable, throwError, interval, timer, Subscription } from 'rxjs'; -import { exhaustMap, tap, takeUntil, switchMap, switchMapTo, catchError } from 'rxjs/operators'; +import { Subject, Observable, throwError, timer, Subscription } from 'rxjs'; import { noop } from 'lodash'; +import { exhaustMap, tap, takeUntil, switchMap, switchMapTo, catchError } from 'rxjs/operators'; const DEFAULT_HEARTBEAT_INTERVAL = 1000; @@ -29,7 +29,7 @@ export function createObservableMonitor( }: ObservableMonitorOptions = {} ): Observable { return new Observable((subscriber) => { - const subscription: Subscription = interval(heartbeatInterval) + const subscription: Subscription = timer(0, heartbeatInterval) .pipe( // switch from the heartbeat interval to the instantiated observable until it completes / errors exhaustMap(() => takeUntilDurationOfInactivity(observableFactory(), inactivityTimeout)), diff --git a/x-pack/plugins/task_manager/server/polling/task_poller.test.ts b/x-pack/plugins/task_manager/server/polling/task_poller.test.ts index 607e2ac2b80fa..956c8b05f3860 100644 --- a/x-pack/plugins/task_manager/server/polling/task_poller.test.ts +++ b/x-pack/plugins/task_manager/server/polling/task_poller.test.ts @@ -5,11 +5,11 @@ */ import _ from 'lodash'; -import { Subject } from 'rxjs'; +import { Subject, of, BehaviorSubject } from 'rxjs'; import { Option, none, some } from 'fp-ts/lib/Option'; import { createTaskPoller, PollingError, PollingErrorType } from './task_poller'; import { fakeSchedulers } from 'rxjs-marbles/jest'; -import { sleep, resolvable, Resolvable } from '../test_utils'; +import { sleep, resolvable, Resolvable, mockLogger } from '../test_utils'; import { asOk, asErr } from '../lib/result_type'; describe('TaskPoller', () => { @@ -24,10 +24,12 @@ describe('TaskPoller', () => { const work = jest.fn(async () => true); createTaskPoller({ - pollInterval, + logger: mockLogger(), + pollInterval$: of(pollInterval), bufferCapacity, getCapacity: () => 1, work, + workTimeout: pollInterval * 5, pollRequests$: new Subject>(), }).subscribe(() => {}); @@ -40,9 +42,52 @@ describe('TaskPoller', () => { await sleep(0); expect(work).toHaveBeenCalledTimes(1); + await sleep(0); + await sleep(0); + advance(pollInterval + 10); + await sleep(0); + expect(work).toHaveBeenCalledTimes(2); + }) + ); + + test( + 'poller adapts to pollInterval changes', + fakeSchedulers(async (advance) => { + const pollInterval = 100; + const pollInterval$ = new BehaviorSubject(pollInterval); + const bufferCapacity = 5; + + const work = jest.fn(async () => true); + createTaskPoller({ + logger: mockLogger(), + pollInterval$, + bufferCapacity, + getCapacity: () => 1, + work, + workTimeout: pollInterval * 5, + pollRequests$: new Subject>(), + }).subscribe(() => {}); + + // `work` is async, we have to force a node `tick` await sleep(0); advance(pollInterval); + expect(work).toHaveBeenCalledTimes(1); + + pollInterval$.next(pollInterval * 2); + + // `work` is async, we have to force a node `tick` + await sleep(0); + advance(pollInterval); + expect(work).toHaveBeenCalledTimes(1); + advance(pollInterval); expect(work).toHaveBeenCalledTimes(2); + + pollInterval$.next(pollInterval / 2); + + // `work` is async, we have to force a node `tick` + await sleep(0); + advance(pollInterval / 2); + expect(work).toHaveBeenCalledTimes(3); }) ); @@ -56,9 +101,11 @@ describe('TaskPoller', () => { let hasCapacity = true; createTaskPoller({ - pollInterval, + logger: mockLogger(), + pollInterval$: of(pollInterval), bufferCapacity, work, + workTimeout: pollInterval * 5, getCapacity: () => (hasCapacity ? 1 : 0), pollRequests$: new Subject>(), }).subscribe(() => {}); @@ -113,9 +160,11 @@ describe('TaskPoller', () => { const work = jest.fn(async () => true); const pollRequests$ = new Subject>(); createTaskPoller({ - pollInterval, + logger: mockLogger(), + pollInterval$: of(pollInterval), bufferCapacity, work, + workTimeout: pollInterval * 5, getCapacity: () => 1, pollRequests$, }).subscribe(jest.fn()); @@ -157,9 +206,11 @@ describe('TaskPoller', () => { const work = jest.fn(async () => true); const pollRequests$ = new Subject>(); createTaskPoller({ - pollInterval, + logger: mockLogger(), + pollInterval$: of(pollInterval), bufferCapacity, work, + workTimeout: pollInterval * 5, getCapacity: () => (hasCapacity ? 1 : 0), pollRequests$, }).subscribe(() => {}); @@ -200,9 +251,11 @@ describe('TaskPoller', () => { const work = jest.fn(async () => true); const pollRequests$ = new Subject>(); createTaskPoller({ - pollInterval, + logger: mockLogger(), + pollInterval$: of(pollInterval), bufferCapacity, work, + workTimeout: pollInterval * 5, getCapacity: () => 1, pollRequests$, }).subscribe(() => {}); @@ -235,7 +288,8 @@ describe('TaskPoller', () => { const handler = jest.fn(); const pollRequests$ = new Subject>(); createTaskPoller({ - pollInterval, + logger: mockLogger(), + pollInterval$: of(pollInterval), bufferCapacity, work: async (...args) => { await worker; @@ -285,7 +339,8 @@ describe('TaskPoller', () => { type ResolvableTupple = [string, PromiseLike & Resolvable]; const pollRequests$ = new Subject>(); createTaskPoller<[string, Resolvable], string[]>({ - pollInterval, + logger: mockLogger(), + pollInterval$: of(pollInterval), bufferCapacity, work: async (...resolvables) => { await Promise.all(resolvables.map(([, future]) => future)); @@ -344,11 +399,13 @@ describe('TaskPoller', () => { const handler = jest.fn(); const pollRequests$ = new Subject>(); createTaskPoller({ - pollInterval, + logger: mockLogger(), + pollInterval$: of(pollInterval), bufferCapacity, work: async (...args) => { throw new Error('failed to work'); }, + workTimeout: pollInterval * 5, getCapacity: () => 5, pollRequests$, }).subscribe(handler); @@ -383,9 +440,11 @@ describe('TaskPoller', () => { return callCount; }); createTaskPoller({ - pollInterval, + logger: mockLogger(), + pollInterval$: of(pollInterval), bufferCapacity, work, + workTimeout: pollInterval * 5, getCapacity: () => 5, pollRequests$, }).subscribe(handler); @@ -424,9 +483,11 @@ describe('TaskPoller', () => { const work = jest.fn(async () => {}); const pollRequests$ = new Subject>(); createTaskPoller({ - pollInterval, + logger: mockLogger(), + pollInterval$: of(pollInterval), bufferCapacity, work, + workTimeout: pollInterval * 5, getCapacity: () => 5, pollRequests$, }).subscribe(handler); diff --git a/x-pack/plugins/task_manager/server/polling/task_poller.ts b/x-pack/plugins/task_manager/server/polling/task_poller.ts index a1435ffafe8f8..7515668a19d40 100644 --- a/x-pack/plugins/task_manager/server/polling/task_poller.ts +++ b/x-pack/plugins/task_manager/server/polling/task_poller.ts @@ -11,10 +11,11 @@ import { performance } from 'perf_hooks'; import { after } from 'lodash'; import { Subject, merge, interval, of, Observable } from 'rxjs'; -import { mapTo, filter, scan, concatMap, tap, catchError } from 'rxjs/operators'; +import { mapTo, filter, scan, concatMap, tap, catchError, switchMap } from 'rxjs/operators'; import { pipe } from 'fp-ts/lib/pipeable'; import { Option, none, map as mapOptional, getOrElse } from 'fp-ts/lib/Option'; +import { Logger } from '../types'; import { pullFromSet } from '../lib/pull_from_set'; import { Result, @@ -30,12 +31,13 @@ import { timeoutPromiseAfter } from './timeout_promise_after'; type WorkFn = (...params: T[]) => Promise; interface Opts { - pollInterval: number; + logger: Logger; + pollInterval$: Observable; bufferCapacity: number; getCapacity: () => number; pollRequests$: Observable>; work: WorkFn; - workTimeout?: number; + workTimeout: number; } /** @@ -52,7 +54,8 @@ interface Opts { * of unique request argumets of type T. The queue holds all the buffered request arguments streamed in via pollRequests$ */ export function createTaskPoller({ - pollInterval, + logger, + pollInterval$, getCapacity, pollRequests$, bufferCapacity, @@ -67,7 +70,13 @@ export function createTaskPoller({ // emit a polling event on demand pollRequests$, // emit a polling event on a fixed interval - interval(pollInterval).pipe(mapTo(none)) + pollInterval$.pipe( + switchMap((period) => { + logger.debug(`Task poller now using interval of ${period}ms`); + return interval(period); + }), + mapTo(none) + ) ).pipe( // buffer all requests in a single set (to remove duplicates) as we don't want // work to take place in parallel (it could cause Task Manager to pull in the same @@ -95,7 +104,7 @@ export function createTaskPoller({ await promiseResult( timeoutPromiseAfter( work(...pullFromSet(set, getCapacity())), - workTimeout ?? pollInterval, + workTimeout, () => new Error(`work has timed out`) ) ), diff --git a/x-pack/plugins/task_manager/server/task_manager.ts b/x-pack/plugins/task_manager/server/task_manager.ts index fb2d5e07030a4..cc611e124ea7b 100644 --- a/x-pack/plugins/task_manager/server/task_manager.ts +++ b/x-pack/plugins/task_manager/server/task_manager.ts @@ -17,6 +17,7 @@ import { ISavedObjectsRepository, } from '../../../../src/core/server'; import { Result, asOk, asErr, either, map, mapErr, promiseResult } from './lib/result_type'; +import { createManagedConfiguration } from './lib/create_managed_configuration'; import { TaskManagerConfig } from './config'; import { Logger } from './types'; @@ -149,6 +150,13 @@ export class TaskManager { // pipe store events into the TaskManager's event stream this.store.events.subscribe((event) => this.events$.next(event)); + const { maxWorkersConfiguration$, pollIntervalConfiguration$ } = createManagedConfiguration({ + logger: this.logger, + errors$: this.store.errors$, + startingMaxWorkers: opts.config.max_workers, + startingPollInterval: opts.config.poll_interval, + }); + this.bufferedStore = new BufferedTaskStore(this.store, { bufferMaxOperations: opts.config.max_workers, logger: this.logger, @@ -156,7 +164,7 @@ export class TaskManager { this.pool = new TaskPool({ logger: this.logger, - maxWorkers: opts.config.max_workers, + maxWorkers$: maxWorkersConfiguration$, }); const { @@ -166,7 +174,8 @@ export class TaskManager { this.poller$ = createObservableMonitor>, Error>( () => createTaskPoller({ - pollInterval, + logger: this.logger, + pollInterval$: pollIntervalConfiguration$, bufferCapacity: opts.config.request_capacity, getCapacity: () => this.pool.availableWorkers, pollRequests$: this.claimRequests$, diff --git a/x-pack/plugins/task_manager/server/task_pool.test.ts b/x-pack/plugins/task_manager/server/task_pool.test.ts index 8b2bce455589e..12b731b2b78ae 100644 --- a/x-pack/plugins/task_manager/server/task_pool.test.ts +++ b/x-pack/plugins/task_manager/server/task_pool.test.ts @@ -5,6 +5,7 @@ */ import sinon from 'sinon'; +import { of, Subject } from 'rxjs'; import { TaskPool, TaskPoolRunResult } from './task_pool'; import { mockLogger, resolvable, sleep } from './test_utils'; import { asOk } from './lib/result_type'; @@ -14,7 +15,7 @@ import moment from 'moment'; describe('TaskPool', () => { test('occupiedWorkers are a sum of running tasks', async () => { const pool = new TaskPool({ - maxWorkers: 200, + maxWorkers$: of(200), logger: mockLogger(), }); @@ -26,7 +27,7 @@ describe('TaskPool', () => { test('availableWorkers are a function of total_capacity - occupiedWorkers', async () => { const pool = new TaskPool({ - maxWorkers: 10, + maxWorkers$: of(10), logger: mockLogger(), }); @@ -36,9 +37,21 @@ describe('TaskPool', () => { expect(pool.availableWorkers).toEqual(7); }); + test('availableWorkers is 0 until maxWorkers$ pushes a value', async () => { + const maxWorkers$ = new Subject(); + const pool = new TaskPool({ + maxWorkers$, + logger: mockLogger(), + }); + + expect(pool.availableWorkers).toEqual(0); + maxWorkers$.next(10); + expect(pool.availableWorkers).toEqual(10); + }); + test('does not run tasks that are beyond its available capacity', async () => { const pool = new TaskPool({ - maxWorkers: 2, + maxWorkers$: of(2), logger: mockLogger(), }); @@ -60,7 +73,7 @@ describe('TaskPool', () => { test('should log when marking a Task as running fails', async () => { const logger = mockLogger(); const pool = new TaskPool({ - maxWorkers: 2, + maxWorkers$: of(2), logger, }); @@ -83,7 +96,7 @@ describe('TaskPool', () => { test('should log when running a Task fails', async () => { const logger = mockLogger(); const pool = new TaskPool({ - maxWorkers: 3, + maxWorkers$: of(3), logger, }); @@ -106,7 +119,7 @@ describe('TaskPool', () => { test('should not log when running a Task fails due to the Task SO having been deleted while in flight', async () => { const logger = mockLogger(); const pool = new TaskPool({ - maxWorkers: 3, + maxWorkers$: of(3), logger, }); @@ -117,11 +130,9 @@ describe('TaskPool', () => { const result = await pool.run([mockTask(), taskFailedToRun, mockTask()]); - expect(logger.debug.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "Task TaskType \\"shooooo\\" failed in attempt to run: Saved object [task/foo] not found", - ] - `); + expect(logger.debug).toHaveBeenCalledWith( + 'Task TaskType "shooooo" failed in attempt to run: Saved object [task/foo] not found' + ); expect(logger.warn).not.toHaveBeenCalled(); expect(result).toEqual(TaskPoolRunResult.RunningAllClaimedTasks); @@ -130,7 +141,7 @@ describe('TaskPool', () => { test('Running a task which fails still takes up capacity', async () => { const logger = mockLogger(); const pool = new TaskPool({ - maxWorkers: 1, + maxWorkers$: of(1), logger, }); @@ -147,7 +158,7 @@ describe('TaskPool', () => { test('clears up capacity when a task completes', async () => { const pool = new TaskPool({ - maxWorkers: 1, + maxWorkers$: of(1), logger: mockLogger(), }); @@ -193,7 +204,7 @@ describe('TaskPool', () => { test('run cancels expired tasks prior to running new tasks', async () => { const logger = mockLogger(); const pool = new TaskPool({ - maxWorkers: 2, + maxWorkers$: of(2), logger, }); @@ -251,7 +262,7 @@ describe('TaskPool', () => { const logger = mockLogger(); const pool = new TaskPool({ logger, - maxWorkers: 20, + maxWorkers$: of(20), }); const cancelled = resolvable(); diff --git a/x-pack/plugins/task_manager/server/task_pool.ts b/x-pack/plugins/task_manager/server/task_pool.ts index 92374908c60f7..44f5f5648c2ac 100644 --- a/x-pack/plugins/task_manager/server/task_pool.ts +++ b/x-pack/plugins/task_manager/server/task_pool.ts @@ -8,6 +8,7 @@ * This module contains the logic that ensures we don't run too many * tasks at once in a given Kibana instance. */ +import { Observable } from 'rxjs'; import moment, { Duration } from 'moment'; import { performance } from 'perf_hooks'; import { padStart } from 'lodash'; @@ -16,7 +17,7 @@ import { TaskRunner } from './task_runner'; import { isTaskSavedObjectNotFoundError } from './lib/is_task_not_found_error'; interface Opts { - maxWorkers: number; + maxWorkers$: Observable; logger: Logger; } @@ -31,7 +32,7 @@ const VERSION_CONFLICT_MESSAGE = 'Task has been claimed by another Kibana servic * Runs tasks in batches, taking costs into account. */ export class TaskPool { - private maxWorkers: number; + private maxWorkers: number = 0; private running = new Set(); private logger: Logger; @@ -44,8 +45,11 @@ export class TaskPool { * @prop {Logger} logger - The task manager logger. */ constructor(opts: Opts) { - this.maxWorkers = opts.maxWorkers; this.logger = opts.logger; + opts.maxWorkers$.subscribe((maxWorkers) => { + this.logger.debug(`Task pool now using ${maxWorkers} as the max worker value`); + this.maxWorkers = maxWorkers; + }); } /** diff --git a/x-pack/plugins/task_manager/server/task_store.test.ts b/x-pack/plugins/task_manager/server/task_store.test.ts index f5fafe83748d9..5a3ee12d593c9 100644 --- a/x-pack/plugins/task_manager/server/task_store.test.ts +++ b/x-pack/plugins/task_manager/server/task_store.test.ts @@ -7,7 +7,7 @@ import _ from 'lodash'; import sinon from 'sinon'; import uuid from 'uuid'; -import { filter, take } from 'rxjs/operators'; +import { filter, take, first } from 'rxjs/operators'; import { Option, some, none } from 'fp-ts/lib/Option'; import { @@ -66,8 +66,21 @@ const mockedDate = new Date('2019-02-12T21:01:22.479Z'); describe('TaskStore', () => { describe('schedule', () => { + let store: TaskStore; + + beforeAll(() => { + store = new TaskStore({ + index: 'tasky', + taskManagerId: '', + serializer, + callCluster: jest.fn(), + maxAttempts: 2, + definitions: taskDefinitions, + savedObjectsRepository: savedObjectsClient, + }); + }); + async function testSchedule(task: unknown) { - const callCluster = jest.fn(); savedObjectsClient.create.mockImplementation(async (type: string, attributes: unknown) => ({ id: 'testid', type, @@ -75,15 +88,6 @@ describe('TaskStore', () => { references: [], version: '123', })); - const store = new TaskStore({ - index: 'tasky', - taskManagerId: '', - serializer, - callCluster, - maxAttempts: 2, - definitions: taskDefinitions, - savedObjectsRepository: savedObjectsClient, - }); const result = await store.schedule(task as TaskInstance); expect(savedObjectsClient.create).toHaveBeenCalledTimes(1); @@ -176,12 +180,28 @@ describe('TaskStore', () => { /Unsupported task type "nope"/i ); }); + + test('pushes error from saved objects client to errors$', async () => { + const task: TaskInstance = { + id: 'id', + params: { hello: 'world' }, + state: { foo: 'bar' }, + taskType: 'report', + }; + + const firstErrorPromise = store.errors$.pipe(first()).toPromise(); + savedObjectsClient.create.mockRejectedValue(new Error('Failure')); + await expect(store.schedule(task)).rejects.toThrowErrorMatchingInlineSnapshot(`"Failure"`); + expect(await firstErrorPromise).toMatchInlineSnapshot(`[Error: Failure]`); + }); }); describe('fetch', () => { - async function testFetch(opts?: SearchOpts, hits: unknown[] = []) { - const callCluster = sinon.spy(async (name: string, params?: unknown) => ({ hits: { hits } })); - const store = new TaskStore({ + let store: TaskStore; + const callCluster = jest.fn(); + + beforeAll(() => { + store = new TaskStore({ index: 'tasky', taskManagerId: '', serializer, @@ -190,15 +210,19 @@ describe('TaskStore', () => { definitions: taskDefinitions, savedObjectsRepository: savedObjectsClient, }); + }); + + async function testFetch(opts?: SearchOpts, hits: unknown[] = []) { + callCluster.mockResolvedValue({ hits: { hits } }); const result = await store.fetch(opts); - sinon.assert.calledOnce(callCluster); - sinon.assert.calledWith(callCluster, 'search'); + expect(callCluster).toHaveBeenCalledTimes(1); + expect(callCluster).toHaveBeenCalledWith('search', expect.anything()); return { result, - args: callCluster.args[0][1], + args: callCluster.mock.calls[0][1], }; } @@ -230,6 +254,13 @@ describe('TaskStore', () => { }, }); }); + + test('pushes error from call cluster to errors$', async () => { + const firstErrorPromise = store.errors$.pipe(first()).toPromise(); + callCluster.mockRejectedValue(new Error('Failure')); + await expect(store.fetch()).rejects.toThrowErrorMatchingInlineSnapshot(`"Failure"`); + expect(await firstErrorPromise).toMatchInlineSnapshot(`[Error: Failure]`); + }); }); describe('claimAvailableTasks', () => { @@ -928,9 +959,46 @@ if (doc['task.runAt'].size()!=0) { }, ]); }); + + test('pushes error from saved objects client to errors$', async () => { + const callCluster = jest.fn(); + const store = new TaskStore({ + index: 'tasky', + taskManagerId: '', + serializer, + callCluster, + definitions: taskDefinitions, + maxAttempts: 2, + savedObjectsRepository: savedObjectsClient, + }); + + const firstErrorPromise = store.errors$.pipe(first()).toPromise(); + callCluster.mockRejectedValue(new Error('Failure')); + await expect( + store.claimAvailableTasks({ + claimOwnershipUntil: new Date(), + size: 10, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Failure"`); + expect(await firstErrorPromise).toMatchInlineSnapshot(`[Error: Failure]`); + }); }); describe('update', () => { + let store: TaskStore; + + beforeAll(() => { + store = new TaskStore({ + index: 'tasky', + taskManagerId: '', + serializer, + callCluster: jest.fn(), + maxAttempts: 2, + definitions: taskDefinitions, + savedObjectsRepository: savedObjectsClient, + }); + }); + test('refreshes the index, handles versioning', async () => { const task = { runAt: mockedDate, @@ -959,16 +1027,6 @@ if (doc['task.runAt'].size()!=0) { } ); - const store = new TaskStore({ - index: 'tasky', - taskManagerId: '', - serializer, - callCluster: jest.fn(), - maxAttempts: 2, - definitions: taskDefinitions, - savedObjectsRepository: savedObjectsClient, - }); - const result = await store.update(task); expect(savedObjectsClient.update).toHaveBeenCalledWith( @@ -1002,28 +1060,116 @@ if (doc['task.runAt'].size()!=0) { version: '123', }); }); + + test('pushes error from saved objects client to errors$', async () => { + const task = { + runAt: mockedDate, + scheduledAt: mockedDate, + startedAt: null, + retryAt: null, + id: 'task:324242', + params: { hello: 'world' }, + state: { foo: 'bar' }, + taskType: 'report', + attempts: 3, + status: 'idle' as TaskStatus, + version: '123', + ownerId: null, + }; + + const firstErrorPromise = store.errors$.pipe(first()).toPromise(); + savedObjectsClient.update.mockRejectedValue(new Error('Failure')); + await expect(store.update(task)).rejects.toThrowErrorMatchingInlineSnapshot(`"Failure"`); + expect(await firstErrorPromise).toMatchInlineSnapshot(`[Error: Failure]`); + }); + }); + + describe('bulkUpdate', () => { + let store: TaskStore; + + beforeAll(() => { + store = new TaskStore({ + index: 'tasky', + taskManagerId: '', + serializer, + callCluster: jest.fn(), + maxAttempts: 2, + definitions: taskDefinitions, + savedObjectsRepository: savedObjectsClient, + }); + }); + + test('pushes error from saved objects client to errors$', async () => { + const task = { + runAt: mockedDate, + scheduledAt: mockedDate, + startedAt: null, + retryAt: null, + id: 'task:324242', + params: { hello: 'world' }, + state: { foo: 'bar' }, + taskType: 'report', + attempts: 3, + status: 'idle' as TaskStatus, + version: '123', + ownerId: null, + }; + + const firstErrorPromise = store.errors$.pipe(first()).toPromise(); + savedObjectsClient.bulkUpdate.mockRejectedValue(new Error('Failure')); + await expect(store.bulkUpdate([task])).rejects.toThrowErrorMatchingInlineSnapshot( + `"Failure"` + ); + expect(await firstErrorPromise).toMatchInlineSnapshot(`[Error: Failure]`); + }); }); describe('remove', () => { - test('removes the task with the specified id', async () => { - const id = `id-${_.random(1, 20)}`; - const callCluster = jest.fn(); - const store = new TaskStore({ + let store: TaskStore; + + beforeAll(() => { + store = new TaskStore({ index: 'tasky', taskManagerId: '', serializer, - callCluster, + callCluster: jest.fn(), maxAttempts: 2, definitions: taskDefinitions, savedObjectsRepository: savedObjectsClient, }); + }); + + test('removes the task with the specified id', async () => { + const id = `id-${_.random(1, 20)}`; const result = await store.remove(id); expect(result).toBeUndefined(); expect(savedObjectsClient.delete).toHaveBeenCalledWith('task', id); }); + + test('pushes error from saved objects client to errors$', async () => { + const id = `id-${_.random(1, 20)}`; + const firstErrorPromise = store.errors$.pipe(first()).toPromise(); + savedObjectsClient.delete.mockRejectedValue(new Error('Failure')); + await expect(store.remove(id)).rejects.toThrowErrorMatchingInlineSnapshot(`"Failure"`); + expect(await firstErrorPromise).toMatchInlineSnapshot(`[Error: Failure]`); + }); }); describe('get', () => { + let store: TaskStore; + + beforeAll(() => { + store = new TaskStore({ + index: 'tasky', + taskManagerId: '', + serializer, + callCluster: jest.fn(), + maxAttempts: 2, + definitions: taskDefinitions, + savedObjectsRepository: savedObjectsClient, + }); + }); + test('gets the task with the specified id', async () => { const id = `id-${_.random(1, 20)}`; const task = { @@ -1041,7 +1187,6 @@ if (doc['task.runAt'].size()!=0) { ownerId: null, }; - const callCluster = jest.fn(); savedObjectsClient.get.mockImplementation(async (type: string, objectId: string) => ({ id: objectId, type, @@ -1053,22 +1198,20 @@ if (doc['task.runAt'].size()!=0) { version: '123', })); - const store = new TaskStore({ - index: 'tasky', - taskManagerId: '', - serializer, - callCluster, - maxAttempts: 2, - definitions: taskDefinitions, - savedObjectsRepository: savedObjectsClient, - }); - const result = await store.get(id); expect(result).toEqual(task); expect(savedObjectsClient.get).toHaveBeenCalledWith('task', id); }); + + test('pushes error from saved objects client to errors$', async () => { + const id = `id-${_.random(1, 20)}`; + const firstErrorPromise = store.errors$.pipe(first()).toPromise(); + savedObjectsClient.get.mockRejectedValue(new Error('Failure')); + await expect(store.get(id)).rejects.toThrowErrorMatchingInlineSnapshot(`"Failure"`); + expect(await firstErrorPromise).toMatchInlineSnapshot(`[Error: Failure]`); + }); }); describe('getLifecycle', () => { diff --git a/x-pack/plugins/task_manager/server/task_store.ts b/x-pack/plugins/task_manager/server/task_store.ts index acd19bd75f7a3..15261be3d89ae 100644 --- a/x-pack/plugins/task_manager/server/task_store.ts +++ b/x-pack/plugins/task_manager/server/task_store.ts @@ -121,6 +121,7 @@ export class TaskStore { public readonly maxAttempts: number; public readonly index: string; public readonly taskManagerId: string; + public readonly errors$ = new Subject(); private callCluster: ElasticJs; private definitions: TaskDictionary; @@ -171,11 +172,17 @@ export class TaskStore { ); } - const savedObject = await this.savedObjectsRepository.create( - 'task', - taskInstanceToAttributes(taskInstance), - { id: taskInstance.id, refresh: false } - ); + let savedObject; + try { + savedObject = await this.savedObjectsRepository.create( + 'task', + taskInstanceToAttributes(taskInstance), + { id: taskInstance.id, refresh: false } + ); + } catch (e) { + this.errors$.next(e); + throw e; + } return savedObjectToConcreteTaskInstance(savedObject); } @@ -333,12 +340,22 @@ export class TaskStore { */ public async update(doc: ConcreteTaskInstance): Promise { const attributes = taskInstanceToAttributes(doc); - const updatedSavedObject = await this.savedObjectsRepository.update< - SerializedConcreteTaskInstance - >('task', doc.id, attributes, { - refresh: false, - version: doc.version, - }); + + let updatedSavedObject; + try { + updatedSavedObject = await this.savedObjectsRepository.update( + 'task', + doc.id, + attributes, + { + refresh: false, + version: doc.version, + } + ); + } catch (e) { + this.errors$.next(e); + throw e; + } return savedObjectToConcreteTaskInstance( // The SavedObjects update api forces a Partial on the `attributes` on the response, @@ -362,8 +379,11 @@ export class TaskStore { return attrsById; }, new Map()); - const updatedSavedObjects: Array = ( - await this.savedObjectsRepository.bulkUpdate( + let updatedSavedObjects: Array; + try { + ({ saved_objects: updatedSavedObjects } = await this.savedObjectsRepository.bulkUpdate< + SerializedConcreteTaskInstance + >( docs.map((doc) => ({ type: 'task', id: doc.id, @@ -373,8 +393,11 @@ export class TaskStore { { refresh: false, } - ) - ).saved_objects; + )); + } catch (e) { + this.errors$.next(e); + throw e; + } return updatedSavedObjects.map((updatedSavedObject, index) => isSavedObjectsUpdateResponse(updatedSavedObject) @@ -404,7 +427,12 @@ export class TaskStore { * @returns {Promise} */ public async remove(id: string): Promise { - await this.savedObjectsRepository.delete('task', id); + try { + await this.savedObjectsRepository.delete('task', id); + } catch (e) { + this.errors$.next(e); + throw e; + } } /** @@ -414,7 +442,14 @@ export class TaskStore { * @returns {Promise} */ public async get(id: string): Promise { - return savedObjectToConcreteTaskInstance(await this.savedObjectsRepository.get('task', id)); + let result; + try { + result = await this.savedObjectsRepository.get('task', id); + } catch (e) { + this.errors$.next(e); + throw e; + } + return savedObjectToConcreteTaskInstance(result); } /** @@ -438,14 +473,20 @@ export class TaskStore { private async search(opts: SearchOpts = {}): Promise { const { query } = ensureQueryOnlyReturnsTaskObjects(opts); - const result = await this.callCluster('search', { - index: this.index, - ignoreUnavailable: true, - body: { - ...opts, - query, - }, - }); + let result; + try { + result = await this.callCluster('search', { + index: this.index, + ignoreUnavailable: true, + body: { + ...opts, + query, + }, + }); + } catch (e) { + this.errors$.next(e); + throw e; + } const rawDocs = (result as SearchResponse).hits.hits; @@ -464,17 +505,23 @@ export class TaskStore { { max_docs }: UpdateByQueryOpts = {} ): Promise { const { query } = ensureQueryOnlyReturnsTaskObjects(opts); - const result = await this.callCluster('updateByQuery', { - index: this.index, - ignoreUnavailable: true, - refresh: true, - max_docs, - conflicts: 'proceed', - body: { - ...opts, - query, - }, - }); + let result; + try { + result = await this.callCluster('updateByQuery', { + index: this.index, + ignoreUnavailable: true, + refresh: true, + max_docs, + conflicts: 'proceed', + body: { + ...opts, + query, + }, + }); + } catch (e) { + this.errors$.next(e); + throw e; + } // eslint-disable-next-line @typescript-eslint/naming-convention const { total, updated, version_conflicts } = result as UpdateDocumentByQueryResponse; From 1d1c3c7ef305cb4a8ca8eea8634b92ec8fde5fd7 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Tue, 13 Oct 2020 16:47:23 +0300 Subject: [PATCH 002/128] [Step 1] use Observables on server search API (#79874) * use Observables on server search API * fix PR comments Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...plugin-plugins-data-server.isearchstart.md | 2 +- ...plugins-data-server.isearchstart.search.md | 2 +- ...gin-plugins-data-server.isearchstrategy.md | 2 +- ...gins-data-server.isearchstrategy.search.md | 2 +- .../search_examples/server/my_strategy.ts | 15 +-- .../server/routes/server_search_route.ts | 34 +++--- .../data/public/search/search_service.ts | 2 +- .../es_search/es_search_strategy.test.ts | 77 +++++++------ .../search/es_search/es_search_strategy.ts | 80 +++++++------- .../data/server/search/routes/search.test.ts | 25 +++-- .../data/server/search/routes/search.ts | 18 +-- .../data/server/search/search_service.ts | 37 ++++--- src/plugins/data/server/search/types.ts | 39 +++---- src/plugins/data/server/server.api.md | 6 +- .../server/routes/validate_es.ts | 15 ++- .../server/series_functions/es/es.test.js | 7 +- .../server/series_functions/es/index.js | 12 +- .../abstract_search_strategy.test.js | 7 +- .../strategies/abstract_search_strategy.ts | 28 ++--- .../server/search/eql_search_strategy.test.ts | 48 +++++--- .../server/search/eql_search_strategy.ts | 82 +++++++------- .../server/search/es_search_strategy.test.ts | 26 +++-- .../server/search/es_search_strategy.ts | 68 ++++++------ .../search_strategy/index_fields/index.ts | 104 +++++++++--------- .../security_solution/index.ts | 8 +- .../server/search_strategy/timeline/index.ts | 9 +- 26 files changed, 423 insertions(+), 332 deletions(-) diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md index 9c47ea1a166d5..b99c5f0f10a9e 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.md @@ -16,6 +16,6 @@ export interface ISearchStartAggsStart | | | [getSearchStrategy](./kibana-plugin-plugins-data-server.isearchstart.getsearchstrategy.md) | (name: string) => ISearchStrategy<SearchStrategyRequest, SearchStrategyResponse> | Get other registered search strategies. For example, if a new strategy needs to use the already-registered ES search strategy, it can use this function to accomplish that. | -| [search](./kibana-plugin-plugins-data-server.isearchstart.search.md) | (context: RequestHandlerContext, request: SearchStrategyRequest, options: ISearchOptions) => Promise<SearchStrategyResponse> | | +| [search](./kibana-plugin-plugins-data-server.isearchstart.search.md) | ISearchStrategy['search'] | | | [searchSource](./kibana-plugin-plugins-data-server.isearchstart.searchsource.md) | {
asScoped: (request: KibanaRequest) => Promise<ISearchStartSearchSource>;
} | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.search.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.search.md index fdcd4d6768db5..98ea175aaaea7 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.search.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstart.search.md @@ -7,5 +7,5 @@ Signature: ```typescript -search: (context: RequestHandlerContext, request: SearchStrategyRequest, options: ISearchOptions) => Promise; +search: ISearchStrategy['search']; ``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md index 3d2caf417f3cb..6dd95da2be3c1 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.md @@ -17,5 +17,5 @@ export interface ISearchStrategy(context: RequestHandlerContext, id: string) => Promise<void> | | -| [search](./kibana-plugin-plugins-data-server.isearchstrategy.search.md) | (context: RequestHandlerContext, request: SearchStrategyRequest, options?: ISearchOptions) => Promise<SearchStrategyResponse> | | +| [search](./kibana-plugin-plugins-data-server.isearchstrategy.search.md) | (request: SearchStrategyRequest, options: ISearchOptions, context: RequestHandlerContext) => Observable<SearchStrategyResponse> | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md index 45f43648ab603..84b90ae23f916 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchstrategy.search.md @@ -7,5 +7,5 @@ Signature: ```typescript -search: (context: RequestHandlerContext, request: SearchStrategyRequest, options?: ISearchOptions) => Promise; +search: (request: SearchStrategyRequest, options: ISearchOptions, context: RequestHandlerContext) => Observable; ``` diff --git a/examples/search_examples/server/my_strategy.ts b/examples/search_examples/server/my_strategy.ts index 169982544e6e8..26e7056cdd787 100644 --- a/examples/search_examples/server/my_strategy.ts +++ b/examples/search_examples/server/my_strategy.ts @@ -17,6 +17,7 @@ * under the License. */ +import { map } from 'rxjs/operators'; import { ISearchStrategy, PluginStart } from '../../../src/plugins/data/server'; import { IMyStrategyResponse, IMyStrategyRequest } from '../common'; @@ -25,13 +26,13 @@ export const mySearchStrategyProvider = ( ): ISearchStrategy => { const es = data.search.getSearchStrategy('es'); return { - search: async (context, request, options): Promise => { - const esSearchRes = await es.search(context, request, options); - return { - ...esSearchRes, - cool: request.get_cool ? 'YES' : 'NOPE', - }; - }, + search: (request, options, context) => + es.search(request, options, context).pipe( + map((esSearchRes) => ({ + ...esSearchRes, + cool: request.get_cool ? 'YES' : 'NOPE', + })) + ), cancel: async (context, id) => { if (es.cancel) { es.cancel(context, id); diff --git a/examples/search_examples/server/routes/server_search_route.ts b/examples/search_examples/server/routes/server_search_route.ts index 6eb21cf34b4a3..21ae38b99f3d2 100644 --- a/examples/search_examples/server/routes/server_search_route.ts +++ b/examples/search_examples/server/routes/server_search_route.ts @@ -39,26 +39,28 @@ export function registerServerSearchRoute(router: IRouter, data: DataPluginStart // Run a synchronous search server side, by enforcing a high keepalive and waiting for completion. // If you wish to run the search with polling (in basic+), you'd have to poll on the search API. // Please reach out to the @app-arch-team if you need this to be implemented. - const res = await data.search.search( - context, - { - params: { - index, - body: { - aggs: { - '1': { - avg: { - field, + const res = await data.search + .search( + { + params: { + index, + body: { + aggs: { + '1': { + avg: { + field, + }, }, }, }, + waitForCompletionTimeout: '5m', + keepAlive: '5m', }, - waitForCompletionTimeout: '5m', - keepAlive: '5m', - }, - } as IEsSearchRequest, - {} - ); + } as IEsSearchRequest, + {}, + context + ) + .toPromise(); return response.ok({ body: { diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 2d582b30bcd14..734e88e085661 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -127,7 +127,7 @@ export class SearchService implements Plugin { request: SearchStrategyRequest, options: ISearchOptions ) => { - return search(request, options).toPromise() as Promise; + return search(request, options).toPromise(); }, onResponse: handleResponse, legacy: { diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts index 504ce728481f0..2dbcc3196aa75 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.test.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.test.ts @@ -35,7 +35,8 @@ describe('ES search strategy', () => { }, }, }); - const mockContext = { + + const mockContext = ({ core: { uiSettings: { client: { @@ -44,7 +45,8 @@ describe('ES search strategy', () => { }, elasticsearch: { client: { asCurrentUser: { search: mockApiCaller } } }, }, - }; + } as unknown) as RequestHandlerContext; + const mockConfig$ = pluginInitializerContextConfigMock({}).legacy.globalConfig$; beforeEach(() => { @@ -57,44 +59,51 @@ describe('ES search strategy', () => { expect(typeof esSearch.search).toBe('function'); }); - it('calls the API caller with the params with defaults', async () => { + it('calls the API caller with the params with defaults', async (done) => { const params = { index: 'logstash-*' }; - const esSearch = await esSearchStrategyProvider(mockConfig$, mockLogger); - await esSearch.search((mockContext as unknown) as RequestHandlerContext, { params }); - - expect(mockApiCaller).toBeCalled(); - expect(mockApiCaller.mock.calls[0][0]).toEqual({ - ...params, - ignore_unavailable: true, - track_total_hits: true, - }); + await esSearchStrategyProvider(mockConfig$, mockLogger) + .search({ params }, {}, mockContext) + .subscribe(() => { + expect(mockApiCaller).toBeCalled(); + expect(mockApiCaller.mock.calls[0][0]).toEqual({ + ...params, + ignore_unavailable: true, + track_total_hits: true, + }); + done(); + }); }); - it('calls the API caller with overridden defaults', async () => { + it('calls the API caller with overridden defaults', async (done) => { const params = { index: 'logstash-*', ignore_unavailable: false, timeout: '1000ms' }; - const esSearch = await esSearchStrategyProvider(mockConfig$, mockLogger); - - await esSearch.search((mockContext as unknown) as RequestHandlerContext, { params }); - expect(mockApiCaller).toBeCalled(); - expect(mockApiCaller.mock.calls[0][0]).toEqual({ - ...params, - track_total_hits: true, - }); + await esSearchStrategyProvider(mockConfig$, mockLogger) + .search({ params }, {}, mockContext) + .subscribe(() => { + expect(mockApiCaller).toBeCalled(); + expect(mockApiCaller.mock.calls[0][0]).toEqual({ + ...params, + track_total_hits: true, + }); + done(); + }); }); - it('has all response parameters', async () => { - const params = { index: 'logstash-*' }; - const esSearch = await esSearchStrategyProvider(mockConfig$, mockLogger); - - const response = await esSearch.search((mockContext as unknown) as RequestHandlerContext, { - params, - }); - - expect(response.isRunning).toBe(false); - expect(response.isPartial).toBe(false); - expect(response).toHaveProperty('loaded'); - expect(response).toHaveProperty('rawResponse'); - }); + it('has all response parameters', async (done) => + await esSearchStrategyProvider(mockConfig$, mockLogger) + .search( + { + params: { index: 'logstash-*' }, + }, + {}, + mockContext + ) + .subscribe((data) => { + expect(data.isRunning).toBe(false); + expect(data.isPartial).toBe(false); + expect(data).toHaveProperty('loaded'); + expect(data).toHaveProperty('rawResponse'); + done(); + })); }); diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.ts b/src/plugins/data/server/search/es_search/es_search_strategy.ts index 6e185d30ad56a..92cc941e14853 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.ts @@ -16,10 +16,10 @@ * specific language governing permissions and limitations * under the License. */ +import { Observable, from } from 'rxjs'; import { first } from 'rxjs/operators'; import { SharedGlobalConfig, Logger } from 'kibana/server'; import { SearchResponse } from 'elasticsearch'; -import { Observable } from 'rxjs'; import { ApiResponse } from '@elastic/elasticsearch'; import { SearchUsage } from '../collectors/usage'; import { toSnakeCase } from './to_snake_case'; @@ -29,6 +29,7 @@ import { getTotalLoaded, getShardTimeout, shimAbortSignal, + IEsSearchResponse, } from '..'; export const esSearchStrategyProvider = ( @@ -37,47 +38,52 @@ export const esSearchStrategyProvider = ( usage?: SearchUsage ): ISearchStrategy => { return { - search: async (context, request, options) => { - logger.debug(`search ${request.params?.index}`); - const config = await config$.pipe(first()).toPromise(); - const uiSettingsClient = await context.core.uiSettings.client; + search: (request, options, context) => + from( + new Promise(async (resolve, reject) => { + logger.debug(`search ${request.params?.index}`); + const config = await config$.pipe(first()).toPromise(); + const uiSettingsClient = await context.core.uiSettings.client; - // Only default index pattern type is supported here. - // See data_enhanced for other type support. - if (!!request.indexType) { - throw new Error(`Unsupported index pattern type ${request.indexType}`); - } + // Only default index pattern type is supported here. + // See data_enhanced for other type support. + if (!!request.indexType) { + throw new Error(`Unsupported index pattern type ${request.indexType}`); + } - // ignoreThrottled is not supported in OSS - const { ignoreThrottled, ...defaultParams } = await getDefaultSearchParams(uiSettingsClient); + // ignoreThrottled is not supported in OSS + const { ignoreThrottled, ...defaultParams } = await getDefaultSearchParams( + uiSettingsClient + ); - const params = toSnakeCase({ - ...defaultParams, - ...getShardTimeout(config), - ...request.params, - }); + const params = toSnakeCase({ + ...defaultParams, + ...getShardTimeout(config), + ...request.params, + }); - try { - const promise = shimAbortSignal( - context.core.elasticsearch.client.asCurrentUser.search(params), - options?.abortSignal - ); - const { body: rawResponse } = (await promise) as ApiResponse>; + try { + const promise = shimAbortSignal( + context.core.elasticsearch.client.asCurrentUser.search(params), + options?.abortSignal + ); + const { body: rawResponse } = (await promise) as ApiResponse>; - if (usage) usage.trackSuccess(rawResponse.took); + if (usage) usage.trackSuccess(rawResponse.took); - // The above query will either complete or timeout and throw an error. - // There is no progress indication on this api. - return { - isPartial: false, - isRunning: false, - rawResponse, - ...getTotalLoaded(rawResponse._shards), - }; - } catch (e) { - if (usage) usage.trackError(); - throw e; - } - }, + // The above query will either complete or timeout and throw an error. + // There is no progress indication on this api. + resolve({ + isPartial: false, + isRunning: false, + rawResponse, + ...getTotalLoaded(rawResponse._shards), + }); + } catch (e) { + if (usage) usage.trackError(); + reject(e); + } + }) + ), }; }; diff --git a/src/plugins/data/server/search/routes/search.test.ts b/src/plugins/data/server/search/routes/search.test.ts index d4404c318ab47..834e5de5c3121 100644 --- a/src/plugins/data/server/search/routes/search.test.ts +++ b/src/plugins/data/server/search/routes/search.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Observable } from 'rxjs'; +import { Observable, from } from 'rxjs'; import { CoreSetup, @@ -66,7 +66,8 @@ describe('Search service', () => { }, }, }; - mockDataStart.search.search.mockResolvedValue(response); + + mockDataStart.search.search.mockReturnValue(from(Promise.resolve(response))); const mockContext = {}; const mockBody = { id: undefined, params: {} }; const mockParams = { strategy: 'foo' }; @@ -83,7 +84,7 @@ describe('Search service', () => { await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); expect(mockDataStart.search.search).toBeCalled(); - expect(mockDataStart.search.search.mock.calls[0][1]).toStrictEqual(mockBody); + expect(mockDataStart.search.search.mock.calls[0][0]).toStrictEqual(mockBody); expect(mockResponse.ok).toBeCalled(); expect(mockResponse.ok.mock.calls[0][0]).toEqual({ body: response, @@ -91,12 +92,16 @@ describe('Search service', () => { }); it('handler throws an error if the search throws an error', async () => { - mockDataStart.search.search.mockRejectedValue({ - message: 'oh no', - body: { - error: 'oops', - }, - }); + const rejectedValue = from( + Promise.reject({ + message: 'oh no', + body: { + error: 'oops', + }, + }) + ); + + mockDataStart.search.search.mockReturnValue(rejectedValue); const mockContext = {}; const mockBody = { id: undefined, params: {} }; @@ -114,7 +119,7 @@ describe('Search service', () => { await handler((mockContext as unknown) as RequestHandlerContext, mockRequest, mockResponse); expect(mockDataStart.search.search).toBeCalled(); - expect(mockDataStart.search.search.mock.calls[0][1]).toStrictEqual(mockBody); + expect(mockDataStart.search.search.mock.calls[0][0]).toStrictEqual(mockBody); expect(mockResponse.customError).toBeCalled(); const error: any = mockResponse.customError.mock.calls[0][0]; expect(error.body.message).toBe('oh no'); diff --git a/src/plugins/data/server/search/routes/search.ts b/src/plugins/data/server/search/routes/search.ts index 492ad4395b32a..1e8433d9685e3 100644 --- a/src/plugins/data/server/search/routes/search.ts +++ b/src/plugins/data/server/search/routes/search.ts @@ -49,14 +49,16 @@ export function registerSearchRoute( const [, , selfStart] = await getStartServices(); try { - const response = await selfStart.search.search( - context, - { ...searchRequest, id }, - { - abortSignal, - strategy, - } - ); + const response = await selfStart.search + .search( + { ...searchRequest, id }, + { + abortSignal, + strategy, + }, + context + ) + .toPromise(); return res.ok({ body: { diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index 6e66f8027207b..0130d3aacc91f 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -49,10 +49,10 @@ import { IKibanaSearchResponse, IEsSearchRequest, IEsSearchResponse, - ISearchOptions, SearchSourceDependencies, SearchSourceService, searchSourceRequiredUiSettings, + ISearchOptions, } from '../../common/search'; import { getShardDelayBucketAgg, @@ -151,13 +151,7 @@ export class SearchService implements Plugin { return { aggs: this.aggsService.start({ fieldFormats, uiSettings }), getSearchStrategy: this.getSearchStrategy, - search: ( - context: RequestHandlerContext, - searchRequest: IKibanaSearchRequest, - options: Record - ) => { - return this.search(context, searchRequest, options); - }, + search: this.search.bind(this), searchSource: { asScoped: async (request: KibanaRequest) => { const esClient = elasticsearch.client.asScoped(request); @@ -175,7 +169,13 @@ export class SearchService implements Plugin { const searchSourceDependencies: SearchSourceDependencies = { getConfig: (key: string): T => uiSettingsCache[key], - search: (searchRequest, options) => { + search: < + SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, + SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse + >( + searchStrategyRequest: SearchStrategyRequest, + options: ISearchOptions + ) => { /** * Unless we want all SearchSource users to provide both a KibanaRequest * (needed for index patterns) AND the RequestHandlerContext (needed for @@ -195,7 +195,12 @@ export class SearchService implements Plugin { }, }, } as RequestHandlerContext; - return this.search(fakeRequestHandlerContext, searchRequest, options); + + return this.search( + searchStrategyRequest, + options, + fakeRequestHandlerContext + ).toPromise(); }, // onResponse isn't used on the server, so we just return the original value onResponse: (req, res) => res, @@ -234,13 +239,15 @@ export class SearchService implements Plugin { SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse >( - context: RequestHandlerContext, searchRequest: SearchStrategyRequest, - options: ISearchOptions - ): Promise => { - return this.getSearchStrategy( + options: ISearchOptions, + context: RequestHandlerContext + ) => { + const strategy = this.getSearchStrategy( options.strategy || this.defaultSearchStrategyName - ).search(context, searchRequest, options); + ); + + return strategy.search(searchRequest, options, context); }; private getSearchStrategy = < diff --git a/src/plugins/data/server/search/types.ts b/src/plugins/data/server/search/types.ts index 0de4ef529e896..9ba06d88dc4b3 100644 --- a/src/plugins/data/server/search/types.ts +++ b/src/plugins/data/server/search/types.ts @@ -17,6 +17,7 @@ * under the License. */ +import { Observable } from 'rxjs'; import { KibanaRequest, RequestHandlerContext } from 'src/core/server'; import { ISearchOptions, @@ -57,6 +58,22 @@ export interface ISearchSetup { __enhance: (enhancements: SearchEnhancements) => void; } +/** + * Search strategy interface contains a search method that takes in a request and returns a promise + * that resolves to a response. + */ +export interface ISearchStrategy< + SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, + SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse +> { + search: ( + request: SearchStrategyRequest, + options: ISearchOptions, + context: RequestHandlerContext + ) => Observable; + cancel?: (context: RequestHandlerContext, id: string) => Promise; +} + export interface ISearchStart< SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse @@ -69,28 +86,8 @@ export interface ISearchStart< getSearchStrategy: ( name: string ) => ISearchStrategy; - search: ( - context: RequestHandlerContext, - request: SearchStrategyRequest, - options: ISearchOptions - ) => Promise; + search: ISearchStrategy['search']; searchSource: { asScoped: (request: KibanaRequest) => Promise; }; } - -/** - * Search strategy interface contains a search method that takes in a request and returns a promise - * that resolves to a response. - */ -export interface ISearchStrategy< - SearchStrategyRequest extends IKibanaSearchRequest = IEsSearchRequest, - SearchStrategyResponse extends IKibanaSearchResponse = IEsSearchResponse -> { - search: ( - context: RequestHandlerContext, - request: SearchStrategyRequest, - options?: ISearchOptions - ) => Promise; - cancel?: (context: RequestHandlerContext, id: string) => Promise; -} diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 07afad1c96a06..f2c8ff5344b9a 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -713,7 +713,7 @@ export interface ISearchStart ISearchStrategy; // (undocumented) - search: (context: RequestHandlerContext, request: SearchStrategyRequest, options: ISearchOptions) => Promise; + search: ISearchStrategy['search']; // (undocumented) searchSource: { asScoped: (request: KibanaRequest) => Promise; @@ -727,7 +727,7 @@ export interface ISearchStrategy Promise; // (undocumented) - search: (context: RequestHandlerContext, request: SearchStrategyRequest, options?: ISearchOptions) => Promise; + search: (request: SearchStrategyRequest, options: ISearchOptions, context: RequestHandlerContext) => Observable; } // @public (undocumented) @@ -1140,7 +1140,7 @@ export function usageProvider(core: CoreSetup_2): SearchUsage; // src/plugins/data/server/index.ts:254:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/server/index_patterns/index_patterns_service.ts:50:14 - (ae-forgotten-export) The symbol "IndexPatternsService" needs to be exported by the entry point index.d.ts // src/plugins/data/server/plugin.ts:88:66 - (ae-forgotten-export) The symbol "DataEnhancements" needs to be exported by the entry point index.d.ts -// src/plugins/data/server/search/types.ts:78:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts +// src/plugins/data/server/search/types.ts:91:5 - (ae-forgotten-export) The symbol "ISearchStartSearchSource" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/src/plugins/vis_type_timelion/server/routes/validate_es.ts b/src/plugins/vis_type_timelion/server/routes/validate_es.ts index ea08310499a96..242be515e52bc 100644 --- a/src/plugins/vis_type_timelion/server/routes/validate_es.ts +++ b/src/plugins/vis_type_timelion/server/routes/validate_es.ts @@ -57,10 +57,17 @@ export function validateEsRoute(router: IRouter, core: CoreSetup) { let resp; try { - resp = await deps.data.search.search(context, body, { - strategy: ES_SEARCH_STRATEGY, - }); - resp = resp.rawResponse; + resp = ( + await deps.data.search + .search( + body, + { + strategy: ES_SEARCH_STRATEGY, + }, + context + ) + .toPromise() + ).rawResponse; } catch (errResp) { resp = errResp; } diff --git a/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js b/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js index c5fc4b7b93269..8be3cf5171c65 100644 --- a/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js +++ b/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js @@ -16,9 +16,9 @@ * specific language governing permissions and limitations * under the License. */ +import { from } from 'rxjs'; import es from './index'; - import tlConfigFn from '../fixtures/tl_config'; import * as aggResponse from './lib/agg_response_to_series_list'; import buildRequest from './lib/build_request'; @@ -36,7 +36,10 @@ function stubRequestAndServer(response, indexPatternSavedObjects = []) { getStartServices: sinon .stub() .returns( - Promise.resolve([{}, { data: { search: { search: () => Promise.resolve(response) } } }]) + Promise.resolve([ + {}, + { data: { search: { search: () => from(Promise.resolve(response)) } } }, + ]) ), savedObjectsClient: { find: function () { diff --git a/src/plugins/vis_type_timelion/server/series_functions/es/index.js b/src/plugins/vis_type_timelion/server/series_functions/es/index.js index bfa8d75900d11..fc3250f0d4726 100644 --- a/src/plugins/vis_type_timelion/server/series_functions/es/index.js +++ b/src/plugins/vis_type_timelion/server/series_functions/es/index.js @@ -132,9 +132,15 @@ export default new Datasource('es', { const deps = (await tlConfig.getStartServices())[1]; - const resp = await deps.data.search.search(tlConfig.context, body, { - strategy: ES_SEARCH_STRATEGY, - }); + const resp = await deps.data.search + .search( + body, + { + strategy: ES_SEARCH_STRATEGY, + }, + tlConfig.context + ) + .toPromise(); if (!resp.rawResponse._shards.total) { throw new Error( diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.js b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.js index 4dcc67dc46976..ceae784cf74a6 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.js +++ b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.test.js @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { from } from 'rxjs'; import { AbstractSearchStrategy } from './abstract_search_strategy'; describe('AbstractSearchStrategy', () => { @@ -55,7 +56,7 @@ describe('AbstractSearchStrategy', () => { test('should return response', async () => { const searches = [{ body: 'body', index: 'index' }]; - const searchFn = jest.fn().mockReturnValue(Promise.resolve({})); + const searchFn = jest.fn().mockReturnValue(from(Promise.resolve({}))); const responses = await abstractSearchStrategy.search( { @@ -82,7 +83,6 @@ describe('AbstractSearchStrategy', () => { expect(responses).toEqual([{}]); expect(searchFn).toHaveBeenCalledWith( - {}, { params: { body: 'body', @@ -92,7 +92,8 @@ describe('AbstractSearchStrategy', () => { }, { strategy: 'es', - } + }, + {} ); }); }); diff --git a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts index 2eb92b2b777e8..7b62ad310a354 100644 --- a/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts +++ b/src/plugins/vis_type_timeseries/server/lib/search_strategies/strategies/abstract_search_strategy.ts @@ -60,20 +60,22 @@ export class AbstractSearchStrategy { const requests: any[] = []; bodies.forEach((body) => { requests.push( - deps.data.search.search( - req.requestContext, - { - params: { - ...body, - ...this.additionalParams, + deps.data.search + .search( + { + params: { + ...body, + ...this.additionalParams, + }, + indexType: this.indexType, }, - indexType: this.indexType, - }, - { - ...options, - strategy: this.searchStrategyName, - } - ) + { + ...options, + strategy: this.searchStrategyName, + }, + req.requestContext + ) + .toPromise() ); }); return Promise.all(requests); diff --git a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts index 7f6663a39eeb3..5b634fe4cf26c 100644 --- a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.test.ts @@ -82,7 +82,7 @@ describe('EQL search strategy', () => { describe('async functionality', () => { it('performs an eql client search with params when no ID is provided', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search(mockContext, { options, params }); + await eqlSearch.search({ options, params }, {}, mockContext).toPromise(); const [[request, requestOptions]] = mockEqlSearch.mock.calls; expect(request.index).toEqual('logstash-*'); @@ -92,7 +92,7 @@ describe('EQL search strategy', () => { it('retrieves the current request if an id is provided', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search(mockContext, { id: 'my-search-id' }); + await eqlSearch.search({ id: 'my-search-id' }, {}, mockContext).toPromise(); const [[requestParams]] = mockEqlGet.mock.calls; expect(mockEqlSearch).not.toHaveBeenCalled(); @@ -103,7 +103,7 @@ describe('EQL search strategy', () => { describe('arguments', () => { it('sends along async search options', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search(mockContext, { options, params }); + await eqlSearch.search({ options, params }, {}, mockContext).toPromise(); const [[request]] = mockEqlSearch.mock.calls; expect(request).toEqual( @@ -116,7 +116,7 @@ describe('EQL search strategy', () => { it('sends along default search parameters', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search(mockContext, { options, params }); + await eqlSearch.search({ options, params }, {}, mockContext).toPromise(); const [[request]] = mockEqlSearch.mock.calls; expect(request).toEqual( @@ -129,14 +129,20 @@ describe('EQL search strategy', () => { it('allows search parameters to be overridden', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search(mockContext, { - options, - params: { - ...params, - wait_for_completion_timeout: '5ms', - keep_on_completion: false, - }, - }); + await eqlSearch + .search( + { + options, + params: { + ...params, + wait_for_completion_timeout: '5ms', + keep_on_completion: false, + }, + }, + {}, + mockContext + ) + .toPromise(); const [[request]] = mockEqlSearch.mock.calls; expect(request).toEqual( @@ -150,10 +156,16 @@ describe('EQL search strategy', () => { it('allows search options to be overridden', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search(mockContext, { - options: { ...options, maxRetries: 2, ignore: [300] }, - params, - }); + await eqlSearch + .search( + { + options: { ...options, maxRetries: 2, ignore: [300] }, + params, + }, + {}, + mockContext + ) + .toPromise(); const [[, requestOptions]] = mockEqlSearch.mock.calls; expect(requestOptions).toEqual( @@ -166,7 +178,9 @@ describe('EQL search strategy', () => { it('passes transport options for an existing request', async () => { const eqlSearch = await eqlSearchStrategyProvider(mockLogger); - await eqlSearch.search(mockContext, { id: 'my-search-id', options: { ignore: [400] } }); + await eqlSearch + .search({ id: 'my-search-id', options: { ignore: [400] } }, {}, mockContext) + .toPromise(); const [[, requestOptions]] = mockEqlGet.mock.calls; expect(mockEqlSearch).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts index 2516693a7f29b..a7ca999699e23 100644 --- a/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/eql_search_strategy.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { from } from 'rxjs'; import { Logger } from 'kibana/server'; import { ApiResponse, TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; @@ -26,48 +27,51 @@ export const eqlSearchStrategyProvider = ( id, }); }, - search: async (context, request, options) => { - logger.debug(`_eql/search ${JSON.stringify(request.params) || request.id}`); - let promise: TransportRequestPromise; - const eqlClient = context.core.elasticsearch.client.asCurrentUser.eql; - const uiSettingsClient = await context.core.uiSettings.client; - const asyncOptions = getAsyncOptions(); - const searchOptions = toSnakeCase({ ...request.options }); + search: (request, options, context) => + from( + new Promise(async (resolve) => { + logger.debug(`_eql/search ${JSON.stringify(request.params) || request.id}`); + let promise: TransportRequestPromise; + const eqlClient = context.core.elasticsearch.client.asCurrentUser.eql; + const uiSettingsClient = await context.core.uiSettings.client; + const asyncOptions = getAsyncOptions(); + const searchOptions = toSnakeCase({ ...request.options }); - if (request.id) { - promise = eqlClient.get( - { - id: request.id, - ...toSnakeCase(asyncOptions), - }, - searchOptions - ); - } else { - const { ignoreThrottled, ignoreUnavailable } = await getDefaultSearchParams( - uiSettingsClient - ); - const searchParams = toSnakeCase({ - ignoreThrottled, - ignoreUnavailable, - ...asyncOptions, - ...request.params, - }); + if (request.id) { + promise = eqlClient.get( + { + id: request.id, + ...toSnakeCase(asyncOptions), + }, + searchOptions + ); + } else { + const { ignoreThrottled, ignoreUnavailable } = await getDefaultSearchParams( + uiSettingsClient + ); + const searchParams = toSnakeCase({ + ignoreThrottled, + ignoreUnavailable, + ...asyncOptions, + ...request.params, + }); - promise = eqlClient.search( - searchParams as EqlSearchStrategyRequest['params'], - searchOptions - ); - } + promise = eqlClient.search( + searchParams as EqlSearchStrategyRequest['params'], + searchOptions + ); + } - const rawResponse = await shimAbortSignal(promise, options?.abortSignal); - const { id, is_partial: isPartial, is_running: isRunning } = rawResponse.body; + const rawResponse = await shimAbortSignal(promise, options?.abortSignal); + const { id, is_partial: isPartial, is_running: isRunning } = rawResponse.body; - return { - id, - isPartial, - isRunning, - rawResponse, - }; - }, + resolve({ + id, + isPartial, + isRunning, + rawResponse, + }); + }) + ), }; }; diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts index f4f3d894a4576..bab304b6afc9f 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts @@ -86,7 +86,9 @@ describe('ES search strategy', () => { const params = { index: 'logstash-*', body: { query: {} } }; const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$, mockLogger); - await esSearch.search((mockContext as unknown) as RequestHandlerContext, { params }); + await esSearch + .search({ params }, {}, (mockContext as unknown) as RequestHandlerContext) + .toPromise(); expect(mockSubmitCaller).toBeCalled(); const request = mockSubmitCaller.mock.calls[0][0]; @@ -100,7 +102,9 @@ describe('ES search strategy', () => { const params = { index: 'logstash-*', body: { query: {} } }; const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$, mockLogger); - await esSearch.search((mockContext as unknown) as RequestHandlerContext, { id: 'foo', params }); + await esSearch + .search({ id: 'foo', params }, {}, (mockContext as unknown) as RequestHandlerContext) + .toPromise(); expect(mockGetCaller).toBeCalled(); const request = mockGetCaller.mock.calls[0][0]; @@ -115,10 +119,16 @@ describe('ES search strategy', () => { const params = { index: 'foo-程', body: {} }; const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$, mockLogger); - await esSearch.search((mockContext as unknown) as RequestHandlerContext, { - indexType: 'rollup', - params, - }); + await esSearch + .search( + { + indexType: 'rollup', + params, + }, + {}, + (mockContext as unknown) as RequestHandlerContext + ) + .toPromise(); expect(mockApiCaller).toBeCalled(); const { method, path } = mockApiCaller.mock.calls[0][0]; @@ -132,7 +142,9 @@ describe('ES search strategy', () => { const params = { index: 'foo-*', body: {} }; const esSearch = await enhancedEsSearchStrategyProvider(mockConfig$, mockLogger); - await esSearch.search((mockContext as unknown) as RequestHandlerContext, { params }); + await esSearch + .search({ params }, {}, (mockContext as unknown) as RequestHandlerContext) + .toPromise(); expect(mockSubmitCaller).toBeCalled(); const request = mockSubmitCaller.mock.calls[0][0]; diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts index 7475228724388..9b89fb9fab3cb 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { from } from 'rxjs'; import { first } from 'rxjs/operators'; import { SearchResponse } from 'elasticsearch'; import { Observable } from 'rxjs'; @@ -36,35 +37,38 @@ export const enhancedEsSearchStrategyProvider = ( logger: Logger, usage?: SearchUsage ): ISearchStrategy => { - const search = async ( - context: RequestHandlerContext, + const search = ( request: IEnhancedEsSearchRequest, - options?: ISearchOptions - ) => { - logger.debug(`search ${JSON.stringify(request.params) || request.id}`); - - const isAsync = request.indexType !== 'rollup'; - - try { - const response = isAsync - ? await asyncSearch(context, request, options) - : await rollupSearch(context, request, options); - - if ( - usage && - isAsync && - isEnhancedEsSearchResponse(response) && - isCompleteResponse(response) - ) { - usage.trackSuccess(response.rawResponse.took); - } - - return response; - } catch (e) { - if (usage) usage.trackError(); - throw e; - } - }; + options: ISearchOptions, + context: RequestHandlerContext + ) => + from( + new Promise(async (resolve, reject) => { + logger.debug(`search ${JSON.stringify(request.params) || request.id}`); + + const isAsync = request.indexType !== 'rollup'; + + try { + const response = isAsync + ? await asyncSearch(request, options, context) + : await rollupSearch(request, options, context); + + if ( + usage && + isAsync && + isEnhancedEsSearchResponse(response) && + isCompleteResponse(response) + ) { + usage.trackSuccess(response.rawResponse.took); + } + + resolve(response); + } catch (e) { + if (usage) usage.trackError(); + reject(e); + } + }) + ); const cancel = async (context: RequestHandlerContext, id: string) => { logger.debug(`cancel ${id}`); @@ -74,9 +78,9 @@ export const enhancedEsSearchStrategyProvider = ( }; async function asyncSearch( - context: RequestHandlerContext, request: IEnhancedEsSearchRequest, - options?: ISearchOptions + options: ISearchOptions, + context: RequestHandlerContext ): Promise { let promise: TransportRequestPromise; const esClient = context.core.elasticsearch.client.asCurrentUser; @@ -112,9 +116,9 @@ export const enhancedEsSearchStrategyProvider = ( } const rollupSearch = async function ( - context: RequestHandlerContext, request: IEnhancedEsSearchRequest, - options?: ISearchOptions + options: ISearchOptions, + context: RequestHandlerContext ): Promise { const esClient = context.core.elasticsearch.client.asCurrentUser; const uiSettingsClient = await context.core.uiSettings.client; diff --git a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts index da29cae0eebeb..bc461f3885a70 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/index_fields/index.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { from } from 'rxjs'; import isEmpty from 'lodash/isEmpty'; import { IndexPatternsFetcher, ISearchStrategy } from '../../../../../../src/plugins/data/server'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths @@ -25,60 +26,63 @@ export const securitySolutionIndexFieldsProvider = (): ISearchStrategy< const beatFields: BeatFields = require('../../utils/beat_schema/fields').fieldsBeat; return { - search: async (context, request) => { - const { elasticsearch } = context.core; - const indexPatternsFetcher = new IndexPatternsFetcher( - elasticsearch.legacy.client.callAsCurrentUser - ); - const dedupeIndices = dedupeIndexName(request.indices); + search: (request, options, context) => + from( + new Promise(async (resolve) => { + const { elasticsearch } = context.core; + const indexPatternsFetcher = new IndexPatternsFetcher( + elasticsearch.legacy.client.callAsCurrentUser + ); + const dedupeIndices = dedupeIndexName(request.indices); - const responsesIndexFields = await Promise.all( - dedupeIndices - .map((index) => - indexPatternsFetcher.getFieldsForWildcard({ - pattern: index, - }) - ) - .map((p) => p.catch((e) => false)) - ); - let indexFields: IndexField[] = []; + const responsesIndexFields = await Promise.all( + dedupeIndices + .map((index) => + indexPatternsFetcher.getFieldsForWildcard({ + pattern: index, + }) + ) + .map((p) => p.catch((e) => false)) + ); + let indexFields: IndexField[] = []; - if (!request.onlyCheckIfIndicesExist) { - indexFields = await formatIndexFields( - beatFields, - responsesIndexFields.filter((rif) => rif !== false) as FieldDescriptor[][], - dedupeIndices - ); - } + if (!request.onlyCheckIfIndicesExist) { + indexFields = await formatIndexFields( + beatFields, + responsesIndexFields.filter((rif) => rif !== false) as FieldDescriptor[][], + dedupeIndices + ); + } - return Promise.resolve({ - indexFields, - indicesExist: dedupeIndices.filter((index, i) => responsesIndexFields[i] !== false), - rawResponse: { - timed_out: false, - took: -1, - _shards: { - total: -1, - successful: -1, - failed: -1, - skipped: -1, - }, - hits: { - total: -1, - max_score: -1, - hits: [ - { - _index: '', - _type: '', - _id: '', - _score: -1, - _source: null, + return resolve({ + indexFields, + indicesExist: dedupeIndices.filter((index, i) => responsesIndexFields[i] !== false), + rawResponse: { + timed_out: false, + took: -1, + _shards: { + total: -1, + successful: -1, + failed: -1, + skipped: -1, }, - ], - }, - }, - }); - }, + hits: { + total: -1, + max_score: -1, + hits: [ + { + _index: '', + _type: '', + _id: '', + _score: -1, + _source: null, + }, + ], + }, + }, + }); + }) + ), }; }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts index d94a32174cd7a..962865880df5f 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/index.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { mergeMap } from 'rxjs/operators'; import { ISearchStrategy, PluginStart } from '../../../../../../src/plugins/data/server'; import { FactoryQueryTypes, @@ -19,15 +20,16 @@ export const securitySolutionSearchStrategyProvider = { + search: (request, options, context) => { if (request.factoryQueryType == null) { throw new Error('factoryQueryType is required'); } const queryFactory: SecuritySolutionFactory = securitySolutionFactory[request.factoryQueryType]; const dsl = queryFactory.buildDsl(request); - const esSearchRes = await es.search(context, { ...request, params: dsl }, options); - return queryFactory.parse(request, esSearchRes); + return es + .search({ ...request, params: dsl }, options, context) + .pipe(mergeMap((esSearchRes) => queryFactory.parse(request, esSearchRes))); }, cancel: async (context, id) => { if (es.cancel) { diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts index 6d8505211123b..165f0f586ebdb 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/index.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { mergeMap } from 'rxjs/operators'; import { ISearchStrategy, PluginStart } from '../../../../../../src/plugins/data/server'; import { TimelineFactoryQueryTypes, @@ -19,15 +20,17 @@ export const securitySolutionTimelineSearchStrategyProvider = { + search: (request, options, context) => { if (request.factoryQueryType == null) { throw new Error('factoryQueryType is required'); } const queryFactory: SecuritySolutionTimelineFactory = securitySolutionTimelineFactory[request.factoryQueryType]; const dsl = queryFactory.buildDsl(request); - const esSearchRes = await es.search(context, { ...request, params: dsl }, options); - return queryFactory.parse(request, esSearchRes); + + return es + .search({ ...request, params: dsl }, options, context) + .pipe(mergeMap((esSearchRes) => queryFactory.parse(request, esSearchRes))); }, cancel: async (context, id) => { if (es.cancel) { From acfee87210a1690744f8ceedfca1ca56c8f5ccd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Tue, 13 Oct 2020 15:11:55 +0100 Subject: [PATCH 003/128] [APM] React key warning when opening popover with external resources (#80328) --- .../public/components/app/ServiceMap/Popover/Info.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Info.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Info.tsx index 7771a232a5c9e..d0902c427aac8 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Info.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/Popover/Info.tsx @@ -10,7 +10,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import cytoscape from 'cytoscape'; -import React from 'react'; +import React, { Fragment } from 'react'; import styled from 'styled-components'; import { SPAN_SUBTYPE, @@ -71,7 +71,7 @@ export function Info(data: InfoProps) { resource.label || resource['span.destination.service.resource']; const desc = `${resource['span.type']} (${resource['span.subtype']})`; return ( - <> + {desc} - + ); })} @@ -97,8 +97,8 @@ export function Info(data: InfoProps) { {listItems.map( ({ title, description }) => description && ( -
- +
+ {title} From 62b849846d388234af8c2cb3e1e8c8c09453fb57 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko Date: Tue, 13 Oct 2020 07:14:03 -0700 Subject: [PATCH 004/128] Improved AlertsClient tests structure by splitting a huge alerts_client.tests.ts file into a specific files defined by its responsibility. (#80088) fairly large refactor, but no functional changes --- .../alerts/server/alerts_client.test.ts | 4567 ----------------- .../{ => alerts_client}/alerts_client.ts | 36 +- .../alerts/server/alerts_client/index.ts | 6 + .../server/alerts_client/tests/create.test.ts | 1097 ++++ .../server/alerts_client/tests/delete.test.ts | 204 + .../alerts_client/tests/disable.test.ts | 253 + .../server/alerts_client/tests/enable.test.ts | 361 ++ .../server/alerts_client/tests/find.test.ts | 251 + .../server/alerts_client/tests/get.test.ts | 194 + .../tests/get_alert_instance_summary.test.ts | 292 ++ .../tests/get_alert_state.test.ts | 239 + .../alerts/server/alerts_client/tests/lib.ts | 103 + .../tests/list_alert_types.test.ts | 134 + .../alerts_client/tests/mute_all.test.ts | 138 + .../alerts_client/tests/mute_instance.test.ts | 181 + .../alerts_client/tests/unmute_all.test.ts | 139 + .../tests/unmute_instance.test.ts | 179 + .../server/alerts_client/tests/update.test.ts | 1257 +++++ .../tests/update_api_key.test.ts | 229 + 19 files changed, 5275 insertions(+), 4585 deletions(-) delete mode 100644 x-pack/plugins/alerts/server/alerts_client.test.ts rename x-pack/plugins/alerts/server/{ => alerts_client}/alerts_client.ts (96%) create mode 100644 x-pack/plugins/alerts/server/alerts_client/index.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/delete.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/disable.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/enable.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/find.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/get.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/get_alert_instance_summary.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/get_alert_state.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/lib.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/list_alert_types.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/mute_all.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/mute_instance.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/unmute_all.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/unmute_instance.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/update.test.ts create mode 100644 x-pack/plugins/alerts/server/alerts_client/tests/update_api_key.test.ts diff --git a/x-pack/plugins/alerts/server/alerts_client.test.ts b/x-pack/plugins/alerts/server/alerts_client.test.ts deleted file mode 100644 index b20018fcc26f7..0000000000000 --- a/x-pack/plugins/alerts/server/alerts_client.test.ts +++ /dev/null @@ -1,4567 +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 uuid from 'uuid'; -import { schema } from '@kbn/config-schema'; -import { AlertsClient, CreateOptions, ConstructorOptions } from './alerts_client'; -import { savedObjectsClientMock, loggingSystemMock } from '../../../../src/core/server/mocks'; -import { nodeTypes } from '../../../../src/plugins/data/common'; -import { esKuery } from '../../../../src/plugins/data/server'; -import { taskManagerMock } from '../../task_manager/server/task_manager.mock'; -import { alertTypeRegistryMock } from './alert_type_registry.mock'; -import { alertsAuthorizationMock } from './authorization/alerts_authorization.mock'; -import { TaskStatus } from '../../task_manager/server'; -import { IntervalSchedule, RawAlert } from './types'; -import { resolvable } from './test_utils'; -import { encryptedSavedObjectsMock } from '../../encrypted_saved_objects/server/mocks'; -import { actionsClientMock, actionsAuthorizationMock } from '../../actions/server/mocks'; -import { AlertsAuthorization } from './authorization/alerts_authorization'; -import { ActionsAuthorization } from '../../actions/server'; -import { eventLogClientMock } from '../../event_log/server/mocks'; -import { QueryEventsBySavedObjectResult } from '../../event_log/server'; -import { SavedObject } from 'kibana/server'; -import { EventsFactory } from './lib/alert_instance_summary_from_event_log.test'; - -const taskManager = taskManagerMock.start(); -const alertTypeRegistry = alertTypeRegistryMock.create(); -const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); -const eventLogClient = eventLogClientMock.create(); - -const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); -const authorization = alertsAuthorizationMock.create(); -const actionsAuthorization = actionsAuthorizationMock.create(); - -const kibanaVersion = 'v7.10.0'; -const alertsClientParams: jest.Mocked = { - taskManager, - alertTypeRegistry, - unsecuredSavedObjectsClient, - authorization: (authorization as unknown) as AlertsAuthorization, - actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, - spaceId: 'default', - namespace: 'default', - getUserName: jest.fn(), - createAPIKey: jest.fn(), - invalidateAPIKey: jest.fn(), - logger: loggingSystemMock.create().get(), - encryptedSavedObjectsClient: encryptedSavedObjects, - getActionsClient: jest.fn(), - getEventLogClient: jest.fn(), - kibanaVersion, -}; - -beforeEach(() => { - jest.resetAllMocks(); - alertsClientParams.createAPIKey.mockResolvedValue({ apiKeysEnabled: false }); - alertsClientParams.invalidateAPIKey.mockResolvedValue({ - apiKeysEnabled: true, - result: { - invalidated_api_keys: [], - previously_invalidated_api_keys: [], - error_count: 0, - }, - }); - alertsClientParams.getUserName.mockResolvedValue('elastic'); - taskManager.runNow.mockResolvedValue({ id: '' }); - const actionsClient = actionsClientMock.create(); - actionsClient.getBulk.mockResolvedValueOnce([ - { - id: '1', - isPreconfigured: false, - actionTypeId: 'test', - name: 'test', - config: { - foo: 'bar', - }, - }, - { - id: '2', - isPreconfigured: false, - actionTypeId: 'test2', - name: 'test2', - config: { - foo: 'bar', - }, - }, - { - id: 'testPreconfigured', - actionTypeId: '.slack', - isPreconfigured: true, - name: 'test', - }, - ]); - alertsClientParams.getActionsClient.mockResolvedValue(actionsClient); - - alertTypeRegistry.get.mockImplementation((id) => ({ - id: '123', - name: 'Test', - actionGroups: [{ id: 'default', name: 'Default' }], - defaultActionGroupId: 'default', - async executor() {}, - producer: 'alerts', - })); - alertsClientParams.getEventLogClient.mockResolvedValue(eventLogClient); -}); - -const mockedDateString = '2019-02-12T21:01:22.479Z'; -const mockedDate = new Date(mockedDateString); -const DateOriginal = Date; - -// A version of date that responds to `new Date(null|undefined)` and `Date.now()` -// by returning a fixed date, otherwise should be same as Date. -/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ -(global as any).Date = class Date { - constructor(...args: unknown[]) { - // sometimes the ctor has no args, sometimes has a single `null` arg - if (args[0] == null) { - // @ts-ignore - return mockedDate; - } else { - // @ts-ignore - return new DateOriginal(...args); - } - } - static now() { - return mockedDate.getTime(); - } - static parse(string: string) { - return DateOriginal.parse(string); - } -}; - -function getMockData(overwrites: Record = {}): CreateOptions['data'] { - return { - enabled: true, - name: 'abc', - tags: ['foo'], - alertTypeId: '123', - consumer: 'bar', - schedule: { interval: '10s' }, - throttle: null, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - ], - ...overwrites, - }; -} - -describe('create()', () => { - let alertsClient: AlertsClient; - - beforeEach(() => { - alertsClient = new AlertsClient(alertsClientParams); - }); - - describe('authorization', () => { - function tryToExecuteOperation(options: CreateOptions): Promise { - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: '123', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - createdAt: '2019-02-12T21:01:22.479Z', - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - taskManager.schedule.mockResolvedValueOnce({ - id: 'task-123', - taskType: 'alerting:123', - scheduledAt: new Date(), - attempts: 1, - status: TaskStatus.Idle, - runAt: new Date(), - startedAt: null, - retryAt: null, - state: {}, - params: {}, - ownerId: null, - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [], - scheduledTaskId: 'task-123', - }, - references: [ - { - id: '1', - name: 'action_0', - type: 'action', - }, - ], - }); - - return alertsClient.create(options); - } - - test('ensures user is authorised to create this type of alert under the consumer', async () => { - const data = getMockData({ - alertTypeId: 'myType', - consumer: 'myApp', - }); - - await tryToExecuteOperation({ data }); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'create'); - }); - - test('throws when user is not authorised to create this type of alert', async () => { - const data = getMockData({ - alertTypeId: 'myType', - consumer: 'myApp', - }); - - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to create a "myType" alert for "myApp"`) - ); - - await expect(tryToExecuteOperation({ data })).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to create a "myType" alert for "myApp"]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'create'); - }); - }); - - test('creates an alert', async () => { - const data = getMockData(); - const createdAttributes = { - ...data, - alertTypeId: '123', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - createdAt: '2019-02-12T21:01:22.479Z', - createdBy: 'elastic', - updatedBy: 'elastic', - muteAll: false, - mutedInstanceIds: [], - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - }; - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: createdAttributes, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - taskManager.schedule.mockResolvedValueOnce({ - id: 'task-123', - taskType: 'alerting:123', - scheduledAt: new Date(), - attempts: 1, - status: TaskStatus.Idle, - runAt: new Date(), - startedAt: null, - retryAt: null, - state: {}, - params: {}, - ownerId: null, - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - ...createdAttributes, - scheduledTaskId: 'task-123', - }, - references: [ - { - id: '1', - name: 'action_0', - type: 'action', - }, - ], - }); - const result = await alertsClient.create({ data }); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('123', 'bar', 'create'); - expect(result).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionTypeId": "test", - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "123", - "consumer": "bar", - "createdAt": 2019-02-12T21:01:22.479Z, - "createdBy": "elastic", - "enabled": true, - "id": "1", - "muteAll": false, - "mutedInstanceIds": Array [], - "name": "abc", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "scheduledTaskId": "task-123", - "tags": Array [ - "foo", - ], - "throttle": null, - "updatedAt": 2019-02-12T21:01:22.479Z, - "updatedBy": "elastic", - } - `); - expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionRef": "action_0", - "actionTypeId": "test", - "group": "default", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "123", - "apiKey": null, - "apiKeyOwner": null, - "consumer": "bar", - "createdAt": "2019-02-12T21:01:22.479Z", - "createdBy": "elastic", - "enabled": true, - "executionStatus": Object { - "error": null, - "lastExecutionDate": "2019-02-12T21:01:22.479Z", - "status": "pending", - }, - "meta": Object { - "versionApiKeyLastmodified": "v7.10.0", - }, - "muteAll": false, - "mutedInstanceIds": Array [], - "name": "abc", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "tags": Array [ - "foo", - ], - "throttle": null, - "updatedBy": "elastic", - } - `); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][2]).toMatchInlineSnapshot(` - Object { - "references": Array [ - Object { - "id": "1", - "name": "action_0", - "type": "action", - }, - ], - } - `); - expect(taskManager.schedule).toHaveBeenCalledTimes(1); - expect(taskManager.schedule.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "params": Object { - "alertId": "1", - "spaceId": "default", - }, - "scope": Array [ - "alerting", - ], - "state": Object { - "alertInstances": Object {}, - "alertTypeState": Object {}, - "previousStartedAt": null, - }, - "taskType": "alerting:123", - }, - ] - `); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.update.mock.calls[0]).toHaveLength(3); - expect(unsecuredSavedObjectsClient.update.mock.calls[0][0]).toEqual('alert'); - expect(unsecuredSavedObjectsClient.update.mock.calls[0][1]).toEqual('1'); - expect(unsecuredSavedObjectsClient.update.mock.calls[0][2]).toMatchInlineSnapshot(` - Object { - "scheduledTaskId": "task-123", - } - `); - }); - - test('creates an alert with multiple actions', async () => { - const data = getMockData({ - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - { - group: 'default', - id: '2', - params: { - foo: true, - }, - }, - ], - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: '123', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - createdAt: new Date().toISOString(), - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - { - group: 'default', - actionRef: 'action_1', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - { - group: 'default', - actionRef: 'action_2', - actionTypeId: 'test2', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - { - name: 'action_1', - type: 'action', - id: '1', - }, - { - name: 'action_2', - type: 'action', - id: '2', - }, - ], - }); - taskManager.schedule.mockResolvedValueOnce({ - id: 'task-123', - taskType: 'alerting:123', - scheduledAt: new Date(), - attempts: 1, - status: TaskStatus.Idle, - runAt: new Date(), - startedAt: null, - retryAt: null, - state: {}, - params: {}, - ownerId: null, - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [], - scheduledTaskId: 'task-123', - }, - references: [], - }); - const result = await alertsClient.create({ data }); - expect(result).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionTypeId": "test", - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - Object { - "actionTypeId": "test", - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - Object { - "actionTypeId": "test2", - "group": "default", - "id": "2", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "123", - "createdAt": 2019-02-12T21:01:22.479Z, - "id": "1", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "scheduledTaskId": "task-123", - "updatedAt": 2019-02-12T21:01:22.479Z, - } - `); - }); - - test('creates a disabled alert', async () => { - const data = getMockData({ enabled: false }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - enabled: false, - alertTypeId: '123', - schedule: { interval: 10000 }, - params: { - bar: true, - }, - createdAt: new Date().toISOString(), - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - const result = await alertsClient.create({ data }); - expect(result).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionTypeId": "test", - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "123", - "createdAt": 2019-02-12T21:01:22.479Z, - "enabled": false, - "id": "1", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": 10000, - }, - "updatedAt": 2019-02-12T21:01:22.479Z, - } - `); - expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); - expect(taskManager.schedule).toHaveBeenCalledTimes(0); - }); - - test('should trim alert name when creating API key', async () => { - const data = getMockData({ name: ' my alert name ' }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - enabled: false, - name: ' my alert name ', - alertTypeId: '123', - schedule: { interval: 10000 }, - params: { - bar: true, - }, - createdAt: new Date().toISOString(), - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - taskManager.schedule.mockResolvedValueOnce({ - id: 'task-123', - taskType: 'alerting:123', - scheduledAt: new Date(), - attempts: 1, - status: TaskStatus.Idle, - runAt: new Date(), - startedAt: null, - retryAt: null, - state: {}, - params: {}, - ownerId: null, - }); - - await alertsClient.create({ data }); - expect(alertsClientParams.createAPIKey).toHaveBeenCalledWith('Alerting: 123/my alert name'); - }); - - test('should validate params', async () => { - const data = getMockData(); - alertTypeRegistry.get.mockReturnValue({ - id: '123', - name: 'Test', - actionGroups: [ - { - id: 'default', - name: 'Default', - }, - ], - defaultActionGroupId: 'default', - validate: { - params: schema.object({ - param1: schema.string(), - threshold: schema.number({ min: 0, max: 1 }), - }), - }, - async executor() {}, - producer: 'alerts', - }); - await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( - `"params invalid: [param1]: expected value of type [string] but got [undefined]"` - ); - }); - - test('throws error if loading actions fails', async () => { - const data = getMockData(); - const actionsClient = actionsClientMock.create(); - actionsClient.getBulk.mockRejectedValueOnce(new Error('Test Error')); - alertsClientParams.getActionsClient.mockResolvedValue(actionsClient); - await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Test Error"` - ); - expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); - expect(taskManager.schedule).not.toHaveBeenCalled(); - }); - - test('throws error and invalidates API key when create saved object fails', async () => { - const data = getMockData(); - alertsClientParams.createAPIKey.mockResolvedValueOnce({ - apiKeysEnabled: true, - result: { id: '123', name: '123', api_key: 'abc' }, - }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockRejectedValueOnce(new Error('Test failure')); - await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Test failure"` - ); - expect(taskManager.schedule).not.toHaveBeenCalled(); - expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); - }); - - test('attempts to remove saved object if scheduling failed', async () => { - const data = getMockData(); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: '123', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - taskManager.schedule.mockRejectedValueOnce(new Error('Test failure')); - unsecuredSavedObjectsClient.delete.mockResolvedValueOnce({}); - await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Test failure"` - ); - expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.delete.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "alert", - "1", - ] - `); - }); - - test('returns task manager error if cleanup fails, logs to console', async () => { - const data = getMockData(); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: '123', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - taskManager.schedule.mockRejectedValueOnce(new Error('Task manager error')); - unsecuredSavedObjectsClient.delete.mockRejectedValueOnce( - new Error('Saved object delete error') - ); - await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Task manager error"` - ); - expect(alertsClientParams.logger.error).toHaveBeenCalledWith( - 'Failed to cleanup alert "1" after scheduling task failed. Error: Saved object delete error' - ); - }); - - test('throws an error if alert type not registerd', async () => { - const data = getMockData(); - alertTypeRegistry.get.mockImplementation(() => { - throw new Error('Invalid type'); - }); - await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Invalid type"` - ); - }); - - test('calls the API key function', async () => { - const data = getMockData(); - alertsClientParams.createAPIKey.mockResolvedValueOnce({ - apiKeysEnabled: true, - result: { id: '123', name: '123', api_key: 'abc' }, - }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: '123', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - taskManager.schedule.mockResolvedValueOnce({ - id: 'task-123', - taskType: 'alerting:123', - scheduledAt: new Date(), - attempts: 1, - status: TaskStatus.Idle, - runAt: new Date(), - startedAt: null, - retryAt: null, - state: {}, - params: {}, - ownerId: null, - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [], - scheduledTaskId: 'task-123', - }, - references: [ - { - id: '1', - name: 'action_0', - type: 'action', - }, - ], - }); - await alertsClient.create({ data }); - - expect(alertsClientParams.createAPIKey).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', - { - actions: [ - { - actionRef: 'action_0', - group: 'default', - actionTypeId: 'test', - params: { foo: true }, - }, - ], - alertTypeId: '123', - consumer: 'bar', - name: 'abc', - params: { bar: true }, - apiKey: Buffer.from('123:abc').toString('base64'), - apiKeyOwner: 'elastic', - createdBy: 'elastic', - createdAt: '2019-02-12T21:01:22.479Z', - updatedBy: 'elastic', - enabled: true, - meta: { - versionApiKeyLastmodified: 'v7.10.0', - }, - schedule: { interval: '10s' }, - throttle: null, - muteAll: false, - mutedInstanceIds: [], - tags: ['foo'], - executionStatus: { - lastExecutionDate: '2019-02-12T21:01:22.479Z', - status: 'pending', - error: null, - }, - }, - { - references: [ - { - id: '1', - name: 'action_0', - type: 'action', - }, - ], - } - ); - }); - - test(`doesn't create API key for disabled alerts`, async () => { - const data = getMockData({ enabled: false }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: '123', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - taskManager.schedule.mockResolvedValueOnce({ - id: 'task-123', - taskType: 'alerting:123', - scheduledAt: new Date(), - attempts: 1, - status: TaskStatus.Idle, - runAt: new Date(), - startedAt: null, - retryAt: null, - state: {}, - params: {}, - ownerId: null, - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [], - scheduledTaskId: 'task-123', - }, - references: [ - { - id: '1', - name: 'action_0', - type: 'action', - }, - ], - }); - await alertsClient.create({ data }); - - expect(alertsClientParams.createAPIKey).not.toHaveBeenCalled(); - expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', - { - actions: [ - { - actionRef: 'action_0', - group: 'default', - actionTypeId: 'test', - params: { foo: true }, - }, - ], - alertTypeId: '123', - consumer: 'bar', - name: 'abc', - params: { bar: true }, - apiKey: null, - apiKeyOwner: null, - createdBy: 'elastic', - createdAt: '2019-02-12T21:01:22.479Z', - updatedBy: 'elastic', - enabled: false, - meta: { - versionApiKeyLastmodified: 'v7.10.0', - }, - schedule: { interval: '10s' }, - throttle: null, - muteAll: false, - mutedInstanceIds: [], - tags: ['foo'], - executionStatus: { - lastExecutionDate: '2019-02-12T21:01:22.479Z', - status: 'pending', - error: null, - }, - }, - { - references: [ - { - id: '1', - name: 'action_0', - type: 'action', - }, - ], - } - ); - }); -}); - -describe('enable()', () => { - let alertsClient: AlertsClient; - const existingAlert = { - id: '1', - type: 'alert', - attributes: { - consumer: 'myApp', - schedule: { interval: '10s' }, - alertTypeId: 'myType', - enabled: false, - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - }, - version: '123', - references: [], - }; - - beforeEach(() => { - alertsClient = new AlertsClient(alertsClientParams); - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingAlert); - unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); - alertsClientParams.createAPIKey.mockResolvedValue({ - apiKeysEnabled: false, - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - ...existingAlert, - attributes: { - ...existingAlert.attributes, - enabled: true, - apiKey: null, - apiKeyOwner: null, - updatedBy: 'elastic', - }, - }); - taskManager.schedule.mockResolvedValue({ - id: 'task-123', - scheduledAt: new Date(), - attempts: 0, - status: TaskStatus.Idle, - runAt: new Date(), - state: {}, - params: {}, - taskType: '', - startedAt: null, - retryAt: null, - ownerId: null, - }); - }); - - describe('authorization', () => { - beforeEach(() => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingAlert); - unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); - alertsClientParams.createAPIKey.mockResolvedValue({ - apiKeysEnabled: false, - }); - taskManager.schedule.mockResolvedValue({ - id: 'task-123', - scheduledAt: new Date(), - attempts: 0, - status: TaskStatus.Idle, - runAt: new Date(), - state: {}, - params: {}, - taskType: '', - startedAt: null, - retryAt: null, - ownerId: null, - }); - }); - - test('ensures user is authorised to enable this type of alert under the consumer', async () => { - await alertsClient.enable({ id: '1' }); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'enable'); - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); - }); - - test('throws when user is not authorised to enable this type of alert', async () => { - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to enable a "myType" alert for "myApp"`) - ); - - await expect(alertsClient.enable({ id: '1' })).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to enable a "myType" alert for "myApp"]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'enable'); - }); - }); - - test('enables an alert', async () => { - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - ...existingAlert, - attributes: { - ...existingAlert.attributes, - enabled: true, - apiKey: null, - apiKeyOwner: null, - updatedBy: 'elastic', - }, - }); - - await alertsClient.enable({ id: '1' }); - expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); - expect(alertsClientParams.createAPIKey).toHaveBeenCalled(); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', - '1', - { - schedule: { interval: '10s' }, - alertTypeId: 'myType', - consumer: 'myApp', - enabled: true, - meta: { - versionApiKeyLastmodified: kibanaVersion, - }, - updatedBy: 'elastic', - apiKey: null, - apiKeyOwner: null, - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - }, - { - version: '123', - } - ); - expect(taskManager.schedule).toHaveBeenCalledWith({ - taskType: `alerting:myType`, - params: { - alertId: '1', - spaceId: 'default', - }, - state: { - alertInstances: {}, - alertTypeState: {}, - previousStartedAt: null, - }, - scope: ['alerting'], - }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith('alert', '1', { - scheduledTaskId: 'task-123', - }); - }); - - test('invalidates API key if ever one existed prior to updating', async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue({ - ...existingAlert, - attributes: { - ...existingAlert.attributes, - apiKey: Buffer.from('123:abc').toString('base64'), - }, - }); - - await alertsClient.enable({ id: '1' }); - expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); - expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); - }); - - test(`doesn't enable already enabled alerts`, async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({ - ...existingAlert, - attributes: { - ...existingAlert.attributes, - enabled: true, - }, - }); - - await alertsClient.enable({ id: '1' }); - expect(alertsClientParams.getUserName).not.toHaveBeenCalled(); - expect(alertsClientParams.createAPIKey).not.toHaveBeenCalled(); - expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); - expect(taskManager.schedule).not.toHaveBeenCalled(); - }); - - test('sets API key when createAPIKey returns one', async () => { - alertsClientParams.createAPIKey.mockResolvedValueOnce({ - apiKeysEnabled: true, - result: { id: '123', name: '123', api_key: 'abc' }, - }); - - await alertsClient.enable({ id: '1' }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', - '1', - { - schedule: { interval: '10s' }, - alertTypeId: 'myType', - consumer: 'myApp', - enabled: true, - meta: { - versionApiKeyLastmodified: kibanaVersion, - }, - apiKey: Buffer.from('123:abc').toString('base64'), - apiKeyOwner: 'elastic', - updatedBy: 'elastic', - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - }, - { - version: '123', - } - ); - }); - - test('falls back when failing to getDecryptedAsInternalUser', async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); - - await alertsClient.enable({ id: '1' }); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); - expect(alertsClientParams.logger.error).toHaveBeenCalledWith( - 'enable(): Failed to load API key to invalidate on alert 1: Fail' - ); - }); - - test('throws error when failing to load the saved object using SOC', async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); - unsecuredSavedObjectsClient.get.mockRejectedValueOnce(new Error('Fail to get')); - - await expect(alertsClient.enable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Fail to get"` - ); - expect(alertsClientParams.getUserName).not.toHaveBeenCalled(); - expect(alertsClientParams.createAPIKey).not.toHaveBeenCalled(); - expect(unsecuredSavedObjectsClient.update).not.toHaveBeenCalled(); - expect(taskManager.schedule).not.toHaveBeenCalled(); - }); - - test('throws error when failing to update the first time', async () => { - alertsClientParams.createAPIKey.mockResolvedValueOnce({ - apiKeysEnabled: true, - result: { id: '123', name: '123', api_key: 'abc' }, - }); - unsecuredSavedObjectsClient.update.mockReset(); - unsecuredSavedObjectsClient.update.mockRejectedValueOnce(new Error('Fail to update')); - - await expect(alertsClient.enable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Fail to update"` - ); - expect(alertsClientParams.getUserName).toHaveBeenCalled(); - expect(alertsClientParams.createAPIKey).toHaveBeenCalled(); - expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(1); - expect(taskManager.schedule).not.toHaveBeenCalled(); - }); - - test('throws error when failing to update the second time', async () => { - unsecuredSavedObjectsClient.update.mockReset(); - unsecuredSavedObjectsClient.update.mockResolvedValueOnce({ - ...existingAlert, - attributes: { - ...existingAlert.attributes, - enabled: true, - }, - }); - unsecuredSavedObjectsClient.update.mockRejectedValueOnce( - new Error('Fail to update second time') - ); - - await expect(alertsClient.enable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Fail to update second time"` - ); - expect(alertsClientParams.getUserName).toHaveBeenCalled(); - expect(alertsClientParams.createAPIKey).toHaveBeenCalled(); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(2); - expect(taskManager.schedule).toHaveBeenCalled(); - }); - - test('throws error when failing to schedule task', async () => { - taskManager.schedule.mockRejectedValueOnce(new Error('Fail to schedule')); - - await expect(alertsClient.enable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Fail to schedule"` - ); - expect(alertsClientParams.getUserName).toHaveBeenCalled(); - expect(alertsClientParams.createAPIKey).toHaveBeenCalled(); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalled(); - }); -}); - -describe('disable()', () => { - let alertsClient: AlertsClient; - const existingAlert = { - id: '1', - type: 'alert', - attributes: { - consumer: 'myApp', - schedule: { interval: '10s' }, - alertTypeId: 'myType', - enabled: true, - scheduledTaskId: 'task-123', - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - }, - version: '123', - references: [], - }; - const existingDecryptedAlert = { - ...existingAlert, - attributes: { - ...existingAlert.attributes, - apiKey: Buffer.from('123:abc').toString('base64'), - }, - version: '123', - references: [], - }; - - beforeEach(() => { - alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingDecryptedAlert); - }); - - describe('authorization', () => { - test('ensures user is authorised to disable this type of alert under the consumer', async () => { - await alertsClient.disable({ id: '1' }); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'disable'); - }); - - test('throws when user is not authorised to disable this type of alert', async () => { - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to disable a "myType" alert for "myApp"`) - ); - - await expect(alertsClient.disable({ id: '1' })).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to disable a "myType" alert for "myApp"]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'disable'); - }); - }); - - test('disables an alert', async () => { - await alertsClient.disable({ id: '1' }); - expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', - '1', - { - consumer: 'myApp', - schedule: { interval: '10s' }, - alertTypeId: 'myType', - enabled: false, - meta: { - versionApiKeyLastmodified: kibanaVersion, - }, - scheduledTaskId: null, - apiKey: null, - apiKeyOwner: null, - updatedBy: 'elastic', - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - }, - { - version: '123', - } - ); - expect(taskManager.remove).toHaveBeenCalledWith('task-123'); - expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); - }); - - test('falls back when getDecryptedAsInternalUser throws an error', async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValueOnce(new Error('Fail')); - - await alertsClient.disable({ id: '1' }); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', - '1', - { - consumer: 'myApp', - schedule: { interval: '10s' }, - alertTypeId: 'myType', - enabled: false, - meta: { - versionApiKeyLastmodified: kibanaVersion, - }, - scheduledTaskId: null, - apiKey: null, - apiKeyOwner: null, - updatedBy: 'elastic', - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - }, - { - version: '123', - } - ); - expect(taskManager.remove).toHaveBeenCalledWith('task-123'); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); - }); - - test(`doesn't disable already disabled alerts`, async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({ - ...existingDecryptedAlert, - attributes: { - ...existingDecryptedAlert.attributes, - actions: [], - enabled: false, - }, - }); - - await alertsClient.disable({ id: '1' }); - expect(unsecuredSavedObjectsClient.update).not.toHaveBeenCalled(); - expect(taskManager.remove).not.toHaveBeenCalled(); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); - }); - - test(`doesn't invalidate when no API key is used`, async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce(existingAlert); - - await alertsClient.disable({ id: '1' }); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); - }); - - test('swallows error when failing to load decrypted saved object', async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValueOnce(new Error('Fail')); - - await alertsClient.disable({ id: '1' }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalled(); - expect(taskManager.remove).toHaveBeenCalled(); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); - expect(alertsClientParams.logger.error).toHaveBeenCalledWith( - 'disable(): Failed to load API key to invalidate on alert 1: Fail' - ); - }); - - test('throws when unsecuredSavedObjectsClient update fails', async () => { - unsecuredSavedObjectsClient.update.mockRejectedValueOnce(new Error('Failed to update')); - - await expect(alertsClient.disable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Failed to update"` - ); - }); - - test('swallows error when invalidate API key throws', async () => { - alertsClientParams.invalidateAPIKey.mockRejectedValueOnce(new Error('Fail')); - - await alertsClient.disable({ id: '1' }); - expect(alertsClientParams.logger.error).toHaveBeenCalledWith( - 'Failed to invalidate API Key: Fail' - ); - }); - - test('throws when failing to remove task from task manager', async () => { - taskManager.remove.mockRejectedValueOnce(new Error('Failed to remove task')); - - await expect(alertsClient.disable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Failed to remove task"` - ); - }); -}); - -describe('muteAll()', () => { - test('mutes an alert', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - muteAll: false, - }, - references: [], - version: '123', - }); - - await alertsClient.muteAll({ id: '1' }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', - '1', - { - muteAll: true, - mutedInstanceIds: [], - updatedBy: 'elastic', - }, - { - version: '123', - } - ); - }); - - describe('authorization', () => { - beforeEach(() => { - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - consumer: 'myApp', - schedule: { interval: '10s' }, - alertTypeId: 'myType', - apiKey: null, - apiKeyOwner: null, - enabled: false, - scheduledTaskId: null, - updatedBy: 'elastic', - muteAll: false, - }, - references: [], - }); - }); - - test('ensures user is authorised to muteAll this type of alert under the consumer', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - await alertsClient.muteAll({ id: '1' }); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'muteAll'); - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); - }); - - test('throws when user is not authorised to muteAll this type of alert', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to muteAll a "myType" alert for "myApp"`) - ); - - await expect(alertsClient.muteAll({ id: '1' })).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to muteAll a "myType" alert for "myApp"]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'muteAll'); - }); - }); -}); - -describe('unmuteAll()', () => { - test('unmutes an alert', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - muteAll: true, - }, - references: [], - version: '123', - }); - - await alertsClient.unmuteAll({ id: '1' }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', - '1', - { - muteAll: false, - mutedInstanceIds: [], - updatedBy: 'elastic', - }, - { - version: '123', - } - ); - }); - - describe('authorization', () => { - beforeEach(() => { - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - consumer: 'myApp', - schedule: { interval: '10s' }, - alertTypeId: 'myType', - apiKey: null, - apiKeyOwner: null, - enabled: false, - scheduledTaskId: null, - updatedBy: 'elastic', - muteAll: false, - }, - references: [], - }); - }); - - test('ensures user is authorised to unmuteAll this type of alert under the consumer', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - await alertsClient.unmuteAll({ id: '1' }); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'unmuteAll'); - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); - }); - - test('throws when user is not authorised to unmuteAll this type of alert', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to unmuteAll a "myType" alert for "myApp"`) - ); - - await expect(alertsClient.unmuteAll({ id: '1' })).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to unmuteAll a "myType" alert for "myApp"]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'unmuteAll'); - }); - }); -}); - -describe('muteInstance()', () => { - test('mutes an alert instance', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [], - schedule: { interval: '10s' }, - alertTypeId: '2', - enabled: true, - scheduledTaskId: 'task-123', - mutedInstanceIds: [], - }, - version: '123', - references: [], - }); - - await alertsClient.muteInstance({ alertId: '1', alertInstanceId: '2' }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', - '1', - { - mutedInstanceIds: ['2'], - updatedBy: 'elastic', - }, - { - version: '123', - } - ); - }); - - test('skips muting when alert instance already muted', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [], - schedule: { interval: '10s' }, - alertTypeId: '2', - enabled: true, - scheduledTaskId: 'task-123', - mutedInstanceIds: ['2'], - }, - references: [], - }); - - await alertsClient.muteInstance({ alertId: '1', alertInstanceId: '2' }); - expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); - }); - - test('skips muting when alert is muted', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [], - schedule: { interval: '10s' }, - alertTypeId: '2', - enabled: true, - scheduledTaskId: 'task-123', - mutedInstanceIds: [], - muteAll: true, - }, - references: [], - }); - - await alertsClient.muteInstance({ alertId: '1', alertInstanceId: '2' }); - expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); - }); - - describe('authorization', () => { - beforeEach(() => { - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - schedule: { interval: '10s' }, - alertTypeId: 'myType', - consumer: 'myApp', - enabled: true, - scheduledTaskId: 'task-123', - mutedInstanceIds: [], - }, - version: '123', - references: [], - }); - }); - - test('ensures user is authorised to muteInstance this type of alert under the consumer', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - await alertsClient.muteInstance({ alertId: '1', alertInstanceId: '2' }); - - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith( - 'myType', - 'myApp', - 'muteInstance' - ); - }); - - test('throws when user is not authorised to muteInstance this type of alert', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to muteInstance a "myType" alert for "myApp"`) - ); - - await expect( - alertsClient.muteInstance({ alertId: '1', alertInstanceId: '2' }) - ).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to muteInstance a "myType" alert for "myApp"]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith( - 'myType', - 'myApp', - 'muteInstance' - ); - }); - }); -}); - -describe('unmuteInstance()', () => { - test('unmutes an alert instance', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [], - schedule: { interval: '10s' }, - alertTypeId: '2', - enabled: true, - scheduledTaskId: 'task-123', - mutedInstanceIds: ['2'], - }, - version: '123', - references: [], - }); - - await alertsClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', - '1', - { - mutedInstanceIds: [], - updatedBy: 'elastic', - }, - { version: '123' } - ); - }); - - test('skips unmuting when alert instance not muted', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [], - schedule: { interval: '10s' }, - alertTypeId: '2', - enabled: true, - scheduledTaskId: 'task-123', - mutedInstanceIds: [], - }, - references: [], - }); - - await alertsClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' }); - expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); - }); - - test('skips unmuting when alert is muted', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [], - schedule: { interval: '10s' }, - alertTypeId: '2', - enabled: true, - scheduledTaskId: 'task-123', - mutedInstanceIds: [], - muteAll: true, - }, - references: [], - }); - - await alertsClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' }); - expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); - }); - - describe('authorization', () => { - beforeEach(() => { - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - alertTypeId: 'myType', - consumer: 'myApp', - schedule: { interval: '10s' }, - enabled: true, - scheduledTaskId: 'task-123', - mutedInstanceIds: ['2'], - }, - version: '123', - references: [], - }); - }); - - test('ensures user is authorised to unmuteInstance this type of alert under the consumer', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - await alertsClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' }); - - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith( - 'myType', - 'myApp', - 'unmuteInstance' - ); - }); - - test('throws when user is not authorised to unmuteInstance this type of alert', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to unmuteInstance a "myType" alert for "myApp"`) - ); - - await expect( - alertsClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' }) - ).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to unmuteInstance a "myType" alert for "myApp"]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith( - 'myType', - 'myApp', - 'unmuteInstance' - ); - }); - }); -}); - -describe('get()', () => { - test('calls saved objects client with given params', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: '123', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - createdAt: new Date().toISOString(), - actions: [ - { - group: 'default', - actionRef: 'action_0', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - const result = await alertsClient.get({ id: '1' }); - expect(result).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "123", - "createdAt": 2019-02-12T21:01:22.479Z, - "id": "1", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "updatedAt": 2019-02-12T21:01:22.479Z, - } - `); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "alert", - "1", - ] - `); - }); - - test(`throws an error when references aren't found`, async () => { - const alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: '123', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - params: { - foo: true, - }, - }, - ], - }, - references: [], - }); - await expect(alertsClient.get({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Action reference \\"action_0\\" not found in alert id: 1"` - ); - }); - - describe('authorization', () => { - beforeEach(() => { - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: 'myType', - consumer: 'myApp', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - }); - - test('ensures user is authorised to get this type of alert under the consumer', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - await alertsClient.get({ id: '1' }); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'get'); - }); - - test('throws when user is not authorised to get this type of alert', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to get a "myType" alert for "myApp"`) - ); - - await expect(alertsClient.get({ id: '1' })).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to get a "myType" alert for "myApp"]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'get'); - }); - }); -}); - -describe('getAlertState()', () => { - test('calls saved objects client with given params', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: '123', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - - taskManager.get.mockResolvedValueOnce({ - id: '1', - taskType: 'alerting:123', - scheduledAt: new Date(), - attempts: 1, - status: TaskStatus.Idle, - runAt: new Date(), - startedAt: null, - retryAt: null, - state: {}, - params: {}, - ownerId: null, - }); - - await alertsClient.getAlertState({ id: '1' }); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "alert", - "1", - ] - `); - }); - - test('gets the underlying task from TaskManager', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - - const scheduledTaskId = 'task-123'; - - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: '123', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - params: { - foo: true, - }, - }, - ], - enabled: true, - scheduledTaskId, - mutedInstanceIds: [], - muteAll: true, - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - - taskManager.get.mockResolvedValueOnce({ - id: scheduledTaskId, - taskType: 'alerting:123', - scheduledAt: new Date(), - attempts: 1, - status: TaskStatus.Idle, - runAt: new Date(), - startedAt: null, - retryAt: null, - state: {}, - params: { - alertId: '1', - }, - ownerId: null, - }); - - await alertsClient.getAlertState({ id: '1' }); - expect(taskManager.get).toHaveBeenCalledTimes(1); - expect(taskManager.get).toHaveBeenCalledWith(scheduledTaskId); - }); - - describe('authorization', () => { - beforeEach(() => { - unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: 'myType', - consumer: 'myApp', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - - taskManager.get.mockResolvedValueOnce({ - id: '1', - taskType: 'alerting:123', - scheduledAt: new Date(), - attempts: 1, - status: TaskStatus.Idle, - runAt: new Date(), - startedAt: null, - retryAt: null, - state: {}, - params: {}, - ownerId: null, - }); - }); - - test('ensures user is authorised to get this type of alert under the consumer', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - await alertsClient.getAlertState({ id: '1' }); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith( - 'myType', - 'myApp', - 'getAlertState' - ); - }); - - test('throws when user is not authorised to getAlertState this type of alert', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - // `get` check - authorization.ensureAuthorized.mockResolvedValueOnce(); - // `getAlertState` check - authorization.ensureAuthorized.mockRejectedValueOnce( - new Error(`Unauthorized to getAlertState a "myType" alert for "myApp"`) - ); - - await expect(alertsClient.getAlertState({ id: '1' })).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to getAlertState a "myType" alert for "myApp"]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith( - 'myType', - 'myApp', - 'getAlertState' - ); - }); - }); -}); - -const AlertInstanceSummaryFindEventsResult: QueryEventsBySavedObjectResult = { - page: 1, - per_page: 10000, - total: 0, - data: [], -}; - -const AlertInstanceSummaryIntervalSeconds = 1; - -const BaseAlertInstanceSummarySavedObject: SavedObject = { - id: '1', - type: 'alert', - attributes: { - enabled: true, - name: 'alert-name', - tags: ['tag-1', 'tag-2'], - alertTypeId: '123', - consumer: 'alert-consumer', - schedule: { interval: `${AlertInstanceSummaryIntervalSeconds}s` }, - actions: [], - params: {}, - createdBy: null, - updatedBy: null, - createdAt: mockedDateString, - apiKey: null, - apiKeyOwner: null, - throttle: null, - muteAll: false, - mutedInstanceIds: [], - executionStatus: { - status: 'unknown', - lastExecutionDate: '2020-08-20T19:23:38Z', - error: null, - }, - }, - references: [], -}; - -function getAlertInstanceSummarySavedObject( - attributes: Partial = {} -): SavedObject { - return { - ...BaseAlertInstanceSummarySavedObject, - attributes: { ...BaseAlertInstanceSummarySavedObject.attributes, ...attributes }, - }; -} - -describe('getAlertInstanceSummary()', () => { - let alertsClient: AlertsClient; - - beforeEach(() => { - alertsClient = new AlertsClient(alertsClientParams); - }); - - test('runs as expected with some event log data', async () => { - const alertSO = getAlertInstanceSummarySavedObject({ - mutedInstanceIds: ['instance-muted-no-activity'], - }); - unsecuredSavedObjectsClient.get.mockResolvedValueOnce(alertSO); - - const eventsFactory = new EventsFactory(mockedDateString); - const events = eventsFactory - .addExecute() - .addNewInstance('instance-currently-active') - .addNewInstance('instance-previously-active') - .addActiveInstance('instance-currently-active') - .addActiveInstance('instance-previously-active') - .advanceTime(10000) - .addExecute() - .addResolvedInstance('instance-previously-active') - .addActiveInstance('instance-currently-active') - .getEvents(); - const eventsResult = { - ...AlertInstanceSummaryFindEventsResult, - total: events.length, - data: events, - }; - eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(eventsResult); - - const dateStart = new Date(Date.now() - 60 * 1000).toISOString(); - - const result = await alertsClient.getAlertInstanceSummary({ id: '1', dateStart }); - expect(result).toMatchInlineSnapshot(` - Object { - "alertTypeId": "123", - "consumer": "alert-consumer", - "enabled": true, - "errorMessages": Array [], - "id": "1", - "instances": Object { - "instance-currently-active": Object { - "activeStartDate": "2019-02-12T21:01:22.479Z", - "muted": false, - "status": "Active", - }, - "instance-muted-no-activity": Object { - "activeStartDate": undefined, - "muted": true, - "status": "OK", - }, - "instance-previously-active": Object { - "activeStartDate": undefined, - "muted": false, - "status": "OK", - }, - }, - "lastRun": "2019-02-12T21:01:32.479Z", - "muteAll": false, - "name": "alert-name", - "status": "Active", - "statusEndDate": "2019-02-12T21:01:22.479Z", - "statusStartDate": "2019-02-12T21:00:22.479Z", - "tags": Array [ - "tag-1", - "tag-2", - ], - "throttle": null, - } - `); - }); - - // Further tests don't check the result of `getAlertInstanceSummary()`, as the result - // is just the result from the `alertInstanceSummaryFromEventLog()`, which itself - // has a complete set of tests. These tests just make sure the data gets - // sent into `getAlertInstanceSummary()` as appropriate. - - test('calls saved objects and event log client with default params', async () => { - unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject()); - eventLogClient.findEventsBySavedObject.mockResolvedValueOnce( - AlertInstanceSummaryFindEventsResult - ); - - await alertsClient.getAlertInstanceSummary({ id: '1' }); - - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); - expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1); - expect(eventLogClient.findEventsBySavedObject.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "alert", - "1", - Object { - "end": "2019-02-12T21:01:22.479Z", - "page": 1, - "per_page": 10000, - "sort_order": "desc", - "start": "2019-02-12T21:00:22.479Z", - }, - ] - `); - // calculate the expected start/end date for one test - const { start, end } = eventLogClient.findEventsBySavedObject.mock.calls[0][2]!; - expect(end).toBe(mockedDateString); - - const startMillis = Date.parse(start!); - const endMillis = Date.parse(end!); - const expectedDuration = 60 * AlertInstanceSummaryIntervalSeconds * 1000; - expect(endMillis - startMillis).toBeGreaterThan(expectedDuration - 2); - expect(endMillis - startMillis).toBeLessThan(expectedDuration + 2); - }); - - test('calls event log client with start date', async () => { - unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject()); - eventLogClient.findEventsBySavedObject.mockResolvedValueOnce( - AlertInstanceSummaryFindEventsResult - ); - - const dateStart = new Date( - Date.now() - 60 * AlertInstanceSummaryIntervalSeconds * 1000 - ).toISOString(); - await alertsClient.getAlertInstanceSummary({ id: '1', dateStart }); - - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); - expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1); - const { start, end } = eventLogClient.findEventsBySavedObject.mock.calls[0][2]!; - - expect({ start, end }).toMatchInlineSnapshot(` - Object { - "end": "2019-02-12T21:01:22.479Z", - "start": "2019-02-12T21:00:22.479Z", - } - `); - }); - - test('calls event log client with relative start date', async () => { - unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject()); - eventLogClient.findEventsBySavedObject.mockResolvedValueOnce( - AlertInstanceSummaryFindEventsResult - ); - - const dateStart = '2m'; - await alertsClient.getAlertInstanceSummary({ id: '1', dateStart }); - - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); - expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1); - const { start, end } = eventLogClient.findEventsBySavedObject.mock.calls[0][2]!; - - expect({ start, end }).toMatchInlineSnapshot(` - Object { - "end": "2019-02-12T21:01:22.479Z", - "start": "2019-02-12T20:59:22.479Z", - } - `); - }); - - test('invalid start date throws an error', async () => { - unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject()); - eventLogClient.findEventsBySavedObject.mockResolvedValueOnce( - AlertInstanceSummaryFindEventsResult - ); - - const dateStart = 'ain"t no way this will get parsed as a date'; - expect( - alertsClient.getAlertInstanceSummary({ id: '1', dateStart }) - ).rejects.toMatchInlineSnapshot( - `[Error: Invalid date for parameter dateStart: "ain"t no way this will get parsed as a date"]` - ); - }); - - test('saved object get throws an error', async () => { - unsecuredSavedObjectsClient.get.mockRejectedValueOnce(new Error('OMG!')); - eventLogClient.findEventsBySavedObject.mockResolvedValueOnce( - AlertInstanceSummaryFindEventsResult - ); - - expect(alertsClient.getAlertInstanceSummary({ id: '1' })).rejects.toMatchInlineSnapshot( - `[Error: OMG!]` - ); - }); - - test('findEvents throws an error', async () => { - unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject()); - eventLogClient.findEventsBySavedObject.mockRejectedValueOnce(new Error('OMG 2!')); - - // error eaten but logged - await alertsClient.getAlertInstanceSummary({ id: '1' }); - }); -}); - -describe('find()', () => { - const listedTypes = new Set([ - { - actionGroups: [], - actionVariables: undefined, - defaultActionGroupId: 'default', - id: 'myType', - name: 'myType', - producer: 'myApp', - }, - ]); - beforeEach(() => { - authorization.getFindAuthorizationFilter.mockResolvedValue({ - ensureAlertTypeIsAuthorized() {}, - logSuccessfulAuthorization() {}, - }); - unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ - total: 1, - per_page: 10, - page: 1, - saved_objects: [ - { - id: '1', - type: 'alert', - attributes: { - alertTypeId: 'myType', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - createdAt: new Date().toISOString(), - actions: [ - { - group: 'default', - actionRef: 'action_0', - params: { - foo: true, - }, - }, - ], - }, - score: 1, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }, - ], - }); - alertTypeRegistry.list.mockReturnValue(listedTypes); - authorization.filterByAlertTypeAuthorization.mockResolvedValue( - new Set([ - { - id: 'myType', - name: 'Test', - actionGroups: [{ id: 'default', name: 'Default' }], - defaultActionGroupId: 'default', - producer: 'alerts', - authorizedConsumers: { - myApp: { read: true, all: true }, - }, - }, - ]) - ); - }); - - test('calls saved objects client with given params', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - const result = await alertsClient.find({ options: {} }); - expect(result).toMatchInlineSnapshot(` - Object { - "data": Array [ - Object { - "actions": Array [ - Object { - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "myType", - "createdAt": 2019-02-12T21:01:22.479Z, - "id": "1", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "updatedAt": 2019-02-12T21:01:22.479Z, - }, - ], - "page": 1, - "perPage": 10, - "total": 1, - } - `); - expect(unsecuredSavedObjectsClient.find).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.find.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "fields": undefined, - "filter": undefined, - "type": "alert", - }, - ] - `); - }); - - describe('authorization', () => { - test('ensures user is query filter types down to those the user is authorized to find', async () => { - const filter = esKuery.fromKueryExpression( - '((alert.attributes.alertTypeId:myType and alert.attributes.consumer:myApp) or (alert.attributes.alertTypeId:myOtherType and alert.attributes.consumer:myApp) or (alert.attributes.alertTypeId:myOtherType and alert.attributes.consumer:myOtherApp))' - ); - authorization.getFindAuthorizationFilter.mockResolvedValue({ - filter, - ensureAlertTypeIsAuthorized() {}, - logSuccessfulAuthorization() {}, - }); - - const alertsClient = new AlertsClient(alertsClientParams); - await alertsClient.find({ options: { filter: 'someTerm' } }); - - const [options] = unsecuredSavedObjectsClient.find.mock.calls[0]; - expect(options.filter).toEqual( - nodeTypes.function.buildNode('and', [esKuery.fromKueryExpression('someTerm'), filter]) - ); - expect(authorization.getFindAuthorizationFilter).toHaveBeenCalledTimes(1); - }); - - test('throws if user is not authorized to find any types', async () => { - const alertsClient = new AlertsClient(alertsClientParams); - authorization.getFindAuthorizationFilter.mockRejectedValue(new Error('not authorized')); - await expect(alertsClient.find({ options: {} })).rejects.toThrowErrorMatchingInlineSnapshot( - `"not authorized"` - ); - }); - - test('ensures authorization even when the fields required to authorize are omitted from the find', async () => { - const ensureAlertTypeIsAuthorized = jest.fn(); - const logSuccessfulAuthorization = jest.fn(); - authorization.getFindAuthorizationFilter.mockResolvedValue({ - ensureAlertTypeIsAuthorized, - logSuccessfulAuthorization, - }); - - unsecuredSavedObjectsClient.find.mockReset(); - unsecuredSavedObjectsClient.find.mockResolvedValue({ - total: 1, - per_page: 10, - page: 1, - saved_objects: [ - { - id: '1', - type: 'alert', - attributes: { - actions: [], - alertTypeId: 'myType', - consumer: 'myApp', - tags: ['myTag'], - }, - score: 1, - references: [], - }, - ], - }); - - const alertsClient = new AlertsClient(alertsClientParams); - expect(await alertsClient.find({ options: { fields: ['tags'] } })).toMatchInlineSnapshot(` - Object { - "data": Array [ - Object { - "actions": Array [], - "id": "1", - "schedule": undefined, - "tags": Array [ - "myTag", - ], - }, - ], - "page": 1, - "perPage": 10, - "total": 1, - } - `); - - expect(unsecuredSavedObjectsClient.find).toHaveBeenCalledWith({ - fields: ['tags', 'alertTypeId', 'consumer'], - type: 'alert', - }); - expect(ensureAlertTypeIsAuthorized).toHaveBeenCalledWith('myType', 'myApp'); - expect(logSuccessfulAuthorization).toHaveBeenCalled(); - }); - }); -}); - -describe('delete()', () => { - let alertsClient: AlertsClient; - const existingAlert = { - id: '1', - type: 'alert', - attributes: { - alertTypeId: 'myType', - consumer: 'myApp', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - scheduledTaskId: 'task-123', - actions: [ - { - group: 'default', - actionTypeId: '.no-op', - actionRef: 'action_0', - params: { - foo: true, - }, - }, - ], - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }; - const existingDecryptedAlert = { - ...existingAlert, - attributes: { - ...existingAlert.attributes, - apiKey: Buffer.from('123:abc').toString('base64'), - }, - }; - - beforeEach(() => { - alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); - unsecuredSavedObjectsClient.delete.mockResolvedValue({ - success: true, - }); - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingDecryptedAlert); - }); - - test('successfully removes an alert', async () => { - const result = await alertsClient.delete({ id: '1' }); - expect(result).toEqual({ success: true }); - expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledWith('alert', '1'); - expect(taskManager.remove).toHaveBeenCalledWith('task-123'); - expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); - expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - }); - - test('falls back to SOC.get when getDecryptedAsInternalUser throws an error', async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); - - const result = await alertsClient.delete({ id: '1' }); - expect(result).toEqual({ success: true }); - expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledWith('alert', '1'); - expect(taskManager.remove).toHaveBeenCalledWith('task-123'); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); - expect(alertsClientParams.logger.error).toHaveBeenCalledWith( - 'delete(): Failed to load API key to invalidate on alert 1: Fail' - ); - }); - - test(`doesn't remove a task when scheduledTaskId is null`, async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue({ - ...existingDecryptedAlert, - attributes: { - ...existingDecryptedAlert.attributes, - scheduledTaskId: null, - }, - }); - - await alertsClient.delete({ id: '1' }); - expect(taskManager.remove).not.toHaveBeenCalled(); - }); - - test(`doesn't invalidate API key when apiKey is null`, async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue({ - ...existingAlert, - attributes: { - ...existingAlert.attributes, - apiKey: null, - }, - }); - - await alertsClient.delete({ id: '1' }); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); - }); - - test('swallows error when invalidate API key throws', async () => { - alertsClientParams.invalidateAPIKey.mockRejectedValueOnce(new Error('Fail')); - - await alertsClient.delete({ id: '1' }); - expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); - expect(alertsClientParams.logger.error).toHaveBeenCalledWith( - 'Failed to invalidate API Key: Fail' - ); - }); - - test('swallows error when getDecryptedAsInternalUser throws an error', async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); - - await alertsClient.delete({ id: '1' }); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); - expect(alertsClientParams.logger.error).toHaveBeenCalledWith( - 'delete(): Failed to load API key to invalidate on alert 1: Fail' - ); - }); - - test('throws error when unsecuredSavedObjectsClient.get throws an error', async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); - unsecuredSavedObjectsClient.get.mockRejectedValue(new Error('SOC Fail')); - - await expect(alertsClient.delete({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( - `"SOC Fail"` - ); - }); - - test('throws error when taskManager.remove throws an error', async () => { - taskManager.remove.mockRejectedValue(new Error('TM Fail')); - - await expect(alertsClient.delete({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( - `"TM Fail"` - ); - }); - - describe('authorization', () => { - test('ensures user is authorised to delete this type of alert under the consumer', async () => { - await alertsClient.delete({ id: '1' }); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'delete'); - }); - - test('throws when user is not authorised to delete this type of alert', async () => { - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to delete a "myType" alert for "myApp"`) - ); - - await expect(alertsClient.delete({ id: '1' })).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to delete a "myType" alert for "myApp"]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'delete'); - }); - }); -}); - -describe('update()', () => { - let alertsClient: AlertsClient; - const existingAlert = { - id: '1', - type: 'alert', - attributes: { - enabled: true, - tags: ['foo'], - alertTypeId: 'myType', - schedule: { interval: '10s' }, - consumer: 'myApp', - scheduledTaskId: 'task-123', - params: {}, - throttle: null, - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - }, - references: [], - version: '123', - }; - const existingDecryptedAlert = { - ...existingAlert, - attributes: { - ...existingAlert.attributes, - apiKey: Buffer.from('123:abc').toString('base64'), - }, - }; - - beforeEach(() => { - alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingDecryptedAlert); - alertTypeRegistry.get.mockReturnValue({ - id: 'myType', - name: 'Test', - actionGroups: [{ id: 'default', name: 'Default' }], - defaultActionGroupId: 'default', - async executor() {}, - producer: 'alerts', - }); - }); - - test('updates given parameters', async () => { - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - enabled: true, - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - { - group: 'default', - actionRef: 'action_1', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - { - group: 'default', - actionRef: 'action_2', - actionTypeId: 'test2', - params: { - foo: true, - }, - }, - ], - scheduledTaskId: 'task-123', - createdAt: new Date().toISOString(), - }, - updated_at: new Date().toISOString(), - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - { - name: 'action_1', - type: 'action', - id: '1', - }, - { - name: 'action_2', - type: 'action', - id: '2', - }, - ], - }); - const result = await alertsClient.update({ - id: '1', - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: null, - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - { - group: 'default', - id: '2', - params: { - foo: true, - }, - }, - ], - }, - }); - expect(result).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionTypeId": "test", - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - Object { - "actionTypeId": "test", - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - Object { - "actionTypeId": "test2", - "group": "default", - "id": "2", - "params": Object { - "foo": true, - }, - }, - ], - "createdAt": 2019-02-12T21:01:22.479Z, - "enabled": true, - "id": "1", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "scheduledTaskId": "task-123", - "updatedAt": 2019-02-12T21:01:22.479Z, - } - `); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); - expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionRef": "action_0", - "actionTypeId": "test", - "group": "default", - "params": Object { - "foo": true, - }, - }, - Object { - "actionRef": "action_1", - "actionTypeId": "test", - "group": "default", - "params": Object { - "foo": true, - }, - }, - Object { - "actionRef": "action_2", - "actionTypeId": "test2", - "group": "default", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "myType", - "apiKey": null, - "apiKeyOwner": null, - "consumer": "myApp", - "enabled": true, - "meta": Object { - "versionApiKeyLastmodified": "v7.10.0", - }, - "name": "abc", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "scheduledTaskId": "task-123", - "tags": Array [ - "foo", - ], - "throttle": null, - "updatedBy": "elastic", - } - `); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][2]).toMatchInlineSnapshot(` - Object { - "id": "1", - "overwrite": true, - "references": Array [ - Object { - "id": "1", - "name": "action_0", - "type": "action", - }, - Object { - "id": "1", - "name": "action_1", - "type": "action", - }, - Object { - "id": "2", - "name": "action_2", - "type": "action", - }, - ], - "version": "123", - } - `); - }); - - it('calls the createApiKey function', async () => { - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - alertsClientParams.createAPIKey.mockResolvedValueOnce({ - apiKeysEnabled: true, - result: { id: '123', name: '123', api_key: 'abc' }, - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - enabled: true, - schedule: { interval: '10s' }, - params: { - bar: true, - }, - createdAt: new Date().toISOString(), - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - apiKey: Buffer.from('123:abc').toString('base64'), - scheduledTaskId: 'task-123', - }, - updated_at: new Date().toISOString(), - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - const result = await alertsClient.update({ - id: '1', - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: '5m', - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - ], - }, - }); - expect(result).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionTypeId": "test", - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - ], - "apiKey": "MTIzOmFiYw==", - "createdAt": 2019-02-12T21:01:22.479Z, - "enabled": true, - "id": "1", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "scheduledTaskId": "task-123", - "updatedAt": 2019-02-12T21:01:22.479Z, - } - `); - expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionRef": "action_0", - "actionTypeId": "test", - "group": "default", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "myType", - "apiKey": "MTIzOmFiYw==", - "apiKeyOwner": "elastic", - "consumer": "myApp", - "enabled": true, - "meta": Object { - "versionApiKeyLastmodified": "v7.10.0", - }, - "name": "abc", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "scheduledTaskId": "task-123", - "tags": Array [ - "foo", - ], - "throttle": "5m", - "updatedBy": "elastic", - } - `); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][2]).toMatchInlineSnapshot(` - Object { - "id": "1", - "overwrite": true, - "references": Array [ - Object { - "id": "1", - "name": "action_0", - "type": "action", - }, - ], - "version": "123", - } - `); - }); - - it(`doesn't call the createAPIKey function when alert is disabled`, async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue({ - ...existingDecryptedAlert, - attributes: { - ...existingDecryptedAlert.attributes, - enabled: false, - }, - }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - enabled: false, - schedule: { interval: '10s' }, - params: { - bar: true, - }, - createdAt: new Date().toISOString(), - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - scheduledTaskId: 'task-123', - apiKey: null, - }, - updated_at: new Date().toISOString(), - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - const result = await alertsClient.update({ - id: '1', - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: '5m', - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - ], - }, - }); - expect(alertsClientParams.createAPIKey).not.toHaveBeenCalled(); - expect(result).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionTypeId": "test", - "group": "default", - "id": "1", - "params": Object { - "foo": true, - }, - }, - ], - "apiKey": null, - "createdAt": 2019-02-12T21:01:22.479Z, - "enabled": false, - "id": "1", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "scheduledTaskId": "task-123", - "updatedAt": 2019-02-12T21:01:22.479Z, - } - `); - expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); - expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` - Object { - "actions": Array [ - Object { - "actionRef": "action_0", - "actionTypeId": "test", - "group": "default", - "params": Object { - "foo": true, - }, - }, - ], - "alertTypeId": "myType", - "apiKey": null, - "apiKeyOwner": null, - "consumer": "myApp", - "enabled": false, - "meta": Object { - "versionApiKeyLastmodified": "v7.10.0", - }, - "name": "abc", - "params": Object { - "bar": true, - }, - "schedule": Object { - "interval": "10s", - }, - "scheduledTaskId": "task-123", - "tags": Array [ - "foo", - ], - "throttle": "5m", - "updatedBy": "elastic", - } - `); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][2]).toMatchInlineSnapshot(` - Object { - "id": "1", - "overwrite": true, - "references": Array [ - Object { - "id": "1", - "name": "action_0", - "type": "action", - }, - ], - "version": "123", - } - `); - }); - - it('should validate params', async () => { - alertTypeRegistry.get.mockReturnValueOnce({ - id: '123', - name: 'Test', - actionGroups: [{ id: 'default', name: 'Default' }], - defaultActionGroupId: 'default', - validate: { - params: schema.object({ - param1: schema.string(), - }), - }, - async executor() {}, - producer: 'alerts', - }); - await expect( - alertsClient.update({ - id: '1', - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: null, - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - ], - }, - }) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"params invalid: [param1]: expected value of type [string] but got [undefined]"` - ); - }); - - it('should trim alert name in the API key name', async () => { - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - enabled: false, - name: ' my alert name ', - schedule: { interval: '10s' }, - params: { - bar: true, - }, - createdAt: new Date().toISOString(), - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - scheduledTaskId: 'task-123', - apiKey: null, - }, - updated_at: new Date().toISOString(), - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - await alertsClient.update({ - id: '1', - data: { - ...existingAlert.attributes, - name: ' my alert name ', - }, - }); - - expect(alertsClientParams.createAPIKey).toHaveBeenCalledWith('Alerting: myType/my alert name'); - }); - - it('swallows error when invalidate API key throws', async () => { - alertsClientParams.invalidateAPIKey.mockRejectedValueOnce(new Error('Fail')); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - enabled: true, - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - scheduledTaskId: 'task-123', - }, - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - ], - }); - await alertsClient.update({ - id: '1', - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: null, - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - ], - }, - }); - expect(alertsClientParams.logger.error).toHaveBeenCalledWith( - 'Failed to invalidate API Key: Fail' - ); - }); - - it('swallows error when getDecryptedAsInternalUser throws', async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - { - id: '2', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test2', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - enabled: true, - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - { - group: 'default', - actionRef: 'action_1', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - { - group: 'default', - actionRef: 'action_2', - actionTypeId: 'test2', - params: { - foo: true, - }, - }, - ], - scheduledTaskId: 'task-123', - createdAt: new Date().toISOString(), - }, - updated_at: new Date().toISOString(), - references: [ - { - name: 'action_0', - type: 'action', - id: '1', - }, - { - name: 'action_1', - type: 'action', - id: '1', - }, - { - name: 'action_2', - type: 'action', - id: '2', - }, - ], - }); - await alertsClient.update({ - id: '1', - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: '5m', - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - { - group: 'default', - id: '2', - params: { - foo: true, - }, - }, - ], - }, - }); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); - expect(alertsClientParams.logger.error).toHaveBeenCalledWith( - 'update(): Failed to load API key to invalidate on alert 1: Fail' - ); - }); - - test('throws when unsecuredSavedObjectsClient update fails and invalidates newly created API key', async () => { - alertsClientParams.createAPIKey.mockResolvedValueOnce({ - apiKeysEnabled: true, - result: { id: '234', name: '234', api_key: 'abc' }, - }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - unsecuredSavedObjectsClient.create.mockRejectedValue(new Error('Fail')); - await expect( - alertsClient.update({ - id: '1', - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: null, - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - ], - }, - }) - ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalledWith({ id: '123' }); - expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '234' }); - }); - - describe('updating an alert schedule', () => { - function mockApiCalls( - alertId: string, - taskId: string, - currentSchedule: IntervalSchedule, - updatedSchedule: IntervalSchedule - ) { - // mock return values from deps - alertTypeRegistry.get.mockReturnValueOnce({ - id: '123', - name: 'Test', - actionGroups: [{ id: 'default', name: 'Default' }], - defaultActionGroupId: 'default', - async executor() {}, - producer: 'alerts', - }); - unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ - saved_objects: [ - { - id: '1', - type: 'action', - attributes: { - actions: [], - actionTypeId: 'test', - }, - references: [], - }, - ], - }); - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({ - id: alertId, - type: 'alert', - attributes: { - actions: [], - enabled: true, - alertTypeId: '123', - schedule: currentSchedule, - scheduledTaskId: 'task-123', - }, - references: [], - version: '123', - }); - - taskManager.schedule.mockResolvedValueOnce({ - id: taskId, - taskType: 'alerting:123', - scheduledAt: new Date(), - attempts: 1, - status: TaskStatus.Idle, - runAt: new Date(), - startedAt: null, - retryAt: null, - state: {}, - params: {}, - ownerId: null, - }); - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: alertId, - type: 'alert', - attributes: { - enabled: true, - schedule: updatedSchedule, - actions: [ - { - group: 'default', - actionRef: 'action_0', - actionTypeId: 'test', - params: { - foo: true, - }, - }, - ], - scheduledTaskId: taskId, - }, - references: [ - { - name: 'action_0', - type: 'action', - id: alertId, - }, - ], - }); - - taskManager.runNow.mockReturnValueOnce(Promise.resolve({ id: alertId })); - } - - test('updating the alert schedule should rerun the task immediately', async () => { - const alertId = uuid.v4(); - const taskId = uuid.v4(); - - mockApiCalls(alertId, taskId, { interval: '60m' }, { interval: '10s' }); - - await alertsClient.update({ - id: alertId, - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: null, - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - ], - }, - }); - - expect(taskManager.runNow).toHaveBeenCalledWith(taskId); - }); - - test('updating the alert without changing the schedule should not rerun the task', async () => { - const alertId = uuid.v4(); - const taskId = uuid.v4(); - - mockApiCalls(alertId, taskId, { interval: '10s' }, { interval: '10s' }); - - await alertsClient.update({ - id: alertId, - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: null, - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - ], - }, - }); - - expect(taskManager.runNow).not.toHaveBeenCalled(); - }); - - test('updating the alert should not wait for the rerun the task to complete', async () => { - const alertId = uuid.v4(); - const taskId = uuid.v4(); - - mockApiCalls(alertId, taskId, { interval: '10s' }, { interval: '30s' }); - - const resolveAfterAlertUpdatedCompletes = resolvable<{ id: string }>(); - - taskManager.runNow.mockReset(); - taskManager.runNow.mockReturnValue(resolveAfterAlertUpdatedCompletes); - - await alertsClient.update({ - id: alertId, - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: null, - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - ], - }, - }); - - expect(taskManager.runNow).toHaveBeenCalled(); - resolveAfterAlertUpdatedCompletes.resolve({ id: alertId }); - }); - - test('logs when the rerun of an alerts underlying task fails', async () => { - const alertId = uuid.v4(); - const taskId = uuid.v4(); - - mockApiCalls(alertId, taskId, { interval: '10s' }, { interval: '30s' }); - - taskManager.runNow.mockReset(); - taskManager.runNow.mockRejectedValue(new Error('Failed to run alert')); - - await alertsClient.update({ - id: alertId, - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: null, - actions: [ - { - group: 'default', - id: '1', - params: { - foo: true, - }, - }, - ], - }, - }); - - expect(taskManager.runNow).toHaveBeenCalled(); - - expect(alertsClientParams.logger.error).toHaveBeenCalledWith( - `Alert update failed to run its underlying task. TaskManager runNow failed with Error: Failed to run alert` - ); - }); - }); - - describe('authorization', () => { - beforeEach(() => { - unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ - id: '1', - type: 'alert', - attributes: { - alertTypeId: 'myType', - consumer: 'myApp', - enabled: true, - schedule: { interval: '10s' }, - params: { - bar: true, - }, - actions: [], - scheduledTaskId: 'task-123', - createdAt: new Date().toISOString(), - }, - updated_at: new Date().toISOString(), - references: [], - }); - }); - - test('ensures user is authorised to update this type of alert under the consumer', async () => { - await alertsClient.update({ - id: '1', - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: null, - actions: [], - }, - }); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'update'); - }); - - test('throws when user is not authorised to update this type of alert', async () => { - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to update a "myType" alert for "myApp"`) - ); - - await expect( - alertsClient.update({ - id: '1', - data: { - schedule: { interval: '10s' }, - name: 'abc', - tags: ['foo'], - params: { - bar: true, - }, - throttle: null, - actions: [], - }, - }) - ).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to update a "myType" alert for "myApp"]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'update'); - }); - }); -}); - -describe('updateApiKey()', () => { - let alertsClient: AlertsClient; - const existingAlert = { - id: '1', - type: 'alert', - attributes: { - schedule: { interval: '10s' }, - alertTypeId: 'myType', - consumer: 'myApp', - enabled: true, - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - }, - version: '123', - references: [], - }; - const existingEncryptedAlert = { - ...existingAlert, - attributes: { - ...existingAlert.attributes, - apiKey: Buffer.from('123:abc').toString('base64'), - }, - }; - - beforeEach(() => { - alertsClient = new AlertsClient(alertsClientParams); - unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); - encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingEncryptedAlert); - alertsClientParams.createAPIKey.mockResolvedValueOnce({ - apiKeysEnabled: true, - result: { id: '234', name: '123', api_key: 'abc' }, - }); - }); - - test('updates the API key for the alert', async () => { - await alertsClient.updateApiKey({ id: '1' }); - expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', - '1', - { - schedule: { interval: '10s' }, - alertTypeId: 'myType', - consumer: 'myApp', - enabled: true, - apiKey: Buffer.from('234:abc').toString('base64'), - apiKeyOwner: 'elastic', - updatedBy: 'elastic', - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - meta: { - versionApiKeyLastmodified: kibanaVersion, - }, - }, - { version: '123' } - ); - expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); - }); - - test('falls back to SOC when getDecryptedAsInternalUser throws an error', async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValueOnce(new Error('Fail')); - - await alertsClient.updateApiKey({ id: '1' }); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', - '1', - { - schedule: { interval: '10s' }, - alertTypeId: 'myType', - consumer: 'myApp', - enabled: true, - apiKey: Buffer.from('234:abc').toString('base64'), - apiKeyOwner: 'elastic', - updatedBy: 'elastic', - actions: [ - { - group: 'default', - id: '1', - actionTypeId: '1', - actionRef: '1', - params: { - foo: true, - }, - }, - ], - meta: { - versionApiKeyLastmodified: kibanaVersion, - }, - }, - { version: '123' } - ); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); - }); - - test('swallows error when invalidate API key throws', async () => { - alertsClientParams.invalidateAPIKey.mockRejectedValue(new Error('Fail')); - - await alertsClient.updateApiKey({ id: '1' }); - expect(alertsClientParams.logger.error).toHaveBeenCalledWith( - 'Failed to invalidate API Key: Fail' - ); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalled(); - }); - - test('swallows error when getting decrypted object throws', async () => { - encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValueOnce(new Error('Fail')); - - await alertsClient.updateApiKey({ id: '1' }); - expect(alertsClientParams.logger.error).toHaveBeenCalledWith( - 'updateApiKey(): Failed to load API key to invalidate on alert 1: Fail' - ); - expect(unsecuredSavedObjectsClient.update).toHaveBeenCalled(); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); - }); - - test('throws when unsecuredSavedObjectsClient update fails and invalidates newly created API key', async () => { - alertsClientParams.createAPIKey.mockResolvedValueOnce({ - apiKeysEnabled: true, - result: { id: '234', name: '234', api_key: 'abc' }, - }); - unsecuredSavedObjectsClient.update.mockRejectedValueOnce(new Error('Fail')); - - await expect(alertsClient.updateApiKey({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( - `"Fail"` - ); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalledWith({ id: '123' }); - expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '234' }); - }); - - describe('authorization', () => { - test('ensures user is authorised to updateApiKey this type of alert under the consumer', async () => { - await alertsClient.updateApiKey({ id: '1' }); - - expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); - expect(authorization.ensureAuthorized).toHaveBeenCalledWith( - 'myType', - 'myApp', - 'updateApiKey' - ); - }); - - test('throws when user is not authorised to updateApiKey this type of alert', async () => { - authorization.ensureAuthorized.mockRejectedValue( - new Error(`Unauthorized to updateApiKey a "myType" alert for "myApp"`) - ); - - await expect(alertsClient.updateApiKey({ id: '1' })).rejects.toMatchInlineSnapshot( - `[Error: Unauthorized to updateApiKey a "myType" alert for "myApp"]` - ); - - expect(authorization.ensureAuthorized).toHaveBeenCalledWith( - 'myType', - 'myApp', - 'updateApiKey' - ); - }); - }); -}); - -describe('listAlertTypes', () => { - let alertsClient: AlertsClient; - const alertingAlertType = { - actionGroups: [], - actionVariables: undefined, - defaultActionGroupId: 'default', - id: 'alertingAlertType', - name: 'alertingAlertType', - producer: 'alerts', - }; - const myAppAlertType = { - actionGroups: [], - actionVariables: undefined, - defaultActionGroupId: 'default', - id: 'myAppAlertType', - name: 'myAppAlertType', - producer: 'myApp', - }; - const setOfAlertTypes = new Set([myAppAlertType, alertingAlertType]); - - const authorizedConsumers = { - alerts: { read: true, all: true }, - myApp: { read: true, all: true }, - myOtherApp: { read: true, all: true }, - }; - - beforeEach(() => { - alertsClient = new AlertsClient(alertsClientParams); - }); - - test('should return a list of AlertTypes that exist in the registry', async () => { - alertTypeRegistry.list.mockReturnValue(setOfAlertTypes); - authorization.filterByAlertTypeAuthorization.mockResolvedValue( - new Set([ - { ...myAppAlertType, authorizedConsumers }, - { ...alertingAlertType, authorizedConsumers }, - ]) - ); - expect(await alertsClient.listAlertTypes()).toEqual( - new Set([ - { ...myAppAlertType, authorizedConsumers }, - { ...alertingAlertType, authorizedConsumers }, - ]) - ); - }); - - describe('authorization', () => { - const listedTypes = new Set([ - { - actionGroups: [], - actionVariables: undefined, - defaultActionGroupId: 'default', - id: 'myType', - name: 'myType', - producer: 'myApp', - }, - { - id: 'myOtherType', - name: 'Test', - actionGroups: [{ id: 'default', name: 'Default' }], - defaultActionGroupId: 'default', - producer: 'alerts', - }, - ]); - beforeEach(() => { - alertTypeRegistry.list.mockReturnValue(listedTypes); - }); - - test('should return a list of AlertTypes that exist in the registry only if the user is authorised to get them', async () => { - const authorizedTypes = new Set([ - { - id: 'myType', - name: 'Test', - actionGroups: [{ id: 'default', name: 'Default' }], - defaultActionGroupId: 'default', - producer: 'alerts', - authorizedConsumers: { - myApp: { read: true, all: true }, - }, - }, - ]); - authorization.filterByAlertTypeAuthorization.mockResolvedValue(authorizedTypes); - - expect(await alertsClient.listAlertTypes()).toEqual(authorizedTypes); - }); - }); -}); diff --git a/x-pack/plugins/alerts/server/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts similarity index 96% rename from x-pack/plugins/alerts/server/alerts_client.ts rename to x-pack/plugins/alerts/server/alerts_client/alerts_client.ts index bd278d39c6229..ef3a9e42b983f 100644 --- a/x-pack/plugins/alerts/server/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts @@ -14,8 +14,8 @@ import { SavedObject, PluginInitializerContext, } from 'src/core/server'; -import { esKuery } from '../../../../src/plugins/data/server'; -import { ActionsClient, ActionsAuthorization } from '../../actions/server'; +import { esKuery } from '../../../../../src/plugins/data/server'; +import { ActionsClient, ActionsAuthorization } from '../../../actions/server'; import { Alert, PartialAlert, @@ -27,26 +27,26 @@ import { SanitizedAlert, AlertTaskState, AlertInstanceSummary, -} from './types'; -import { validateAlertTypeParams, alertExecutionStatusFromRaw } from './lib'; +} from '../types'; +import { validateAlertTypeParams, alertExecutionStatusFromRaw } from '../lib'; import { InvalidateAPIKeyParams, GrantAPIKeyResult as SecurityPluginGrantAPIKeyResult, InvalidateAPIKeyResult as SecurityPluginInvalidateAPIKeyResult, -} from '../../security/server'; -import { EncryptedSavedObjectsClient } from '../../encrypted_saved_objects/server'; -import { TaskManagerStartContract } from '../../task_manager/server'; -import { taskInstanceToAlertTaskInstance } from './task_runner/alert_task_instance'; -import { deleteTaskIfItExists } from './lib/delete_task_if_it_exists'; -import { RegistryAlertType } from './alert_type_registry'; -import { AlertsAuthorization, WriteOperations, ReadOperations, and } from './authorization'; -import { IEventLogClient } from '../../../plugins/event_log/server'; -import { parseIsoOrRelativeDate } from './lib/iso_or_relative_date'; -import { alertInstanceSummaryFromEventLog } from './lib/alert_instance_summary_from_event_log'; -import { IEvent } from '../../event_log/server'; -import { parseDuration } from '../common/parse_duration'; -import { retryIfConflicts } from './lib/retry_if_conflicts'; -import { partiallyUpdateAlert } from './saved_objects'; +} from '../../../security/server'; +import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/server'; +import { TaskManagerStartContract } from '../../../task_manager/server'; +import { taskInstanceToAlertTaskInstance } from '../task_runner/alert_task_instance'; +import { deleteTaskIfItExists } from '../lib/delete_task_if_it_exists'; +import { RegistryAlertType } from '../alert_type_registry'; +import { AlertsAuthorization, WriteOperations, ReadOperations, and } from '../authorization'; +import { IEventLogClient } from '../../../../plugins/event_log/server'; +import { parseIsoOrRelativeDate } from '../lib/iso_or_relative_date'; +import { alertInstanceSummaryFromEventLog } from '../lib/alert_instance_summary_from_event_log'; +import { IEvent } from '../../../event_log/server'; +import { parseDuration } from '../../common/parse_duration'; +import { retryIfConflicts } from '../lib/retry_if_conflicts'; +import { partiallyUpdateAlert } from '../saved_objects'; export interface RegistryAlertTypeWithAuth extends RegistryAlertType { authorizedConsumers: string[]; diff --git a/x-pack/plugins/alerts/server/alerts_client/index.ts b/x-pack/plugins/alerts/server/alerts_client/index.ts new file mode 100644 index 0000000000000..e40076a29fffd --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/index.ts @@ -0,0 +1,6 @@ +/* + * 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 './alerts_client'; diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts new file mode 100644 index 0000000000000..65a30d1750149 --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts @@ -0,0 +1,1097 @@ +/* + * 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 { AlertsClient, ConstructorOptions, CreateOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsClientMock, actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { TaskStatus } from '../../../../task_manager/server'; +import { getBeforeSetup, setGlobalDate } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +setGlobalDate(); + +function getMockData(overwrites: Record = {}): CreateOptions['data'] { + return { + enabled: true, + name: 'abc', + tags: ['foo'], + alertTypeId: '123', + consumer: 'bar', + schedule: { interval: '10s' }, + throttle: null, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + ...overwrites, + }; +} + +describe('create()', () => { + let alertsClient: AlertsClient; + + beforeEach(() => { + alertsClient = new AlertsClient(alertsClientParams); + }); + + describe('authorization', () => { + function tryToExecuteOperation(options: CreateOptions): Promise { + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: '2019-02-12T21:01:22.479Z', + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + taskManager.schedule.mockResolvedValueOnce({ + id: 'task-123', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: {}, + ownerId: null, + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [], + scheduledTaskId: 'task-123', + }, + references: [ + { + id: '1', + name: 'action_0', + type: 'action', + }, + ], + }); + + return alertsClient.create(options); + } + + test('ensures user is authorised to create this type of alert under the consumer', async () => { + const data = getMockData({ + alertTypeId: 'myType', + consumer: 'myApp', + }); + + await tryToExecuteOperation({ data }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'create'); + }); + + test('throws when user is not authorised to create this type of alert', async () => { + const data = getMockData({ + alertTypeId: 'myType', + consumer: 'myApp', + }); + + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to create a "myType" alert for "myApp"`) + ); + + await expect(tryToExecuteOperation({ data })).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to create a "myType" alert for "myApp"]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'create'); + }); + }); + + test('creates an alert', async () => { + const data = getMockData(); + const createdAttributes = { + ...data, + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: '2019-02-12T21:01:22.479Z', + createdBy: 'elastic', + updatedBy: 'elastic', + muteAll: false, + mutedInstanceIds: [], + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + }; + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: createdAttributes, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + taskManager.schedule.mockResolvedValueOnce({ + id: 'task-123', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: {}, + ownerId: null, + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + ...createdAttributes, + scheduledTaskId: 'task-123', + }, + references: [ + { + id: '1', + name: 'action_0', + type: 'action', + }, + ], + }); + const result = await alertsClient.create({ data }); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('123', 'bar', 'create'); + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "consumer": "bar", + "createdAt": 2019-02-12T21:01:22.479Z, + "createdBy": "elastic", + "enabled": true, + "id": "1", + "muteAll": false, + "mutedInstanceIds": Array [], + "name": "abc", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "scheduledTaskId": "task-123", + "tags": Array [ + "foo", + ], + "throttle": null, + "updatedAt": 2019-02-12T21:01:22.479Z, + "updatedBy": "elastic", + } + `); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionRef": "action_0", + "actionTypeId": "test", + "group": "default", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "apiKey": null, + "apiKeyOwner": null, + "consumer": "bar", + "createdAt": "2019-02-12T21:01:22.479Z", + "createdBy": "elastic", + "enabled": true, + "executionStatus": Object { + "error": null, + "lastExecutionDate": "2019-02-12T21:01:22.479Z", + "status": "pending", + }, + "meta": Object { + "versionApiKeyLastmodified": "v7.10.0", + }, + "muteAll": false, + "mutedInstanceIds": Array [], + "name": "abc", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "tags": Array [ + "foo", + ], + "throttle": null, + "updatedBy": "elastic", + } + `); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][2]).toMatchInlineSnapshot(` + Object { + "references": Array [ + Object { + "id": "1", + "name": "action_0", + "type": "action", + }, + ], + } + `); + expect(taskManager.schedule).toHaveBeenCalledTimes(1); + expect(taskManager.schedule.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "params": Object { + "alertId": "1", + "spaceId": "default", + }, + "scope": Array [ + "alerting", + ], + "state": Object { + "alertInstances": Object {}, + "alertTypeState": Object {}, + "previousStartedAt": null, + }, + "taskType": "alerting:123", + }, + ] + `); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.update.mock.calls[0]).toHaveLength(3); + expect(unsecuredSavedObjectsClient.update.mock.calls[0][0]).toEqual('alert'); + expect(unsecuredSavedObjectsClient.update.mock.calls[0][1]).toEqual('1'); + expect(unsecuredSavedObjectsClient.update.mock.calls[0][2]).toMatchInlineSnapshot(` + Object { + "scheduledTaskId": "task-123", + } + `); + }); + + test('creates an alert with multiple actions', async () => { + const data = getMockData({ + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + { + group: 'default', + id: '2', + params: { + foo: true, + }, + }, + ], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + { + group: 'default', + actionRef: 'action_1', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + { + group: 'default', + actionRef: 'action_2', + actionTypeId: 'test2', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + { + name: 'action_1', + type: 'action', + id: '1', + }, + { + name: 'action_2', + type: 'action', + id: '2', + }, + ], + }); + taskManager.schedule.mockResolvedValueOnce({ + id: 'task-123', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: {}, + ownerId: null, + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [], + scheduledTaskId: 'task-123', + }, + references: [], + }); + const result = await alertsClient.create({ data }); + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + Object { + "actionTypeId": "test2", + "group": "default", + "id": "2", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "createdAt": 2019-02-12T21:01:22.479Z, + "id": "1", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "scheduledTaskId": "task-123", + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); + }); + + test('creates a disabled alert', async () => { + const data = getMockData({ enabled: false }); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + enabled: false, + alertTypeId: '123', + schedule: { interval: 10000 }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + const result = await alertsClient.create({ data }); + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "createdAt": 2019-02-12T21:01:22.479Z, + "enabled": false, + "id": "1", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": 10000, + }, + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); + expect(taskManager.schedule).toHaveBeenCalledTimes(0); + }); + + test('should trim alert name when creating API key', async () => { + const data = getMockData({ name: ' my alert name ' }); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + enabled: false, + name: ' my alert name ', + alertTypeId: '123', + schedule: { interval: 10000 }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + taskManager.schedule.mockResolvedValueOnce({ + id: 'task-123', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: {}, + ownerId: null, + }); + + await alertsClient.create({ data }); + expect(alertsClientParams.createAPIKey).toHaveBeenCalledWith('Alerting: 123/my alert name'); + }); + + test('should validate params', async () => { + const data = getMockData(); + alertTypeRegistry.get.mockReturnValue({ + id: '123', + name: 'Test', + actionGroups: [ + { + id: 'default', + name: 'Default', + }, + ], + defaultActionGroupId: 'default', + validate: { + params: schema.object({ + param1: schema.string(), + threshold: schema.number({ min: 0, max: 1 }), + }), + }, + async executor() {}, + producer: 'alerts', + }); + await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( + `"params invalid: [param1]: expected value of type [string] but got [undefined]"` + ); + }); + + test('throws error if loading actions fails', async () => { + const data = getMockData(); + const actionsClient = actionsClientMock.create(); + actionsClient.getBulk.mockRejectedValueOnce(new Error('Test Error')); + alertsClientParams.getActionsClient.mockResolvedValue(actionsClient); + await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Test Error"` + ); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + expect(taskManager.schedule).not.toHaveBeenCalled(); + }); + + test('throws error and invalidates API key when create saved object fails', async () => { + const data = getMockData(); + alertsClientParams.createAPIKey.mockResolvedValueOnce({ + apiKeysEnabled: true, + result: { id: '123', name: '123', api_key: 'abc' }, + }); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockRejectedValueOnce(new Error('Test failure')); + await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Test failure"` + ); + expect(taskManager.schedule).not.toHaveBeenCalled(); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); + }); + + test('attempts to remove saved object if scheduling failed', async () => { + const data = getMockData(); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + taskManager.schedule.mockRejectedValueOnce(new Error('Test failure')); + unsecuredSavedObjectsClient.delete.mockResolvedValueOnce({}); + await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Test failure"` + ); + expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.delete.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "alert", + "1", + ] + `); + }); + + test('returns task manager error if cleanup fails, logs to console', async () => { + const data = getMockData(); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + taskManager.schedule.mockRejectedValueOnce(new Error('Task manager error')); + unsecuredSavedObjectsClient.delete.mockRejectedValueOnce( + new Error('Saved object delete error') + ); + await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Task manager error"` + ); + expect(alertsClientParams.logger.error).toHaveBeenCalledWith( + 'Failed to cleanup alert "1" after scheduling task failed. Error: Saved object delete error' + ); + }); + + test('throws an error if alert type not registerd', async () => { + const data = getMockData(); + alertTypeRegistry.get.mockImplementation(() => { + throw new Error('Invalid type'); + }); + await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Invalid type"` + ); + }); + + test('calls the API key function', async () => { + const data = getMockData(); + alertsClientParams.createAPIKey.mockResolvedValueOnce({ + apiKeysEnabled: true, + result: { id: '123', name: '123', api_key: 'abc' }, + }); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + taskManager.schedule.mockResolvedValueOnce({ + id: 'task-123', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: {}, + ownerId: null, + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [], + scheduledTaskId: 'task-123', + }, + references: [ + { + id: '1', + name: 'action_0', + type: 'action', + }, + ], + }); + await alertsClient.create({ data }); + + expect(alertsClientParams.createAPIKey).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'alert', + { + actions: [ + { + actionRef: 'action_0', + group: 'default', + actionTypeId: 'test', + params: { foo: true }, + }, + ], + alertTypeId: '123', + consumer: 'bar', + name: 'abc', + params: { bar: true }, + apiKey: Buffer.from('123:abc').toString('base64'), + apiKeyOwner: 'elastic', + createdBy: 'elastic', + createdAt: '2019-02-12T21:01:22.479Z', + updatedBy: 'elastic', + enabled: true, + meta: { + versionApiKeyLastmodified: 'v7.10.0', + }, + schedule: { interval: '10s' }, + throttle: null, + muteAll: false, + mutedInstanceIds: [], + tags: ['foo'], + executionStatus: { + lastExecutionDate: '2019-02-12T21:01:22.479Z', + status: 'pending', + error: null, + }, + }, + { + references: [ + { + id: '1', + name: 'action_0', + type: 'action', + }, + ], + } + ); + }); + + test(`doesn't create API key for disabled alerts`, async () => { + const data = getMockData({ enabled: false }); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + taskManager.schedule.mockResolvedValueOnce({ + id: 'task-123', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: {}, + ownerId: null, + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [], + scheduledTaskId: 'task-123', + }, + references: [ + { + id: '1', + name: 'action_0', + type: 'action', + }, + ], + }); + await alertsClient.create({ data }); + + expect(alertsClientParams.createAPIKey).not.toHaveBeenCalled(); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( + 'alert', + { + actions: [ + { + actionRef: 'action_0', + group: 'default', + actionTypeId: 'test', + params: { foo: true }, + }, + ], + alertTypeId: '123', + consumer: 'bar', + name: 'abc', + params: { bar: true }, + apiKey: null, + apiKeyOwner: null, + createdBy: 'elastic', + createdAt: '2019-02-12T21:01:22.479Z', + updatedBy: 'elastic', + enabled: false, + meta: { + versionApiKeyLastmodified: 'v7.10.0', + }, + schedule: { interval: '10s' }, + throttle: null, + muteAll: false, + mutedInstanceIds: [], + tags: ['foo'], + executionStatus: { + lastExecutionDate: '2019-02-12T21:01:22.479Z', + status: 'pending', + error: null, + }, + }, + { + references: [ + { + id: '1', + name: 'action_0', + type: 'action', + }, + ], + } + ); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/delete.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/delete.test.ts new file mode 100644 index 0000000000000..1ebd9fc296b13 --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/delete.test.ts @@ -0,0 +1,204 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { getBeforeSetup } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +describe('delete()', () => { + let alertsClient: AlertsClient; + const existingAlert = { + id: '1', + type: 'alert', + attributes: { + alertTypeId: 'myType', + consumer: 'myApp', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + scheduledTaskId: 'task-123', + actions: [ + { + group: 'default', + actionTypeId: '.no-op', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }; + const existingDecryptedAlert = { + ...existingAlert, + attributes: { + ...existingAlert.attributes, + apiKey: Buffer.from('123:abc').toString('base64'), + }, + }; + + beforeEach(() => { + alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); + unsecuredSavedObjectsClient.delete.mockResolvedValue({ + success: true, + }); + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingDecryptedAlert); + }); + + test('successfully removes an alert', async () => { + const result = await alertsClient.delete({ id: '1' }); + expect(result).toEqual({ success: true }); + expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledWith('alert', '1'); + expect(taskManager.remove).toHaveBeenCalledWith('task-123'); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { + namespace: 'default', + }); + expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); + }); + + test('falls back to SOC.get when getDecryptedAsInternalUser throws an error', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); + + const result = await alertsClient.delete({ id: '1' }); + expect(result).toEqual({ success: true }); + expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledWith('alert', '1'); + expect(taskManager.remove).toHaveBeenCalledWith('task-123'); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); + expect(alertsClientParams.logger.error).toHaveBeenCalledWith( + 'delete(): Failed to load API key to invalidate on alert 1: Fail' + ); + }); + + test(`doesn't remove a task when scheduledTaskId is null`, async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue({ + ...existingDecryptedAlert, + attributes: { + ...existingDecryptedAlert.attributes, + scheduledTaskId: null, + }, + }); + + await alertsClient.delete({ id: '1' }); + expect(taskManager.remove).not.toHaveBeenCalled(); + }); + + test(`doesn't invalidate API key when apiKey is null`, async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue({ + ...existingAlert, + attributes: { + ...existingAlert.attributes, + apiKey: null, + }, + }); + + await alertsClient.delete({ id: '1' }); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); + }); + + test('swallows error when invalidate API key throws', async () => { + alertsClientParams.invalidateAPIKey.mockRejectedValueOnce(new Error('Fail')); + + await alertsClient.delete({ id: '1' }); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); + expect(alertsClientParams.logger.error).toHaveBeenCalledWith( + 'Failed to invalidate API Key: Fail' + ); + }); + + test('swallows error when getDecryptedAsInternalUser throws an error', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); + + await alertsClient.delete({ id: '1' }); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); + expect(alertsClientParams.logger.error).toHaveBeenCalledWith( + 'delete(): Failed to load API key to invalidate on alert 1: Fail' + ); + }); + + test('throws error when unsecuredSavedObjectsClient.get throws an error', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); + unsecuredSavedObjectsClient.get.mockRejectedValue(new Error('SOC Fail')); + + await expect(alertsClient.delete({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( + `"SOC Fail"` + ); + }); + + test('throws error when taskManager.remove throws an error', async () => { + taskManager.remove.mockRejectedValue(new Error('TM Fail')); + + await expect(alertsClient.delete({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( + `"TM Fail"` + ); + }); + + describe('authorization', () => { + test('ensures user is authorised to delete this type of alert under the consumer', async () => { + await alertsClient.delete({ id: '1' }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'delete'); + }); + + test('throws when user is not authorised to delete this type of alert', async () => { + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to delete a "myType" alert for "myApp"`) + ); + + await expect(alertsClient.delete({ id: '1' })).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to delete a "myType" alert for "myApp"]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'delete'); + }); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/disable.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/disable.test.ts new file mode 100644 index 0000000000000..2dd3da07234ce --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/disable.test.ts @@ -0,0 +1,253 @@ +/* + * 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 { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { getBeforeSetup } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +describe('disable()', () => { + let alertsClient: AlertsClient; + const existingAlert = { + id: '1', + type: 'alert', + attributes: { + consumer: 'myApp', + schedule: { interval: '10s' }, + alertTypeId: 'myType', + enabled: true, + scheduledTaskId: 'task-123', + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + version: '123', + references: [], + }; + const existingDecryptedAlert = { + ...existingAlert, + attributes: { + ...existingAlert.attributes, + apiKey: Buffer.from('123:abc').toString('base64'), + }, + version: '123', + references: [], + }; + + beforeEach(() => { + alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingDecryptedAlert); + }); + + describe('authorization', () => { + test('ensures user is authorised to disable this type of alert under the consumer', async () => { + await alertsClient.disable({ id: '1' }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'disable'); + }); + + test('throws when user is not authorised to disable this type of alert', async () => { + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to disable a "myType" alert for "myApp"`) + ); + + await expect(alertsClient.disable({ id: '1' })).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to disable a "myType" alert for "myApp"]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'disable'); + }); + }); + + test('disables an alert', async () => { + await alertsClient.disable({ id: '1' }); + expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { + namespace: 'default', + }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( + 'alert', + '1', + { + consumer: 'myApp', + schedule: { interval: '10s' }, + alertTypeId: 'myType', + enabled: false, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, + scheduledTaskId: null, + apiKey: null, + apiKeyOwner: null, + updatedBy: 'elastic', + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + { + version: '123', + } + ); + expect(taskManager.remove).toHaveBeenCalledWith('task-123'); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); + }); + + test('falls back when getDecryptedAsInternalUser throws an error', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValueOnce(new Error('Fail')); + + await alertsClient.disable({ id: '1' }); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { + namespace: 'default', + }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( + 'alert', + '1', + { + consumer: 'myApp', + schedule: { interval: '10s' }, + alertTypeId: 'myType', + enabled: false, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, + scheduledTaskId: null, + apiKey: null, + apiKeyOwner: null, + updatedBy: 'elastic', + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + { + version: '123', + } + ); + expect(taskManager.remove).toHaveBeenCalledWith('task-123'); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); + }); + + test(`doesn't disable already disabled alerts`, async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({ + ...existingDecryptedAlert, + attributes: { + ...existingDecryptedAlert.attributes, + actions: [], + enabled: false, + }, + }); + + await alertsClient.disable({ id: '1' }); + expect(unsecuredSavedObjectsClient.update).not.toHaveBeenCalled(); + expect(taskManager.remove).not.toHaveBeenCalled(); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); + }); + + test(`doesn't invalidate when no API key is used`, async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce(existingAlert); + + await alertsClient.disable({ id: '1' }); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); + }); + + test('swallows error when failing to load decrypted saved object', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValueOnce(new Error('Fail')); + + await alertsClient.disable({ id: '1' }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalled(); + expect(taskManager.remove).toHaveBeenCalled(); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); + expect(alertsClientParams.logger.error).toHaveBeenCalledWith( + 'disable(): Failed to load API key to invalidate on alert 1: Fail' + ); + }); + + test('throws when unsecuredSavedObjectsClient update fails', async () => { + unsecuredSavedObjectsClient.update.mockRejectedValueOnce(new Error('Failed to update')); + + await expect(alertsClient.disable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Failed to update"` + ); + }); + + test('swallows error when invalidate API key throws', async () => { + alertsClientParams.invalidateAPIKey.mockRejectedValueOnce(new Error('Fail')); + + await alertsClient.disable({ id: '1' }); + expect(alertsClientParams.logger.error).toHaveBeenCalledWith( + 'Failed to invalidate API Key: Fail' + ); + }); + + test('throws when failing to remove task from task manager', async () => { + taskManager.remove.mockRejectedValueOnce(new Error('Failed to remove task')); + + await expect(alertsClient.disable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Failed to remove task"` + ); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/enable.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/enable.test.ts new file mode 100644 index 0000000000000..b214d8ba697b1 --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/enable.test.ts @@ -0,0 +1,361 @@ +/* + * 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 { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { TaskStatus } from '../../../../task_manager/server'; +import { getBeforeSetup } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +describe('enable()', () => { + let alertsClient: AlertsClient; + const existingAlert = { + id: '1', + type: 'alert', + attributes: { + consumer: 'myApp', + schedule: { interval: '10s' }, + alertTypeId: 'myType', + enabled: false, + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + version: '123', + references: [], + }; + + beforeEach(() => { + alertsClient = new AlertsClient(alertsClientParams); + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingAlert); + unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); + alertsClientParams.createAPIKey.mockResolvedValue({ + apiKeysEnabled: false, + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + ...existingAlert, + attributes: { + ...existingAlert.attributes, + enabled: true, + apiKey: null, + apiKeyOwner: null, + updatedBy: 'elastic', + }, + }); + taskManager.schedule.mockResolvedValue({ + id: 'task-123', + scheduledAt: new Date(), + attempts: 0, + status: TaskStatus.Idle, + runAt: new Date(), + state: {}, + params: {}, + taskType: '', + startedAt: null, + retryAt: null, + ownerId: null, + }); + }); + + describe('authorization', () => { + beforeEach(() => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingAlert); + unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); + alertsClientParams.createAPIKey.mockResolvedValue({ + apiKeysEnabled: false, + }); + taskManager.schedule.mockResolvedValue({ + id: 'task-123', + scheduledAt: new Date(), + attempts: 0, + status: TaskStatus.Idle, + runAt: new Date(), + state: {}, + params: {}, + taskType: '', + startedAt: null, + retryAt: null, + ownerId: null, + }); + }); + + test('ensures user is authorised to enable this type of alert under the consumer', async () => { + await alertsClient.enable({ id: '1' }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'enable'); + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + }); + + test('throws when user is not authorised to enable this type of alert', async () => { + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to enable a "myType" alert for "myApp"`) + ); + + await expect(alertsClient.enable({ id: '1' })).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to enable a "myType" alert for "myApp"]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'enable'); + }); + }); + + test('enables an alert', async () => { + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + ...existingAlert, + attributes: { + ...existingAlert.attributes, + enabled: true, + apiKey: null, + apiKeyOwner: null, + updatedBy: 'elastic', + }, + }); + + await alertsClient.enable({ id: '1' }); + expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { + namespace: 'default', + }); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); + expect(alertsClientParams.createAPIKey).toHaveBeenCalled(); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( + 'alert', + '1', + { + schedule: { interval: '10s' }, + alertTypeId: 'myType', + consumer: 'myApp', + enabled: true, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, + updatedBy: 'elastic', + apiKey: null, + apiKeyOwner: null, + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + { + version: '123', + } + ); + expect(taskManager.schedule).toHaveBeenCalledWith({ + taskType: `alerting:myType`, + params: { + alertId: '1', + spaceId: 'default', + }, + state: { + alertInstances: {}, + alertTypeState: {}, + previousStartedAt: null, + }, + scope: ['alerting'], + }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith('alert', '1', { + scheduledTaskId: 'task-123', + }); + }); + + test('invalidates API key if ever one existed prior to updating', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue({ + ...existingAlert, + attributes: { + ...existingAlert.attributes, + apiKey: Buffer.from('123:abc').toString('base64'), + }, + }); + + await alertsClient.enable({ id: '1' }); + expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { + namespace: 'default', + }); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); + }); + + test(`doesn't enable already enabled alerts`, async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({ + ...existingAlert, + attributes: { + ...existingAlert.attributes, + enabled: true, + }, + }); + + await alertsClient.enable({ id: '1' }); + expect(alertsClientParams.getUserName).not.toHaveBeenCalled(); + expect(alertsClientParams.createAPIKey).not.toHaveBeenCalled(); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + expect(taskManager.schedule).not.toHaveBeenCalled(); + }); + + test('sets API key when createAPIKey returns one', async () => { + alertsClientParams.createAPIKey.mockResolvedValueOnce({ + apiKeysEnabled: true, + result: { id: '123', name: '123', api_key: 'abc' }, + }); + + await alertsClient.enable({ id: '1' }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( + 'alert', + '1', + { + schedule: { interval: '10s' }, + alertTypeId: 'myType', + consumer: 'myApp', + enabled: true, + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, + apiKey: Buffer.from('123:abc').toString('base64'), + apiKeyOwner: 'elastic', + updatedBy: 'elastic', + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + { + version: '123', + } + ); + }); + + test('falls back when failing to getDecryptedAsInternalUser', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); + + await alertsClient.enable({ id: '1' }); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); + expect(alertsClientParams.logger.error).toHaveBeenCalledWith( + 'enable(): Failed to load API key to invalidate on alert 1: Fail' + ); + }); + + test('throws error when failing to load the saved object using SOC', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); + unsecuredSavedObjectsClient.get.mockRejectedValueOnce(new Error('Fail to get')); + + await expect(alertsClient.enable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Fail to get"` + ); + expect(alertsClientParams.getUserName).not.toHaveBeenCalled(); + expect(alertsClientParams.createAPIKey).not.toHaveBeenCalled(); + expect(unsecuredSavedObjectsClient.update).not.toHaveBeenCalled(); + expect(taskManager.schedule).not.toHaveBeenCalled(); + }); + + test('throws error when failing to update the first time', async () => { + alertsClientParams.createAPIKey.mockResolvedValueOnce({ + apiKeysEnabled: true, + result: { id: '123', name: '123', api_key: 'abc' }, + }); + unsecuredSavedObjectsClient.update.mockReset(); + unsecuredSavedObjectsClient.update.mockRejectedValueOnce(new Error('Fail to update')); + + await expect(alertsClient.enable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Fail to update"` + ); + expect(alertsClientParams.getUserName).toHaveBeenCalled(); + expect(alertsClientParams.createAPIKey).toHaveBeenCalled(); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(1); + expect(taskManager.schedule).not.toHaveBeenCalled(); + }); + + test('throws error when failing to update the second time', async () => { + unsecuredSavedObjectsClient.update.mockReset(); + unsecuredSavedObjectsClient.update.mockResolvedValueOnce({ + ...existingAlert, + attributes: { + ...existingAlert.attributes, + enabled: true, + }, + }); + unsecuredSavedObjectsClient.update.mockRejectedValueOnce( + new Error('Fail to update second time') + ); + + await expect(alertsClient.enable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Fail to update second time"` + ); + expect(alertsClientParams.getUserName).toHaveBeenCalled(); + expect(alertsClientParams.createAPIKey).toHaveBeenCalled(); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(2); + expect(taskManager.schedule).toHaveBeenCalled(); + }); + + test('throws error when failing to schedule task', async () => { + taskManager.schedule.mockRejectedValueOnce(new Error('Fail to schedule')); + + await expect(alertsClient.enable({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Fail to schedule"` + ); + expect(alertsClientParams.getUserName).toHaveBeenCalled(); + expect(alertsClientParams.createAPIKey).toHaveBeenCalled(); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/find.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/find.test.ts new file mode 100644 index 0000000000000..bf55a2070d8fe --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/find.test.ts @@ -0,0 +1,251 @@ +/* + * 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 { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { nodeTypes } from '../../../../../../src/plugins/data/common'; +import { esKuery } from '../../../../../../src/plugins/data/server'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { getBeforeSetup, setGlobalDate } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +setGlobalDate(); + +describe('find()', () => { + const listedTypes = new Set([ + { + actionGroups: [], + actionVariables: undefined, + defaultActionGroupId: 'default', + id: 'myType', + name: 'myType', + producer: 'myApp', + }, + ]); + beforeEach(() => { + authorization.getFindAuthorizationFilter.mockResolvedValue({ + ensureAlertTypeIsAuthorized() {}, + logSuccessfulAuthorization() {}, + }); + unsecuredSavedObjectsClient.find.mockResolvedValueOnce({ + total: 1, + per_page: 10, + page: 1, + saved_objects: [ + { + id: '1', + type: 'alert', + attributes: { + alertTypeId: 'myType', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + score: 1, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }, + ], + }); + alertTypeRegistry.list.mockReturnValue(listedTypes); + authorization.filterByAlertTypeAuthorization.mockResolvedValue( + new Set([ + { + id: 'myType', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + defaultActionGroupId: 'default', + producer: 'alerts', + authorizedConsumers: { + myApp: { read: true, all: true }, + }, + }, + ]) + ); + }); + + test('calls saved objects client with given params', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + const result = await alertsClient.find({ options: {} }); + expect(result).toMatchInlineSnapshot(` + Object { + "data": Array [ + Object { + "actions": Array [ + Object { + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "myType", + "createdAt": 2019-02-12T21:01:22.479Z, + "id": "1", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "updatedAt": 2019-02-12T21:01:22.479Z, + }, + ], + "page": 1, + "perPage": 10, + "total": 1, + } + `); + expect(unsecuredSavedObjectsClient.find).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.find.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "fields": undefined, + "filter": undefined, + "type": "alert", + }, + ] + `); + }); + + describe('authorization', () => { + test('ensures user is query filter types down to those the user is authorized to find', async () => { + const filter = esKuery.fromKueryExpression( + '((alert.attributes.alertTypeId:myType and alert.attributes.consumer:myApp) or (alert.attributes.alertTypeId:myOtherType and alert.attributes.consumer:myApp) or (alert.attributes.alertTypeId:myOtherType and alert.attributes.consumer:myOtherApp))' + ); + authorization.getFindAuthorizationFilter.mockResolvedValue({ + filter, + ensureAlertTypeIsAuthorized() {}, + logSuccessfulAuthorization() {}, + }); + + const alertsClient = new AlertsClient(alertsClientParams); + await alertsClient.find({ options: { filter: 'someTerm' } }); + + const [options] = unsecuredSavedObjectsClient.find.mock.calls[0]; + expect(options.filter).toEqual( + nodeTypes.function.buildNode('and', [esKuery.fromKueryExpression('someTerm'), filter]) + ); + expect(authorization.getFindAuthorizationFilter).toHaveBeenCalledTimes(1); + }); + + test('throws if user is not authorized to find any types', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + authorization.getFindAuthorizationFilter.mockRejectedValue(new Error('not authorized')); + await expect(alertsClient.find({ options: {} })).rejects.toThrowErrorMatchingInlineSnapshot( + `"not authorized"` + ); + }); + + test('ensures authorization even when the fields required to authorize are omitted from the find', async () => { + const ensureAlertTypeIsAuthorized = jest.fn(); + const logSuccessfulAuthorization = jest.fn(); + authorization.getFindAuthorizationFilter.mockResolvedValue({ + ensureAlertTypeIsAuthorized, + logSuccessfulAuthorization, + }); + + unsecuredSavedObjectsClient.find.mockReset(); + unsecuredSavedObjectsClient.find.mockResolvedValue({ + total: 1, + per_page: 10, + page: 1, + saved_objects: [ + { + id: '1', + type: 'alert', + attributes: { + actions: [], + alertTypeId: 'myType', + consumer: 'myApp', + tags: ['myTag'], + }, + score: 1, + references: [], + }, + ], + }); + + const alertsClient = new AlertsClient(alertsClientParams); + expect(await alertsClient.find({ options: { fields: ['tags'] } })).toMatchInlineSnapshot(` + Object { + "data": Array [ + Object { + "actions": Array [], + "id": "1", + "schedule": undefined, + "tags": Array [ + "myTag", + ], + }, + ], + "page": 1, + "perPage": 10, + "total": 1, + } + `); + + expect(unsecuredSavedObjectsClient.find).toHaveBeenCalledWith({ + fields: ['tags', 'alertTypeId', 'consumer'], + type: 'alert', + }); + expect(ensureAlertTypeIsAuthorized).toHaveBeenCalledWith('myType', 'myApp'); + expect(logSuccessfulAuthorization).toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/get.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/get.test.ts new file mode 100644 index 0000000000000..327a1fa23ef05 --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/get.test.ts @@ -0,0 +1,194 @@ +/* + * 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 { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { getBeforeSetup, setGlobalDate } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +setGlobalDate(); + +describe('get()', () => { + test('calls saved objects client with given params', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + const result = await alertsClient.get({ id: '1' }); + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "123", + "createdAt": 2019-02-12T21:01:22.479Z, + "id": "1", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "alert", + "1", + ] + `); + }); + + test(`throws an error when references aren't found`, async () => { + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + references: [], + }); + await expect(alertsClient.get({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Action reference \\"action_0\\" not found in alert id: 1"` + ); + }); + + describe('authorization', () => { + beforeEach(() => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: 'myType', + consumer: 'myApp', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + }); + + test('ensures user is authorised to get this type of alert under the consumer', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + await alertsClient.get({ id: '1' }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'get'); + }); + + test('throws when user is not authorised to get this type of alert', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to get a "myType" alert for "myApp"`) + ); + + await expect(alertsClient.get({ id: '1' })).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to get a "myType" alert for "myApp"]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'get'); + }); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/get_alert_instance_summary.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/get_alert_instance_summary.test.ts new file mode 100644 index 0000000000000..09212732b76e7 --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/get_alert_instance_summary.test.ts @@ -0,0 +1,292 @@ +/* + * 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 { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { eventLogClientMock } from '../../../../event_log/server/mocks'; +import { QueryEventsBySavedObjectResult } from '../../../../event_log/server'; +import { SavedObject } from 'kibana/server'; +import { EventsFactory } from '../../lib/alert_instance_summary_from_event_log.test'; +import { RawAlert } from '../../types'; +import { getBeforeSetup, mockedDateString, setGlobalDate } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); +const eventLogClient = eventLogClientMock.create(); + +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry, eventLogClient); +}); + +setGlobalDate(); + +const AlertInstanceSummaryFindEventsResult: QueryEventsBySavedObjectResult = { + page: 1, + per_page: 10000, + total: 0, + data: [], +}; + +const AlertInstanceSummaryIntervalSeconds = 1; + +const BaseAlertInstanceSummarySavedObject: SavedObject = { + id: '1', + type: 'alert', + attributes: { + enabled: true, + name: 'alert-name', + tags: ['tag-1', 'tag-2'], + alertTypeId: '123', + consumer: 'alert-consumer', + schedule: { interval: `${AlertInstanceSummaryIntervalSeconds}s` }, + actions: [], + params: {}, + createdBy: null, + updatedBy: null, + createdAt: mockedDateString, + apiKey: null, + apiKeyOwner: null, + throttle: null, + muteAll: false, + mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: '2020-08-20T19:23:38Z', + error: null, + }, + }, + references: [], +}; + +function getAlertInstanceSummarySavedObject( + attributes: Partial = {} +): SavedObject { + return { + ...BaseAlertInstanceSummarySavedObject, + attributes: { ...BaseAlertInstanceSummarySavedObject.attributes, ...attributes }, + }; +} + +describe('getAlertInstanceSummary()', () => { + let alertsClient: AlertsClient; + + beforeEach(() => { + alertsClient = new AlertsClient(alertsClientParams); + }); + + test('runs as expected with some event log data', async () => { + const alertSO = getAlertInstanceSummarySavedObject({ + mutedInstanceIds: ['instance-muted-no-activity'], + }); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(alertSO); + + const eventsFactory = new EventsFactory(mockedDateString); + const events = eventsFactory + .addExecute() + .addNewInstance('instance-currently-active') + .addNewInstance('instance-previously-active') + .addActiveInstance('instance-currently-active') + .addActiveInstance('instance-previously-active') + .advanceTime(10000) + .addExecute() + .addResolvedInstance('instance-previously-active') + .addActiveInstance('instance-currently-active') + .getEvents(); + const eventsResult = { + ...AlertInstanceSummaryFindEventsResult, + total: events.length, + data: events, + }; + eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(eventsResult); + + const dateStart = new Date(Date.now() - 60 * 1000).toISOString(); + + const result = await alertsClient.getAlertInstanceSummary({ id: '1', dateStart }); + expect(result).toMatchInlineSnapshot(` + Object { + "alertTypeId": "123", + "consumer": "alert-consumer", + "enabled": true, + "errorMessages": Array [], + "id": "1", + "instances": Object { + "instance-currently-active": Object { + "activeStartDate": "2019-02-12T21:01:22.479Z", + "muted": false, + "status": "Active", + }, + "instance-muted-no-activity": Object { + "activeStartDate": undefined, + "muted": true, + "status": "OK", + }, + "instance-previously-active": Object { + "activeStartDate": undefined, + "muted": false, + "status": "OK", + }, + }, + "lastRun": "2019-02-12T21:01:32.479Z", + "muteAll": false, + "name": "alert-name", + "status": "Active", + "statusEndDate": "2019-02-12T21:01:22.479Z", + "statusStartDate": "2019-02-12T21:00:22.479Z", + "tags": Array [ + "tag-1", + "tag-2", + ], + "throttle": null, + } + `); + }); + + // Further tests don't check the result of `getAlertInstanceSummary()`, as the result + // is just the result from the `alertInstanceSummaryFromEventLog()`, which itself + // has a complete set of tests. These tests just make sure the data gets + // sent into `getAlertInstanceSummary()` as appropriate. + + test('calls saved objects and event log client with default params', async () => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject()); + eventLogClient.findEventsBySavedObject.mockResolvedValueOnce( + AlertInstanceSummaryFindEventsResult + ); + + await alertsClient.getAlertInstanceSummary({ id: '1' }); + + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); + expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1); + expect(eventLogClient.findEventsBySavedObject.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "alert", + "1", + Object { + "end": "2019-02-12T21:01:22.479Z", + "page": 1, + "per_page": 10000, + "sort_order": "desc", + "start": "2019-02-12T21:00:22.479Z", + }, + ] + `); + // calculate the expected start/end date for one test + const { start, end } = eventLogClient.findEventsBySavedObject.mock.calls[0][2]!; + expect(end).toBe(mockedDateString); + + const startMillis = Date.parse(start!); + const endMillis = Date.parse(end!); + const expectedDuration = 60 * AlertInstanceSummaryIntervalSeconds * 1000; + expect(endMillis - startMillis).toBeGreaterThan(expectedDuration - 2); + expect(endMillis - startMillis).toBeLessThan(expectedDuration + 2); + }); + + test('calls event log client with start date', async () => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject()); + eventLogClient.findEventsBySavedObject.mockResolvedValueOnce( + AlertInstanceSummaryFindEventsResult + ); + + const dateStart = new Date( + Date.now() - 60 * AlertInstanceSummaryIntervalSeconds * 1000 + ).toISOString(); + await alertsClient.getAlertInstanceSummary({ id: '1', dateStart }); + + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); + expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1); + const { start, end } = eventLogClient.findEventsBySavedObject.mock.calls[0][2]!; + + expect({ start, end }).toMatchInlineSnapshot(` + Object { + "end": "2019-02-12T21:01:22.479Z", + "start": "2019-02-12T21:00:22.479Z", + } + `); + }); + + test('calls event log client with relative start date', async () => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject()); + eventLogClient.findEventsBySavedObject.mockResolvedValueOnce( + AlertInstanceSummaryFindEventsResult + ); + + const dateStart = '2m'; + await alertsClient.getAlertInstanceSummary({ id: '1', dateStart }); + + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); + expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1); + const { start, end } = eventLogClient.findEventsBySavedObject.mock.calls[0][2]!; + + expect({ start, end }).toMatchInlineSnapshot(` + Object { + "end": "2019-02-12T21:01:22.479Z", + "start": "2019-02-12T20:59:22.479Z", + } + `); + }); + + test('invalid start date throws an error', async () => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject()); + eventLogClient.findEventsBySavedObject.mockResolvedValueOnce( + AlertInstanceSummaryFindEventsResult + ); + + const dateStart = 'ain"t no way this will get parsed as a date'; + expect( + alertsClient.getAlertInstanceSummary({ id: '1', dateStart }) + ).rejects.toMatchInlineSnapshot( + `[Error: Invalid date for parameter dateStart: "ain"t no way this will get parsed as a date"]` + ); + }); + + test('saved object get throws an error', async () => { + unsecuredSavedObjectsClient.get.mockRejectedValueOnce(new Error('OMG!')); + eventLogClient.findEventsBySavedObject.mockResolvedValueOnce( + AlertInstanceSummaryFindEventsResult + ); + + expect(alertsClient.getAlertInstanceSummary({ id: '1' })).rejects.toMatchInlineSnapshot( + `[Error: OMG!]` + ); + }); + + test('findEvents throws an error', async () => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(getAlertInstanceSummarySavedObject()); + eventLogClient.findEventsBySavedObject.mockRejectedValueOnce(new Error('OMG 2!')); + + // error eaten but logged + await alertsClient.getAlertInstanceSummary({ id: '1' }); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/get_alert_state.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/get_alert_state.test.ts new file mode 100644 index 0000000000000..42e573aea347f --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/get_alert_state.test.ts @@ -0,0 +1,239 @@ +/* + * 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 { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { TaskStatus } from '../../../../task_manager/server'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { getBeforeSetup } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +describe('getAlertState()', () => { + test('calls saved objects client with given params', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + + taskManager.get.mockResolvedValueOnce({ + id: '1', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: {}, + ownerId: null, + }); + + await alertsClient.getAlertState({ id: '1' }); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.get.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + "alert", + "1", + ] + `); + }); + + test('gets the underlying task from TaskManager', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + + const scheduledTaskId = 'task-123'; + + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: '123', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + enabled: true, + scheduledTaskId, + mutedInstanceIds: [], + muteAll: true, + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + + taskManager.get.mockResolvedValueOnce({ + id: scheduledTaskId, + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: { + alertId: '1', + }, + ownerId: null, + }); + + await alertsClient.getAlertState({ id: '1' }); + expect(taskManager.get).toHaveBeenCalledTimes(1); + expect(taskManager.get).toHaveBeenCalledWith(scheduledTaskId); + }); + + describe('authorization', () => { + beforeEach(() => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: 'myType', + consumer: 'myApp', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + params: { + foo: true, + }, + }, + ], + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + + taskManager.get.mockResolvedValueOnce({ + id: '1', + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: {}, + ownerId: null, + }); + }); + + test('ensures user is authorised to get this type of alert under the consumer', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + await alertsClient.getAlertState({ id: '1' }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith( + 'myType', + 'myApp', + 'getAlertState' + ); + }); + + test('throws when user is not authorised to getAlertState this type of alert', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + // `get` check + authorization.ensureAuthorized.mockResolvedValueOnce(); + // `getAlertState` check + authorization.ensureAuthorized.mockRejectedValueOnce( + new Error(`Unauthorized to getAlertState a "myType" alert for "myApp"`) + ); + + await expect(alertsClient.getAlertState({ id: '1' })).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to getAlertState a "myType" alert for "myApp"]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith( + 'myType', + 'myApp', + 'getAlertState' + ); + }); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/lib.ts b/x-pack/plugins/alerts/server/alerts_client/tests/lib.ts new file mode 100644 index 0000000000000..96e49e21b9045 --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/lib.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. + */ +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { TaskManager } from '../../../../task_manager/server/task_manager'; +import { IEventLogClient } from '../../../../event_log/server'; +import { actionsClientMock } from '../../../../actions/server/mocks'; +import { ConstructorOptions } from '../alerts_client'; +import { eventLogClientMock } from '../../../../event_log/server/mocks'; +import { AlertTypeRegistry } from '../../alert_type_registry'; + +export const mockedDateString = '2019-02-12T21:01:22.479Z'; + +export function setGlobalDate() { + const mockedDate = new Date(mockedDateString); + const DateOriginal = Date; + // A version of date that responds to `new Date(null|undefined)` and `Date.now()` + // by returning a fixed date, otherwise should be same as Date. + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + (global as any).Date = class Date { + constructor(...args: unknown[]) { + // sometimes the ctor has no args, sometimes has a single `null` arg + if (args[0] == null) { + // @ts-ignore + return mockedDate; + } else { + // @ts-ignore + return new DateOriginal(...args); + } + } + static now() { + return mockedDate.getTime(); + } + static parse(string: string) { + return DateOriginal.parse(string); + } + }; +} + +export function getBeforeSetup( + alertsClientParams: jest.Mocked, + taskManager: jest.Mocked< + Pick + >, + alertTypeRegistry: jest.Mocked>, + eventLogClient?: jest.Mocked +) { + jest.resetAllMocks(); + alertsClientParams.createAPIKey.mockResolvedValue({ apiKeysEnabled: false }); + alertsClientParams.invalidateAPIKey.mockResolvedValue({ + apiKeysEnabled: true, + result: { + invalidated_api_keys: [], + previously_invalidated_api_keys: [], + error_count: 0, + }, + }); + alertsClientParams.getUserName.mockResolvedValue('elastic'); + taskManager.runNow.mockResolvedValue({ id: '' }); + const actionsClient = actionsClientMock.create(); + + actionsClient.getBulk.mockResolvedValueOnce([ + { + id: '1', + isPreconfigured: false, + actionTypeId: 'test', + name: 'test', + config: { + foo: 'bar', + }, + }, + { + id: '2', + isPreconfigured: false, + actionTypeId: 'test2', + name: 'test2', + config: { + foo: 'bar', + }, + }, + { + id: 'testPreconfigured', + actionTypeId: '.slack', + isPreconfigured: true, + name: 'test', + }, + ]); + alertsClientParams.getActionsClient.mockResolvedValue(actionsClient); + + alertTypeRegistry.get.mockImplementation(() => ({ + id: '123', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + defaultActionGroupId: 'default', + async executor() {}, + producer: 'alerts', + })); + alertsClientParams.getEventLogClient.mockResolvedValue( + eventLogClient ?? eventLogClientMock.create() + ); +} diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/list_alert_types.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/list_alert_types.test.ts new file mode 100644 index 0000000000000..4337ed6c491d4 --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/list_alert_types.test.ts @@ -0,0 +1,134 @@ +/* + * 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 { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { getBeforeSetup } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +describe('listAlertTypes', () => { + let alertsClient: AlertsClient; + const alertingAlertType = { + actionGroups: [], + actionVariables: undefined, + defaultActionGroupId: 'default', + id: 'alertingAlertType', + name: 'alertingAlertType', + producer: 'alerts', + }; + const myAppAlertType = { + actionGroups: [], + actionVariables: undefined, + defaultActionGroupId: 'default', + id: 'myAppAlertType', + name: 'myAppAlertType', + producer: 'myApp', + }; + const setOfAlertTypes = new Set([myAppAlertType, alertingAlertType]); + + const authorizedConsumers = { + alerts: { read: true, all: true }, + myApp: { read: true, all: true }, + myOtherApp: { read: true, all: true }, + }; + + beforeEach(() => { + alertsClient = new AlertsClient(alertsClientParams); + }); + + test('should return a list of AlertTypes that exist in the registry', async () => { + alertTypeRegistry.list.mockReturnValue(setOfAlertTypes); + authorization.filterByAlertTypeAuthorization.mockResolvedValue( + new Set([ + { ...myAppAlertType, authorizedConsumers }, + { ...alertingAlertType, authorizedConsumers }, + ]) + ); + expect(await alertsClient.listAlertTypes()).toEqual( + new Set([ + { ...myAppAlertType, authorizedConsumers }, + { ...alertingAlertType, authorizedConsumers }, + ]) + ); + }); + + describe('authorization', () => { + const listedTypes = new Set([ + { + actionGroups: [], + actionVariables: undefined, + defaultActionGroupId: 'default', + id: 'myType', + name: 'myType', + producer: 'myApp', + }, + { + id: 'myOtherType', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + defaultActionGroupId: 'default', + producer: 'alerts', + }, + ]); + beforeEach(() => { + alertTypeRegistry.list.mockReturnValue(listedTypes); + }); + + test('should return a list of AlertTypes that exist in the registry only if the user is authorised to get them', async () => { + const authorizedTypes = new Set([ + { + id: 'myType', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + defaultActionGroupId: 'default', + producer: 'alerts', + authorizedConsumers: { + myApp: { read: true, all: true }, + }, + }, + ]); + authorization.filterByAlertTypeAuthorization.mockResolvedValue(authorizedTypes); + + expect(await alertsClient.listAlertTypes()).toEqual(authorizedTypes); + }); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/mute_all.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/mute_all.test.ts new file mode 100644 index 0000000000000..44ee6713f2560 --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/mute_all.test.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 { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { getBeforeSetup } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +describe('muteAll()', () => { + test('mutes an alert', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + muteAll: false, + }, + references: [], + version: '123', + }); + + await alertsClient.muteAll({ id: '1' }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( + 'alert', + '1', + { + muteAll: true, + mutedInstanceIds: [], + updatedBy: 'elastic', + }, + { + version: '123', + } + ); + }); + + describe('authorization', () => { + beforeEach(() => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + consumer: 'myApp', + schedule: { interval: '10s' }, + alertTypeId: 'myType', + apiKey: null, + apiKeyOwner: null, + enabled: false, + scheduledTaskId: null, + updatedBy: 'elastic', + muteAll: false, + }, + references: [], + }); + }); + + test('ensures user is authorised to muteAll this type of alert under the consumer', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + await alertsClient.muteAll({ id: '1' }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'muteAll'); + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + }); + + test('throws when user is not authorised to muteAll this type of alert', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to muteAll a "myType" alert for "myApp"`) + ); + + await expect(alertsClient.muteAll({ id: '1' })).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to muteAll a "myType" alert for "myApp"]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'muteAll'); + }); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/mute_instance.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/mute_instance.test.ts new file mode 100644 index 0000000000000..dc9a1600a5776 --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/mute_instance.test.ts @@ -0,0 +1,181 @@ +/* + * 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 { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { getBeforeSetup } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +describe('muteInstance()', () => { + test('mutes an alert instance', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [], + schedule: { interval: '10s' }, + alertTypeId: '2', + enabled: true, + scheduledTaskId: 'task-123', + mutedInstanceIds: [], + }, + version: '123', + references: [], + }); + + await alertsClient.muteInstance({ alertId: '1', alertInstanceId: '2' }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( + 'alert', + '1', + { + mutedInstanceIds: ['2'], + updatedBy: 'elastic', + }, + { + version: '123', + } + ); + }); + + test('skips muting when alert instance already muted', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [], + schedule: { interval: '10s' }, + alertTypeId: '2', + enabled: true, + scheduledTaskId: 'task-123', + mutedInstanceIds: ['2'], + }, + references: [], + }); + + await alertsClient.muteInstance({ alertId: '1', alertInstanceId: '2' }); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + }); + + test('skips muting when alert is muted', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [], + schedule: { interval: '10s' }, + alertTypeId: '2', + enabled: true, + scheduledTaskId: 'task-123', + mutedInstanceIds: [], + muteAll: true, + }, + references: [], + }); + + await alertsClient.muteInstance({ alertId: '1', alertInstanceId: '2' }); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + }); + + describe('authorization', () => { + beforeEach(() => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + schedule: { interval: '10s' }, + alertTypeId: 'myType', + consumer: 'myApp', + enabled: true, + scheduledTaskId: 'task-123', + mutedInstanceIds: [], + }, + version: '123', + references: [], + }); + }); + + test('ensures user is authorised to muteInstance this type of alert under the consumer', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + await alertsClient.muteInstance({ alertId: '1', alertInstanceId: '2' }); + + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith( + 'myType', + 'myApp', + 'muteInstance' + ); + }); + + test('throws when user is not authorised to muteInstance this type of alert', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to muteInstance a "myType" alert for "myApp"`) + ); + + await expect( + alertsClient.muteInstance({ alertId: '1', alertInstanceId: '2' }) + ).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to muteInstance a "myType" alert for "myApp"]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith( + 'myType', + 'myApp', + 'muteInstance' + ); + }); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/unmute_all.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/unmute_all.test.ts new file mode 100644 index 0000000000000..45920db105c2a --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/unmute_all.test.ts @@ -0,0 +1,139 @@ +/* + * 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 { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { getBeforeSetup } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +describe('unmuteAll()', () => { + test('unmutes an alert', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + muteAll: true, + }, + references: [], + version: '123', + }); + + await alertsClient.unmuteAll({ id: '1' }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( + 'alert', + '1', + { + muteAll: false, + mutedInstanceIds: [], + updatedBy: 'elastic', + }, + { + version: '123', + } + ); + }); + + describe('authorization', () => { + beforeEach(() => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + consumer: 'myApp', + schedule: { interval: '10s' }, + alertTypeId: 'myType', + apiKey: null, + apiKeyOwner: null, + enabled: false, + scheduledTaskId: null, + updatedBy: 'elastic', + muteAll: false, + }, + references: [], + }); + }); + + test('ensures user is authorised to unmuteAll this type of alert under the consumer', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + await alertsClient.unmuteAll({ id: '1' }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'unmuteAll'); + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + }); + + test('throws when user is not authorised to unmuteAll this type of alert', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to unmuteAll a "myType" alert for "myApp"`) + ); + + await expect(alertsClient.unmuteAll({ id: '1' })).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to unmuteAll a "myType" alert for "myApp"]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'unmuteAll'); + }); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/unmute_instance.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/unmute_instance.test.ts new file mode 100644 index 0000000000000..5604011501130 --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/unmute_instance.test.ts @@ -0,0 +1,179 @@ +/* + * 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 { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { getBeforeSetup } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +describe('unmuteInstance()', () => { + test('unmutes an alert instance', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [], + schedule: { interval: '10s' }, + alertTypeId: '2', + enabled: true, + scheduledTaskId: 'task-123', + mutedInstanceIds: ['2'], + }, + version: '123', + references: [], + }); + + await alertsClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( + 'alert', + '1', + { + mutedInstanceIds: [], + updatedBy: 'elastic', + }, + { version: '123' } + ); + }); + + test('skips unmuting when alert instance not muted', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [], + schedule: { interval: '10s' }, + alertTypeId: '2', + enabled: true, + scheduledTaskId: 'task-123', + mutedInstanceIds: [], + }, + references: [], + }); + + await alertsClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' }); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + }); + + test('skips unmuting when alert is muted', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [], + schedule: { interval: '10s' }, + alertTypeId: '2', + enabled: true, + scheduledTaskId: 'task-123', + mutedInstanceIds: [], + muteAll: true, + }, + references: [], + }); + + await alertsClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' }); + expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); + }); + + describe('authorization', () => { + beforeEach(() => { + unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + alertTypeId: 'myType', + consumer: 'myApp', + schedule: { interval: '10s' }, + enabled: true, + scheduledTaskId: 'task-123', + mutedInstanceIds: ['2'], + }, + version: '123', + references: [], + }); + }); + + test('ensures user is authorised to unmuteInstance this type of alert under the consumer', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + await alertsClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' }); + + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith( + 'myType', + 'myApp', + 'unmuteInstance' + ); + }); + + test('throws when user is not authorised to unmuteInstance this type of alert', async () => { + const alertsClient = new AlertsClient(alertsClientParams); + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to unmuteInstance a "myType" alert for "myApp"`) + ); + + await expect( + alertsClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' }) + ).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to unmuteInstance a "myType" alert for "myApp"]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith( + 'myType', + 'myApp', + 'unmuteInstance' + ); + }); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/update.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/update.test.ts new file mode 100644 index 0000000000000..14275575f75f4 --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/update.test.ts @@ -0,0 +1,1257 @@ +/* + * 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 { schema } from '@kbn/config-schema'; +import { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { IntervalSchedule } from '../../types'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { resolvable } from '../../test_utils'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { TaskStatus } from '../../../../task_manager/server'; +import { getBeforeSetup, setGlobalDate } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); + +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +setGlobalDate(); + +describe('update()', () => { + let alertsClient: AlertsClient; + const existingAlert = { + id: '1', + type: 'alert', + attributes: { + enabled: true, + tags: ['foo'], + alertTypeId: 'myType', + schedule: { interval: '10s' }, + consumer: 'myApp', + scheduledTaskId: 'task-123', + params: {}, + throttle: null, + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + references: [], + version: '123', + }; + const existingDecryptedAlert = { + ...existingAlert, + attributes: { + ...existingAlert.attributes, + apiKey: Buffer.from('123:abc').toString('base64'), + }, + }; + + beforeEach(() => { + alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingDecryptedAlert); + alertTypeRegistry.get.mockReturnValue({ + id: 'myType', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + defaultActionGroupId: 'default', + async executor() {}, + producer: 'alerts', + }); + }); + + test('updates given parameters', async () => { + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + enabled: true, + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + { + group: 'default', + actionRef: 'action_1', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + { + group: 'default', + actionRef: 'action_2', + actionTypeId: 'test2', + params: { + foo: true, + }, + }, + ], + scheduledTaskId: 'task-123', + createdAt: new Date().toISOString(), + }, + updated_at: new Date().toISOString(), + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + { + name: 'action_1', + type: 'action', + id: '1', + }, + { + name: 'action_2', + type: 'action', + id: '2', + }, + ], + }); + const result = await alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + { + group: 'default', + id: '2', + params: { + foo: true, + }, + }, + ], + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + Object { + "actionTypeId": "test2", + "group": "default", + "id": "2", + "params": Object { + "foo": true, + }, + }, + ], + "createdAt": 2019-02-12T21:01:22.479Z, + "enabled": true, + "id": "1", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "scheduledTaskId": "task-123", + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { + namespace: 'default', + }); + expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionRef": "action_0", + "actionTypeId": "test", + "group": "default", + "params": Object { + "foo": true, + }, + }, + Object { + "actionRef": "action_1", + "actionTypeId": "test", + "group": "default", + "params": Object { + "foo": true, + }, + }, + Object { + "actionRef": "action_2", + "actionTypeId": "test2", + "group": "default", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "myType", + "apiKey": null, + "apiKeyOwner": null, + "consumer": "myApp", + "enabled": true, + "meta": Object { + "versionApiKeyLastmodified": "v7.10.0", + }, + "name": "abc", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "scheduledTaskId": "task-123", + "tags": Array [ + "foo", + ], + "throttle": null, + "updatedBy": "elastic", + } + `); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][2]).toMatchInlineSnapshot(` + Object { + "id": "1", + "overwrite": true, + "references": Array [ + Object { + "id": "1", + "name": "action_0", + "type": "action", + }, + Object { + "id": "1", + "name": "action_1", + "type": "action", + }, + Object { + "id": "2", + "name": "action_2", + "type": "action", + }, + ], + "version": "123", + } + `); + }); + + it('calls the createApiKey function', async () => { + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + alertsClientParams.createAPIKey.mockResolvedValueOnce({ + apiKeysEnabled: true, + result: { id: '123', name: '123', api_key: 'abc' }, + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + enabled: true, + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + apiKey: Buffer.from('123:abc').toString('base64'), + scheduledTaskId: 'task-123', + }, + updated_at: new Date().toISOString(), + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + const result = await alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: '5m', + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + }, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "apiKey": "MTIzOmFiYw==", + "createdAt": 2019-02-12T21:01:22.479Z, + "enabled": true, + "id": "1", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "scheduledTaskId": "task-123", + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionRef": "action_0", + "actionTypeId": "test", + "group": "default", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "myType", + "apiKey": "MTIzOmFiYw==", + "apiKeyOwner": "elastic", + "consumer": "myApp", + "enabled": true, + "meta": Object { + "versionApiKeyLastmodified": "v7.10.0", + }, + "name": "abc", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "scheduledTaskId": "task-123", + "tags": Array [ + "foo", + ], + "throttle": "5m", + "updatedBy": "elastic", + } + `); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][2]).toMatchInlineSnapshot(` + Object { + "id": "1", + "overwrite": true, + "references": Array [ + Object { + "id": "1", + "name": "action_0", + "type": "action", + }, + ], + "version": "123", + } + `); + }); + + it(`doesn't call the createAPIKey function when alert is disabled`, async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue({ + ...existingDecryptedAlert, + attributes: { + ...existingDecryptedAlert.attributes, + enabled: false, + }, + }); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + enabled: false, + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + scheduledTaskId: 'task-123', + apiKey: null, + }, + updated_at: new Date().toISOString(), + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + const result = await alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: '5m', + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + }, + }); + expect(alertsClientParams.createAPIKey).not.toHaveBeenCalled(); + expect(result).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionTypeId": "test", + "group": "default", + "id": "1", + "params": Object { + "foo": true, + }, + }, + ], + "apiKey": null, + "createdAt": 2019-02-12T21:01:22.479Z, + "enabled": false, + "id": "1", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "scheduledTaskId": "task-123", + "updatedAt": 2019-02-12T21:01:22.479Z, + } + `); + expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); + expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` + Object { + "actions": Array [ + Object { + "actionRef": "action_0", + "actionTypeId": "test", + "group": "default", + "params": Object { + "foo": true, + }, + }, + ], + "alertTypeId": "myType", + "apiKey": null, + "apiKeyOwner": null, + "consumer": "myApp", + "enabled": false, + "meta": Object { + "versionApiKeyLastmodified": "v7.10.0", + }, + "name": "abc", + "params": Object { + "bar": true, + }, + "schedule": Object { + "interval": "10s", + }, + "scheduledTaskId": "task-123", + "tags": Array [ + "foo", + ], + "throttle": "5m", + "updatedBy": "elastic", + } + `); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][2]).toMatchInlineSnapshot(` + Object { + "id": "1", + "overwrite": true, + "references": Array [ + Object { + "id": "1", + "name": "action_0", + "type": "action", + }, + ], + "version": "123", + } + `); + }); + + it('should validate params', async () => { + alertTypeRegistry.get.mockReturnValueOnce({ + id: '123', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + defaultActionGroupId: 'default', + validate: { + params: schema.object({ + param1: schema.string(), + }), + }, + async executor() {}, + producer: 'alerts', + }); + await expect( + alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"params invalid: [param1]: expected value of type [string] but got [undefined]"` + ); + }); + + it('should trim alert name in the API key name', async () => { + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + enabled: false, + name: ' my alert name ', + schedule: { interval: '10s' }, + params: { + bar: true, + }, + createdAt: new Date().toISOString(), + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + scheduledTaskId: 'task-123', + apiKey: null, + }, + updated_at: new Date().toISOString(), + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + await alertsClient.update({ + id: '1', + data: { + ...existingAlert.attributes, + name: ' my alert name ', + }, + }); + + expect(alertsClientParams.createAPIKey).toHaveBeenCalledWith('Alerting: myType/my alert name'); + }); + + it('swallows error when invalidate API key throws', async () => { + alertsClientParams.invalidateAPIKey.mockRejectedValueOnce(new Error('Fail')); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + enabled: true, + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + scheduledTaskId: 'task-123', + }, + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + ], + }); + await alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + }, + }); + expect(alertsClientParams.logger.error).toHaveBeenCalledWith( + 'Failed to invalidate API Key: Fail' + ); + }); + + it('swallows error when getDecryptedAsInternalUser throws', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + { + id: '2', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test2', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + enabled: true, + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + { + group: 'default', + actionRef: 'action_1', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + { + group: 'default', + actionRef: 'action_2', + actionTypeId: 'test2', + params: { + foo: true, + }, + }, + ], + scheduledTaskId: 'task-123', + createdAt: new Date().toISOString(), + }, + updated_at: new Date().toISOString(), + references: [ + { + name: 'action_0', + type: 'action', + id: '1', + }, + { + name: 'action_1', + type: 'action', + id: '1', + }, + { + name: 'action_2', + type: 'action', + id: '2', + }, + ], + }); + await alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: '5m', + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + { + group: 'default', + id: '2', + params: { + foo: true, + }, + }, + ], + }, + }); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); + expect(alertsClientParams.logger.error).toHaveBeenCalledWith( + 'update(): Failed to load API key to invalidate on alert 1: Fail' + ); + }); + + test('throws when unsecuredSavedObjectsClient update fails and invalidates newly created API key', async () => { + alertsClientParams.createAPIKey.mockResolvedValueOnce({ + apiKeysEnabled: true, + result: { id: '234', name: '234', api_key: 'abc' }, + }); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockRejectedValue(new Error('Fail')); + await expect( + alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalledWith({ id: '123' }); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '234' }); + }); + + describe('updating an alert schedule', () => { + function mockApiCalls( + alertId: string, + taskId: string, + currentSchedule: IntervalSchedule, + updatedSchedule: IntervalSchedule + ) { + // mock return values from deps + alertTypeRegistry.get.mockReturnValueOnce({ + id: '123', + name: 'Test', + actionGroups: [{ id: 'default', name: 'Default' }], + defaultActionGroupId: 'default', + async executor() {}, + producer: 'alerts', + }); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({ + id: alertId, + type: 'alert', + attributes: { + actions: [], + enabled: true, + alertTypeId: '123', + schedule: currentSchedule, + scheduledTaskId: 'task-123', + }, + references: [], + version: '123', + }); + + taskManager.schedule.mockResolvedValueOnce({ + id: taskId, + taskType: 'alerting:123', + scheduledAt: new Date(), + attempts: 1, + status: TaskStatus.Idle, + runAt: new Date(), + startedAt: null, + retryAt: null, + state: {}, + params: {}, + ownerId: null, + }); + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: alertId, + type: 'alert', + attributes: { + enabled: true, + schedule: updatedSchedule, + actions: [ + { + group: 'default', + actionRef: 'action_0', + actionTypeId: 'test', + params: { + foo: true, + }, + }, + ], + scheduledTaskId: taskId, + }, + references: [ + { + name: 'action_0', + type: 'action', + id: alertId, + }, + ], + }); + + taskManager.runNow.mockReturnValueOnce(Promise.resolve({ id: alertId })); + } + + test('updating the alert schedule should rerun the task immediately', async () => { + const alertId = uuid.v4(); + const taskId = uuid.v4(); + + mockApiCalls(alertId, taskId, { interval: '60m' }, { interval: '10s' }); + + await alertsClient.update({ + id: alertId, + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + }, + }); + + expect(taskManager.runNow).toHaveBeenCalledWith(taskId); + }); + + test('updating the alert without changing the schedule should not rerun the task', async () => { + const alertId = uuid.v4(); + const taskId = uuid.v4(); + + mockApiCalls(alertId, taskId, { interval: '10s' }, { interval: '10s' }); + + await alertsClient.update({ + id: alertId, + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + }, + }); + + expect(taskManager.runNow).not.toHaveBeenCalled(); + }); + + test('updating the alert should not wait for the rerun the task to complete', async () => { + const alertId = uuid.v4(); + const taskId = uuid.v4(); + + mockApiCalls(alertId, taskId, { interval: '10s' }, { interval: '30s' }); + + const resolveAfterAlertUpdatedCompletes = resolvable<{ id: string }>(); + + taskManager.runNow.mockReset(); + taskManager.runNow.mockReturnValue(resolveAfterAlertUpdatedCompletes); + + await alertsClient.update({ + id: alertId, + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + }, + }); + + expect(taskManager.runNow).toHaveBeenCalled(); + resolveAfterAlertUpdatedCompletes.resolve({ id: alertId }); + }); + + test('logs when the rerun of an alerts underlying task fails', async () => { + const alertId = uuid.v4(); + const taskId = uuid.v4(); + + mockApiCalls(alertId, taskId, { interval: '10s' }, { interval: '30s' }); + + taskManager.runNow.mockReset(); + taskManager.runNow.mockRejectedValue(new Error('Failed to run alert')); + + await alertsClient.update({ + id: alertId, + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + }, + }); + + expect(taskManager.runNow).toHaveBeenCalled(); + + expect(alertsClientParams.logger.error).toHaveBeenCalledWith( + `Alert update failed to run its underlying task. TaskManager runNow failed with Error: Failed to run alert` + ); + }); + }); + + describe('authorization', () => { + beforeEach(() => { + unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ + id: '1', + type: 'alert', + attributes: { + alertTypeId: 'myType', + consumer: 'myApp', + enabled: true, + schedule: { interval: '10s' }, + params: { + bar: true, + }, + actions: [], + scheduledTaskId: 'task-123', + createdAt: new Date().toISOString(), + }, + updated_at: new Date().toISOString(), + references: [], + }); + }); + + test('ensures user is authorised to update this type of alert under the consumer', async () => { + await alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + actions: [], + }, + }); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'update'); + }); + + test('throws when user is not authorised to update this type of alert', async () => { + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to update a "myType" alert for "myApp"`) + ); + + await expect( + alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + actions: [], + }, + }) + ).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to update a "myType" alert for "myApp"]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'update'); + }); + }); +}); diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/update_api_key.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/update_api_key.test.ts new file mode 100644 index 0000000000000..97ddfa5e4adb4 --- /dev/null +++ b/x-pack/plugins/alerts/server/alerts_client/tests/update_api_key.test.ts @@ -0,0 +1,229 @@ +/* + * 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 { AlertsClient, ConstructorOptions } from '../alerts_client'; +import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; +import { taskManagerMock } from '../../../../task_manager/server/task_manager.mock'; +import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; +import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; +import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { AlertsAuthorization } from '../../authorization/alerts_authorization'; +import { ActionsAuthorization } from '../../../../actions/server'; +import { getBeforeSetup } from './lib'; + +const taskManager = taskManagerMock.start(); +const alertTypeRegistry = alertTypeRegistryMock.create(); +const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); +const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); +const authorization = alertsAuthorizationMock.create(); +const actionsAuthorization = actionsAuthorizationMock.create(); + +const kibanaVersion = 'v7.10.0'; +const alertsClientParams: jest.Mocked = { + taskManager, + alertTypeRegistry, + unsecuredSavedObjectsClient, + authorization: (authorization as unknown) as AlertsAuthorization, + actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, + spaceId: 'default', + namespace: 'default', + getUserName: jest.fn(), + createAPIKey: jest.fn(), + invalidateAPIKey: jest.fn(), + logger: loggingSystemMock.create().get(), + encryptedSavedObjectsClient: encryptedSavedObjects, + getActionsClient: jest.fn(), + getEventLogClient: jest.fn(), + kibanaVersion, +}; + +beforeEach(() => { + getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); +}); + +describe('updateApiKey()', () => { + let alertsClient: AlertsClient; + const existingAlert = { + id: '1', + type: 'alert', + attributes: { + schedule: { interval: '10s' }, + alertTypeId: 'myType', + consumer: 'myApp', + enabled: true, + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + }, + version: '123', + references: [], + }; + const existingEncryptedAlert = { + ...existingAlert, + attributes: { + ...existingAlert.attributes, + apiKey: Buffer.from('123:abc').toString('base64'), + }, + }; + + beforeEach(() => { + alertsClient = new AlertsClient(alertsClientParams); + unsecuredSavedObjectsClient.get.mockResolvedValue(existingAlert); + encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValue(existingEncryptedAlert); + alertsClientParams.createAPIKey.mockResolvedValueOnce({ + apiKeysEnabled: true, + result: { id: '234', name: '123', api_key: 'abc' }, + }); + }); + + test('updates the API key for the alert', async () => { + await alertsClient.updateApiKey({ id: '1' }); + expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { + namespace: 'default', + }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( + 'alert', + '1', + { + schedule: { interval: '10s' }, + alertTypeId: 'myType', + consumer: 'myApp', + enabled: true, + apiKey: Buffer.from('234:abc').toString('base64'), + apiKeyOwner: 'elastic', + updatedBy: 'elastic', + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, + }, + { version: '123' } + ); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); + }); + + test('falls back to SOC when getDecryptedAsInternalUser throws an error', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValueOnce(new Error('Fail')); + + await alertsClient.updateApiKey({ id: '1' }); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { + namespace: 'default', + }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( + 'alert', + '1', + { + schedule: { interval: '10s' }, + alertTypeId: 'myType', + consumer: 'myApp', + enabled: true, + apiKey: Buffer.from('234:abc').toString('base64'), + apiKeyOwner: 'elastic', + updatedBy: 'elastic', + actions: [ + { + group: 'default', + id: '1', + actionTypeId: '1', + actionRef: '1', + params: { + foo: true, + }, + }, + ], + meta: { + versionApiKeyLastmodified: kibanaVersion, + }, + }, + { version: '123' } + ); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); + }); + + test('swallows error when invalidate API key throws', async () => { + alertsClientParams.invalidateAPIKey.mockRejectedValue(new Error('Fail')); + + await alertsClient.updateApiKey({ id: '1' }); + expect(alertsClientParams.logger.error).toHaveBeenCalledWith( + 'Failed to invalidate API Key: Fail' + ); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalled(); + }); + + test('swallows error when getting decrypted object throws', async () => { + encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValueOnce(new Error('Fail')); + + await alertsClient.updateApiKey({ id: '1' }); + expect(alertsClientParams.logger.error).toHaveBeenCalledWith( + 'updateApiKey(): Failed to load API key to invalidate on alert 1: Fail' + ); + expect(unsecuredSavedObjectsClient.update).toHaveBeenCalled(); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); + }); + + test('throws when unsecuredSavedObjectsClient update fails and invalidates newly created API key', async () => { + alertsClientParams.createAPIKey.mockResolvedValueOnce({ + apiKeysEnabled: true, + result: { id: '234', name: '234', api_key: 'abc' }, + }); + unsecuredSavedObjectsClient.update.mockRejectedValueOnce(new Error('Fail')); + + await expect(alertsClient.updateApiKey({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( + `"Fail"` + ); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalledWith({ id: '123' }); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '234' }); + }); + + describe('authorization', () => { + test('ensures user is authorised to updateApiKey this type of alert under the consumer', async () => { + await alertsClient.updateApiKey({ id: '1' }); + + expect(actionsAuthorization.ensureAuthorized).toHaveBeenCalledWith('execute'); + expect(authorization.ensureAuthorized).toHaveBeenCalledWith( + 'myType', + 'myApp', + 'updateApiKey' + ); + }); + + test('throws when user is not authorised to updateApiKey this type of alert', async () => { + authorization.ensureAuthorized.mockRejectedValue( + new Error(`Unauthorized to updateApiKey a "myType" alert for "myApp"`) + ); + + await expect(alertsClient.updateApiKey({ id: '1' })).rejects.toMatchInlineSnapshot( + `[Error: Unauthorized to updateApiKey a "myType" alert for "myApp"]` + ); + + expect(authorization.ensureAuthorized).toHaveBeenCalledWith( + 'myType', + 'myApp', + 'updateApiKey' + ); + }); + }); +}); From 1290ef479f356e8e929e3f27468b2a6d87d1b2d2 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Tue, 13 Oct 2020 16:30:32 +0200 Subject: [PATCH 005/128] [Lens] Smokescreen lens test unskip (#80190) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/test/functional/apps/lens/smokescreen.ts | 6 ++++-- x-pack/test/functional/page_objects/lens_page.ts | 12 ++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index d26c92a2bcd63..6c4fa94a259e9 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -13,8 +13,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const listingTable = getService('listingTable'); const testSubjects = getService('testSubjects'); - // Failing: See https://github.com/elastic/kibana/issues/77969 - describe.skip('lens smokescreen tests', () => { + describe('lens smokescreen tests', () => { it('should allow creation of lens xy chart', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); @@ -153,6 +152,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { dimension: 'lnsXY_yDimensionPanel > lns-dimensionTrigger', operation: 'max', field: 'memory', + keepOpen: true, }); await PageObjects.lens.editDimensionLabel('Test of label'); await PageObjects.lens.editDimensionFormat('Percent'); @@ -160,6 +160,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.lens.editMissingValues('Linear'); await PageObjects.lens.assertMissingValues('Linear'); + + await PageObjects.lens.openDimensionEditor('lnsXY_yDimensionPanel > lns-dimensionTrigger'); await PageObjects.lens.assertColor('#ff0000'); await testSubjects.existOrFail('indexPattern-dimension-formatDecimals'); diff --git a/x-pack/test/functional/page_objects/lens_page.ts b/x-pack/test/functional/page_objects/lens_page.ts index ec7281e53c5e1..f8ecacbc1141d 100644 --- a/x-pack/test/functional/page_objects/lens_page.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -114,6 +114,18 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont } }, + /** + * Open the specified dimension. + * + * @param dimension - the selector of the dimension panel to open + * @param layerIndex - the index of the layer + */ + async openDimensionEditor(dimension: string, layerIndex = 0) { + await retry.try(async () => { + await testSubjects.click(`lns-layerPanel-${layerIndex} > ${dimension}`); + }); + }, + // closes the dimension editor flyout async closeDimensionEditor() { await testSubjects.click('lns-indexPattern-dimensionContainerTitle'); From f4534674f2dcdf8cc0b0c910142e5bda28048d1c Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Tue, 13 Oct 2020 16:54:15 +0200 Subject: [PATCH 006/128] [ML] Fixes for anomaly swim lane (#80299) * [ML] add swim lane styles for dark theme * [ML] fix global time range update on sell selection * [ML] fix getSelectionTimeRange * [ML] fix range selection for embeddable * [ML] fix job selection * [ML] fix swim lane limit --- .../components/job_selector/job_selector.tsx | 36 +++-- .../job_selector/job_selector_flyout.tsx | 4 +- .../job_selector/use_job_selection.ts | 2 +- .../date_picker_wrapper.tsx | 2 +- .../application/explorer/explorer_utils.js | 2 +- .../explorer/hooks/use_selected_cells.ts | 2 +- .../explorer/swimlane_container.tsx | 136 ++++++++++-------- .../application/routing/routes/explorer.tsx | 12 +- .../ml/public/application/util/url_state.tsx | 6 +- .../anomaly_swimlane_embeddable.tsx | 25 ++-- .../embeddable_swim_lane_container.tsx | 2 +- .../ui_actions/apply_time_range_action.tsx | 3 +- 12 files changed, 123 insertions(+), 109 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx b/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx index a00284860d668..d25d2c3a858ed 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx +++ b/x-pack/plugins/ml/public/application/components/job_selector/job_selector.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import { EuiButtonEmpty, EuiFlexItem, EuiFlexGroup, EuiFlyout } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -109,24 +109,22 @@ export function JobSelector({ dateFormatTz, singleSelection, timeseriesOnly }: J showFlyout(); } - const applySelection: JobSelectorFlyoutProps['onSelectionConfirmed'] = ({ - newSelection, - jobIds, - groups: newGroups, - time, - }) => { - setSelectedIds(newSelection); - - setGlobalState({ - ml: { - jobIds, - groups: newGroups, - }, - ...(time !== undefined ? { time } : {}), - }); - - closeFlyout(); - }; + const applySelection: JobSelectorFlyoutProps['onSelectionConfirmed'] = useCallback( + ({ newSelection, jobIds, groups: newGroups, time }) => { + setSelectedIds(newSelection); + + setGlobalState({ + ml: { + jobIds, + groups: newGroups, + }, + ...(time !== undefined ? { time } : {}), + }); + + closeFlyout(); + }, + [setGlobalState, setSelectedIds] + ); function renderJobSelectionBar() { return ( diff --git a/x-pack/plugins/ml/public/application/components/job_selector/job_selector_flyout.tsx b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_flyout.tsx index 1e8ac4c15fd15..a90eb8cfde532 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/job_selector_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/job_selector/job_selector_flyout.tsx @@ -82,7 +82,7 @@ export const JobSelectorFlyoutContent: FC = ({ const flyoutEl = useRef(null); - function applySelection() { + const applySelection = useCallback(() => { // allNewSelection will be a list of all job ids (including those from groups) selected from the table const allNewSelection: string[] = []; const groupSelection: Array<{ groupId: string; jobIds: string[] }> = []; @@ -110,7 +110,7 @@ export const JobSelectorFlyoutContent: FC = ({ groups: groupSelection, time, }); - } + }, [onSelectionConfirmed, newSelection, jobGroupsMaps, applyTimeRange]); function removeId(id: string) { setNewSelection(newSelection.filter((item) => item !== id)); diff --git a/x-pack/plugins/ml/public/application/components/job_selector/use_job_selection.ts b/x-pack/plugins/ml/public/application/components/job_selector/use_job_selection.ts index 0717348d1db22..04fa3e9201c6c 100644 --- a/x-pack/plugins/ml/public/application/components/job_selector/use_job_selection.ts +++ b/x-pack/plugins/ml/public/application/components/job_selector/use_job_selection.ts @@ -88,7 +88,7 @@ export const useJobSelection = (jobs: MlJobWithTimeRange[]) => { ...(time !== undefined ? { time } : {}), }); } - }, [jobs, validIds]); + }, [jobs, validIds, setGlobalState, globalState?.ml]); return jobSelection; }; diff --git a/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx index beafae1ecd2f6..409bd11e0bde3 100644 --- a/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/components/navigation_menu/date_picker_wrapper/date_picker_wrapper.tsx @@ -54,7 +54,7 @@ export const DatePickerWrapper: FC = () => { useEffect(() => { setGlobalState({ refreshInterval }); timefilter.setRefreshInterval(refreshInterval); - }, [refreshInterval?.pause, refreshInterval?.value]); + }, [refreshInterval?.pause, refreshInterval?.value, setGlobalState]); const [time, setTime] = useState(timefilter.getTime()); const [recentlyUsedRanges, setRecentlyUsedRanges] = useState(getRecentlyUsedRanges()); diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_utils.js b/x-pack/plugins/ml/public/application/explorer/explorer_utils.js index c309e1f4ef8e8..c3bdacde5abd8 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_utils.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_utils.js @@ -198,7 +198,7 @@ export function getSelectionTimeRange(selectedCells, interval, bounds) { latestMs = bounds.max.valueOf(); if (selectedCells.times[1] !== undefined) { // Subtract 1 ms so search does not include start of next bucket. - latestMs = (selectedCells.times[1] + interval) * 1000 - 1; + latestMs = selectedCells.times[1] * 1000 - 1; } } diff --git a/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.ts b/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.ts index f356d79c0a8e1..c7cda2372bceb 100644 --- a/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.ts +++ b/x-pack/plugins/ml/public/application/explorer/hooks/use_selected_cells.ts @@ -60,7 +60,7 @@ export const useSelectedCells = ( setAppState('mlExplorerSwimlane', mlExplorerSwimlane); } }, - [appState?.mlExplorerSwimlane, selectedCells] + [appState?.mlExplorerSwimlane, selectedCells, setAppState] ); return [selectedCells, setSelectedCells]; diff --git a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx index 0a2791edb9c50..9d6d6c14ed659 100644 --- a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx +++ b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx @@ -41,6 +41,7 @@ import { getFormattedSeverityScore } from '../../../common/util/anomaly_utils'; import './_explorer.scss'; import { EMPTY_FIELD_VALUE_LABEL } from '../timeseriesexplorer/components/entity_control/entity_control'; +import { useUiSettings } from '../contexts/kibana'; /** * Ignore insignificant resize, e.g. browser scrollbar appearance. @@ -159,6 +160,8 @@ export const SwimlaneContainer: FC = ({ }) => { const [chartWidth, setChartWidth] = useState(0); + const isDarkTheme = !!useUiSettings().get('theme:darkMode'); + // Holds the container height for previously fetched data const containerHeightRef = useRef(); @@ -235,67 +238,76 @@ export const SwimlaneContainer: FC = ({ return { x: selection.times.map((v) => v * 1000), y: selection.lanes }; }, [selection, swimlaneData, swimlaneType]); - const swimLaneConfig: HeatmapSpec['config'] = useMemo( - () => - showSwimlane - ? { - onBrushEnd: (e: HeatmapBrushEvent) => { - onCellsSelection({ - lanes: e.y as string[], - times: e.x.map((v) => (v as number) / 1000), - type: swimlaneType, - viewByFieldName: swimlaneData.fieldName, - }); - }, - grid: { - cellHeight: { - min: CELL_HEIGHT, - max: CELL_HEIGHT, - }, - stroke: { - width: 1, - color: '#D3DAE6', - }, - }, - cell: { - maxWidth: 'fill', - maxHeight: 'fill', - label: { - visible: false, - }, - border: { - stroke: '#D3DAE6', - strokeWidth: 0, - }, - }, - yAxisLabel: { - visible: true, - width: 170, - // eui color subdued - fill: `#6a717d`, - padding: 8, - formatter: (laneLabel: string) => { - return laneLabel === '' ? EMPTY_FIELD_VALUE_LABEL : laneLabel; - }, - }, - xAxisLabel: { - visible: showTimeline, - // eui color subdued - fill: `#98A2B3`, - formatter: (v: number) => { - timeBuckets.setInterval(`${swimlaneData.interval}s`); - const a = timeBuckets.getScaledDateFormat(); - return moment(v).format(a); - }, - }, - brushMask: { - fill: 'rgb(247 247 247 / 50%)', - }, - maxLegendHeight: LEGEND_HEIGHT, - } - : {}, - [showSwimlane, swimlaneType, swimlaneData?.fieldName] - ); + const swimLaneConfig: HeatmapSpec['config'] = useMemo(() => { + if (!showSwimlane) return {}; + + return { + onBrushEnd: (e: HeatmapBrushEvent) => { + onCellsSelection({ + lanes: e.y as string[], + times: e.x.map((v) => (v as number) / 1000), + type: swimlaneType, + viewByFieldName: swimlaneData.fieldName, + }); + }, + grid: { + cellHeight: { + min: CELL_HEIGHT, + max: CELL_HEIGHT, + }, + stroke: { + width: 1, + color: '#D3DAE6', + }, + }, + cell: { + maxWidth: 'fill', + maxHeight: 'fill', + label: { + visible: false, + }, + border: { + stroke: '#D3DAE6', + strokeWidth: 0, + }, + }, + yAxisLabel: { + visible: true, + width: 170, + // eui color subdued + fill: `#6a717d`, + padding: 8, + formatter: (laneLabel: string) => { + return laneLabel === '' ? EMPTY_FIELD_VALUE_LABEL : laneLabel; + }, + }, + xAxisLabel: { + visible: showTimeline, + // eui color subdued + fill: `#98A2B3`, + formatter: (v: number) => { + timeBuckets.setInterval(`${swimlaneData.interval}s`); + const scaledDateFormat = timeBuckets.getScaledDateFormat(); + return moment(v).format(scaledDateFormat); + }, + }, + brushMask: { + fill: isDarkTheme ? 'rgb(30,31,35,80%)' : 'rgb(247,247,247,50%)', + }, + brushArea: { + stroke: isDarkTheme ? 'rgb(255, 255, 255)' : 'rgb(105, 112, 125)', + }, + maxLegendHeight: LEGEND_HEIGHT, + timeZone: 'UTC', + }; + }, [ + showSwimlane, + swimlaneType, + swimlaneData?.fieldName, + isDarkTheme, + timeBuckets, + onCellsSelection, + ]); // @ts-ignore const onElementClick: ElementClickListener = useCallback( @@ -310,7 +322,7 @@ export const SwimlaneContainer: FC = ({ }; onCellsSelection(payload); }, - [swimlaneType, swimlaneData?.fieldName, swimlaneData?.interval] + [swimlaneType, swimlaneData?.fieldName, swimlaneData?.interval, onCellsSelection] ); const tooltipOptions: TooltipSettings = useMemo( diff --git a/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx b/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx index 00d64a2f1bd1d..cb6944e0ecf05 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/explorer.tsx @@ -82,8 +82,9 @@ const ExplorerUrlStateManager: FC = ({ jobsWithTim const { jobIds } = useJobSelection(jobsWithTimeRange); const refresh = useRefresh(); + useEffect(() => { - if (refresh !== undefined) { + if (refresh !== undefined && lastRefresh !== refresh.lastRefresh) { setLastRefresh(refresh?.lastRefresh); if (refresh.timeRange !== undefined) { @@ -94,7 +95,7 @@ const ExplorerUrlStateManager: FC = ({ jobsWithTim }); } } - }, [refresh?.lastRefresh]); + }, [refresh?.lastRefresh, lastRefresh, setLastRefresh, setGlobalState]); // We cannot simply infer bounds from the globalState's `time` attribute // with `moment` since it can contain custom strings such as `now-15m`. @@ -194,6 +195,7 @@ const ExplorerUrlStateManager: FC = ({ jobsWithTim const [tableSeverity] = useTableSeverity(); const [selectedCells, setSelectedCells] = useSelectedCells(appState, setAppState); + useEffect(() => { explorerService.setSelectedCells(selectedCells); }, [JSON.stringify(selectedCells)]); @@ -220,9 +222,9 @@ const ExplorerUrlStateManager: FC = ({ jobsWithTim if (explorerState && explorerState.swimlaneContainerWidth > 0) { loadExplorerData({ ...loadExplorerDataConfig, - swimlaneLimit: - isViewBySwimLaneData(explorerState?.viewBySwimlaneData) && - explorerState?.viewBySwimlaneData.cardinality, + swimlaneLimit: isViewBySwimLaneData(explorerState?.viewBySwimlaneData) + ? explorerState?.viewBySwimlaneData.cardinality + : undefined, }); } }, [JSON.stringify(loadExplorerDataConfig)]); diff --git a/x-pack/plugins/ml/public/application/util/url_state.tsx b/x-pack/plugins/ml/public/application/util/url_state.tsx index c288a00bb06da..a3c70e1130904 100644 --- a/x-pack/plugins/ml/public/application/util/url_state.tsx +++ b/x-pack/plugins/ml/public/application/util/url_state.tsx @@ -140,12 +140,12 @@ export const useUrlState = (accessor: Accessor) => { if (typeof fullUrlState === 'object') { return fullUrlState[accessor]; } - return undefined; }, [searchString]); const setUrlState = useCallback( - (attribute: string | Dictionary, value?: any) => - setUrlStateContext(accessor, attribute, value), + (attribute: string | Dictionary, value?: any) => { + setUrlStateContext(accessor, attribute, value); + }, [accessor, setUrlStateContext] ); return [urlState, setUrlState]; diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx index 6e67ff1aef03d..4730371c611c1 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/anomaly_swimlane_embeddable.tsx @@ -9,6 +9,7 @@ import ReactDOM from 'react-dom'; import { CoreStart } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { Subject } from 'rxjs'; +import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; import { Embeddable, IContainer } from '../../../../../../src/plugins/embeddable/public'; import { EmbeddableSwimLaneContainer } from './embeddable_swim_lane_container_lazy'; import type { JobId } from '../../../common/types/anomaly_detection_jobs'; @@ -59,17 +60,19 @@ export class AnomalySwimlaneEmbeddable extends Embeddable< ReactDOM.render( - - - + + + + + , node ); diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx index 17ae97e3c07bb..5efe70ba552f5 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx +++ b/x-pack/plugins/ml/public/embeddables/anomaly_swimlane/embeddable_swim_lane_container.tsx @@ -89,7 +89,7 @@ export const EmbeddableSwimLaneContainer: FC = ( }); } }, - [swimlaneData, perPage, fromPage] + [swimlaneData, perPage, fromPage, setSelectedCells] ); if (error) { diff --git a/x-pack/plugins/ml/public/ui_actions/apply_time_range_action.tsx b/x-pack/plugins/ml/public/ui_actions/apply_time_range_action.tsx index 325e903de0e2d..79e6ff53bff43 100644 --- a/x-pack/plugins/ml/public/ui_actions/apply_time_range_action.tsx +++ b/x-pack/plugins/ml/public/ui_actions/apply_time_range_action.tsx @@ -39,8 +39,7 @@ export function createApplyTimeRangeSelectionAction( let [from, to] = data.times; from = from * 1000; - // extend bounds with the interval - to = to * 1000 + interval * 1000; + to = to * 1000; timefilter.setTime({ from: moment(from), From 255b865be8131fc7012a968d675f3c9113ed0bdd Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Tue, 13 Oct 2020 17:00:38 +0200 Subject: [PATCH 007/128] removing kibana_datatable` in favor of `datatable` (#75184) --- ...beddable-public.rangeselectcontext.data.md | 2 +- ...ns-embeddable-public.rangeselectcontext.md | 2 +- ...mbeddable-public.valueclickcontext.data.md | 2 +- ...ins-embeddable-public.valueclickcontext.md | 2 +- ...-expressions-public.datatablecolumntype.md | 4 +- ...ressions-public.kibanadatatable.columns.md | 11 - ...gins-expressions-public.kibanadatatable.md | 20 -- ...expressions-public.kibanadatatable.rows.md | 11 - ...expressions-public.kibanadatatable.type.md | 11 - ...public.kibanadatatablecolumn.formathint.md | 11 - ...essions-public.kibanadatatablecolumn.id.md | 11 - ...xpressions-public.kibanadatatablecolumn.md | 21 -- ...sions-public.kibanadatatablecolumn.meta.md | 11 - ...sions-public.kibanadatatablecolumn.name.md | 11 - ...banadatatablecolumnmeta.aggconfigparams.md | 11 - ...ibanadatatablecolumnmeta.indexpatternid.md | 11 - ...ssions-public.kibanadatatablecolumnmeta.md | 20 -- ...s-public.kibanadatatablecolumnmeta.type.md | 11 - ...s-expressions-public.kibanadatatablerow.md | 11 - ...ibana-plugin-plugins-expressions-public.md | 6 +- ...-expressions-server.datatablecolumntype.md | 4 +- ...ressions-server.kibanadatatable.columns.md | 11 - ...gins-expressions-server.kibanadatatable.md | 20 -- ...expressions-server.kibanadatatable.rows.md | 11 - ...expressions-server.kibanadatatable.type.md | 11 - ...server.kibanadatatablecolumn.formathint.md | 11 - ...essions-server.kibanadatatablecolumn.id.md | 11 - ...xpressions-server.kibanadatatablecolumn.md | 21 -- ...sions-server.kibanadatatablecolumn.meta.md | 11 - ...sions-server.kibanadatatablecolumn.name.md | 11 - ...banadatatablecolumnmeta.aggconfigparams.md | 11 - ...ibanadatatablecolumnmeta.indexpatternid.md | 11 - ...ssions-server.kibanadatatablecolumnmeta.md | 20 -- ...s-server.kibanadatatablecolumnmeta.type.md | 11 - ...s-expressions-server.kibanadatatablerow.md | 11 - ...ibana-plugin-plugins-expressions-server.md | 6 +- .../data/common/search/expressions/esaggs.ts | 4 +- .../create_filters_from_range_select.test.ts | 53 +++-- .../create_filters_from_range_select.ts | 17 +- .../create_filters_from_value_click.test.ts | 20 +- .../create_filters_from_value_click.ts | 36 ++-- .../data/public/search/expressions/esaggs.ts | 38 ++-- .../data/public/search/expressions/index.ts | 1 - .../public/search/expressions/utils/index.ts | 20 -- .../expressions/utils/serialize_agg_config.ts | 54 ----- .../public/lib/triggers/triggers.ts | 6 +- src/plugins/embeddable/public/public.api.md | 10 +- .../expression_types/specs/datatable.ts | 41 +++- .../common/expression_types/specs/index.ts | 3 - .../specs/kibana_datatable.ts | 75 ------- src/plugins/expressions/public/index.ts | 4 - src/plugins/expressions/public/public.api.md | 52 +---- src/plugins/expressions/server/index.ts | 4 - src/plugins/expressions/server/server.api.md | 52 +---- .../public/input_control_fn.ts | 4 +- .../__snapshots__/region_map_fn.test.js.snap | 2 +- .../region_map/public/region_map_fn.js | 2 +- .../region_map/public/region_map_fn.test.js | 2 +- src/plugins/tile_map/public/tile_map_fn.js | 2 +- .../tile_map/public/tile_map_visualization.js | 16 +- .../tile_map/public/tilemap_fn.test.js | 2 +- .../public/markdown_renderer.tsx | 2 +- .../__snapshots__/metric_vis_fn.test.ts.snap | 2 +- .../components/metric_vis_component.tsx | 4 +- .../public/metric_vis_fn.test.ts | 2 +- .../vis_type_metric/public/metric_vis_fn.ts | 6 +- .../public/metric_vis_renderer.tsx | 6 +- .../public/table_vis_fn.test.ts | 2 +- .../vis_type_table/public/table_vis_fn.ts | 6 +- .../__snapshots__/tag_cloud_fn.test.ts.snap | 2 +- .../public/tag_cloud_fn.test.ts | 2 +- .../vis_type_tagcloud/public/tag_cloud_fn.ts | 8 +- .../public/tag_cloud_vis_renderer.tsx | 2 +- .../public/timelion_vis_renderer.tsx | 2 +- .../vis_type_vislib/public/pie_fn.test.ts | 2 +- src/plugins/vis_type_vislib/public/pie_fn.ts | 6 +- .../public/vis_type_vislib_vis_fn.ts | 6 +- .../components/visualization_container.tsx | 5 +- .../public/expression_functions/range.ts | 4 +- .../expression_functions/vis_dimension.ts | 10 +- .../snapshots/baseline/combined_test2.json | 2 +- .../snapshots/baseline/combined_test3.json | 2 +- .../snapshots/baseline/final_output_test.json | 2 +- .../snapshots/baseline/metric_all_data.json | 2 +- .../baseline/metric_invalid_data.json | 2 +- .../baseline/metric_multi_metric_data.json | 2 +- .../baseline/metric_percentage_mode.json | 2 +- .../baseline/metric_single_metric_data.json | 2 +- .../snapshots/baseline/partial_test_1.json | 2 +- .../snapshots/baseline/partial_test_2.json | 2 +- .../snapshots/baseline/partial_test_3.json | 2 +- .../snapshots/baseline/step_output_test2.json | 2 +- .../snapshots/baseline/step_output_test3.json | 2 +- .../snapshots/baseline/tagcloud_all_data.json | 2 +- .../snapshots/baseline/tagcloud_fontsize.json | 2 +- .../baseline/tagcloud_invalid_data.json | 2 +- .../baseline/tagcloud_metric_data.json | 2 +- .../snapshots/baseline/tagcloud_options.json | 2 +- .../snapshots/session/combined_test2.json | 2 +- .../snapshots/session/combined_test3.json | 2 +- .../snapshots/session/final_output_test.json | 2 +- .../snapshots/session/metric_all_data.json | 2 +- .../session/metric_invalid_data.json | 2 +- .../session/metric_multi_metric_data.json | 2 +- .../session/metric_percentage_mode.json | 2 +- .../session/metric_single_metric_data.json | 2 +- .../snapshots/session/partial_test_1.json | 2 +- .../snapshots/session/partial_test_2.json | 2 +- .../snapshots/session/partial_test_3.json | 2 +- .../snapshots/session/step_output_test2.json | 2 +- .../snapshots/session/step_output_test3.json | 2 +- .../snapshots/session/tagcloud_all_data.json | 2 +- .../snapshots/session/tagcloud_fontsize.json | 2 +- .../session/tagcloud_invalid_data.json | 2 +- .../session/tagcloud_metric_data.json | 2 +- .../snapshots/session/tagcloud_options.json | 2 +- .../test_suites/run_pipeline/helpers.ts | 14 +- .../public/components/datatable/datatable.tsx | 6 +- x-pack/plugins/canvas/types/state.ts | 2 - .../public/lib/url_drilldown.test.ts | 18 +- .../public/lib/url_drilldown_scope.test.ts | 11 +- .../public/lib/url_drilldown_scope.ts | 4 +- .../expression.test.tsx | 70 ++++++- .../datatable_visualization/expression.tsx | 13 +- .../editor_frame_service/format_column.ts | 26 ++- .../editor_frame_service/merge_tables.test.ts | 18 +- .../editor_frame_service/merge_tables.ts | 8 +- .../rename_columns.test.ts | 56 +++-- .../indexpattern_datasource/rename_columns.ts | 18 +- .../metric_visualization/expression.test.tsx | 8 +- .../metric_visualization/expression.tsx | 4 +- .../render_function.test.tsx | 19 +- .../pie_visualization/render_function.tsx | 4 +- .../pie_visualization/render_helpers.test.ts | 55 ++--- .../pie_visualization/render_helpers.ts | 6 +- .../lens/public/pie_visualization/types.ts | 6 +- x-pack/plugins/lens/public/types.ts | 4 +- x-pack/plugins/lens/public/utils.test.ts | 8 +- x-pack/plugins/lens/public/utils.ts | 6 +- .../axes_configuration.test.ts | 132 +++++++----- .../xy_visualization/axes_configuration.ts | 9 +- .../xy_visualization/expression.test.tsx | 196 ++++++++++-------- .../public/xy_visualization/expression.tsx | 23 +- 143 files changed, 708 insertions(+), 1099 deletions(-) delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.type.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md delete mode 100644 docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablerow.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.type.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md delete mode 100644 docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablerow.md delete mode 100644 src/plugins/data/public/search/expressions/utils/index.ts delete mode 100644 src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts delete mode 100644 src/plugins/expressions/common/expression_types/specs/kibana_datatable.ts diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md index 6d2774d86f109..f11003887a6df 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md @@ -8,7 +8,7 @@ ```typescript data: { - table: KibanaDatatable; + table: Datatable; column: number; range: number[]; timeFieldName?: string; diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.md index 0f92ed86301da..f23cb44a7f014 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.rangeselectcontext.md @@ -14,6 +14,6 @@ export interface RangeSelectContext | Property | Type | Description | | --- | --- | --- | -| [data](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md) | {
table: KibanaDatatable;
column: number;
range: number[];
timeFieldName?: string;
} | | +| [data](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.data.md) | {
table: Datatable;
column: number;
range: number[];
timeFieldName?: string;
} | | | [embeddable](./kibana-plugin-plugins-embeddable-public.rangeselectcontext.embeddable.md) | T | | diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md index 92c33affc47a9..e7c1be172cd70 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md @@ -9,7 +9,7 @@ ```typescript data: { data: Array<{ - table: Pick; + table: Pick; column: number; row: number; value: any; diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.md index 13133095956c6..875c8d276160e 100644 --- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.md +++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.valueclickcontext.md @@ -14,6 +14,6 @@ export interface ValueClickContext | Property | Type | Description | | --- | --- | --- | -| [data](./kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md) | {
data: Array<{
table: Pick<KibanaDatatable, 'rows' | 'columns'>;
column: number;
row: number;
value: any;
}>;
timeFieldName?: string;
negate?: boolean;
} | | +| [data](./kibana-plugin-plugins-embeddable-public.valueclickcontext.data.md) | {
data: Array<{
table: Pick<Datatable, 'rows' | 'columns'>;
column: number;
row: number;
value: any;
}>;
timeFieldName?: string;
negate?: boolean;
} | | | [embeddable](./kibana-plugin-plugins-embeddable-public.valueclickcontext.embeddable.md) | T | | diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumntype.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumntype.md index a06ab351e62c3..8f134bd3bfe95 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumntype.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.datatablecolumntype.md @@ -4,10 +4,10 @@ ## DatatableColumnType type -This type represents the `type` of any `DatatableColumn` in a `Datatable`. +This type represents the `type` of any `DatatableColumn` in a `Datatable`. its duplicated from KBN\_FIELD\_TYPES Signature: ```typescript -export declare type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; +export declare type DatatableColumnType = '_source' | 'attachment' | 'boolean' | 'date' | 'geo_point' | 'geo_shape' | 'ip' | 'murmur3' | 'number' | 'string' | 'unknown' | 'conflict' | 'object' | 'nested' | 'histogram' | 'null'; ``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md deleted file mode 100644 index c8aa768a883d6..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) > [columns](./kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md) - -## KibanaDatatable.columns property - -Signature: - -```typescript -columns: KibanaDatatableColumn[]; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.md deleted file mode 100644 index 4ea1d6f42b66d..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) - -## KibanaDatatable interface - -Signature: - -```typescript -export interface KibanaDatatable -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [columns](./kibana-plugin-plugins-expressions-public.kibanadatatable.columns.md) | KibanaDatatableColumn[] | | -| [rows](./kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md) | KibanaDatatableRow[] | | -| [type](./kibana-plugin-plugins-expressions-public.kibanadatatable.type.md) | typeof name | | - diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md deleted file mode 100644 index 43f3243dc4fa7..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) > [rows](./kibana-plugin-plugins-expressions-public.kibanadatatable.rows.md) - -## KibanaDatatable.rows property - -Signature: - -```typescript -rows: KibanaDatatableRow[]; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.type.md deleted file mode 100644 index 996f59cbb77a1..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatable.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) > [type](./kibana-plugin-plugins-expressions-public.kibanadatatable.type.md) - -## KibanaDatatable.type property - -Signature: - -```typescript -type: typeof name; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md deleted file mode 100644 index b517c1610261b..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) > [formatHint](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md) - -## KibanaDatatableColumn.formatHint property - -Signature: - -```typescript -formatHint?: SerializedFieldFormat; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md deleted file mode 100644 index e7d43190589a7..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) > [id](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md) - -## KibanaDatatableColumn.id property - -Signature: - -```typescript -id: string; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md deleted file mode 100644 index 138c19f0ec7bd..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) - -## KibanaDatatableColumn interface - -Signature: - -```typescript -export interface KibanaDatatableColumn -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [formatHint](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.formathint.md) | SerializedFieldFormat | | -| [id](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.id.md) | string | | -| [meta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md) | KibanaDatatableColumnMeta | | -| [name](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md) | string | | - diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md deleted file mode 100644 index df2d09bf3cc55..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) > [meta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.meta.md) - -## KibanaDatatableColumn.meta property - -Signature: - -```typescript -meta?: KibanaDatatableColumnMeta; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md deleted file mode 100644 index 841ad67f3f521..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) > [name](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.name.md) - -## KibanaDatatableColumn.name property - -Signature: - -```typescript -name: string; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md deleted file mode 100644 index 2ec6edda4cbca..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) > [aggConfigParams](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md) - -## KibanaDatatableColumnMeta.aggConfigParams property - -Signature: - -```typescript -aggConfigParams?: Record; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md deleted file mode 100644 index 2287c28398f7f..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) > [indexPatternId](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md) - -## KibanaDatatableColumnMeta.indexPatternId property - -Signature: - -```typescript -indexPatternId?: string; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md deleted file mode 100644 index b2f8c9d06a727..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) - -## KibanaDatatableColumnMeta interface - -Signature: - -```typescript -export interface KibanaDatatableColumnMeta -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [aggConfigParams](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.aggconfigparams.md) | Record<string, any> | | -| [indexPatternId](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.indexpatternid.md) | string | | -| [type](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md) | string | | - diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md deleted file mode 100644 index 98d4a0c2d43c3..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) > [type](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.type.md) - -## KibanaDatatableColumnMeta.type property - -Signature: - -```typescript -type: string; -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablerow.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablerow.md deleted file mode 100644 index cb5f1ad70f628..0000000000000 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.kibanadatatablerow.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-public](./kibana-plugin-plugins-expressions-public.md) > [KibanaDatatableRow](./kibana-plugin-plugins-expressions-public.kibanadatatablerow.md) - -## KibanaDatatableRow interface - -Signature: - -```typescript -export interface KibanaDatatableRow -``` diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md index b0c732188a46e..db09f966e2fa5 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.md @@ -72,10 +72,6 @@ | [IExpressionLoaderParams](./kibana-plugin-plugins-expressions-public.iexpressionloaderparams.md) | | | [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-public.iinterpreterrenderhandlers.md) | | | [IRegistry](./kibana-plugin-plugins-expressions-public.iregistry.md) | | -| [KibanaDatatable](./kibana-plugin-plugins-expressions-public.kibanadatatable.md) | | -| [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumn.md) | | -| [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-public.kibanadatatablecolumnmeta.md) | | -| [KibanaDatatableRow](./kibana-plugin-plugins-expressions-public.kibanadatatablerow.md) | | | [PointSeriesColumn](./kibana-plugin-plugins-expressions-public.pointseriescolumn.md) | Column in a PointSeries | | [Range](./kibana-plugin-plugins-expressions-public.range.md) | | | [ReactExpressionRendererProps](./kibana-plugin-plugins-expressions-public.reactexpressionrendererprops.md) | | @@ -95,7 +91,7 @@ | [AnyExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-public.anyexpressionfunctiondefinition.md) | Type to capture every possible expression function definition. | | [AnyExpressionTypeDefinition](./kibana-plugin-plugins-expressions-public.anyexpressiontypedefinition.md) | | | [ArgumentType](./kibana-plugin-plugins-expressions-public.argumenttype.md) | This type represents all of the possible combinations of properties of an Argument in an Expression Function. The presence or absence of certain fields influence the shape and presence of others within each arg in the specification. | -| [DatatableColumnType](./kibana-plugin-plugins-expressions-public.datatablecolumntype.md) | This type represents the type of any DatatableColumn in a Datatable. | +| [DatatableColumnType](./kibana-plugin-plugins-expressions-public.datatablecolumntype.md) | This type represents the type of any DatatableColumn in a Datatable. its duplicated from KBN\_FIELD\_TYPES | | [DatatableRow](./kibana-plugin-plugins-expressions-public.datatablerow.md) | This type represents a row in a Datatable. | | [ExecutionContainer](./kibana-plugin-plugins-expressions-public.executioncontainer.md) | | | [ExecutorContainer](./kibana-plugin-plugins-expressions-public.executorcontainer.md) | | diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumntype.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumntype.md index 4afce913526de..dc98acffa1236 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumntype.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.datatablecolumntype.md @@ -4,10 +4,10 @@ ## DatatableColumnType type -This type represents the `type` of any `DatatableColumn` in a `Datatable`. +This type represents the `type` of any `DatatableColumn` in a `Datatable`. its duplicated from KBN\_FIELD\_TYPES Signature: ```typescript -export declare type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; +export declare type DatatableColumnType = '_source' | 'attachment' | 'boolean' | 'date' | 'geo_point' | 'geo_shape' | 'ip' | 'murmur3' | 'number' | 'string' | 'unknown' | 'conflict' | 'object' | 'nested' | 'histogram' | 'null'; ``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md deleted file mode 100644 index 423e543e4307a..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) > [columns](./kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md) - -## KibanaDatatable.columns property - -Signature: - -```typescript -columns: KibanaDatatableColumn[]; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.md deleted file mode 100644 index 30ee3ac2fcd13..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) - -## KibanaDatatable interface - -Signature: - -```typescript -export interface KibanaDatatable -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [columns](./kibana-plugin-plugins-expressions-server.kibanadatatable.columns.md) | KibanaDatatableColumn[] | | -| [rows](./kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md) | KibanaDatatableRow[] | | -| [type](./kibana-plugin-plugins-expressions-server.kibanadatatable.type.md) | typeof name | | - diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md deleted file mode 100644 index 42170a83fc3c8..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) > [rows](./kibana-plugin-plugins-expressions-server.kibanadatatable.rows.md) - -## KibanaDatatable.rows property - -Signature: - -```typescript -rows: KibanaDatatableRow[]; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.type.md deleted file mode 100644 index c36674540a1ba..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatable.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) > [type](./kibana-plugin-plugins-expressions-server.kibanadatatable.type.md) - -## KibanaDatatable.type property - -Signature: - -```typescript -type: typeof name; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md deleted file mode 100644 index a1e6949019dcb..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) > [formatHint](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md) - -## KibanaDatatableColumn.formatHint property - -Signature: - -```typescript -formatHint?: SerializedFieldFormat; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md deleted file mode 100644 index 6f90da1ac9c94..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) > [id](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md) - -## KibanaDatatableColumn.id property - -Signature: - -```typescript -id: string; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md deleted file mode 100644 index 171477911502f..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md +++ /dev/null @@ -1,21 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) - -## KibanaDatatableColumn interface - -Signature: - -```typescript -export interface KibanaDatatableColumn -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [formatHint](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.formathint.md) | SerializedFieldFormat | | -| [id](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.id.md) | string | | -| [meta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md) | KibanaDatatableColumnMeta | | -| [name](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md) | string | | - diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md deleted file mode 100644 index 40b20d51e6ec6..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) > [meta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.meta.md) - -## KibanaDatatableColumn.meta property - -Signature: - -```typescript -meta?: KibanaDatatableColumnMeta; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md deleted file mode 100644 index 3a85e2325483a..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) > [name](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.name.md) - -## KibanaDatatableColumn.name property - -Signature: - -```typescript -name: string; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md deleted file mode 100644 index 539b24174f725..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) > [aggConfigParams](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md) - -## KibanaDatatableColumnMeta.aggConfigParams property - -Signature: - -```typescript -aggConfigParams?: Record; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md deleted file mode 100644 index 2704915a15071..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) > [indexPatternId](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md) - -## KibanaDatatableColumnMeta.indexPatternId property - -Signature: - -```typescript -indexPatternId?: string; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md deleted file mode 100644 index d9a96e665f010..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) - -## KibanaDatatableColumnMeta interface - -Signature: - -```typescript -export interface KibanaDatatableColumnMeta -``` - -## Properties - -| Property | Type | Description | -| --- | --- | --- | -| [aggConfigParams](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.aggconfigparams.md) | Record<string, any> | | -| [indexPatternId](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.indexpatternid.md) | string | | -| [type](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md) | string | | - diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md deleted file mode 100644 index 56e3757ef621a..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) > [type](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.type.md) - -## KibanaDatatableColumnMeta.type property - -Signature: - -```typescript -type: string; -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablerow.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablerow.md deleted file mode 100644 index dd0f3f4cb2f60..0000000000000 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.kibanadatatablerow.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-plugins-expressions-server](./kibana-plugin-plugins-expressions-server.md) > [KibanaDatatableRow](./kibana-plugin-plugins-expressions-server.kibanadatatablerow.md) - -## KibanaDatatableRow interface - -Signature: - -```typescript -export interface KibanaDatatableRow -``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md index dd7c7af466bd0..9e2189dad2732 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.md @@ -63,10 +63,6 @@ | [Font](./kibana-plugin-plugins-expressions-server.font.md) | An interface representing a font in Canvas, with a textual label and the CSS font-value. | | [IInterpreterRenderHandlers](./kibana-plugin-plugins-expressions-server.iinterpreterrenderhandlers.md) | | | [IRegistry](./kibana-plugin-plugins-expressions-server.iregistry.md) | | -| [KibanaDatatable](./kibana-plugin-plugins-expressions-server.kibanadatatable.md) | | -| [KibanaDatatableColumn](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumn.md) | | -| [KibanaDatatableColumnMeta](./kibana-plugin-plugins-expressions-server.kibanadatatablecolumnmeta.md) | | -| [KibanaDatatableRow](./kibana-plugin-plugins-expressions-server.kibanadatatablerow.md) | | | [PointSeriesColumn](./kibana-plugin-plugins-expressions-server.pointseriescolumn.md) | Column in a PointSeries | | [Range](./kibana-plugin-plugins-expressions-server.range.md) | | | [SerializedDatatable](./kibana-plugin-plugins-expressions-server.serializeddatatable.md) | | @@ -79,7 +75,7 @@ | [AnyExpressionFunctionDefinition](./kibana-plugin-plugins-expressions-server.anyexpressionfunctiondefinition.md) | Type to capture every possible expression function definition. | | [AnyExpressionTypeDefinition](./kibana-plugin-plugins-expressions-server.anyexpressiontypedefinition.md) | | | [ArgumentType](./kibana-plugin-plugins-expressions-server.argumenttype.md) | This type represents all of the possible combinations of properties of an Argument in an Expression Function. The presence or absence of certain fields influence the shape and presence of others within each arg in the specification. | -| [DatatableColumnType](./kibana-plugin-plugins-expressions-server.datatablecolumntype.md) | This type represents the type of any DatatableColumn in a Datatable. | +| [DatatableColumnType](./kibana-plugin-plugins-expressions-server.datatablecolumntype.md) | This type represents the type of any DatatableColumn in a Datatable. its duplicated from KBN\_FIELD\_TYPES | | [DatatableRow](./kibana-plugin-plugins-expressions-server.datatablerow.md) | This type represents a row in a Datatable. | | [ExecutionContainer](./kibana-plugin-plugins-expressions-server.executioncontainer.md) | | | [ExecutorContainer](./kibana-plugin-plugins-expressions-server.executorcontainer.md) | | diff --git a/src/plugins/data/common/search/expressions/esaggs.ts b/src/plugins/data/common/search/expressions/esaggs.ts index 2957512886b4d..4f65babdcd360 100644 --- a/src/plugins/data/common/search/expressions/esaggs.ts +++ b/src/plugins/data/common/search/expressions/esaggs.ts @@ -19,12 +19,12 @@ import { KibanaContext, - KibanaDatatable, + Datatable, ExpressionFunctionDefinition, } from '../../../../../plugins/expressions/common'; type Input = KibanaContext | null; -type Output = Promise; +type Output = Promise; interface Arguments { index: string; diff --git a/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts index aa96d77d873d3..6dcfa4d02bcb2 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.test.ts @@ -21,7 +21,12 @@ import moment from 'moment'; import { createFiltersFromRangeSelectAction } from './create_filters_from_range_select'; -import { IndexPatternsContract, RangeFilter } from '../../../public'; +import { + fieldFormats, + FieldFormatsGetConfigFn, + IndexPatternsContract, + RangeFilter, +} from '../../../public'; import { dataPluginMock } from '../../../public/mocks'; import { setIndexPatterns, setSearchService } from '../../../public/services'; import { TriggerContextMapping } from '../../../../ui_actions/public'; @@ -31,23 +36,30 @@ describe('brushEvent', () => { const JAN_01_2014 = 1388559600000; let baseEvent: TriggerContextMapping['SELECT_RANGE_TRIGGER']['data']; + const mockField = { + name: 'time', + indexPattern: { + id: 'logstash-*', + }, + filterable: true, + format: new fieldFormats.DateFormat({}, (() => {}) as FieldFormatsGetConfigFn), + }; + const indexPattern = { id: 'indexPatternId', timeFieldName: 'time', fields: { - getByName: () => undefined, - filter: () => [], + getByName: () => mockField, + filter: () => [mockField], }, }; - const aggConfigs = [ - { - params: { - field: {}, - }, - getIndexPattern: () => indexPattern, + const serializedAggConfig = { + type: 'date_histogram', + params: { + field: {}, }, - ]; + }; beforeEach(() => { const dataStart = dataPluginMock.createStartContract(); @@ -60,15 +72,18 @@ describe('brushEvent', () => { baseEvent = { column: 0, table: { - type: 'kibana_datatable', + type: 'datatable', columns: [ { id: '1', name: '1', meta: { - type: 'histogram', - indexPatternId: 'indexPatternId', - aggConfigParams: aggConfigs[0].params, + type: 'date', + sourceParams: { + indexPatternId: 'indexPatternId', + ...serializedAggConfig, + }, + source: 'esaggs', }, }, ], @@ -90,7 +105,7 @@ describe('brushEvent', () => { describe('handles an event when the x-axis field is a date field', () => { describe('date field is index pattern timefield', () => { beforeEach(() => { - aggConfigs[0].params.field = { + serializedAggConfig.params.field = { name: 'time', type: 'date', }; @@ -98,7 +113,7 @@ describe('brushEvent', () => { afterAll(() => { baseEvent.range = []; - aggConfigs[0].params.field = {}; + serializedAggConfig.params.field = {}; }); test('by ignoring the event when range spans zero time', async () => { @@ -123,7 +138,7 @@ describe('brushEvent', () => { describe('date field is not index pattern timefield', () => { beforeEach(() => { - aggConfigs[0].params.field = { + serializedAggConfig.params.field = { name: 'anotherTimeField', type: 'date', }; @@ -131,7 +146,7 @@ describe('brushEvent', () => { afterAll(() => { baseEvent.range = []; - aggConfigs[0].params.field = {}; + serializedAggConfig.params.field = {}; }); test('creates a new range filter', async () => { @@ -157,7 +172,7 @@ describe('brushEvent', () => { describe('handles an event when the x-axis field is a number', () => { beforeAll(() => { - aggConfigs[0].params.field = { + serializedAggConfig.params.field = { name: 'numberField', type: 'number', }; diff --git a/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts index d9aa1b8ec8048..2d7aeff79a689 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_range_select.ts @@ -20,9 +20,9 @@ import { last } from 'lodash'; import moment from 'moment'; import { esFilters, IFieldType, RangeFilterParams } from '../../../public'; -import { getIndexPatterns } from '../../../public/services'; -import { deserializeAggConfig } from '../../search/expressions/utils'; -import type { RangeSelectContext } from '../../../../embeddable/public'; +import { getIndexPatterns, getSearchService } from '../../../public/services'; +import { RangeSelectContext } from '../../../../embeddable/public'; +import { AggConfigSerialized } from '../../../common/search/aggs'; export async function createFiltersFromRangeSelectAction(event: RangeSelectContext['data']) { const column: Record = event.table.columns[event.column]; @@ -31,11 +31,12 @@ export async function createFiltersFromRangeSelectAction(event: RangeSelectConte return []; } - const indexPattern = await getIndexPatterns().get(column.meta.indexPatternId); - const aggConfig = deserializeAggConfig({ - ...column.meta, - indexPattern, - }); + const { indexPatternId, ...aggConfigs } = column.meta.sourceParams; + const indexPattern = await getIndexPatterns().get(indexPatternId); + const aggConfigsInstance = getSearchService().aggs.createAggConfigs(indexPattern, [ + aggConfigs as AggConfigSerialized, + ]); + const aggConfig = aggConfigsInstance.aggs[0]; const field: IFieldType = aggConfig.params.field; if (!field || event.range.length <= 1) { diff --git a/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts b/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts index 2ad20c3807819..23d2ab080d75e 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_value_click.test.ts @@ -45,12 +45,16 @@ describe('createFiltersFromValueClick', () => { name: 'test', id: '1-1', meta: { - type: 'histogram', - indexPatternId: 'logstash-*', - aggConfigParams: { - field: 'bytes', - interval: 30, - otherBucket: true, + type: 'date', + source: 'esaggs', + sourceParams: { + indexPatternId: 'logstash-*', + type: 'histogram', + params: { + field: 'bytes', + interval: 30, + otherBucket: true, + }, }, }, }, @@ -91,9 +95,7 @@ describe('createFiltersFromValueClick', () => { }); test('handles an event when aggregations type is a terms', async () => { - if (dataPoints[0].table.columns[0].meta) { - dataPoints[0].table.columns[0].meta.type = 'terms'; - } + (dataPoints[0].table.columns[0].meta.sourceParams as any).type = 'terms'; const filters = await createFiltersFromValueClickAction({ data: dataPoints }); expect(filters.length).toEqual(1); diff --git a/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts b/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts index 9429df91f693c..ce7ecf434056a 100644 --- a/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts +++ b/src/plugins/data/public/actions/filters/create_filters_from_value_click.ts @@ -17,11 +17,11 @@ * under the License. */ -import { KibanaDatatable } from '../../../../../plugins/expressions/public'; -import { deserializeAggConfig } from '../../search/expressions'; +import { Datatable } from '../../../../../plugins/expressions/public'; import { esFilters, Filter } from '../../../public'; -import { getIndexPatterns } from '../../../public/services'; -import type { ValueClickContext } from '../../../../embeddable/public'; +import { getIndexPatterns, getSearchService } from '../../../public/services'; +import { ValueClickContext } from '../../../../embeddable/public'; +import { AggConfigSerialized } from '../../../common/search/aggs'; /** * For terms aggregations on `__other__` buckets, this assembles a list of applicable filter @@ -33,7 +33,7 @@ import type { ValueClickContext } from '../../../../embeddable/public'; * @return {array} - array of terms to filter against */ const getOtherBucketFilterTerms = ( - table: Pick, + table: Pick, columnIndex: number, rowIndex: number ) => { @@ -71,22 +71,28 @@ const getOtherBucketFilterTerms = ( * @return {Filter[]|undefined} - list of filters to provide to queryFilter.addFilters() */ const createFilter = async ( - table: Pick, + table: Pick, columnIndex: number, rowIndex: number ) => { - if (!table || !table.columns || !table.columns[columnIndex]) { + if ( + !table || + !table.columns || + !table.columns[columnIndex] || + !table.columns[columnIndex].meta || + table.columns[columnIndex].meta.source !== 'esaggs' || + !table.columns[columnIndex].meta.sourceParams?.indexPatternId + ) { return; } const column = table.columns[columnIndex]; - if (!column.meta || !column.meta.indexPatternId) { - return; - } - const aggConfig = deserializeAggConfig({ - type: column.meta.type, - aggConfigParams: column.meta.aggConfigParams ? column.meta.aggConfigParams : {}, - indexPattern: await getIndexPatterns().get(column.meta.indexPatternId), - }); + const { indexPatternId, ...aggConfigParams } = table.columns[columnIndex].meta + .sourceParams as any; + const aggConfigsInstance = getSearchService().aggs.createAggConfigs( + await getIndexPatterns().get(indexPatternId), + [aggConfigParams as AggConfigSerialized] + ); + const aggConfig = aggConfigsInstance.aggs[0]; let filter: Filter[] = []; const value: any = rowIndex > -1 ? table.rows[rowIndex][column.id] : null; if (value === null || value === undefined || !aggConfig.isFilterable()) { diff --git a/src/plugins/data/public/search/expressions/esaggs.ts b/src/plugins/data/public/search/expressions/esaggs.ts index 1021ef0f91d52..a84b95f22e6e8 100644 --- a/src/plugins/data/public/search/expressions/esaggs.ts +++ b/src/plugins/data/public/search/expressions/esaggs.ts @@ -19,7 +19,7 @@ import { get, hasIn } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { KibanaDatatable, KibanaDatatableColumn } from 'src/plugins/expressions/public'; +import { Datatable, DatatableColumn } from 'src/plugins/expressions/public'; import { PersistedState } from '../../../../../plugins/visualizations/public'; import { Adapters } from '../../../../../plugins/inspector/public'; @@ -49,7 +49,6 @@ import { getSearchService, } from '../../services'; import { buildTabularInspectorData } from './build_tabular_inspector_data'; -import { serializeAggConfig } from './utils'; export interface RequestHandlerParams { searchSource: ISearchSource; @@ -193,11 +192,9 @@ const handleCourierRequest = async ({ : undefined, }; - (searchSource as any).tabifiedResponse = tabifyAggResponse( - aggs, - (searchSource as any).finalResponse, - tabifyParams - ); + const response = tabifyAggResponse(aggs, (searchSource as any).finalResponse, tabifyParams); + + (searchSource as any).tabifiedResponse = response; inspectorAdapters.data.setTabularLoader( () => @@ -208,12 +205,12 @@ const handleCourierRequest = async ({ { returnsFormattedValues: true } ); - return (searchSource as any).tabifiedResponse; + return response; }; export const esaggs = (): EsaggsExpressionFunctionDefinition => ({ name, - type: 'kibana_datatable', + type: 'datatable', inputTypes: ['kibana_context', 'null'], help: i18n.translate('data.functions.esaggs.help', { defaultMessage: 'Run AggConfig aggregation', @@ -279,18 +276,25 @@ export const esaggs = (): EsaggsExpressionFunctionDefinition => ({ abortSignal: (abortSignal as unknown) as AbortSignal, }); - const table: KibanaDatatable = { - type: 'kibana_datatable', + const table: Datatable = { + type: 'datatable', rows: response.rows, - columns: response.columns.map((column: any) => { - const cleanedColumn: KibanaDatatableColumn = { + columns: response.columns.map((column) => { + const cleanedColumn: DatatableColumn = { id: column.id, name: column.name, - meta: serializeAggConfig(column.aggConfig), + meta: { + type: column.aggConfig.params.field?.type || 'number', + field: column.aggConfig.params.field?.name, + index: indexPattern.title, + params: column.aggConfig.toSerializedFieldFormat(), + source: 'esaggs', + sourceParams: { + indexPatternId: indexPattern.id, + ...column.aggConfig.serialize(), + }, + }, }; - if (args.includeFormatHints) { - cleanedColumn.formatHint = column.aggConfig.toSerializedFieldFormat(); - } return cleanedColumn; }), }; diff --git a/src/plugins/data/public/search/expressions/index.ts b/src/plugins/data/public/search/expressions/index.ts index 02df7986479ad..98ed1d08af8ad 100644 --- a/src/plugins/data/public/search/expressions/index.ts +++ b/src/plugins/data/public/search/expressions/index.ts @@ -20,4 +20,3 @@ export * from './esaggs'; export * from './es_raw_response'; export * from './esdsl'; -export * from './utils'; diff --git a/src/plugins/data/public/search/expressions/utils/index.ts b/src/plugins/data/public/search/expressions/utils/index.ts deleted file mode 100644 index 094536fc18437..0000000000000 --- a/src/plugins/data/public/search/expressions/utils/index.ts +++ /dev/null @@ -1,20 +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 * from './serialize_agg_config'; diff --git a/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts b/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts deleted file mode 100644 index 6ba323b65783f..0000000000000 --- a/src/plugins/data/public/search/expressions/utils/serialize_agg_config.ts +++ /dev/null @@ -1,54 +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 { KibanaDatatableColumnMeta } from '../../../../../../plugins/expressions/public'; -import { IAggConfig } from '../../../../common'; -import { IndexPattern } from '../../../index_patterns'; -import { getSearchService } from '../../../../public/services'; - -/** @internal */ -export const serializeAggConfig = (aggConfig: IAggConfig): KibanaDatatableColumnMeta => { - return { - type: aggConfig.type.name, - indexPatternId: aggConfig.getIndexPattern().id, - aggConfigParams: aggConfig.serialize().params, - }; -}; - -interface DeserializeAggConfigParams { - type: string; - aggConfigParams: Record; - indexPattern: IndexPattern; -} - -/** @internal */ -export const deserializeAggConfig = ({ - type, - aggConfigParams, - indexPattern, -}: DeserializeAggConfigParams) => { - const { aggs } = getSearchService(); - const aggConfigs = aggs.createAggConfigs(indexPattern); - const aggConfig = aggConfigs.createAggConfig({ - enabled: true, - type, - params: aggConfigParams, - }); - return aggConfig; -}; diff --git a/src/plugins/embeddable/public/lib/triggers/triggers.ts b/src/plugins/embeddable/public/lib/triggers/triggers.ts index ccba5cf771088..54c7a2ecc129d 100644 --- a/src/plugins/embeddable/public/lib/triggers/triggers.ts +++ b/src/plugins/embeddable/public/lib/triggers/triggers.ts @@ -17,7 +17,7 @@ * under the License. */ -import { KibanaDatatable } from '../../../../expressions'; +import { Datatable } from '../../../../expressions'; import { Trigger } from '../../../../ui_actions/public'; import { IEmbeddable } from '..'; @@ -29,7 +29,7 @@ export interface ValueClickContext { embeddable?: T; data: { data: Array<{ - table: Pick; + table: Pick; column: number; row: number; value: any; @@ -42,7 +42,7 @@ export interface ValueClickContext { export interface RangeSelectContext { embeddable?: T; data: { - table: KibanaDatatable; + table: Datatable; column: number; range: number[]; timeFieldName?: string; diff --git a/src/plugins/embeddable/public/public.api.md b/src/plugins/embeddable/public/public.api.md index 6280d3a2e4a50..a6d90f2766c18 100644 --- a/src/plugins/embeddable/public/public.api.md +++ b/src/plugins/embeddable/public/public.api.md @@ -326,7 +326,7 @@ export abstract class Embeddable { +export class EmbeddableChildPanel extends React.Component { constructor(props: EmbeddableChildPanelProps); // (undocumented) [panel: string]: any; @@ -477,7 +477,7 @@ export interface EmbeddablePackageState { // Warning: (ae-missing-release-tag) "EmbeddablePanel" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export class EmbeddablePanel extends React.Component { +export class EmbeddablePanel extends React.Component { constructor(props: Props); // (undocumented) closeMyContextMenuPanel: () => void; @@ -810,7 +810,7 @@ export interface PropertySpec { export interface RangeSelectContext { // (undocumented) data: { - table: KibanaDatatable; + table: Datatable; column: number; range: number[]; timeFieldName?: string; @@ -841,7 +841,7 @@ export interface ValueClickContext { // (undocumented) data: { data: Array<{ - table: Pick; + table: Pick; column: number; row: number; value: any; @@ -881,7 +881,7 @@ export const withEmbeddableSubscription: /** * This type represents the `type` of any `DatatableColumn` in a `Datatable`. + * its duplicated from KBN_FIELD_TYPES */ -export type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; +export type DatatableColumnType = + | '_source' + | 'attachment' + | 'boolean' + | 'date' + | 'geo_point' + | 'geo_shape' + | 'ip' + | 'murmur3' + | 'number' + | 'string' + | 'unknown' + | 'conflict' + | 'object' + | 'nested' + | 'histogram' + | 'null'; /** * This type represents a row in a `Datatable`. */ export type DatatableRow = Record; +/** + * Datatable column meta information + */ export interface DatatableColumnMeta { type: DatatableColumnType; + /** + * field this column is based on + */ field?: string; + /** + * index/table this column is based on + */ index?: string; - params?: SerializableState; + /** + * serialized field format + */ + params?: SerializedFieldFormat; + /** + * source function that produced this column + */ source?: string; + /** + * any extra parameters for the source that produced this column + */ sourceParams?: SerializableState; } + /** * This type represents the shape of a column in a `Datatable`. */ diff --git a/src/plugins/expressions/common/expression_types/specs/index.ts b/src/plugins/expressions/common/expression_types/specs/index.ts index 31210b11f6b7a..00c52a2545cd6 100644 --- a/src/plugins/expressions/common/expression_types/specs/index.ts +++ b/src/plugins/expressions/common/expression_types/specs/index.ts @@ -23,7 +23,6 @@ import { error } from './error'; import { filter } from './filter'; import { image } from './image'; import { kibanaContext } from './kibana_context'; -import { kibanaDatatable } from './kibana_datatable'; import { nullType } from './null'; import { num } from './num'; import { number } from './number'; @@ -42,7 +41,6 @@ export const typeSpecs: AnyExpressionTypeDefinition[] = [ filter, image, kibanaContext, - kibanaDatatable, nullType, num, number, @@ -60,7 +58,6 @@ export * from './error'; export * from './filter'; export * from './image'; export * from './kibana_context'; -export * from './kibana_datatable'; export * from './null'; export * from './num'; export * from './number'; diff --git a/src/plugins/expressions/common/expression_types/specs/kibana_datatable.ts b/src/plugins/expressions/common/expression_types/specs/kibana_datatable.ts deleted file mode 100644 index e226f3b124eed..0000000000000 --- a/src/plugins/expressions/common/expression_types/specs/kibana_datatable.ts +++ /dev/null @@ -1,75 +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 { map } from 'lodash'; -import { SerializedFieldFormat } from '../../types/common'; -import { Datatable, PointSeries, PointSeriesColumn } from '.'; - -const name = 'kibana_datatable'; - -export interface KibanaDatatableColumnMeta { - type: string; - indexPatternId?: string; - aggConfigParams?: Record; -} - -export interface KibanaDatatableColumn { - id: string; - name: string; - meta?: KibanaDatatableColumnMeta; - formatHint?: SerializedFieldFormat; -} - -export interface KibanaDatatableRow { - [key: string]: unknown; -} - -export interface KibanaDatatable { - type: typeof name; - columns: KibanaDatatableColumn[]; - rows: KibanaDatatableRow[]; -} - -export const kibanaDatatable = { - name, - from: { - datatable: (context: Datatable) => { - return { - type: name, - rows: context.rows, - columns: context.columns.map((column) => { - return { - id: column.name, - name: column.name, - }; - }), - }; - }, - pointseries: (context: PointSeries) => { - const columns = map(context.columns, (column: PointSeriesColumn, n) => { - return { id: n, name: n, ...column }; - }); - return { - type: name, - rows: context.rows, - columns, - }; - }, - }, -}; diff --git a/src/plugins/expressions/public/index.ts b/src/plugins/expressions/public/index.ts index 039890c9233cf..893d68238747d 100644 --- a/src/plugins/expressions/public/index.ts +++ b/src/plugins/expressions/public/index.ts @@ -98,10 +98,6 @@ export { isExpressionAstBuilder, KIBANA_CONTEXT_NAME, KibanaContext, - KibanaDatatable, - KibanaDatatableColumn, - KibanaDatatableColumnMeta, - KibanaDatatableRow, KnownTypeToString, Overflow, parse, diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index 57ad6f747e5af..b1a7a4040964e 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -82,7 +82,7 @@ export interface DatatableColumn { // Warning: (ae-missing-release-tag) "DatatableColumnType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; +export type DatatableColumnType = '_source' | 'attachment' | 'boolean' | 'date' | 'geo_point' | 'geo_shape' | 'ip' | 'murmur3' | 'number' | 'string' | 'unknown' | 'conflict' | 'object' | 'nested' | 'histogram' | 'null'; // Warning: (ae-missing-release-tag) "DatatableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -942,54 +942,6 @@ export type KIBANA_CONTEXT_NAME = 'kibana_context'; // @public (undocumented) export type KibanaContext = ExpressionValueSearchContext; -// Warning: (ae-missing-release-tag) "KibanaDatatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatable { - // (undocumented) - columns: KibanaDatatableColumn[]; - // (undocumented) - rows: KibanaDatatableRow[]; - // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts - // - // (undocumented) - type: typeof name_3; -} - -// Warning: (ae-missing-release-tag) "KibanaDatatableColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatableColumn { - // (undocumented) - formatHint?: SerializedFieldFormat; - // (undocumented) - id: string; - // (undocumented) - meta?: KibanaDatatableColumnMeta; - // (undocumented) - name: string; -} - -// Warning: (ae-missing-release-tag) "KibanaDatatableColumnMeta" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatableColumnMeta { - // (undocumented) - aggConfigParams?: Record; - // (undocumented) - indexPatternId?: string; - // (undocumented) - type: string; -} - -// Warning: (ae-missing-release-tag) "KibanaDatatableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatableRow { - // (undocumented) - [key: string]: unknown; -} - // Warning: (ae-missing-release-tag) "KnownTypeToString" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -1073,7 +1025,7 @@ export interface Range { // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts // // (undocumented) - type: typeof name_4; + type: typeof name_3; } // Warning: (ae-missing-release-tag) "ReactExpressionRenderer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/expressions/server/index.ts b/src/plugins/expressions/server/index.ts index 6785457321595..cc22d4b500d97 100644 --- a/src/plugins/expressions/server/index.ts +++ b/src/plugins/expressions/server/index.ts @@ -89,10 +89,6 @@ export { isExpressionAstBuilder, KIBANA_CONTEXT_NAME, KibanaContext, - KibanaDatatable, - KibanaDatatableColumn, - KibanaDatatableColumnMeta, - KibanaDatatableRow, KnownTypeToString, Overflow, parse, diff --git a/src/plugins/expressions/server/server.api.md b/src/plugins/expressions/server/server.api.md index 1a905de4a3bdf..e151d1bb58a61 100644 --- a/src/plugins/expressions/server/server.api.md +++ b/src/plugins/expressions/server/server.api.md @@ -79,7 +79,7 @@ export interface DatatableColumn { // Warning: (ae-missing-release-tag) "DatatableColumnType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export type DatatableColumnType = 'string' | 'number' | 'boolean' | 'date' | 'null'; +export type DatatableColumnType = '_source' | 'attachment' | 'boolean' | 'date' | 'geo_point' | 'geo_shape' | 'ip' | 'murmur3' | 'number' | 'string' | 'unknown' | 'conflict' | 'object' | 'nested' | 'histogram' | 'null'; // Warning: (ae-missing-release-tag) "DatatableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -768,54 +768,6 @@ export type KIBANA_CONTEXT_NAME = 'kibana_context'; // @public (undocumented) export type KibanaContext = ExpressionValueSearchContext; -// Warning: (ae-missing-release-tag) "KibanaDatatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatable { - // (undocumented) - columns: KibanaDatatableColumn[]; - // (undocumented) - rows: KibanaDatatableRow[]; - // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts - // - // (undocumented) - type: typeof name_3; -} - -// Warning: (ae-missing-release-tag) "KibanaDatatableColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatableColumn { - // (undocumented) - formatHint?: SerializedFieldFormat; - // (undocumented) - id: string; - // (undocumented) - meta?: KibanaDatatableColumnMeta; - // (undocumented) - name: string; -} - -// Warning: (ae-missing-release-tag) "KibanaDatatableColumnMeta" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatableColumnMeta { - // (undocumented) - aggConfigParams?: Record; - // (undocumented) - indexPatternId?: string; - // (undocumented) - type: string; -} - -// Warning: (ae-missing-release-tag) "KibanaDatatableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KibanaDatatableRow { - // (undocumented) - [key: string]: unknown; -} - // Warning: (ae-missing-release-tag) "KnownTypeToString" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -898,7 +850,7 @@ export interface Range { // Warning: (ae-forgotten-export) The symbol "name" needs to be exported by the entry point index.d.ts // // (undocumented) - type: typeof name_4; + type: typeof name_3; } // Warning: (ae-missing-release-tag) "SerializedDatatable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/src/plugins/input_control_vis/public/input_control_fn.ts b/src/plugins/input_control_vis/public/input_control_fn.ts index 59c0e03505bb7..1664555b916b6 100644 --- a/src/plugins/input_control_vis/public/input_control_fn.ts +++ b/src/plugins/input_control_vis/public/input_control_fn.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, KibanaDatatable, Render } from '../../expressions/public'; +import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressions/public'; interface Arguments { visConfig: string; @@ -34,7 +34,7 @@ interface RenderValue { export const createInputControlVisFn = (): ExpressionFunctionDefinition< 'input_control_vis', - KibanaDatatable, + Datatable, Arguments, Render > => ({ diff --git a/src/plugins/region_map/public/__snapshots__/region_map_fn.test.js.snap b/src/plugins/region_map/public/__snapshots__/region_map_fn.test.js.snap index 2d615a105906c..cb12712ae824f 100644 --- a/src/plugins/region_map/public/__snapshots__/region_map_fn.test.js.snap +++ b/src/plugins/region_map/public/__snapshots__/region_map_fn.test.js.snap @@ -50,7 +50,7 @@ Object { "col-0-1": 0, }, ], - "type": "kibana_datatable", + "type": "datatable", }, "visType": "region_map", }, diff --git a/src/plugins/region_map/public/region_map_fn.js b/src/plugins/region_map/public/region_map_fn.js index 314def1fbfdca..fdb7c273720fa 100644 --- a/src/plugins/region_map/public/region_map_fn.js +++ b/src/plugins/region_map/public/region_map_fn.js @@ -23,7 +23,7 @@ export const createRegionMapFn = () => ({ name: 'regionmap', type: 'render', context: { - types: ['kibana_datatable'], + types: ['datatable'], }, help: i18n.translate('regionMap.function.help', { defaultMessage: 'Regionmap visualization', diff --git a/src/plugins/region_map/public/region_map_fn.test.js b/src/plugins/region_map/public/region_map_fn.test.js index 684cc5e897df4..32467541dee02 100644 --- a/src/plugins/region_map/public/region_map_fn.test.js +++ b/src/plugins/region_map/public/region_map_fn.test.js @@ -24,7 +24,7 @@ import { createRegionMapFn } from './region_map_fn'; describe('interpreter/functions#regionmap', () => { const fn = functionWrapper(createRegionMapFn()); const context = { - type: 'kibana_datatable', + type: 'datatable', rows: [{ 'col-0-1': 0 }], columns: [{ id: 'col-0-1', name: 'Count' }], }; diff --git a/src/plugins/tile_map/public/tile_map_fn.js b/src/plugins/tile_map/public/tile_map_fn.js index 5f43077bcb24b..3253598d98d94 100644 --- a/src/plugins/tile_map/public/tile_map_fn.js +++ b/src/plugins/tile_map/public/tile_map_fn.js @@ -23,7 +23,7 @@ export const createTileMapFn = () => ({ name: 'tilemap', type: 'render', context: { - types: ['kibana_datatable'], + types: ['datatable'], }, help: i18n.translate('tileMap.function.help', { defaultMessage: 'Tilemap visualization', diff --git a/src/plugins/tile_map/public/tile_map_visualization.js b/src/plugins/tile_map/public/tile_map_visualization.js index b09a2f3bac48f..80084be283658 100644 --- a/src/plugins/tile_map/public/tile_map_visualization.js +++ b/src/plugins/tile_map/public/tile_map_visualization.js @@ -73,19 +73,19 @@ export const createTileMapVisualization = (dependencies) => { }; const bounds = this._kibanaMap.getBounds(); const mapCollar = scaleBounds(bounds); - if (!geoContains(geohashAgg.aggConfigParams.boundingBox, mapCollar)) { + if (!geoContains(geohashAgg.sourceParams.params.boundingBox, mapCollar)) { updateVarsObject.data.boundingBox = { top_left: mapCollar.top_left, bottom_right: mapCollar.bottom_right, }; } else { - updateVarsObject.data.boundingBox = geohashAgg.aggConfigParams.boundingBox; + updateVarsObject.data.boundingBox = geohashAgg.sourceParams.params.boundingBox; } // todo: autoPrecision should be vis parameter, not aggConfig one const zoomPrecision = getZoomPrecision(); - updateVarsObject.data.precision = geohashAgg.aggConfigParams.autoPrecision + updateVarsObject.data.precision = geohashAgg.sourceParams.params.autoPrecision ? zoomPrecision[this.vis.getUiState().get('mapZoom')] - : getPrecision(geohashAgg.aggConfigParams.precision); + : getPrecision(geohashAgg.sourceParams.params.precision); this.vis.eventsSubject.next(updateVarsObject); }; @@ -118,8 +118,8 @@ export const createTileMapVisualization = (dependencies) => { return; } const isAutoPrecision = - typeof geohashAgg.aggConfigParams.autoPrecision === 'boolean' - ? geohashAgg.aggConfigParams.autoPrecision + typeof geohashAgg.sourceParams.params.autoPrecision === 'boolean' + ? geohashAgg.sourceParams.params.autoPrecision : true; if (!isAutoPrecision) { return; @@ -243,7 +243,7 @@ export const createTileMapVisualization = (dependencies) => { } const indexPatternName = agg.indexPatternId; - const field = agg.aggConfigParams.field; + const field = agg.field; const filter = { meta: { negate: false, index: indexPatternName } }; filter[filterName] = { ignore_unmapped: true }; filter[filterName][field] = filterData; @@ -264,7 +264,7 @@ export const createTileMapVisualization = (dependencies) => { const DEFAULT = false; const agg = this._getGeoHashAgg(); if (agg) { - return get(agg, 'aggConfigParams.isFilteredByCollar', DEFAULT); + return get(agg, 'sourceParams.params.isFilteredByCollar', DEFAULT); } else { return DEFAULT; } diff --git a/src/plugins/tile_map/public/tilemap_fn.test.js b/src/plugins/tile_map/public/tilemap_fn.test.js index 8fa12c9f9dbbe..df9fc10a7303c 100644 --- a/src/plugins/tile_map/public/tilemap_fn.test.js +++ b/src/plugins/tile_map/public/tilemap_fn.test.js @@ -41,7 +41,7 @@ import { convertToGeoJson } from '../../maps_legacy/public'; describe('interpreter/functions#tilemap', () => { const fn = functionWrapper(createTileMapFn()); const context = { - type: 'kibana_datatable', + type: 'datatable', rows: [{ 'col-0-1': 0 }], columns: [{ id: 'col-0-1', name: 'Count' }], }; diff --git a/src/plugins/vis_type_markdown/public/markdown_renderer.tsx b/src/plugins/vis_type_markdown/public/markdown_renderer.tsx index 8071196c6a213..f36ffadff7c56 100644 --- a/src/plugins/vis_type_markdown/public/markdown_renderer.tsx +++ b/src/plugins/vis_type_markdown/public/markdown_renderer.tsx @@ -36,7 +36,7 @@ export const markdownVisRenderer: ExpressionRenderDefinition + , domNode diff --git a/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap b/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap index 706d2a902aa90..fd8f3a712d8ae 100644 --- a/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap +++ b/src/plugins/vis_type_metric/public/__snapshots__/metric_vis_fn.test.ts.snap @@ -43,7 +43,7 @@ Object { "col-0-1": 0, }, ], - "type": "kibana_datatable", + "type": "datatable", }, "visType": "metric", }, diff --git a/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx b/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx index e5c7db65c09a8..5ab3ee6eed8eb 100644 --- a/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx +++ b/src/plugins/vis_type_metric/public/components/metric_vis_component.tsx @@ -23,7 +23,7 @@ import { isColorDark } from '@elastic/eui'; import { MetricVisValue } from './metric_vis_value'; import { Input } from '../metric_vis_fn'; import { FieldFormatsContentType, IFieldFormat } from '../../../data/public'; -import { KibanaDatatable } from '../../../expressions/public'; +import { Datatable } from '../../../expressions/public'; import { getHeatmapColors } from '../../../charts/public'; import { VisParams, MetricVisMetric } from '../types'; import { getFormatService } from '../services'; @@ -109,7 +109,7 @@ class MetricVisComponent extends Component { return fieldFormatter.convert(value, format); }; - private processTableGroups(table: KibanaDatatable) { + private processTableGroups(table: Datatable) { const config = this.props.visParams.metric; const dimensions = this.props.visParams.dimensions; const isPercentageMode = config.percentageMode; diff --git a/src/plugins/vis_type_metric/public/metric_vis_fn.test.ts b/src/plugins/vis_type_metric/public/metric_vis_fn.test.ts index 3ed8f8f79a83f..8faa3d2aab265 100644 --- a/src/plugins/vis_type_metric/public/metric_vis_fn.test.ts +++ b/src/plugins/vis_type_metric/public/metric_vis_fn.test.ts @@ -23,7 +23,7 @@ import { functionWrapper } from '../../expressions/common/expression_functions/s describe('interpreter/functions#metric', () => { const fn = functionWrapper(createMetricVisFn()); const context = { - type: 'kibana_datatable', + type: 'datatable', rows: [{ 'col-0-1': 0 }], columns: [{ id: 'col-0-1', name: 'Count' }], }; diff --git a/src/plugins/vis_type_metric/public/metric_vis_fn.ts b/src/plugins/vis_type_metric/public/metric_vis_fn.ts index 97b1e6822333e..20de22f50e63a 100644 --- a/src/plugins/vis_type_metric/public/metric_vis_fn.ts +++ b/src/plugins/vis_type_metric/public/metric_vis_fn.ts @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition, - KibanaDatatable, + Datatable, Range, Render, Style, @@ -29,7 +29,7 @@ import { import { visType, DimensionsVisParam, VisParams } from './types'; import { ColorSchemas, vislibColorMaps, ColorModes } from '../../charts/public'; -export type Input = KibanaDatatable; +export type Input = Datatable; interface Arguments { percentageMode: boolean; @@ -63,7 +63,7 @@ export type MetricVisExpressionFunctionDefinition = ExpressionFunctionDefinition export const createMetricVisFn = (): MetricVisExpressionFunctionDefinition => ({ name: 'metricVis', type: 'render', - inputTypes: ['kibana_datatable'], + inputTypes: ['datatable'], help: i18n.translate('visTypeMetric.function.help', { defaultMessage: 'Metric visualization', }), diff --git a/src/plugins/vis_type_metric/public/metric_vis_renderer.tsx b/src/plugins/vis_type_metric/public/metric_vis_renderer.tsx index bf0d6da9fba05..8e0cb35ca52aa 100644 --- a/src/plugins/vis_type_metric/public/metric_vis_renderer.tsx +++ b/src/plugins/vis_type_metric/public/metric_vis_renderer.tsx @@ -36,7 +36,11 @@ export const metricVisRenderer: () => ExpressionRenderDefinition + ({ describe('interpreter/functions#table', () => { const fn = functionWrapper(createTableVisFn()); const context = { - type: 'kibana_datatable', + type: 'datatable', rows: [{ 'col-0-1': 0 }], columns: [{ id: 'col-0-1', name: 'Count' }], }; diff --git a/src/plugins/vis_type_table/public/table_vis_fn.ts b/src/plugins/vis_type_table/public/table_vis_fn.ts index 2e446ba4e4fcf..28990f28caf31 100644 --- a/src/plugins/vis_type_table/public/table_vis_fn.ts +++ b/src/plugins/vis_type_table/public/table_vis_fn.ts @@ -19,10 +19,10 @@ import { i18n } from '@kbn/i18n'; import { tableVisResponseHandler, TableContext } from './table_vis_response_handler'; -import { ExpressionFunctionDefinition, KibanaDatatable, Render } from '../../expressions/public'; +import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressions/public'; import { TableVisConfig } from './types'; -export type Input = KibanaDatatable; +export type Input = Datatable; interface Arguments { visConfig: string | null; @@ -44,7 +44,7 @@ export type TableExpressionFunctionDefinition = ExpressionFunctionDefinition< export const createTableVisFn = (): TableExpressionFunctionDefinition => ({ name: 'kibana_table', type: 'render', - inputTypes: ['kibana_datatable'], + inputTypes: ['datatable'], help: i18n.translate('visTypeTable.function.help', { defaultMessage: 'Table visualization', }), diff --git a/src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap b/src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap index debc7ab27c632..17a91a4d43cc7 100644 --- a/src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap +++ b/src/plugins/vis_type_tagcloud/public/__snapshots__/tag_cloud_fn.test.ts.snap @@ -17,7 +17,7 @@ Object { "col-0-1": 0, }, ], - "type": "kibana_datatable", + "type": "datatable", }, "visParams": Object { "maxFontSize": 72, diff --git a/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts b/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts index eb16b0855a138..e481c311d5453 100644 --- a/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts +++ b/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.test.ts @@ -24,7 +24,7 @@ import { functionWrapper } from '../../expressions/common/expression_functions/s describe('interpreter/functions#tagcloud', () => { const fn = functionWrapper(createTagCloudFn()); const context = { - type: 'kibana_datatable', + type: 'datatable', rows: [{ 'col-0-1': 0 }], columns: [{ id: 'col-0-1', name: 'Count' }], }; diff --git a/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.ts b/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.ts index 42e126908c00f..ff59572e0817d 100644 --- a/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.ts +++ b/src/plugins/vis_type_tagcloud/public/tag_cloud_fn.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, KibanaDatatable, Render } from '../../expressions/public'; +import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressions/public'; import { TagCloudVisParams } from './types'; const name = 'tagcloud'; @@ -31,13 +31,13 @@ interface Arguments extends TagCloudVisParams { export interface TagCloudVisRenderValue { visType: typeof name; - visData: KibanaDatatable; + visData: Datatable; visParams: Arguments; } export type TagcloudExpressionFunctionDefinition = ExpressionFunctionDefinition< typeof name, - KibanaDatatable, + Datatable, Arguments, Render >; @@ -45,7 +45,7 @@ export type TagcloudExpressionFunctionDefinition = ExpressionFunctionDefinition< export const createTagCloudFn = (): TagcloudExpressionFunctionDefinition => ({ name, type: 'render', - inputTypes: ['kibana_datatable'], + inputTypes: ['datatable'], help: i18n.translate('visTypeTagCloud.function.help', { defaultMessage: 'Tagcloud visualization', }), diff --git a/src/plugins/vis_type_tagcloud/public/tag_cloud_vis_renderer.tsx b/src/plugins/vis_type_tagcloud/public/tag_cloud_vis_renderer.tsx index b433ed9cbed21..21194189745aa 100644 --- a/src/plugins/vis_type_tagcloud/public/tag_cloud_vis_renderer.tsx +++ b/src/plugins/vis_type_tagcloud/public/tag_cloud_vis_renderer.tsx @@ -39,7 +39,7 @@ export const getTagCloudVisRenderer: ( }); render( - + + ({ describe('interpreter/functions#pie', () => { const fn = functionWrapper(createPieVisFn()); const context = { - type: 'kibana_datatable', + type: 'datatable', rows: [{ 'col-0-1': 0 }], columns: [{ id: 'col-0-1', name: 'Count' }], }; diff --git a/src/plugins/vis_type_vislib/public/pie_fn.ts b/src/plugins/vis_type_vislib/public/pie_fn.ts index 52da0f7ac14ec..bee200cbe30ee 100644 --- a/src/plugins/vis_type_vislib/public/pie_fn.ts +++ b/src/plugins/vis_type_vislib/public/pie_fn.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, KibanaDatatable, Render } from '../../expressions/public'; +import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressions/public'; // @ts-ignore import { vislibSlicesResponseHandler } from './vislib/response_handler'; @@ -34,13 +34,13 @@ interface RenderValue { export const createPieVisFn = (): ExpressionFunctionDefinition< 'kibana_pie', - KibanaDatatable, + Datatable, Arguments, Render > => ({ name: 'kibana_pie', type: 'render', - inputTypes: ['kibana_datatable'], + inputTypes: ['datatable'], help: i18n.translate('visTypeVislib.functions.pie.help', { defaultMessage: 'Pie visualization', }), diff --git a/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts b/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts index a4243c6d25c41..557f9930f55b1 100644 --- a/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts +++ b/src/plugins/vis_type_vislib/public/vis_type_vislib_vis_fn.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, KibanaDatatable, Render } from '../../expressions/public'; +import { ExpressionFunctionDefinition, Datatable, Render } from '../../expressions/public'; // @ts-ignore import { vislibSeriesResponseHandler } from './vislib/response_handler'; @@ -36,13 +36,13 @@ interface RenderValue { export const createVisTypeVislibVisFn = (): ExpressionFunctionDefinition< 'vislib', - KibanaDatatable, + Datatable, Arguments, Render > => ({ name: 'vislib', type: 'render', - inputTypes: ['kibana_datatable'], + inputTypes: ['datatable'], help: i18n.translate('visTypeVislib.functions.vislib.help', { defaultMessage: 'Vislib visualization', }), diff --git a/src/plugins/visualizations/public/components/visualization_container.tsx b/src/plugins/visualizations/public/components/visualization_container.tsx index 007a9e6e9dde4..5695a84269bd4 100644 --- a/src/plugins/visualizations/public/components/visualization_container.tsx +++ b/src/plugins/visualizations/public/components/visualization_container.tsx @@ -21,16 +21,19 @@ import React, { ReactNode, Suspense } from 'react'; import { EuiLoadingChart } from '@elastic/eui'; import classNames from 'classnames'; import { VisualizationNoResults } from './visualization_noresults'; +import { IInterpreterRenderHandlers } from '../../../expressions/common'; interface VisualizationContainerProps { className?: string; children: ReactNode; + handlers: IInterpreterRenderHandlers; showNoResult?: boolean; } export const VisualizationContainer = ({ className, children, + handlers, showNoResult = false, }: VisualizationContainerProps) => { const classes = classNames('visualization', className); @@ -44,7 +47,7 @@ export const VisualizationContainer = ({ return (
- {showNoResult ? : children} + {showNoResult ? handlers.done()} /> : children}
); diff --git a/src/plugins/visualizations/public/expression_functions/range.ts b/src/plugins/visualizations/public/expression_functions/range.ts index 42eb6aa781970..409199deb8181 100644 --- a/src/plugins/visualizations/public/expression_functions/range.ts +++ b/src/plugins/visualizations/public/expression_functions/range.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { ExpressionFunctionDefinition, KibanaDatatable, Range } from '../../../expressions/public'; +import { ExpressionFunctionDefinition, Datatable, Range } from '../../../expressions/public'; interface Arguments { from: number; @@ -27,7 +27,7 @@ interface Arguments { export const range = (): ExpressionFunctionDefinition< 'range', - KibanaDatatable | null, + Datatable | null, Arguments, Range > => ({ diff --git a/src/plugins/visualizations/public/expression_functions/vis_dimension.ts b/src/plugins/visualizations/public/expression_functions/vis_dimension.ts index 286804d2fa76a..a78634b7eef14 100644 --- a/src/plugins/visualizations/public/expression_functions/vis_dimension.ts +++ b/src/plugins/visualizations/public/expression_functions/vis_dimension.ts @@ -21,8 +21,8 @@ import { i18n } from '@kbn/i18n'; import { ExpressionFunctionDefinition, ExpressionValueBoxed, - KibanaDatatable, - KibanaDatatableColumn, + Datatable, + DatatableColumn, } from '../../../expressions/public'; interface Arguments { @@ -34,7 +34,7 @@ interface Arguments { type ExpressionValueVisDimension = ExpressionValueBoxed< 'vis_dimension', { - accessor: number | KibanaDatatableColumn; + accessor: number | DatatableColumn; format: { id?: string; params: unknown; @@ -44,7 +44,7 @@ type ExpressionValueVisDimension = ExpressionValueBoxed< export const visDimension = (): ExpressionFunctionDefinition< 'visdimension', - KibanaDatatable, + Datatable, Arguments, ExpressionValueVisDimension > => ({ @@ -53,7 +53,7 @@ export const visDimension = (): ExpressionFunctionDefinition< defaultMessage: 'Generates visConfig dimension object', }), type: 'vis_dimension', - inputTypes: ['kibana_datatable'], + inputTypes: ['datatable'], args: { accessor: { types: ['string', 'number'], diff --git a/test/interpreter_functional/snapshots/baseline/combined_test2.json b/test/interpreter_functional/snapshots/baseline/combined_test2.json index 84203617ff853..3ed4441e7896f 100644 --- a/test/interpreter_functional/snapshots/baseline/combined_test2.json +++ b/test/interpreter_functional/snapshots/baseline/combined_test2.json @@ -1 +1 @@ -{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"} \ No newline at end of file +{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/combined_test3.json b/test/interpreter_functional/snapshots/baseline/combined_test3.json index 2760875119197..f4d03ee7ffc15 100644 --- a/test/interpreter_functional/snapshots/baseline/combined_test3.json +++ b/test/interpreter_functional/snapshots/baseline/combined_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/final_output_test.json b/test/interpreter_functional/snapshots/baseline/final_output_test.json index 2760875119197..f4d03ee7ffc15 100644 --- a/test/interpreter_functional/snapshots/baseline/final_output_test.json +++ b/test/interpreter_functional/snapshots/baseline/final_output_test.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_all_data.json b/test/interpreter_functional/snapshots/baseline/metric_all_data.json index ae72bcfa6d5ec..b31efb0934860 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_all_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_all_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"},{"id":"col-2-1","meta":{"aggConfigParams":{"field":"bytes"},"indexPatternId":"logstash-*","type":"max"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":{"parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_invalid_data.json b/test/interpreter_functional/snapshots/baseline/metric_invalid_data.json index fa5892190e5ba..0a47cdb8ff74a 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_invalid_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_invalid_data.json @@ -1 +1 @@ -"[metricVis] > [visdimension] > Can not cast 'null' to any of 'kibana_datatable'" \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json index 8568215fd9e1a..3c2d7102554bf 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_multi_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"},{"id":"col-2-1","meta":{"aggConfigParams":{"field":"bytes"},"indexPatternId":"logstash-*","type":"max"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":{"parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json index d11e1dfb925f1..7c1ebb186d1d5 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json +++ b/test/interpreter_functional/snapshots/baseline/metric_percentage_mode.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"},{"id":"col-2-1","meta":{"aggConfigParams":{"field":"bytes"},"indexPatternId":"logstash-*","type":"max"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":{"parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json index b160e05935f17..9bb09ddfd959a 100644 --- a/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/metric_single_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"},{"id":"col-2-1","meta":{"aggConfigParams":{"field":"bytes"},"indexPatternId":"logstash-*","type":"max"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":{"parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/partial_test_1.json b/test/interpreter_functional/snapshots/baseline/partial_test_1.json index 9c642e5e266d0..829db0e190822 100644 --- a/test/interpreter_functional/snapshots/baseline/partial_test_1.json +++ b/test/interpreter_functional/snapshots/baseline/partial_test_1.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visParams":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/partial_test_2.json b/test/interpreter_functional/snapshots/baseline/partial_test_2.json index 2760875119197..f4d03ee7ffc15 100644 --- a/test/interpreter_functional/snapshots/baseline/partial_test_2.json +++ b/test/interpreter_functional/snapshots/baseline/partial_test_2.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/partial_test_3.json b/test/interpreter_functional/snapshots/baseline/partial_test_3.json index 4241d6f208bfd..821b42aea81ee 100644 --- a/test/interpreter_functional/snapshots/baseline/partial_test_3.json +++ b/test/interpreter_functional/snapshots/baseline/partial_test_3.json @@ -1 +1 @@ -{"as":"visualization","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"bucket":{"accessor":0},"metric":{"accessor":1,"format":{"id":"number"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visType":"region_map"}} \ No newline at end of file +{"as":"visualization","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"bucket":{"accessor":0},"metric":{"accessor":1,"format":{"id":"number"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"region_map"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/step_output_test2.json b/test/interpreter_functional/snapshots/baseline/step_output_test2.json index 84203617ff853..3ed4441e7896f 100644 --- a/test/interpreter_functional/snapshots/baseline/step_output_test2.json +++ b/test/interpreter_functional/snapshots/baseline/step_output_test2.json @@ -1 +1 @@ -{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"} \ No newline at end of file +{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/step_output_test3.json b/test/interpreter_functional/snapshots/baseline/step_output_test3.json index 2760875119197..f4d03ee7ffc15 100644 --- a/test/interpreter_functional/snapshots/baseline/step_output_test3.json +++ b/test/interpreter_functional/snapshots/baseline/step_output_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json b/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json index 153eea71dd8d1..935f0265f50f7 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_all_data.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json b/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json index d5f01afa468ab..f404d63d7f83d 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_fontsize.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_invalid_data.json b/test/interpreter_functional/snapshots/baseline/tagcloud_invalid_data.json index 46b52a7b3eaae..0c50947beca97 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_invalid_data.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_invalid_data.json @@ -1 +1 @@ -"[tagcloud] > [visdimension] > Can not cast 'null' to any of 'kibana_datatable'" \ No newline at end of file +{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json b/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json index 72b5e957c19a5..90c31ff410ab9 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_metric_data.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/baseline/tagcloud_options.json b/test/interpreter_functional/snapshots/baseline/tagcloud_options.json index 7cbe7cc79882f..4e2dd3be02d90 100644 --- a/test/interpreter_functional/snapshots/baseline/tagcloud_options.json +++ b/test/interpreter_functional/snapshots/baseline/tagcloud_options.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:6121","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/combined_test2.json b/test/interpreter_functional/snapshots/session/combined_test2.json index 84203617ff853..697ebde0fedd9 100644 --- a/test/interpreter_functional/snapshots/session/combined_test2.json +++ b/test/interpreter_functional/snapshots/session/combined_test2.json @@ -1 +1 @@ -{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"} \ No newline at end of file +{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/combined_test3.json b/test/interpreter_functional/snapshots/session/combined_test3.json index 2760875119197..622e4f3543884 100644 --- a/test/interpreter_functional/snapshots/session/combined_test3.json +++ b/test/interpreter_functional/snapshots/session/combined_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/final_output_test.json b/test/interpreter_functional/snapshots/session/final_output_test.json index 2760875119197..622e4f3543884 100644 --- a/test/interpreter_functional/snapshots/session/final_output_test.json +++ b/test/interpreter_functional/snapshots/session/final_output_test.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_all_data.json b/test/interpreter_functional/snapshots/session/metric_all_data.json index ae72bcfa6d5ec..5795a6acb2032 100644 --- a/test/interpreter_functional/snapshots/session/metric_all_data.json +++ b/test/interpreter_functional/snapshots/session/metric_all_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"},{"id":"col-2-1","meta":{"aggConfigParams":{"field":"bytes"},"indexPatternId":"logstash-*","type":"max"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":2,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":{"parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_invalid_data.json b/test/interpreter_functional/snapshots/session/metric_invalid_data.json index fa5892190e5ba..0a47cdb8ff74a 100644 --- a/test/interpreter_functional/snapshots/session/metric_invalid_data.json +++ b/test/interpreter_functional/snapshots/session/metric_invalid_data.json @@ -1 +1 @@ -"[metricVis] > [visdimension] > Can not cast 'null' to any of 'kibana_datatable'" \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json b/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json index 8568215fd9e1a..1f47b8a0cb380 100644 --- a/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json +++ b/test/interpreter_functional/snapshots/session/metric_multi_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"},{"id":"col-2-1","meta":{"aggConfigParams":{"field":"bytes"},"indexPatternId":"logstash-*","type":"max"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":{"parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_percentage_mode.json b/test/interpreter_functional/snapshots/session/metric_percentage_mode.json index d11e1dfb925f1..b18ed9aa8ae72 100644 --- a/test/interpreter_functional/snapshots/session/metric_percentage_mode.json +++ b/test/interpreter_functional/snapshots/session/metric_percentage_mode.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"},{"id":"col-2-1","meta":{"aggConfigParams":{"field":"bytes"},"indexPatternId":"logstash-*","type":"max"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":1000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":true,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":{"parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/metric_single_metric_data.json b/test/interpreter_functional/snapshots/session/metric_single_metric_data.json index b160e05935f17..759368c89f6a4 100644 --- a/test/interpreter_functional/snapshots/session/metric_single_metric_data.json +++ b/test/interpreter_functional/snapshots/session/metric_single_metric_data.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"},{"id":"col-2-1","meta":{"aggConfigParams":{"field":"bytes"},"indexPatternId":"logstash-*","type":"max"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"metrics":[{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"},{"id":"col-2-1","meta":{"field":"bytes","index":"logstash-*","params":{"id":"bytes","params":{"parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{"field":"bytes"},"schema":"metric","type":"max"},"type":"number"},"name":"Max bytes"}],"rows":[{"col-0-2":"200","col-1-1":12891,"col-2-1":19986},{"col-0-2":"404","col-1-1":696,"col-2-1":19881},{"col-0-2":"503","col-1-1":417,"col-2-1":0}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/partial_test_1.json b/test/interpreter_functional/snapshots/session/partial_test_1.json index 9c642e5e266d0..4ad63a3e263e2 100644 --- a/test/interpreter_functional/snapshots/session/partial_test_1.json +++ b/test/interpreter_functional/snapshots/session/partial_test_1.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visParams":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/partial_test_2.json b/test/interpreter_functional/snapshots/session/partial_test_2.json index 2760875119197..622e4f3543884 100644 --- a/test/interpreter_functional/snapshots/session/partial_test_2.json +++ b/test/interpreter_functional/snapshots/session/partial_test_2.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/partial_test_3.json b/test/interpreter_functional/snapshots/session/partial_test_3.json index 4241d6f208bfd..78aa75b0e6725 100644 --- a/test/interpreter_functional/snapshots/session/partial_test_3.json +++ b/test/interpreter_functional/snapshots/session/partial_test_3.json @@ -1 +1 @@ -{"as":"visualization","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"bucket":{"accessor":0},"metric":{"accessor":1,"format":{"id":"number"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visType":"region_map"}} \ No newline at end of file +{"as":"visualization","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"bucket":{"accessor":0},"metric":{"accessor":1,"format":{"id":"number"}}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"region_map"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/step_output_test2.json b/test/interpreter_functional/snapshots/session/step_output_test2.json index 84203617ff853..697ebde0fedd9 100644 --- a/test/interpreter_functional/snapshots/session/step_output_test2.json +++ b/test/interpreter_functional/snapshots/session/step_output_test2.json @@ -1 +1 @@ -{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"} \ No newline at end of file +{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/step_output_test3.json b/test/interpreter_functional/snapshots/session/step_output_test3.json index 2760875119197..622e4f3543884 100644 --- a/test/interpreter_functional/snapshots/session/step_output_test3.json +++ b/test/interpreter_functional/snapshots/session/step_output_test3.json @@ -1 +1 @@ -{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visType":"metric"}} \ No newline at end of file +{"as":"metric_vis","type":"render","value":{"params":{"listenOnChange":true},"visConfig":{"dimensions":{"bucket":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"metrics":[{"accessor":1,"format":{"id":"number","params":{}},"type":"vis_dimension"}]},"metric":{"colorSchema":"Green to Red","colorsRange":[{"from":0,"to":10000,"type":"range"}],"invertColors":false,"labels":{"show":true},"metricColorMode":"None","percentageMode":false,"style":{"bgColor":false,"bgFill":"#000","fontSize":60,"labelColor":false,"subText":""},"useRanges":false}},"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visType":"metric"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_all_data.json b/test/interpreter_functional/snapshots/session/tagcloud_all_data.json index 153eea71dd8d1..3340fc9d43b09 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_all_data.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_all_data.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_fontsize.json b/test/interpreter_functional/snapshots/session/tagcloud_fontsize.json index d5f01afa468ab..1649f550f03d6 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_fontsize.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_fontsize.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":40,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":20,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_invalid_data.json b/test/interpreter_functional/snapshots/session/tagcloud_invalid_data.json index 46b52a7b3eaae..0c50947beca97 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_invalid_data.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_invalid_data.json @@ -1 +1 @@ -"[tagcloud] > [visdimension] > Can not cast 'null' to any of 'kibana_datatable'" \ No newline at end of file +{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[],"meta":{},"rows":[],"type":"datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_metric_data.json b/test/interpreter_functional/snapshots/session/tagcloud_metric_data.json index 72b5e957c19a5..c2aefa4baebc2 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_metric_data.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_metric_data.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"single","scale":"linear","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/snapshots/session/tagcloud_options.json b/test/interpreter_functional/snapshots/session/tagcloud_options.json index 7cbe7cc79882f..14864d4c07812 100644 --- a/test/interpreter_functional/snapshots/session/tagcloud_options.json +++ b/test/interpreter_functional/snapshots/session/tagcloud_options.json @@ -1 +1 @@ -{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"aggConfigParams":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"indexPatternId":"logstash-*","type":"terms"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"aggConfigParams":{},"indexPatternId":"logstash-*","type":"count"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"kibana_datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file +{"as":"tagloud_vis","type":"render","value":{"visData":{"columns":[{"id":"col-0-2","meta":{"field":"response.raw","index":"logstash-*","params":{"id":"terms","params":{"id":"string","missingBucketLabel":"Missing","otherBucketLabel":"Other","parsedUrl":{"basePath":"","origin":"http://localhost:5620","pathname":"/app/management"}}},"source":"esaggs","sourceParams":{"enabled":true,"id":"2","indexPatternId":"logstash-*","params":{"field":"response.raw","missingBucket":false,"missingBucketLabel":"Missing","order":"desc","orderBy":"1","otherBucket":false,"otherBucketLabel":"Other","size":4},"schema":"segment","type":"terms"},"type":"string"},"name":"response.raw: Descending"},{"id":"col-1-1","meta":{"field":null,"index":"logstash-*","params":{"id":"number"},"source":"esaggs","sourceParams":{"enabled":true,"id":"1","indexPatternId":"logstash-*","params":{},"schema":"metric","type":"count"},"type":"number"},"name":"Count"}],"rows":[{"col-0-2":"200","col-1-1":12891},{"col-0-2":"404","col-1-1":696},{"col-0-2":"503","col-1-1":417}],"type":"datatable"},"visParams":{"bucket":{"accessor":1,"format":{"id":"string","params":{}},"type":"vis_dimension"},"maxFontSize":72,"metric":{"accessor":0,"format":{"id":"string","params":{}},"type":"vis_dimension"},"minFontSize":18,"orientation":"multiple","scale":"log","showLabel":true},"visType":"tagcloud"}} \ No newline at end of file diff --git a/test/interpreter_functional/test_suites/run_pipeline/helpers.ts b/test/interpreter_functional/test_suites/run_pipeline/helpers.ts index bbf45b003c330..e5130ac95b7f8 100644 --- a/test/interpreter_functional/test_suites/run_pipeline/helpers.ts +++ b/test/interpreter_functional/test_suites/run_pipeline/helpers.ts @@ -176,10 +176,16 @@ export function expectExpressionProvider({ log.debug('starting to render'); const result = await browser.executeAsync( (_context: ExpressionResult, done: (renderResult: any) => void) => - window.renderPipelineResponse(_context).then((renderResult: any) => { - done(renderResult); - return renderResult; - }), + window + .renderPipelineResponse(_context) + .then((renderResult: any) => { + done(renderResult); + return renderResult; + }) + .catch((e) => { + done(e); + return e; + }), pipelineResponse ); log.debug('response of rendering: ', result); diff --git a/x-pack/plugins/canvas/public/components/datatable/datatable.tsx b/x-pack/plugins/canvas/public/components/datatable/datatable.tsx index bd343b15758bf..cd8a0e91510a3 100644 --- a/x-pack/plugins/canvas/public/components/datatable/datatable.tsx +++ b/x-pack/plugins/canvas/public/components/datatable/datatable.tsx @@ -9,11 +9,9 @@ import PropTypes from 'prop-types'; import { EuiIcon, EuiPagination } from '@elastic/eui'; import moment from 'moment'; import { Paginate } from '../paginate'; -import { Datatable as DatatableType, DatatableColumn } from '../../../types'; +import { Datatable as DatatableType, DatatableColumn, DatatableColumnType } from '../../../types'; -type IconType = 'string' | 'number' | 'date' | 'boolean' | 'null'; - -const getIcon = (type: IconType) => { +const getIcon = (type: DatatableColumnType | null) => { if (type === null) { return; } diff --git a/x-pack/plugins/canvas/types/state.ts b/x-pack/plugins/canvas/types/state.ts index e9b580f81e668..60407b78ab5e3 100644 --- a/x-pack/plugins/canvas/types/state.ts +++ b/x-pack/plugins/canvas/types/state.ts @@ -10,7 +10,6 @@ import { ExpressionImage, ExpressionFunction, KibanaContext, - KibanaDatatable, PointSeries, Render, Style, @@ -49,7 +48,6 @@ type ExpressionType = | ExpressionValueFilter | ExpressionImage | KibanaContext - | KibanaDatatable | PointSeries | Style | Range; diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts index 64af67aefa4be..79d380991f5fd 100644 --- a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts +++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown.test.ts @@ -6,6 +6,7 @@ import { UrlDrilldown, ActionContext, Config } from './url_drilldown'; import { IEmbeddable } from '../../../../../../src/plugins/embeddable/public/lib/embeddables'; +import { DatatableColumnType } from '../../../../../../src/plugins/expressions/common'; const mockDataPoints = [ { @@ -15,12 +16,17 @@ const mockDataPoints = [ name: 'test', id: '1-1', meta: { - type: 'histogram', - indexPatternId: 'logstash-*', - aggConfigParams: { - field: 'bytes', - interval: 30, - otherBucket: true, + type: 'number' as DatatableColumnType, + field: 'bytes', + index: 'logstash-*', + sourceParams: { + indexPatternId: 'logstash-*', + type: 'histogram', + params: { + field: 'bytes', + interval: 30, + otherBucket: true, + }, }, }, }, diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.test.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.test.ts index bb1baf5b96428..6989819da2b0b 100644 --- a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.test.ts +++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.test.ts @@ -9,6 +9,7 @@ import { getMockEventScope, ValueClickTriggerEventScope, } from './url_drilldown_scope'; +import { DatatableColumnType } from '../../../../../../src/plugins/expressions/common'; const createPoint = ({ field, @@ -23,10 +24,12 @@ const createPoint = ({ name: field, id: '1-1', meta: { - type: 'histogram', - indexPatternId: 'logstash-*', - aggConfigParams: { - field, + type: 'date' as DatatableColumnType, + field, + source: 'esaggs', + sourceParams: { + type: 'histogram', + indexPatternId: 'logstash-*', interval: 30, otherBucket: true, }, diff --git a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.ts b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.ts index 15a9a3ba77d88..0f66cb144c967 100644 --- a/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.ts +++ b/x-pack/plugins/drilldowns/url_drilldown/public/lib/url_drilldown_scope.ts @@ -131,7 +131,7 @@ function getEventScopeFromRangeSelectTriggerContext( const { table, column: columnIndex, range } = eventScopeInput.data; const column = table.columns[columnIndex]; return cleanEmptyKeys({ - key: toPrimitiveOrUndefined(column?.meta?.aggConfigParams?.field) as string, + key: toPrimitiveOrUndefined(column?.meta.field) as string, from: toPrimitiveOrUndefined(range[0]) as string | number | undefined, to: toPrimitiveOrUndefined(range[range.length - 1]) as string | number | undefined, }); @@ -145,7 +145,7 @@ function getEventScopeFromValueClickTriggerContext( const column = table.columns[columnIndex]; return { value: toPrimitiveOrUndefined(value) as Primitive, - key: toPrimitiveOrUndefined(column?.meta?.aggConfigParams?.field) as string | undefined, + key: column?.meta?.field, }; }); diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx index eb00cf93ccd34..c95f6085b4791 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.test.tsx @@ -13,20 +13,50 @@ 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 onClickValue = jest.fn(); import { EmptyPlaceholder } from '../shared_components'; import { LensIconChartDatatable } from '../assets/chart_datatable'; function sampleArgs() { + const indexPatternId = 'indexPatternId'; const data: LensMultiTable = { type: 'lens_multitable', tables: { l1: { - type: 'kibana_datatable', + type: 'datatable', columns: [ - { id: 'a', name: 'a', meta: { type: 'terms' } }, - { id: 'b', name: 'b', meta: { type: 'date_histogram', aggConfigParams: { field: 'b' } } }, - { id: 'c', name: 'c', meta: { type: 'count' } }, + { + id: 'a', + name: 'a', + meta: { + type: 'string', + source: 'esaggs', + field: 'a', + sourceParams: { type: 'terms', indexPatternId }, + }, + }, + { + id: 'b', + name: 'b', + meta: { + type: 'date', + field: 'b', + source: 'esaggs', + sourceParams: { + type: 'date_histogram', + indexPatternId, + }, + }, + }, + { + id: 'c', + name: 'c', + meta: { + type: 'number', + source: 'esaggs', + field: 'c', + sourceParams: { indexPatternId, type: 'count' }, + }, + }, ], rows: [{ a: 'shoes', b: 1588024800000, c: 3 }], }, @@ -45,6 +75,11 @@ function sampleArgs() { } describe('datatable_expression', () => { + let onClickValue: jest.Mock; + beforeEach(() => { + onClickValue = jest.fn(); + }); + describe('datatable renders', () => { test('it renders with the specified data and args', () => { const { data, args } = sampleArgs(); @@ -106,7 +141,7 @@ describe('datatable_expression', () => { }, ], negate: true, - timeFieldName: undefined, + timeFieldName: 'a', }); }); @@ -150,10 +185,27 @@ describe('datatable_expression', () => { type: 'lens_multitable', tables: { l1: { - type: 'kibana_datatable', + type: 'datatable', columns: [ - { id: 'a', name: 'a', meta: { type: 'date_range', aggConfigParams: { field: 'a' } } }, - { id: 'b', name: 'b', meta: { type: 'count' } }, + { + id: 'a', + name: 'a', + meta: { + type: 'date', + source: 'esaggs', + field: 'a', + sourceParams: { type: 'date_range', indexPatternId: 'a' }, + }, + }, + { + id: 'b', + name: 'b', + meta: { + type: 'number', + source: 'esaggs', + sourceParams: { type: 'count', indexPatternId: 'a' }, + }, + }, ], rows: [{ a: 1588024800000, b: 3 }], }, diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index af1773b413599..6502e07697816 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -166,15 +166,15 @@ export function DatatableComponent(props: DatatableRenderProps) { const formatters: Record> = {}; firstTable.columns.forEach((column) => { - formatters[column.id] = props.formatFactory(column.formatHint); + formatters[column.id] = props.formatFactory(column.meta?.params); }); const { onClickValue } = props; const handleFilterClick = useMemo( () => (field: string, value: unknown, colIndex: number, negate: boolean = false) => { const col = firstTable.columns[colIndex]; - const isDate = col.meta?.type === 'date_histogram' || col.meta?.type === 'date_range'; - const timeFieldName = negate && isDate ? undefined : col?.meta?.aggConfigParams?.field; + const isDate = col.meta?.type === 'date'; + const timeFieldName = negate && isDate ? undefined : col?.meta?.field; const rowIndex = firstTable.rows.findIndex((row) => row[field] === value); const data: LensFilterEvent['data'] = { @@ -196,7 +196,10 @@ export function DatatableComponent(props: DatatableRenderProps) { const bucketColumns = firstTable.columns .filter((col) => { - return col?.meta?.type && props.getType(col.meta.type)?.type === 'buckets'; + return ( + col?.meta?.sourceParams?.type && + props.getType(col.meta.sourceParams.type as string)?.type === 'buckets' + ); }) .map((col) => col.id); @@ -230,7 +233,7 @@ export function DatatableComponent(props: DatatableRenderProps) { name: (col && col.name) || '', render: (value: unknown) => { const formattedValue = formatters[field]?.convert(value); - const fieldName = col?.meta?.aggConfigParams?.field; + const fieldName = col?.meta?.field; if (filterable) { return ( diff --git a/x-pack/plugins/lens/public/editor_frame_service/format_column.ts b/x-pack/plugins/lens/public/editor_frame_service/format_column.ts index b95139a00ec57..2da6e7195a5e1 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/format_column.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/format_column.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ExpressionFunctionDefinition, KibanaDatatable } from 'src/plugins/expressions/public'; +import { ExpressionFunctionDefinition, Datatable } from 'src/plugins/expressions/public'; interface FormatColumn { format: string; @@ -41,12 +41,12 @@ const supportedFormats: Record = { name: 'lens_format_column', - type: 'kibana_datatable', + type: 'datatable', help: '', args: { format: { @@ -64,7 +64,7 @@ export const formatColumn: ExpressionFunctionDefinition< help: '', }, }, - inputTypes: ['kibana_datatable'], + inputTypes: ['datatable'], fn(input, { format, columnId, decimals }: FormatColumn) { return { ...input, @@ -73,15 +73,23 @@ export const formatColumn: ExpressionFunctionDefinition< if (supportedFormats[format]) { return { ...col, - formatHint: { - id: format, - params: { pattern: supportedFormats[format].decimalsToPattern(decimals) }, + meta: { + ...col.meta, + params: { + id: format, + params: { pattern: supportedFormats[format].decimalsToPattern(decimals) }, + }, }, }; } else { return { ...col, - formatHint: { id: format, params: {} }, + meta: { + ...col.meta, + params: { + id: format, + }, + }, }; } } diff --git a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts b/x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts index b3da722de5f34..5afabb9a52367 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/merge_tables.test.ts @@ -6,15 +6,15 @@ import moment from 'moment'; import { mergeTables } from './merge_tables'; -import { KibanaDatatable } from 'src/plugins/expressions'; +import { Datatable } from 'src/plugins/expressions'; describe('lens_merge_tables', () => { it('should produce a row with the nested table as defined', () => { - const sampleTable1: KibanaDatatable = { - type: 'kibana_datatable', + const sampleTable1: Datatable = { + type: 'datatable', columns: [ - { id: 'bucket', name: 'A' }, - { id: 'count', name: 'Count' }, + { id: 'bucket', name: 'A', meta: { type: 'string' } }, + { id: 'count', name: 'Count', meta: { type: 'number' } }, ], rows: [ { bucket: 'a', count: 5 }, @@ -22,11 +22,11 @@ describe('lens_merge_tables', () => { ], }; - const sampleTable2: KibanaDatatable = { - type: 'kibana_datatable', + const sampleTable2: Datatable = { + type: 'datatable', columns: [ - { id: 'bucket', name: 'C' }, - { id: 'avg', name: 'Average' }, + { id: 'bucket', name: 'C', meta: { type: 'string' } }, + { id: 'avg', name: 'Average', meta: { type: 'number' } }, ], rows: [ { bucket: 'a', avg: 2.5 }, diff --git a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts b/x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts index 7c10ee4a57fad..e4f7b07084ea9 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/merge_tables.ts @@ -6,9 +6,9 @@ import { i18n } from '@kbn/i18n'; import { + Datatable, ExpressionFunctionDefinition, ExpressionValueSearchContext, - KibanaDatatable, } from 'src/plugins/expressions/public'; import { search } from '../../../../../src/plugins/data/public'; const { toAbsoluteDates } = search.aggs; @@ -17,7 +17,7 @@ import { LensMultiTable } from '../types'; interface MergeTables { layerIds: string[]; - tables: KibanaDatatable[]; + tables: Datatable[]; } export const mergeTables: ExpressionFunctionDefinition< @@ -38,14 +38,14 @@ export const mergeTables: ExpressionFunctionDefinition< multi: true, }, tables: { - types: ['kibana_datatable'], + types: ['datatable'], help: '', multi: true, }, }, inputTypes: ['kibana_context', 'null'], fn(input, { layerIds, tables }) { - const resultTables: Record = {}; + const resultTables: Record = {}; tables.forEach((table, index) => { resultTables[layerIds[index]] = table; }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.test.ts index 4bfd6a4f93c75..43285d657dd40 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.test.ts @@ -5,16 +5,16 @@ */ import { renameColumns } from './rename_columns'; -import { KibanaDatatable } from '../../../../../src/plugins/expressions/public'; +import { Datatable } from '../../../../../src/plugins/expressions/public'; import { createMockExecutionContext } from '../../../../../src/plugins/expressions/common/mocks'; describe('rename_columns', () => { it('should rename columns of a given datatable', () => { - const input: KibanaDatatable = { - type: 'kibana_datatable', + const input: Datatable = { + type: 'datatable', columns: [ - { id: 'a', name: 'A' }, - { id: 'b', name: 'B' }, + { id: 'a', name: 'A', meta: { type: 'number' } }, + { id: 'b', name: 'B', meta: { type: 'number' } }, ], rows: [ { a: 1, b: 2 }, @@ -46,10 +46,16 @@ describe('rename_columns', () => { "columns": Array [ Object { "id": "b", + "meta": Object { + "type": "number", + }, "name": "Austrailia", }, Object { "id": "c", + "meta": Object { + "type": "number", + }, "name": "Boomerang", }, ], @@ -71,15 +77,15 @@ describe('rename_columns', () => { "c": 8, }, ], - "type": "kibana_datatable", + "type": "datatable", } `); }); it('should replace "" with a visible value', () => { - const input: KibanaDatatable = { - type: 'kibana_datatable', - columns: [{ id: 'a', name: 'A' }], + const input: Datatable = { + type: 'datatable', + columns: [{ id: 'a', name: 'A', meta: { type: 'string' } }], rows: [{ a: '' }], }; @@ -100,11 +106,11 @@ describe('rename_columns', () => { }); it('should keep columns which are not mapped', () => { - const input: KibanaDatatable = { - type: 'kibana_datatable', + const input: Datatable = { + type: 'datatable', columns: [ - { id: 'a', name: 'A' }, - { id: 'b', name: 'B' }, + { id: 'a', name: 'A', meta: { type: 'number' } }, + { id: 'b', name: 'B', meta: { type: 'number' } }, ], rows: [ { a: 1, b: 2 }, @@ -129,10 +135,16 @@ describe('rename_columns', () => { "columns": Array [ Object { "id": "a", + "meta": Object { + "type": "number", + }, "name": "A", }, Object { "id": "c", + "meta": Object { + "type": "number", + }, "name": "Catamaran", }, ], @@ -154,17 +166,17 @@ describe('rename_columns', () => { "c": 8, }, ], - "type": "kibana_datatable", + "type": "datatable", } `); }); it('should rename date histograms', () => { - const input: KibanaDatatable = { - type: 'kibana_datatable', + const input: Datatable = { + type: 'datatable', columns: [ - { id: 'a', name: 'A' }, - { id: 'b', name: 'banana per 30 seconds' }, + { id: 'a', name: 'A', meta: { type: 'number' } }, + { id: 'b', name: 'banana per 30 seconds', meta: { type: 'number' } }, ], rows: [ { a: 1, b: 2 }, @@ -189,10 +201,16 @@ describe('rename_columns', () => { "columns": Array [ Object { "id": "a", + "meta": Object { + "type": "number", + }, "name": "A", }, Object { "id": "c", + "meta": Object { + "type": "number", + }, "name": "Apple per 30 seconds", }, ], @@ -214,7 +232,7 @@ describe('rename_columns', () => { "c": 8, }, ], - "type": "kibana_datatable", + "type": "datatable", } `); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.ts b/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.ts index bf938a3e05ef6..74f143225e293 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/rename_columns.ts @@ -5,11 +5,7 @@ */ import { i18n } from '@kbn/i18n'; -import { - ExpressionFunctionDefinition, - KibanaDatatable, - KibanaDatatableColumn, -} from 'src/plugins/expressions'; +import { ExpressionFunctionDefinition, Datatable, DatatableColumn } from 'src/plugins/expressions'; import { IndexPatternColumn } from './operations'; interface RemapArgs { @@ -20,12 +16,12 @@ export type OriginalColumn = { id: string } & IndexPatternColumn; export const renameColumns: ExpressionFunctionDefinition< 'lens_rename_columns', - KibanaDatatable, + Datatable, RemapArgs, - KibanaDatatable + Datatable > = { name: 'lens_rename_columns', - type: 'kibana_datatable', + type: 'datatable', help: i18n.translate('xpack.lens.functions.renameColumns.help', { defaultMessage: 'A helper to rename the columns of a datatable', }), @@ -38,12 +34,12 @@ export const renameColumns: ExpressionFunctionDefinition< }), }, }, - inputTypes: ['kibana_datatable'], + inputTypes: ['datatable'], fn(data, { idMap: encodedIdMap }) { const idMap = JSON.parse(encodedIdMap) as Record; return { - type: 'kibana_datatable', + type: 'datatable', rows: data.rows.map((row) => { const mappedRow: Record = {}; Object.entries(idMap).forEach(([fromId, toId]) => { @@ -77,7 +73,7 @@ export const renameColumns: ExpressionFunctionDefinition< }, }; -function getColumnName(originalColumn: OriginalColumn, newColumn: KibanaDatatableColumn) { +function getColumnName(originalColumn: OriginalColumn, newColumn: DatatableColumn) { if (originalColumn && originalColumn.operationType === 'date_histogram') { const fieldName = originalColumn.sourceField; diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx index 7e80fcc06dff8..88ce026fc2692 100644 --- a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx @@ -17,11 +17,11 @@ function sampleArgs() { type: 'lens_multitable', tables: { l1: { - type: 'kibana_datatable', + type: 'datatable', columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, + { id: 'a', name: 'a', meta: { type: 'string' } }, + { id: 'b', name: 'b', meta: { type: 'string' } }, + { id: 'c', name: 'c', meta: { type: 'number' } }, ], rows: [{ a: 10110, b: 2, c: 3 }], }, diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.tsx index 58814f62da60a..6522a4c457949 100644 --- a/x-pack/plugins/lens/public/metric_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/expression.tsx @@ -136,8 +136,8 @@ export function MetricChart({ } const value = - column && column.formatHint - ? formatFactory(column.formatHint).convert(row[accessor]) + column && column.meta?.params + ? formatFactory(column.meta?.params).convert(row[accessor]) : Number(Number(row[accessor]).toFixed(3)).toString(); return ( diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx index ac952e307758b..8ab1a8b5a58d8 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.test.tsx @@ -31,11 +31,11 @@ describe('PieVisualization component', () => { type: 'lens_multitable', tables: { first: { - type: 'kibana_datatable', + type: 'datatable', columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, ], rows: [ { a: 6, b: 2, c: 'I', d: 'Row 1' }, @@ -138,14 +138,23 @@ describe('PieVisualization component', () => { "columns": Array [ Object { "id": "a", + "meta": Object { + "type": "number", + }, "name": "a", }, Object { "id": "b", + "meta": Object { + "type": "number", + }, "name": "b", }, Object { "id": "c", + "meta": Object { + "type": "string", + }, "name": "c", }, ], @@ -163,7 +172,7 @@ describe('PieVisualization component', () => { "d": "Row 2", }, ], - "type": "kibana_datatable", + "type": "datatable", }, "value": 6, }, diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx index 8de810f9aa5d3..cb2458a76967c 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx @@ -68,7 +68,7 @@ export function PieComponent( if (!hideLabels) { firstTable.columns.forEach((column) => { - formatters[column.id] = props.formatFactory(column.formatHint); + formatters[column.id] = props.formatFactory(column.meta.params); }); } @@ -108,7 +108,7 @@ export function PieComponent( if (hideLabels || d === EMPTY_SLICE) { return ''; } - if (col.formatHint) { + if (col.meta.params) { return formatters[col.id].convert(d) ?? ''; } return String(d); diff --git a/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts b/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts index 8b94ff3236a44..d9ccda2a99ab2 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts +++ b/x-pack/plugins/lens/public/pie_visualization/render_helpers.test.ts @@ -4,15 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaDatatable } from 'src/plugins/expressions/public'; +import { Datatable } from 'src/plugins/expressions/public'; import { getSliceValueWithFallback, getFilterContext } from './render_helpers'; +import { ColumnGroups } from './types'; describe('render helpers', () => { describe('#getSliceValueWithFallback', () => { describe('without fallback', () => { - const columnGroups = [ - { col: { id: 'a', name: 'A' }, metrics: [] }, - { col: { id: 'b', name: 'C' }, metrics: [] }, + const columnGroups: ColumnGroups = [ + { col: { id: 'a', name: 'A', meta: { type: 'string' } }, metrics: [] }, + { col: { id: 'b', name: 'C', meta: { type: 'string' } }, metrics: [] }, ]; it('returns the metric when positive number', () => { @@ -20,6 +21,7 @@ describe('render helpers', () => { getSliceValueWithFallback({ a: 'Cat', b: 'Home', c: 5 }, columnGroups, { id: 'c', name: 'C', + meta: { type: 'number' }, }) ).toEqual(5); }); @@ -29,6 +31,7 @@ describe('render helpers', () => { getSliceValueWithFallback({ a: 'Cat', b: 'Home', c: -100 }, columnGroups, { id: 'c', name: 'C', + meta: { type: 'number' }, }) ).toEqual(-100); }); @@ -38,15 +41,19 @@ describe('render helpers', () => { getSliceValueWithFallback({ a: 'Cat', b: 'Home', c: 0 }, columnGroups, { id: 'c', name: 'C', + meta: { type: 'number' }, }) ).toEqual(Number.EPSILON); }); }); describe('fallback behavior', () => { - const columnGroups = [ - { col: { id: 'a', name: 'A' }, metrics: [{ id: 'a_subtotal', name: '' }] }, - { col: { id: 'b', name: 'C' }, metrics: [] }, + const columnGroups: ColumnGroups = [ + { + col: { id: 'a', name: 'A', meta: { type: 'string' } }, + metrics: [{ id: 'a_subtotal', name: '', meta: { type: 'number' } }], + }, + { col: { id: 'b', name: 'C', meta: { type: 'string' } }, metrics: [] }, ]; it('falls back to metric from previous column if available', () => { @@ -54,7 +61,7 @@ describe('render helpers', () => { getSliceValueWithFallback( { a: 'Cat', a_subtotal: 5, b: 'Home', c: undefined }, columnGroups, - { id: 'c', name: 'C' } + { id: 'c', name: 'C', meta: { type: 'number' } } ) ).toEqual(5); }); @@ -64,7 +71,7 @@ describe('render helpers', () => { getSliceValueWithFallback( { a: 'Cat', a_subtotal: 0, b: 'Home', c: undefined }, columnGroups, - { id: 'c', name: 'C' } + { id: 'c', name: 'C', meta: { type: 'number' } } ) ).toEqual(Number.EPSILON); }); @@ -74,7 +81,7 @@ describe('render helpers', () => { getSliceValueWithFallback( { a: 'Cat', a_subtotal: undefined, b: 'Home', c: undefined }, columnGroups, - { id: 'c', name: 'C' } + { id: 'c', name: 'C', meta: { type: 'number' } } ) ).toEqual(Number.EPSILON); }); @@ -83,11 +90,11 @@ describe('render helpers', () => { describe('#getFilterContext', () => { it('handles single slice click for single ring', () => { - const table: KibanaDatatable = { - type: 'kibana_datatable', + const table: Datatable = { + type: 'datatable', columns: [ - { id: 'a', name: 'A' }, - { id: 'b', name: 'B' }, + { id: 'a', name: 'A', meta: { type: 'string' } }, + { id: 'b', name: 'B', meta: { type: 'number' } }, ], rows: [ { a: 'Hi', b: 2 }, @@ -108,12 +115,12 @@ describe('render helpers', () => { }); it('handles single slice click with 2 rings', () => { - const table: KibanaDatatable = { - type: 'kibana_datatable', + const table: Datatable = { + type: 'datatable', columns: [ - { id: 'a', name: 'A' }, - { id: 'b', name: 'B' }, - { id: 'c', name: 'C' }, + { id: 'a', name: 'A', meta: { type: 'string' } }, + { id: 'b', name: 'B', meta: { type: 'string' } }, + { id: 'c', name: 'C', meta: { type: 'number' } }, ], rows: [ { a: 'Hi', b: 'Two', c: 2 }, @@ -134,12 +141,12 @@ describe('render helpers', () => { }); it('finds right row for multi slice click', () => { - const table: KibanaDatatable = { - type: 'kibana_datatable', + const table: Datatable = { + type: 'datatable', columns: [ - { id: 'a', name: 'A' }, - { id: 'b', name: 'B' }, - { id: 'c', name: 'C' }, + { id: 'a', name: 'A', meta: { type: 'string' } }, + { id: 'b', name: 'B', meta: { type: 'string' } }, + { id: 'c', name: 'C', meta: { type: 'number' } }, ], rows: [ { a: 'Hi', b: 'Two', c: 2 }, diff --git a/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts b/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts index aafbb477bab22..26b4f9ccda853 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts +++ b/x-pack/plugins/lens/public/pie_visualization/render_helpers.ts @@ -5,14 +5,14 @@ */ import { Datum, LayerValue } from '@elastic/charts'; -import { KibanaDatatable, KibanaDatatableColumn } from 'src/plugins/expressions/public'; +import { Datatable, DatatableColumn } from 'src/plugins/expressions/public'; import { ColumnGroups } from './types'; import { LensFilterEvent } from '../types'; export function getSliceValueWithFallback( d: Datum, reverseGroups: ColumnGroups, - metricColumn: KibanaDatatableColumn + metricColumn: DatatableColumn ) { if (typeof d[metricColumn.id] === 'number' && d[metricColumn.id] !== 0) { return d[metricColumn.id]; @@ -27,7 +27,7 @@ export function getSliceValueWithFallback( export function getFilterContext( clickedLayers: LayerValue[], layerColumnIds: string[], - table: KibanaDatatable + table: Datatable ): LensFilterEvent['data'] { const matchingIndex = table.rows.findIndex((row) => clickedLayers.every((layer, index) => { diff --git a/x-pack/plugins/lens/public/pie_visualization/types.ts b/x-pack/plugins/lens/public/pie_visualization/types.ts index 603c80aa00066..0596e54870a94 100644 --- a/x-pack/plugins/lens/public/pie_visualization/types.ts +++ b/x-pack/plugins/lens/public/pie_visualization/types.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { KibanaDatatableColumn } from 'src/plugins/expressions/public'; +import { DatatableColumn } from 'src/plugins/expressions/public'; import { LensMultiTable } from '../types'; export interface SharedLayerState { @@ -40,6 +40,6 @@ export interface PieExpressionProps { } export type ColumnGroups = Array<{ - col: KibanaDatatableColumn; - metrics: KibanaDatatableColumn[]; + col: DatatableColumn; + metrics: DatatableColumn[]; }>; diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index 2b9ca5a2425f8..e70436163b23d 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -11,7 +11,7 @@ import { SavedObjectReference } from 'kibana/public'; import { ExpressionRendererEvent, IInterpreterRenderHandlers, - KibanaDatatable, + Datatable, SerializedFieldFormat, } from '../../../../src/plugins/expressions/public'; import { DragContextState } from './drag_drop'; @@ -304,7 +304,7 @@ export interface OperationMetadata { export interface LensMultiTable { type: 'lens_multitable'; - tables: Record; + tables: Record; dateRange?: { fromDate: Date; toDate: Date; diff --git a/x-pack/plugins/lens/public/utils.test.ts b/x-pack/plugins/lens/public/utils.test.ts index 170579b7c551b..59b81fd3d1136 100644 --- a/x-pack/plugins/lens/public/utils.test.ts +++ b/x-pack/plugins/lens/public/utils.test.ts @@ -6,10 +6,12 @@ import { LensFilterEvent } from './types'; import { desanitizeFilterContext } from './utils'; +import { Datatable } from '../../../../src/plugins/expressions/common'; describe('desanitizeFilterContext', () => { it(`When filtered value equals '(empty)' replaces it with '' in table and in value.`, () => { - const table = { + const table: Datatable = { + type: 'datatable', rows: [ { 'f903668f-1175-4705-a5bd-713259d10326': 1589414640000, @@ -35,14 +37,17 @@ describe('desanitizeFilterContext', () => { { id: 'f903668f-1175-4705-a5bd-713259d10326', name: 'order_date per 30 seconds', + meta: { type: 'date' }, }, { id: '5d5446b2-72e8-4f86-91e0-88380f0fa14c', name: 'Top values of customer_phone', + meta: { type: 'string' }, }, { id: '9f0b6f88-c399-43a0-a993-0ad943c9af25', name: 'Count of records', + meta: { type: 'number' }, }, ], }; @@ -102,6 +107,7 @@ describe('desanitizeFilterContext', () => { }, ], columns: table.columns, + type: 'datatable', }, }, ], diff --git a/x-pack/plugins/lens/public/utils.ts b/x-pack/plugins/lens/public/utils.ts index 171707dcb9d26..0461e600d2b4c 100644 --- a/x-pack/plugins/lens/public/utils.ts +++ b/x-pack/plugins/lens/public/utils.ts @@ -14,7 +14,7 @@ export const desanitizeFilterContext = ( const emptyTextValue = i18n.translate('xpack.lens.indexpattern.emptyTextColumnValue', { defaultMessage: '(empty)', }); - return { + const result: LensFilterEvent['data'] = { ...context, data: context.data.map((point) => point.value === emptyTextValue @@ -36,4 +36,8 @@ export const desanitizeFilterContext = ( : point ), }; + if (context.timeFieldName) { + result.timeFieldName = context.timeFieldName; + } + return result; }; diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts index 15c08d17e49c6..a823a6370270d 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.test.ts @@ -5,13 +5,13 @@ */ import { LayerArgs } from './types'; -import { KibanaDatatable } from '../../../../../src/plugins/expressions/public'; +import { Datatable } from '../../../../../src/plugins/expressions/public'; import { getAxesConfiguration } from './axes_configuration'; describe('axes_configuration', () => { - const tables: Record = { + const tables: Record = { first: { - type: 'kibana_datatable', + type: 'datatable', rows: [ { xAccessorId: 1585758120000, @@ -99,48 +99,60 @@ describe('axes_configuration', () => { 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: {}, + type: 'date', + field: 'order_date', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'date_histogram', + params: { + 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: {}, + }, }, + params: { params: { id: 'date', params: { pattern: 'HH:mm' } } }, }, - 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', + type: 'string', + field: 'category.keyword', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'terms', + params: { + 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: 'terms', + params: { + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: 'Missing', + parsedUrl: { + origin: 'http://localhost:5601', + pathname: '/jiy/app/kibana', + basePath: '/jiy', + }, }, }, }, @@ -149,41 +161,57 @@ describe('axes_configuration', () => { id: 'yAccessorId', name: 'Count of records', meta: { - type: 'count', - indexPatternId: 'indexPatternId', - aggConfigParams: {}, + type: 'number', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'count', + }, + params: { id: 'number' }, }, - formatHint: { id: 'number' }, }, { id: 'yAccessorId2', name: 'Other column', meta: { - type: 'average', - indexPatternId: 'indexPatternId', - aggConfigParams: {}, + type: 'number', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'average', + }, + params: { id: 'bytes' }, }, - formatHint: { id: 'bytes' }, }, { id: 'yAccessorId3', name: 'Other column', meta: { - type: 'average', - indexPatternId: 'indexPatternId', - aggConfigParams: {}, + type: 'number', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'average', + }, + params: { id: 'currency' }, }, - formatHint: { id: 'currency' }, }, { id: 'yAccessorId4', name: 'Other column', meta: { - type: 'average', - indexPatternId: 'indexPatternId', - aggConfigParams: {}, + type: 'number', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'average', + }, + params: { id: 'currency' }, }, - formatHint: { id: 'currency' }, }, ], }, diff --git a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts index 876baaabb57c5..3c312abf1fd91 100644 --- a/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts +++ b/x-pack/plugins/lens/public/xy_visualization/axes_configuration.ts @@ -5,10 +5,7 @@ */ import { LayerConfig } from './types'; -import { - KibanaDatatable, - SerializedFieldFormat, -} from '../../../../../src/plugins/expressions/public'; +import { Datatable, SerializedFieldFormat } from '../../../../../src/plugins/expressions/public'; import { IFieldFormat } from '../../../../../src/plugins/data/public'; interface FormattedMetric { @@ -34,7 +31,7 @@ export function isFormatterCompatible( export function getAxesConfiguration( layers: LayerConfig[], shouldRotate: boolean, - tables?: Record, + tables?: Record, formatFactory?: (mapping: SerializedFieldFormat) => IFieldFormat ): GroupsConfiguration { const series: { auto: FormattedMetric[]; left: FormattedMetric[]; right: FormattedMetric[] } = { @@ -50,7 +47,7 @@ export function getAxesConfiguration( layer.yConfig?.find((yAxisConfig) => yAxisConfig.forAccessor === accessor)?.axisMode || 'auto'; let formatter: SerializedFieldFormat = table?.columns.find((column) => column.id === accessor) - ?.formatHint || { id: 'number' }; + ?.meta?.params || { id: 'number' }; if (layer.seriesType.includes('percentage') && formatter.id !== 'percent') { formatter = { id: 'percent', diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx index e7da850983de6..9e937399a7969 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx @@ -19,7 +19,7 @@ import { } from '@elastic/charts'; import { xyChart, XYChart } from './expression'; import { LensMultiTable } from '../types'; -import { KibanaDatatable, KibanaDatatableRow } from '../../../../../src/plugins/expressions/public'; +import { Datatable, DatatableRow } from '../../../../../src/plugins/expressions/public'; import React from 'react'; import { shallow } from 'enzyme'; import { @@ -46,7 +46,7 @@ const dateHistogramData: LensMultiTable = { type: 'lens_multitable', tables: { timeLayer: { - type: 'kibana_datatable', + type: 'datatable', rows: [ { xAccessorId: 1585758120000, @@ -104,48 +104,60 @@ const dateHistogramData: LensMultiTable = { 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: {}, + type: 'date', + field: 'order_date', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'date_histogram', + params: { + 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: {}, + }, }, + params: { id: 'date', params: { pattern: 'HH:mm' } }, }, - 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', + type: 'string', + field: 'category.keyword', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + type: 'terms', + params: { + 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: 'terms', + params: { + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: 'Missing', + parsedUrl: { + origin: 'http://localhost:5601', + pathname: '/jiy/app/kibana', + basePath: '/jiy', + }, }, }, }, @@ -154,11 +166,15 @@ const dateHistogramData: LensMultiTable = { id: 'yAccessorId', name: 'Count of records', meta: { - type: 'count', - indexPatternId: 'indexPatternId', - aggConfigParams: {}, + type: 'number', + source: 'esaggs', + index: 'indexPatternId', + sourceParams: { + indexPatternId: 'indexPatternId', + params: {}, + }, + params: { id: 'number' }, }, - formatHint: { id: 'number' }, }, ], }, @@ -181,22 +197,30 @@ const dateHistogramLayer: LayerArgs = { accessors: ['yAccessorId'], }; -const createSampleDatatableWithRows = (rows: KibanaDatatableRow[]): KibanaDatatable => ({ - type: 'kibana_datatable', +const createSampleDatatableWithRows = (rows: DatatableRow[]): Datatable => ({ + type: 'datatable', columns: [ { id: 'a', name: 'a', - formatHint: { id: 'number', params: { pattern: '0,0.000' } }, + meta: { type: 'number', params: { id: 'number', params: { pattern: '0,0.000' } } }, + }, + { + id: 'b', + name: 'b', + meta: { type: 'number', params: { id: 'number', params: { pattern: '000,0' } } }, }, - { id: 'b', name: 'b', formatHint: { id: 'number', params: { pattern: '000,0' } } }, { id: 'c', name: 'c', - formatHint: { id: 'string' }, - meta: { type: 'date-histogram', aggConfigParams: { interval: 'auto' } }, + meta: { + type: 'date', + field: 'order_date', + sourceParams: { type: 'date-histogram', params: { interval: 'auto' } }, + params: { id: 'string' }, + }, }, - { id: 'd', name: 'ColD', formatHint: { id: 'string' } }, + { id: 'd', name: 'ColD', meta: { type: 'string' } }, ], rows, }); @@ -347,12 +371,12 @@ describe('xy_expression', () => { type: 'lens_multitable', tables: { first: { - type: 'kibana_datatable', + type: 'datatable', columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, - { id: 'd', name: 'd' }, + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + { id: 'd', name: 'd', meta: { type: 'string' } }, ], rows: [ { a: 1, b: 2, c: 'I', d: 'Row 1' }, @@ -365,12 +389,12 @@ describe('xy_expression', () => { type: 'lens_multitable', tables: { first: { - type: 'kibana_datatable', + type: 'datatable', columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, - { id: 'd', name: 'd', formatHint: { id: 'custom' } }, + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, + { id: 'd', name: 'd', meta: { type: 'string', params: { id: 'custom' } } }, ], rows: [ { a: 1, b: 2, c: 'I', d: 'Row 1' }, @@ -542,12 +566,12 @@ describe('xy_expression', () => { ); expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` - Object { - "max": 1546491600000, - "min": 1546405200000, - "minInterval": undefined, - } - `); + Object { + "max": 1546491600000, + "min": 1546405200000, + "minInterval": undefined, + } + `); }); test('it generates correct xDomain for a layer with single value and layer with multiple value data (1-n)', () => { const data: LensMultiTable = { @@ -625,12 +649,12 @@ describe('xy_expression', () => { ); expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` - Object { - "max": 1546491600000, - "min": 1546405200000, - "minInterval": undefined, - } - `); + Object { + "max": 1546491600000, + "min": 1546405200000, + "minInterval": undefined, + } + `); }); }); @@ -792,7 +816,7 @@ describe('xy_expression', () => { type: 'lens_multitable', tables: { numberLayer: { - type: 'kibana_datatable', + type: 'datatable', rows: [ { xAccessorId: 5, @@ -815,10 +839,12 @@ describe('xy_expression', () => { { id: 'xAccessorId', name: 'bytes', + meta: { type: 'number' }, }, { id: 'yAccessorId', name: 'Count of records', + meta: { type: 'number' }, }, ], }, @@ -1737,11 +1763,11 @@ describe('xy_expression', () => { type: 'lens_multitable', tables: { first: { - type: 'kibana_datatable', + type: 'datatable', columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, ], rows: [ { a: undefined, b: 2, c: 'I', d: 'Row 1' }, @@ -1749,11 +1775,11 @@ describe('xy_expression', () => { ], }, second: { - type: 'kibana_datatable', + type: 'datatable', columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, ], rows: [ { a: undefined, b: undefined, c: undefined }, @@ -1831,11 +1857,11 @@ describe('xy_expression', () => { type: 'lens_multitable', tables: { first: { - type: 'kibana_datatable', + type: 'datatable', columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'number' } }, ], rows: [ { a: 0, b: 2, c: 5 }, @@ -1903,11 +1929,11 @@ describe('xy_expression', () => { type: 'lens_multitable', tables: { first: { - type: 'kibana_datatable', + type: 'datatable', columns: [ - { id: 'a', name: 'a' }, - { id: 'b', name: 'b' }, - { id: 'c', name: 'c' }, + { id: 'a', name: 'a', meta: { type: 'number' } }, + { id: 'b', name: 'b', meta: { type: 'number' } }, + { id: 'c', name: 'c', meta: { type: 'string' } }, ], rows: [{ a: 1, b: 5, c: 'J' }], }, diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index dad1d31ced71f..4a2c13e1e3520 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -26,7 +26,8 @@ import { ExpressionFunctionDefinition, ExpressionRenderDefinition, ExpressionValueSearchContext, - KibanaDatatable, + Datatable, + DatatableRow, } from 'src/plugins/expressions/public'; import { IconType } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -271,7 +272,7 @@ export function XYChart({ const xAxisColumn = data.tables[filteredLayers[0].layerId].columns.find( ({ id }) => id === filteredLayers[0].xAccessor ); - const xAxisFormatter = formatFactory(xAxisColumn && xAxisColumn.formatHint); + const xAxisFormatter = formatFactory(xAxisColumn && xAxisColumn.meta?.params); const layersAlreadyFormatted: Record = {}; // This is a safe formatter for the xAccessor that abstracts the knowledge of already formatted layers const safeXAccessorLabelRenderer = (value: unknown): string => @@ -330,8 +331,8 @@ export function XYChart({ // 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(); + const params = xAxisColumn?.meta?.sourceParams?.params as Record; + if (params?.interval !== 'auto') return parseInterval(params?.interval)?.asMilliseconds(); const { fromDate, toDate } = data.dateRange; const duration = moment(toDate).diff(moment(fromDate)); @@ -417,8 +418,9 @@ export function XYChart({ const xAxisColumnIndex = table.columns.findIndex( (el) => el.id === filteredLayers[0].xAccessor ); + const timeFieldName = isTimeViz - ? table.columns[xAxisColumnIndex]?.meta?.aggConfigParams?.field + ? table.columns[xAxisColumnIndex]?.meta?.field : undefined; const context: LensBrushEvent['data'] = { @@ -471,8 +473,7 @@ export function XYChart({ }); } - const xAxisFieldName = table.columns.find((el) => el.id === layer.xAccessor)?.meta - ?.aggConfigParams?.field; + const xAxisFieldName = table.columns.find((el) => el.id === layer.xAccessor)?.meta?.field; const timeFieldName = xDomain && xAxisFieldName; const context: LensFilterEvent['data'] = { @@ -552,14 +553,14 @@ export function XYChart({ // what if row values are not primitive? That is the case of, for instance, Ranges // remaps them to their serialized version with the formatHint metadata // In order to do it we need to make a copy of the table as the raw one is required for more features (filters, etc...) later on - const tableConverted: KibanaDatatable = { + const tableConverted: Datatable = { ...table, - rows: table.rows.map((row) => { + rows: table.rows.map((row: DatatableRow) => { const newRow = { ...row }; for (const column of table.columns) { const record = newRow[column.id]; if (record && !isPrimitive(record)) { - newRow[column.id] = formatFactory(column.formatHint).convert(record); + newRow[column.id] = formatFactory(column.meta.params).convert(record); } } return newRow; @@ -634,7 +635,7 @@ export function XYChart({ }, }, name(d) { - const splitHint = table.columns.find((col) => col.id === splitAccessor)?.formatHint; + const splitHint = table.columns.find((col) => col.id === splitAccessor)?.meta?.params; // For multiple y series, the name of the operation is used on each, either: // * Key - Y name From 4f4abf2286fe2b517b6e83aeb65b3241ee930b77 Mon Sep 17 00:00:00 2001 From: spalger Date: Tue, 13 Oct 2020 08:06:23 -0700 Subject: [PATCH 008/128] skip flaky suite (#79389) --- .../cypress/integration/timeline_creation.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts index 9f61d11b7ac0f..8ce60450671b9 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts @@ -45,7 +45,8 @@ import { openTimeline } from '../tasks/timelines'; import { OVERVIEW_URL } from '../urls/navigation'; -describe('Timelines', () => { +// FLAKY: https://github.com/elastic/kibana/issues/79389 +describe.skip('Timelines', () => { before(() => { cy.server(); cy.route('PATCH', '**/api/timeline').as('timeline'); From af6d2876537399ccf1138326670d9f60e7dbde6c Mon Sep 17 00:00:00 2001 From: EamonnTP Date: Tue, 13 Oct 2020 16:08:48 +0100 Subject: [PATCH 009/128] Move observability content (#79978) --- docs/infrastructure/images/infra-sysmon.png | Bin 302360 -> 0 bytes docs/infrastructure/index.asciidoc | 32 --------- docs/logs/images/logs-console.png | Bin 510715 -> 0 bytes docs/logs/index.asciidoc | 21 ------ .../alerting/alert-management.asciidoc | 4 +- docs/observability/images/apm-app.png | Bin 0 -> 465762 bytes docs/observability/images/logs-app.png | Bin 0 -> 1101522 bytes docs/observability/images/metrics-app.png | Bin 0 -> 328281 bytes docs/observability/images/uptime-app.png | Bin 0 -> 492069 bytes docs/observability/index.asciidoc | 65 ++++++++++++++++-- docs/uptime/images/uptime-overview.png | Bin 322479 -> 0 bytes docs/uptime/index.asciidoc | 19 ----- docs/user/alerting/alert-types.asciidoc | 2 +- .../alerting-getting-started.asciidoc | 8 +-- docs/user/alerting/defining-alerts.asciidoc | 2 +- docs/user/index.asciidoc | 6 -- 16 files changed, 69 insertions(+), 90 deletions(-) delete mode 100644 docs/infrastructure/images/infra-sysmon.png delete mode 100644 docs/infrastructure/index.asciidoc delete mode 100644 docs/logs/images/logs-console.png delete mode 100644 docs/logs/index.asciidoc create mode 100644 docs/observability/images/apm-app.png create mode 100644 docs/observability/images/logs-app.png create mode 100644 docs/observability/images/metrics-app.png create mode 100644 docs/observability/images/uptime-app.png delete mode 100644 docs/uptime/images/uptime-overview.png delete mode 100644 docs/uptime/index.asciidoc diff --git a/docs/infrastructure/images/infra-sysmon.png b/docs/infrastructure/images/infra-sysmon.png deleted file mode 100644 index dd653bb046f4511d0c5ad781dd062f0589e4e30e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 302360 zcmeFZcUTkM);10(f}()Zly0L6Nbf~aIz;I`NHtWcp#~69sX>w6RC*Wb9Tf;23BC7T z0t6C5Nq)m~&Uw%KzTfqH-}C(bK3vz3VKRI6%$~K^y4QWLP1rLnuDS0Z~+ZrmCpO{>;tA7UF0_M5GcHXFz7C(@CFhqOJNe z_*vGQnJ3pTlBit%@{~G;=e?Q=$>sa)Z$4Wp$Lqb?zH?h*nd7T@PkE_`@x!5O0O$lUstx;c(~7(<<@ zTPDO~WCb@Sr!_ScZh14N*g+NXc8p|!90GHU)X#|H3!8g%)s9?BQ@cYQ8EwX{{f5(C z(qSo53p7QKa!b)v!=Q2Ftro z%`Ge1FO~DryIYYtE_GehJH=3!KSg#tyCxt1fe)_od!qXa?FF2rjpbzl{fNRVZK}_c zk1Zxi{XI`}sKvvxQkRHXKUkXfDDXdiZdk(sEz-QFIkFNl9ZUC?<0S&qvA{!C3HWb~aO!xD0{yf6UI&@DpSz@op6u;)JDdT+_lL$b%?AwJy)Ex`J>j%+G>-(2XzpBpb~Tzp4;!vM!>li~CU8?mq3U^rEG@q9*v| z&9l^I*8I(aP1%mG1#1b11FgxThI%^|{iFC=@Q@Mt>;TxrlH!}}821*J*Cpv7QDS

DNnLvJ?>F^ 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 z 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

KE&GSp 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

c^n{(V3^GCW(Dw2mFG0?3cSg$eJLVr=*W6* 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 T*y_gt{JV40@J7jXo8bQeiwkP^ diff --git a/docs/infrastructure/index.asciidoc b/docs/infrastructure/index.asciidoc deleted file mode 100644 index 81a3022436a7e..0000000000000 --- a/docs/infrastructure/index.asciidoc +++ /dev/null @@ -1,32 +0,0 @@ -[chapter] -[role="xpack"] -[[xpack-infra]] -= Metrics - -The {metrics-app} in {kib} enables you to monitor your infrastructure metrics and identify problems in real time. -You start with a visual summary of your infrastructure where you can view basic metrics for common servers, containers, and services. -Then you can drill down to view more detailed metrics or other information for that component. - -You can: - -* View your infrastructure metrics by hosts, Kubernetes pods, or Docker containers. -You can group and filter the data in various ways to help you identify the items that interest you. - -* View current and historic values for metrics such as CPU usage, memory usage, and network traffic for each component. -The available metrics depend on the kind of component being inspected. - -* Use *Metrics Explorer* to group and visualize multiple customizable metrics for one or more components in a graphical format. -You can optionally save these views and add them to {kibana-ref}/dashboard.html[dashboards]. - -* Seamlessly switch to view the corresponding logs, application traces or uptime information for a component. - -* Create alerts based on metric thresholds for one or more components. - -[role="screenshot"] -image::infrastructure/images/infra-sysmon.png[Infrastructure Overview in Kibana] - -[float] -=== Get started - -To get started with Metrics, refer to {metrics-guide}/install-metrics-monitoring.html[Install Metrics]. - diff --git a/docs/logs/images/logs-console.png b/docs/logs/images/logs-console.png deleted file mode 100644 index ddd3346475da6621af538092c24b6fde91832778..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 510715 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

Z>(T8?^Z0CVHMJY7AHhGW62Dq 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%0D`fHXmY@$1Z&Npdi&SKQtzFH-?JwS9 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

mlcUTG+pcc&+2=ZETBLtI{ch7f^IIrGP~ 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 diff --git a/docs/logs/index.asciidoc b/docs/logs/index.asciidoc deleted file mode 100644 index 45d4321f40556..0000000000000 --- a/docs/logs/index.asciidoc +++ /dev/null @@ -1,21 +0,0 @@ -[chapter] -[role="xpack"] -[[xpack-logs]] -= Logs - -The Logs app in Kibana enables you to explore logs for common servers, containers, and services. - -The Logs app has a compact, console-like display that you can customize. -You can filter the logs by various fields, start and stop live streaming, and highlight text of interest. - -You can open the Logs app from the *Logs* tab in Kibana. -You can also open the Logs app directly from a component in the Metrics app. -In this case, you will only see the logs for the selected component. - -[role="screenshot"] -image::logs/images/logs-console.png[Logs Console in Kibana] - -[float] -=== Get started - -To get started with Elastic Logs, refer to {logs-guide}/install-logs-monitoring.html[Install Logs]. diff --git a/docs/management/alerting/alert-management.asciidoc b/docs/management/alerting/alert-management.asciidoc index 73cf40c4d7c40..f348812550978 100644 --- a/docs/management/alerting/alert-management.asciidoc +++ b/docs/management/alerting/alert-management.asciidoc @@ -4,7 +4,7 @@ beta[] -The *Alerts* tab provides a cross-app view of alerting. Different {kib} apps like <>, <>, <>, and <> can offer their own alerts, and the *Alerts* tab provides a central place to: +The *Alerts* tab provides a cross-app view of alerting. Different {kib} apps like <>, <>, <>, and <> can offer their own alerts, and the *Alerts* tab provides a central place to: * <> alerts * <> including enabling/disabling, muting/unmuting, and deleting @@ -39,7 +39,7 @@ image::images/alerts-filter-by-action-type.png[Filtering the alert list by type [[create-edit-alerts]] ==== Creating and editing alerts -Many alerts must be created within the context of a {kib} app like <>, <>, or <>, but others are generic. Generic alert types can be created in the *Alerts* management UI by clicking the *Create* button. This will launch a flyout that guides you through selecting an alert type and configuring it's properties. Refer to <> for details on what types of alerts are available and how to configure them. +Many alerts must be created within the context of a {kib} app like <>, <>, or <>, but others are generic. Generic alert types can be created in the *Alerts* management UI by clicking the *Create* button. This will launch a flyout that guides you through selecting an alert type and configuring it's properties. Refer to <> for details on what types of alerts are available and how to configure them. After an alert is created, you can re-open the flyout and change an alerts properties by clicking the *Edit* button shown on each row of the alert listing. diff --git a/docs/observability/images/apm-app.png b/docs/observability/images/apm-app.png new file mode 100644 index 0000000000000000000000000000000000000000..acbaa70c7f2f16c302ef2d9779546c90ddd254c6 GIT binary patch literal 465762 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

!e^~EDAJEk|u}mp_Y_wUL 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 jo3w+^*I)pI>eW4g<@#Y$WC6=l6q| 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

dDR*0TgVZ4>937Cks9zT8}~BH2DJEPOXZ&M#2^ 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

!bcPSypKbSav8ulA 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

+
-
-
- Name -
-
- - - trusted app 6 - - -
-
- OS -
-
- - - Windows - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 6 - - -
-
-

-
+ trusted app 6 + + + +
-
+
+ + + Windows + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 6 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -1972,255 +1881,242 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = ` class="euiPanel" >
-
-
+ Name + +
-
-
- Name -
-
- - - trusted app 7 - - -
-
- OS -
-
- - - Mac OS - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 7 - - -
-
-
-
+ trusted app 7 + + + +
-
+
+ + + Mac OS + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 7 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
-
+ + + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -2236,255 +2132,242 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = ` class="euiPanel" >
-
-
+ Name + +
-
-
- Name -
-
- - - trusted app 8 - - -
-
- OS -
-
- - - Linux - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 8 - - -
-
-
-
+ trusted app 8 + + + +
-
+
+ + + Linux + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 8 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
-
+ + + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -2500,255 +2383,242 @@ exports[`TrustedAppsGrid renders correctly when loaded data 1`] = ` class="euiPanel" >
-
-
+ Name + +
-
-
- Name -
-
- - - trusted app 9 - - -
-
- OS -
-
- - - Windows - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 9 - - -
-
-
-
+ trusted app 9 + + + +
-
+
+ + + Windows + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 9 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
-
+ + + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
-
-
-
-
-
- -
-
-
-
+
+
+
+
+
+
@@ -3054,255 +2924,242 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 0 - - -
-
- OS -
-
- - - Windows - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 0 - - -
-
-
-
+ trusted app 0 + + + +
-
+
+ + + Windows + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 0 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -3318,255 +3175,242 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 1 - - -
-
- OS -
-
- - - Mac OS - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 1 - - -
-
-
-
+ trusted app 1 + + + +
-
+
+ + + Mac OS + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 1 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -3582,255 +3426,242 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 2 - - -
-
- OS -
-
- - - Linux - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 2 - - -
-
-
-
+ trusted app 2 + + + +
-
+
+ + + Linux + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 2 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -3846,255 +3677,242 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 3 - - -
-
- OS -
-
- - - Windows - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 3 - - -
-
-
-
+ trusted app 3 + + + +
-
+
+ + + Windows + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 3 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -4110,255 +3928,242 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 4 - - -
-
- OS -
-
- - - Mac OS - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 4 - - -
-
-
-
+ trusted app 4 + + + +
-
+
+ + + Mac OS + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 4 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -4374,255 +4179,242 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 5 - - -
-
- OS -
-
- - - Linux - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 5 - - -
-
-
-
+ trusted app 5 + + + +
-
+
+ + + Linux + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 5 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -4638,255 +4430,242 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 6 - - -
-
- OS -
-
- - - Windows - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 6 - - -
-
-
-
+ trusted app 6 + + + +
-
+
+ + + Windows + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 6 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -4902,255 +4681,242 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 7 - - -
-
- OS -
-
- - - Mac OS - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 7 - - -
-
-
-
+ trusted app 7 + + + +
-
+
+ + + Mac OS + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 7 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -5166,255 +4932,242 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 8 - - -
-
- OS -
-
- - - Linux - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 8 - - -
-
-
-
+ trusted app 8 + + + +
-
+
+ + + Linux + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 8 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -5430,255 +5183,242 @@ exports[`TrustedAppsGrid renders correctly when loading data for the second time class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 9 - - -
-
- OS -
-
- - - Windows - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 9 - - -
-
-
-
+ trusted app 9 + + + +
-
+
+ + + Windows + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 9 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -5942,255 +5682,242 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 0 - - -
-
- OS -
-
- - - Windows - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 0 - - -
-
-
-
+ trusted app 0 + + + +
-
+
+ + + Windows + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 0 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -6206,255 +5933,242 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 1 - - -
-
- OS -
-
- - - Mac OS - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 1 - - -
-
-
+ + trusted app 1 + + + +
+ OS +
+
+ + + Mac OS + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 1 + + +
+ +
+
+
-
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -6470,255 +6184,242 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 2 - - -
-
- OS -
-
- - - Linux - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 2 - - -
-
-
-
+ trusted app 2 + + + +
-
+
+ + + Linux + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 2 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -6734,255 +6435,242 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 3 - - -
-
- OS -
-
- - - Windows - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 3 - - -
-
-
-
+ trusted app 3 + + + +
-
+
+ + + Windows + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 3 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -6998,255 +6686,242 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 4 - - -
-
- OS -
-
- - - Mac OS - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 4 - - -
-
-
-
+ trusted app 4 + + + +
+ OS +
+
+ + + Mac OS + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
-
+
+ + + Trusted App 4 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -7262,255 +6937,242 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 5 - - -
-
- OS -
-
- - - Linux - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 5 - - -
-
-
-
+ trusted app 5 + + + +
-
+
+ + + Linux + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 5 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -7526,519 +7188,493 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 6 - - -
-
- OS -
-
- - - Windows - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 6 - - -
-
-
-
+ trusted app 6 + + + +
-
+
+ + + Windows + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 6 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ Name +
+
+ + + trusted app 7 + + +
+
+ OS +
+
+ + + Mac OS + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 7 + + +
+
+
-
-
- Name -
-
- - - trusted app 7 - - -
-
- OS -
-
- - - Mac OS - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 7 - - -
-
-
-
-
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -8054,255 +7690,242 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 8 - - -
-
- OS -
-
- - - Linux - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 8 - - -
-
-
-
+ trusted app 8 + + + +
-
+
+ + + Linux + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 8 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
@@ -8318,255 +7941,242 @@ exports[`TrustedAppsGrid renders correctly when new page and page size set (not class="euiPanel" >
-
-
-
+
+ -
- Name -
-
- - - trusted app 9 - - -
-
- OS -
-
- - - Windows - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 9 - - -
-
-
-
+ trusted app 9 + + + +
-
+
+ + + Windows + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 9 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
+
-
+ + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/__snapshots__/index.test.tsx.snap index 181b59c65a3d5..5f652b39ffd56 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_apps_list/__snapshots__/index.test.tsx.snap @@ -779,252 +779,239 @@ exports[`TrustedAppsList renders correctly when item details expanded 1`] = ` class="euiPanel" >
-
-
+ Name + +
-
-
- Name -
-
- - - trusted app 0 - - -
-
- OS -
-
- - - Windows - - -
-
- Date Created -
-
- 1 minute ago -
-
- Created By -
-
- - - someone - - -
-
- Description -
-
- - - Trusted App 0 - - -
-
-
-
+ trusted app 0 + + + +
-
+
+ + + Windows + + +
+
+ Date Created +
+
+ 1 minute ago +
+
+ Created By +
+
+ + + someone + + +
+
+ Description +
+
+ + + Trusted App 0 + + +
+ +
+
+
+
+
-
-
+
+
-
-
-
-
- - - - + +
-
+ + + + + + - + - + - - - - + + + + + + + - - -
+
+
-
- - Field - -
-
+ + +
-
- - Operator - -
-
+ + +
-
- - Value - -
-
- -
- - No items found - -
-
- + No items found + + + +
+
+
+
+
-
-
-
-
- -
-
-
-
+ Remove + + +
From 8940091cf9e31dd6e0b612d2be4b2ef402d2ea9f Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 15 Oct 2020 08:07:17 -0700 Subject: [PATCH 091/128] [ci-stats] record async chunk count (#80606) Co-authored-by: spalger --- packages/kbn-optimizer/src/optimizer/get_output_stats.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/kbn-optimizer/src/optimizer/get_output_stats.ts b/packages/kbn-optimizer/src/optimizer/get_output_stats.ts index cc4cd05f42c3f..82d1a276ccdad 100644 --- a/packages/kbn-optimizer/src/optimizer/get_output_stats.ts +++ b/packages/kbn-optimizer/src/optimizer/get_output_stats.ts @@ -109,6 +109,11 @@ export function getMetrics(log: ToolingLog, config: OptimizerConfig) { id: bundle.id, value: sumSize(asyncChunks), }, + { + group: `async chunk count`, + id: bundle.id, + value: asyncChunks.length, + }, { group: `miscellaneous assets size`, id: bundle.id, From 88591acc0396116b6cc1ac1cf2a3560696d0ad96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Zolt=C3=A1n=20Szab=C3=B3?= Date: Thu, 15 Oct 2020 17:25:55 +0200 Subject: [PATCH 092/128] [DOCS] Adds initial content to Transforms readme.md (#80630) Co-authored-by: Walter Rafelsberger Co-authored-by: Pete Harverson --- docs/developer/plugin-list.asciidoc | 4 +- x-pack/plugins/transform/readme.md | 117 ++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 x-pack/plugins/transform/readme.md diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index b5a810852b94d..3e849ca80db72 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -499,8 +499,8 @@ routes, etc. |Gathers all usage collection, retrieving them from both: OSS and X-Pack plugins. -|{kib-repo}blob/{branch}/x-pack/plugins/transform[transform] -|WARNING: Missing README. +|{kib-repo}blob/{branch}/x-pack/plugins/transform/readme.md[transform] +|This plugin provides access to the transforms features provided by Elastic. |{kib-repo}blob/{branch}/x-pack/plugins/translations[translations] diff --git a/x-pack/plugins/transform/readme.md b/x-pack/plugins/transform/readme.md new file mode 100644 index 0000000000000..2ee2a7b70c5f1 --- /dev/null +++ b/x-pack/plugins/transform/readme.md @@ -0,0 +1,117 @@ +# Documentation for Transforms UI developers + +This plugin provides access to the transforms features provided by Elastic. + +## Requirements + +To use the transforms feature, you must have at least a Basic license. For more +info, refer to +[Set up transforms](https://www.elastic.co/guide/en/elasticsearch/reference/current/transform-setup.html). + + +## Setup local environment + +### Kibana + +1. Fork and clone the [Kibana repo](https://github.com/elastic/kibana). + +1. Install `nvm`, `node`, `yarn` (for example, by using Homebrew). See + [Install dependencies](https://www.elastic.co/guide/en/kibana/master/development-getting-started.html#_install_dependencies). + +1. Make sure that Elasticsearch is deployed and running on `localhost:9200`. + +1. Navigate to the directory of the `kibana` repository on your machine. + +1. Fetch the latest changes from the repository. + +1. Checkout the branch of the version you want to use. For example, if you want + to use a 7.9 version, run `git checkout 7.9`. (Your Elasticsearch and Kibana + instances need to be the same version.) + +1. Run `nvm use`. The response shows the Node version that the environment uses. + If you need to update your Node version, the response message contains the + command you need to run to do it. + +1. Run `yarn kbn bootstrap`. It takes all the dependencies in the code and + installs/checks them. It is recommended to use it every time when you switch + between branches. + +1. Make a copy of `kibana.yml` and save as `kibana.dev.yml`. (Git will not track + the changes in `kibana.dev.yml` but yarn will use it.) + +1. Provide the appropriate password and user name in `kibana.dev.yml`. + +1. Run `yarn start` to start Kibana. + +1. Go to http://localhost:560x/xxx (check the terminal message for the exact + path). + +For more details, refer to this [getting started](https://www.elastic.co/guide/en/kibana/master/development-getting-started.html) page. + +### Adding sample data to Kibana + +Kibana has sample data sets that you can add to your setup so that you can test +different configurations on sample data. + +1. Click the Elastic logo in the upper left hand corner of your browser to + navigate to the Kibana home page. + +1. Click *Load a data set and a Kibana dashboard*. + +1. Pick a data set or feel free to click *Add* on all of the available sample + data sets. + +These data sets are now ready to be used for creating transforms in Kibana. + +## Running tests + +### Jest tests + +Run the test following jest tests from `kibana/x-pack`. + +New snapshots, all plugins: + +``` +node scripts/jest +``` + +Update snapshots for the transform plugin: + +``` +node scripts/jest plugins/transform -u +``` + +Update snapshots for a specific directory only: + +``` +node scripts/jest x-pack/plugins/transform/public/app/sections +``` + +Run tests with verbose output: + +``` +node scripts/jest plugins/transform --verbose +``` + +### Functional tests + +Before running the test server, make sure to quit all other instances of +Elasticsearch. + +1. From one terminal, in the x-pack directory, run: + + node scripts/functional_tests_server.js --config test/functional/config.js + + This command starts an Elasticsearch and Kibana instance that the tests will be run against. + +1. In another tab, run the following command to perform API integration tests (from the x-pack directory): + + node scripts/functional_test_runner.js --include-tag transform --config test/api_integration/config + + The transform API integration tests are located in `x-pack/test/api_integration/apis/transform`. + +1. In another tab, run the following command to perform UI functional tests (from the x-pack directory): + + node scripts/functional_test_runner.js --include-tag transform + + The transform functional tests are located in `x-pack/test/functional/apps/transform`. From 3b62b95dc6eea6f9ba200514bfcac83e4f19eed3 Mon Sep 17 00:00:00 2001 From: Brent Kimmel Date: Thu, 15 Oct 2020 11:28:50 -0400 Subject: [PATCH 093/128] [Security Solution][Resolver]Adjust layout and stacking for submenu/node (#80607) * [Security Solution][Resolver]Adjust layout and stacking for submenu/node Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/resolver/view/edge_line.tsx | 1 + .../resolver/view/process_event_dot.tsx | 5 ++++- .../public/resolver/view/styles.tsx | 20 +++++++++++++++++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/resolver/view/edge_line.tsx b/x-pack/plugins/security_solution/public/resolver/view/edge_line.tsx index 777a7292e9c23..411e4b3e3a5b0 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/edge_line.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/edge_line.tsx @@ -25,6 +25,7 @@ const StyledEdgeLine = styled.div` return `${fontSize(props.magFactorX, 12, 8.5)}px`; }}; background-color: ${(props) => props.resolverEdgeColor}; + z-index: 10; `; interface StyledElapsedTime { diff --git a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx index d93b46dcb0620..7968b4a3a1fd0 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx @@ -62,6 +62,7 @@ const StyledDescriptionText = styled.div` text-align: left; text-transform: uppercase; width: fit-content; + z-index: 40; `; const StyledOuterGroup = styled.g` @@ -311,6 +312,7 @@ const UnstyledProcessEventDot = React.memo( outline: 'transparent', border: 'none', pointerEvents: 'none', + zIndex: 30, }} > @@ -391,6 +393,7 @@ const UnstyledProcessEventDot = React.memo( backgroundColor: colorMap.resolverBackground, alignSelf: 'flex-start', padding: 0, + zIndex: 40, }} > Date: Thu, 15 Oct 2020 19:07:25 +0300 Subject: [PATCH 094/128] [Security Solution][Case] Fix Jira's parent issue placeholder (#80619) --- .../public/cases/components/settings/jira/translations.ts | 4 ++-- .../components/builtin_action_types/jira/translations.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/public/cases/components/settings/jira/translations.ts b/x-pack/plugins/security_solution/public/cases/components/settings/jira/translations.ts index 54c46f064aa75..05a05ac6dd586 100644 --- a/x-pack/plugins/security_solution/public/cases/components/settings/jira/translations.ts +++ b/x-pack/plugins/security_solution/public/cases/components/settings/jira/translations.ts @@ -36,14 +36,14 @@ export const GET_ISSUE_API_ERROR = (id: string) => export const SEARCH_ISSUES_COMBO_BOX_ARIA_LABEL = i18n.translate( 'xpack.securitySolution.components.settings.jira.searchIssuesComboBoxAriaLabel', { - defaultMessage: 'Select parent issue', + defaultMessage: 'Type to search', } ); export const SEARCH_ISSUES_PLACEHOLDER = i18n.translate( 'xpack.securitySolution.components.settings.jira.searchIssuesComboBoxPlaceholder', { - defaultMessage: 'Select parent issue', + defaultMessage: 'Type to search', } ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts index 2517552304d8d..019133b03d55f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/translations.ts @@ -151,14 +151,14 @@ export const GET_ISSUE_API_ERROR = (id: string) => export const SEARCH_ISSUES_COMBO_BOX_ARIA_LABEL = i18n.translate( 'xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxAriaLabel', { - defaultMessage: 'Select parent issue', + defaultMessage: 'Type to search', } ); export const SEARCH_ISSUES_PLACEHOLDER = i18n.translate( 'xpack.triggersActionsUI.components.builtinActionTypes.jira.searchIssuesComboBoxPlaceholder', { - defaultMessage: 'Select parent issue', + defaultMessage: 'Type to search', } ); From 62c93aa009f467af6ae22a78f745ea4b27391afc Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Thu, 15 Oct 2020 12:16:51 -0400 Subject: [PATCH 095/128] Fix sorting of alerts (#80546) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/lib/alerts/fetch_status.test.ts | 73 +++++++++++++++---- .../server/lib/alerts/fetch_status.ts | 10 ++- 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts index fdd7253550624..824eeab7245b4 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts @@ -7,12 +7,16 @@ import { fetchStatus } from './fetch_status'; import { AlertUiState, AlertState } from '../../alerts/types'; import { AlertSeverity } from '../../../common/enums'; -import { ALERT_CPU_USAGE, ALERT_CLUSTER_HEALTH } from '../../../common/constants'; +import { + ALERT_CPU_USAGE, + ALERT_CLUSTER_HEALTH, + ALERT_DISK_USAGE, + ALERT_MISSING_MONITORING_DATA, +} from '../../../common/constants'; describe('fetchStatus', () => { const alertType = ALERT_CPU_USAGE; const alertTypes = [alertType]; - const log = { warn: jest.fn() }; const start = 0; const end = 0; const id = 1; @@ -53,6 +57,7 @@ describe('fetchStatus', () => { afterEach(() => { (alertsClient.find as jest.Mock).mockClear(); (alertsClient.getAlertState as jest.Mock).mockClear(); + alertStates.length = 0; }); it('should fetch from the alerts client', async () => { @@ -62,8 +67,7 @@ describe('fetchStatus', () => { alertTypes, defaultClusterState.clusterUuid, start, - end, - log as any + end ); expect(status).toEqual({ monitoring_alert_cpu_usage: { @@ -98,8 +102,7 @@ describe('fetchStatus', () => { alertTypes, defaultClusterState.clusterUuid, start, - end, - log as any + end ); expect(Object.values(status).length).toBe(1); expect(Object.keys(status)).toEqual(alertTypes); @@ -126,8 +129,7 @@ describe('fetchStatus', () => { alertTypes, defaultClusterState.clusterUuid, customStart, - customEnd, - log as any + customEnd ); expect(Object.values(status).length).toBe(1); expect(Object.keys(status)).toEqual(alertTypes); @@ -141,8 +143,7 @@ describe('fetchStatus', () => { alertTypes, defaultClusterState.clusterUuid, start, - end, - log as any + end ); expect((alertsClient.find as jest.Mock).mock.calls[0][0].options.filter).toBe( `alert.attributes.alertTypeId:${alertType}` @@ -160,8 +161,7 @@ describe('fetchStatus', () => { alertTypes, defaultClusterState.clusterUuid, start, - end, - log as any + end ); expect(status[alertType].states.length).toEqual(0); }); @@ -178,8 +178,7 @@ describe('fetchStatus', () => { alertTypes, defaultClusterState.clusterUuid, start, - end, - log as any + end ); expect(status).toEqual({}); }); @@ -197,9 +196,51 @@ describe('fetchStatus', () => { [ALERT_CLUSTER_HEALTH], defaultClusterState.clusterUuid, start, - end, - log as any + end ); expect(customLicenseService.getWatcherFeature).toHaveBeenCalled(); }); + + it('should sort the alerts', async () => { + const customAlertsClient = { + find: jest.fn(() => ({ + total: 1, + data: [ + { + id, + }, + ], + })), + getAlertState: jest.fn(() => ({ + alertInstances: { + abc: { + state: { + alertStates: [ + { + cluster: defaultClusterState, + ui: { + ...defaultUiState, + isFiring: true, + }, + }, + ], + }, + }, + }, + })), + }; + const status = await fetchStatus( + customAlertsClient as any, + licenseService as any, + [ALERT_CPU_USAGE, ALERT_DISK_USAGE, ALERT_MISSING_MONITORING_DATA], + defaultClusterState.clusterUuid, + start, + end + ); + expect(Object.keys(status)).toEqual([ + ALERT_CPU_USAGE, + ALERT_DISK_USAGE, + ALERT_MISSING_MONITORING_DATA, + ]); + }); }); diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts index 49e688fafbee5..ed49f42e4908c 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts @@ -18,8 +18,9 @@ export async function fetchStatus( clusterUuid: string, start: number, end: number, - filters: CommonAlertFilter[] + filters: CommonAlertFilter[] = [] ): Promise<{ [type: string]: CommonAlertStatus }> { + const types: Array<{ type: string; result: CommonAlertStatus }> = []; const byType: { [type: string]: CommonAlertStatus } = {}; await Promise.all( (alertTypes || ALERTS).map(async (type) => { @@ -39,7 +40,7 @@ export async function fetchStatus( alert: serialized, }; - byType[type] = result; + types.push({ type, result }); const id = alert.getId(); if (!id) { @@ -75,5 +76,10 @@ export async function fetchStatus( }) ); + types.sort((a, b) => (a.type === b.type ? 0 : a.type.length > b.type.length ? 1 : -1)); + for (const { type, result } of types) { + byType[type] = result; + } + return byType; } From e1456372dae7f3d2bbdfc379309d43e892a7468d Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Thu, 15 Oct 2020 17:30:09 +0100 Subject: [PATCH 096/128] [SecuritySolution] Replace act with waitFor (#80648) * replace act with waitFor * update unit test * update unit test * update unit test --- .../embeddables/embedded_map.test.tsx | 73 +++++++++---------- .../export_timeline/export_timeline.test.tsx | 31 ++++---- 2 files changed, 49 insertions(+), 55 deletions(-) diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.test.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.test.tsx index 219409b10be6c..f927173b144d6 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map.test.tsx @@ -7,7 +7,7 @@ import { mount, ReactWrapper, shallow } from 'enzyme'; import React from 'react'; import * as redux from 'react-redux'; -import { act } from 'react-dom/test-utils'; +import { waitFor } from '@testing-library/react'; import '../../../common/mock/match_media'; import { useIndexPatterns } from '../../../common/hooks/use_index_patterns'; @@ -107,20 +107,19 @@ describe('EmbeddedMapComponent', () => { (createEmbeddable as jest.Mock).mockResolvedValue(mockCreateEmbeddable); - let wrapper: ReactWrapper; - await act(async () => { - wrapper = mount( - - - - ); - }); + const wrapper: ReactWrapper = mount( + + + + ); - wrapper!.update(); + await waitFor(() => { + wrapper.update(); - expect(wrapper!.find('[data-test-subj="EmbeddablePanel"]').exists()).toEqual(true); - expect(wrapper!.find('[data-test-subj="IndexPatternsMissingPrompt"]').exists()).toEqual(false); - expect(wrapper!.find('[data-test-subj="loading-panel"]').exists()).toEqual(false); + expect(wrapper.find('[data-test-subj="EmbeddablePanel"]').exists()).toEqual(true); + expect(wrapper.find('[data-test-subj="IndexPatternsMissingPrompt"]').exists()).toEqual(false); + expect(wrapper.find('[data-test-subj="loading-panel"]').exists()).toEqual(false); + }); }); test('renders IndexPatternsMissingPrompt', async () => { @@ -132,20 +131,18 @@ describe('EmbeddedMapComponent', () => { (createEmbeddable as jest.Mock).mockResolvedValue(mockCreateEmbeddable); - let wrapper: ReactWrapper; - await act(async () => { - wrapper = mount( - - - - ); - }); - - wrapper!.update(); + const wrapper: ReactWrapper = mount( + + + + ); + await waitFor(() => { + wrapper.update(); - expect(wrapper!.find('[data-test-subj="EmbeddablePanel"]').exists()).toEqual(false); - expect(wrapper!.find('[data-test-subj="IndexPatternsMissingPrompt"]').exists()).toEqual(true); - expect(wrapper!.find('[data-test-subj="loading-panel"]').exists()).toEqual(false); + expect(wrapper.find('[data-test-subj="EmbeddablePanel"]').exists()).toEqual(false); + expect(wrapper.find('[data-test-subj="IndexPatternsMissingPrompt"]').exists()).toEqual(true); + expect(wrapper.find('[data-test-subj="loading-panel"]').exists()).toEqual(false); + }); }); test('renders Loader', async () => { @@ -154,19 +151,17 @@ describe('EmbeddedMapComponent', () => { (createEmbeddable as jest.Mock).mockResolvedValue(null); - let wrapper: ReactWrapper; - await act(async () => { - wrapper = mount( - - - - ); - }); - - wrapper!.update(); + const wrapper: ReactWrapper = mount( + + + + ); + await waitFor(() => { + wrapper.update(); - expect(wrapper!.find('[data-test-subj="EmbeddablePanel"]').exists()).toEqual(false); - expect(wrapper!.find('[data-test-subj="IndexPatternsMissingPrompt"]').exists()).toEqual(false); - expect(wrapper!.find('[data-test-subj="loading-panel"]').exists()).toEqual(true); + expect(wrapper.find('[data-test-subj="EmbeddablePanel"]').exists()).toEqual(false); + expect(wrapper.find('[data-test-subj="IndexPatternsMissingPrompt"]').exists()).toEqual(false); + expect(wrapper.find('[data-test-subj="loading-panel"]').exists()).toEqual(true); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/export_timeline/export_timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/export_timeline/export_timeline.test.tsx index d0cfbaccde7dd..31051a51a58d5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/export_timeline/export_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/export_timeline/export_timeline.test.tsx @@ -12,7 +12,7 @@ import { mockSelectedTimeline } from './mocks'; import * as i18n from '../translations'; import { ReactWrapper, mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; +import { waitFor } from '@testing-library/react'; import { useParams } from 'react-router-dom'; jest.mock('../translations', () => { @@ -102,15 +102,14 @@ describe('TimelineDownloader', () => { ...defaultTestProps, }; - await act(() => { - wrapper = mount(); - }); - - wrapper.update(); + wrapper = mount(); + await waitFor(() => { + wrapper.update(); - expect(mockDispatchToaster.mock.calls[0][0].title).toEqual( - i18n.SUCCESSFULLY_EXPORTED_TIMELINES - ); + expect(mockDispatchToaster.mock.calls[0][0].title).toEqual( + i18n.SUCCESSFULLY_EXPORTED_TIMELINES + ); + }); }); test('With correct toast message on success for exported templates', async () => { @@ -119,15 +118,15 @@ describe('TimelineDownloader', () => { }; (useParams as jest.Mock).mockReturnValue({ tabName: 'template' }); - await act(() => { - wrapper = mount(); - }); + wrapper = mount(); - wrapper.update(); + await waitFor(() => { + wrapper.update(); - expect(mockDispatchToaster.mock.calls[0][0].title).toEqual( - i18n.SUCCESSFULLY_EXPORTED_TIMELINES - ); + expect(mockDispatchToaster.mock.calls[0][0].title).toEqual( + i18n.SUCCESSFULLY_EXPORTED_TIMELINES + ); + }); }); }); }); From cd9381c1181b2f7557ef4934ecb5ede99e7b7989 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Thu, 15 Oct 2020 12:33:53 -0400 Subject: [PATCH 097/128] [Security Solution][Resolver] Data stream fields being populated (#80216) * Data stream fields being populated * Adding some comments * Switching data stream options to specific functions * Removing unneeded import * Refactoring based on Brent's feedback --- .../common/endpoint/generate_data.test.ts | 51 ++++- .../common/endpoint/generate_data.ts | 190 ++++++++++++++---- .../common/endpoint/index_data.ts | 83 +++++--- .../common/endpoint/types/index.ts | 16 ++ .../pages/endpoint_hosts/view/index.test.tsx | 8 +- .../endpoint/resolver_generator_script.ts | 4 +- .../routes/resolver/utils/pagination.test.ts | 2 +- .../apis/package.ts | 27 ++- .../apis/resolver/children.ts | 38 +++- .../apis/resolver/entity_id.ts | 24 ++- 10 files changed, 346 insertions(+), 97 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts index 7e3b3d125fb5d..66119e098238e 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.test.ts @@ -26,6 +26,53 @@ interface Node { parent_entity_id?: string; } +describe('data generator data streams', () => { + // these tests cast the result of the generate methods so that we can specifically compare the `data_stream` fields + it('creates a generator with default data streams', () => { + const generator = new EndpointDocGenerator('seed'); + expect(generator.generateHostMetadata().data_stream).toEqual({ + type: 'metrics', + dataset: 'endpoint.metadata', + namespace: 'default', + }); + expect(generator.generatePolicyResponse().data_stream).toEqual({ + type: 'metrics', + dataset: 'endpoint.policy', + namespace: 'default', + }); + expect(generator.generateEvent().data_stream).toEqual({ + type: 'logs', + dataset: 'endpoint.events.process', + namespace: 'default', + }); + expect(generator.generateAlert().data_stream).toEqual({ + type: 'logs', + dataset: 'endpoint.alerts', + namespace: 'default', + }); + }); + + it('creates a generator with custom data streams', () => { + const metadataDataStream = { type: 'meta', dataset: 'dataset', namespace: 'name' }; + const policyDataStream = { type: 'policy', dataset: 'fake', namespace: 'something' }; + const eventsDataStream = { type: 'events', dataset: 'events stuff', namespace: 'name' }; + const alertsDataStream = { type: 'alerts', dataset: 'alerts stuff', namespace: 'name' }; + const generator = new EndpointDocGenerator('seed'); + expect(generator.generateHostMetadata(0, metadataDataStream).data_stream).toStrictEqual( + metadataDataStream + ); + expect(generator.generatePolicyResponse({ policyDataStream }).data_stream).toStrictEqual( + policyDataStream + ); + expect(generator.generateEvent({ eventsDataStream }).data_stream).toStrictEqual( + eventsDataStream + ); + expect(generator.generateAlert({ alertsDataStream }).data_stream).toStrictEqual( + alertsDataStream + ); + }); +}); + describe('data generator', () => { let generator: EndpointDocGenerator; beforeEach(() => { @@ -69,7 +116,7 @@ describe('data generator', () => { it('creates policy response documents', () => { const timestamp = new Date().getTime(); - const hostPolicyResponse = generator.generatePolicyResponse(timestamp); + const hostPolicyResponse = generator.generatePolicyResponse({ ts: timestamp }); expect(hostPolicyResponse['@timestamp']).toEqual(timestamp); expect(hostPolicyResponse.event.created).toEqual(timestamp); expect(hostPolicyResponse.Endpoint).not.toBeNull(); @@ -80,7 +127,7 @@ describe('data generator', () => { it('creates alert event documents', () => { const timestamp = new Date().getTime(); - const alert = generator.generateAlert(timestamp); + const alert = generator.generateAlert({ ts: timestamp }); expect(alert['@timestamp']).toEqual(timestamp); expect(alert.event?.action).not.toBeNull(); expect(alert.Endpoint).not.toBeNull(); diff --git a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts index f0254616e6c9d..07b230ffc6cc5 100644 --- a/x-pack/plugins/security_solution/common/endpoint/generate_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/generate_data.ts @@ -7,6 +7,7 @@ import uuid from 'uuid'; import seedrandom from 'seedrandom'; import { AlertEvent, + DataStream, EndpointStatus, Host, HostMetadata, @@ -59,6 +60,7 @@ interface EventOptions { pid?: number; parentPid?: number; extensions?: object; + eventsDataStream?: DataStream; } const Windows: OSFields[] = [ @@ -330,6 +332,8 @@ export interface TreeOptions { percentTerminated?: number; alwaysGenMaxChildrenPerNode?: boolean; ancestryArraySize?: number; + eventsDataStream?: DataStream; + alertsDataStream?: DataStream; } type TreeOptionDefaults = Required; @@ -351,19 +355,51 @@ export function getTreeOptionsWithDef(options?: TreeOptions): TreeOptionDefaults percentTerminated: options?.percentTerminated ?? 100, alwaysGenMaxChildrenPerNode: options?.alwaysGenMaxChildrenPerNode ?? false, ancestryArraySize: options?.ancestryArraySize ?? ANCESTRY_LIMIT, + eventsDataStream: options?.eventsDataStream ?? eventsDefaultDataStream, + alertsDataStream: options?.alertsDataStream ?? alertsDefaultDataStream, }; } +const metadataDefaultDataStream = { + type: 'metrics', + dataset: 'endpoint.metadata', + namespace: 'default', +}; + +const policyDefaultDataStream = { + type: 'metrics', + dataset: 'endpoint.policy', + namespace: 'default', +}; + +const eventsDefaultDataStream = { + type: 'logs', + dataset: 'endpoint.events.process', + namespace: 'default', +}; + +const alertsDefaultDataStream = { + type: 'logs', + dataset: 'endpoint.alerts', + namespace: 'default', +}; + export class EndpointDocGenerator { commonInfo: HostInfo; random: seedrandom.prng; sequence: number = 0; + /** + * The EndpointDocGenerator parameters + * + * @param seed either a string to seed the random number generator or a random number generator function + */ constructor(seed: string | seedrandom.prng = Math.random().toString()) { if (typeof seed === 'string') { this.random = seedrandom(seed); } else { this.random = seed; } + this.commonInfo = this.createHostData(); } @@ -383,6 +419,21 @@ export class EndpointDocGenerator { this.commonInfo.Endpoint.policy.applied.status = this.randomChoice(POLICY_RESPONSE_STATUSES); } + /** + * Parses an index and returns the data stream fields extracted from the index. + * + * @param index the index name to parse into the data stream parts + */ + public static createDataStreamFromIndex(index: string): DataStream { + // e.g. logs-endpoint.events.network-default + const parts = index.split('-'); + return { + type: parts[0], // logs + dataset: parts[1], // endpoint.events.network + namespace: parts[2], // default + }; + } + private createHostData(): HostInfo { const hostName = this.randomHostname(); return { @@ -417,8 +468,12 @@ export class EndpointDocGenerator { /** * Creates a host metadata document * @param ts - Timestamp to put in the event + * @param metadataDataStream the values to populate the data_stream fields when generating metadata documents */ - public generateHostMetadata(ts = new Date().getTime()): HostMetadata { + public generateHostMetadata( + ts = new Date().getTime(), + metadataDataStream = metadataDefaultDataStream + ): HostMetadata { return { '@timestamp': ts, event: { @@ -432,6 +487,7 @@ export class EndpointDocGenerator { dataset: 'endpoint.metadata', }, ...this.commonInfo, + data_stream: metadataDataStream, }; } @@ -441,15 +497,24 @@ export class EndpointDocGenerator { * @param entityID - entityID of the originating process * @param parentEntityID - optional entityID of the parent process, if it exists * @param ancestry - an array of ancestors for the generated alert + * @param alertsDataStream the values to populate the data_stream fields when generating alert documents */ - public generateAlert( + public generateAlert({ ts = new Date().getTime(), entityID = this.randomString(10), - parentEntityID?: string, - ancestry: string[] = [] - ): AlertEvent { + parentEntityID, + ancestry = [], + alertsDataStream = alertsDefaultDataStream, + }: { + ts?: number; + entityID?: string; + parentEntityID?: string; + ancestry?: string[]; + alertsDataStream?: DataStream; + } = {}): AlertEvent { return { ...this.commonInfo, + data_stream: alertsDataStream, '@timestamp': ts, ecs: { version: '1.4.0', @@ -598,6 +663,7 @@ export class EndpointDocGenerator { return {}; })(options.eventCategory); return { + data_stream: options?.eventsDataStream ?? eventsDefaultDataStream, '@timestamp': options.timestamp ? options.timestamp : new Date().getTime(), agent: { ...this.commonInfo.agent, type: 'endpoint' }, ecs: { @@ -813,6 +879,7 @@ export class EndpointDocGenerator { const startDate = new Date().getTime(); const root = this.generateEvent({ timestamp: startDate + 1000, + eventsDataStream: opts.eventsDataStream, }); events.push(root); let ancestor = root; @@ -824,18 +891,24 @@ export class EndpointDocGenerator { secBeforeAlert: number, eventList: Event[] ) => { - for (const relatedAlert of this.relatedAlertsGenerator(node, alertsPerNode, secBeforeAlert)) { + for (const relatedAlert of this.relatedAlertsGenerator({ + node, + relatedAlerts: alertsPerNode, + alertCreationTime: secBeforeAlert, + alertsDataStream: opts.alertsDataStream, + })) { eventList.push(relatedAlert); } }; const addRelatedEvents = (node: Event, secBeforeEvent: number, eventList: Event[]) => { - for (const relatedEvent of this.relatedEventsGenerator( + for (const relatedEvent of this.relatedEventsGenerator({ node, - opts.relatedEvents, - secBeforeEvent, - opts.relatedEventsOrdered - )) { + relatedEvents: opts.relatedEvents, + processDuration: secBeforeEvent, + ordered: opts.relatedEventsOrdered, + eventsDataStream: opts.eventsDataStream, + })) { eventList.push(relatedEvent); } }; @@ -857,6 +930,7 @@ export class EndpointDocGenerator { parentEntityID: parentEntityIDSafeVersion(root), eventCategory: ['process'], eventType: ['end'], + eventsDataStream: opts.eventsDataStream, }) ); } @@ -877,6 +951,7 @@ export class EndpointDocGenerator { ancestryArrayLimit: opts.ancestryArraySize, parentPid: firstNonNullValue(ancestor.process?.pid), pid: this.randomN(5000), + eventsDataStream: opts.eventsDataStream, }); events.push(ancestor); timestamp = timestamp + 1000; @@ -892,6 +967,7 @@ export class EndpointDocGenerator { eventType: ['end'], ancestry: ancestryArray(ancestor), ancestryArrayLimit: opts.ancestryArraySize, + eventsDataStream: opts.eventsDataStream, }) ); } @@ -912,12 +988,13 @@ export class EndpointDocGenerator { timestamp = timestamp + 1000; events.push( - this.generateAlert( - timestamp, - entityIDSafeVersion(ancestor), - parentEntityIDSafeVersion(ancestor), - ancestryArray(ancestor) - ) + this.generateAlert({ + ts: timestamp, + entityID: entityIDSafeVersion(ancestor), + parentEntityID: parentEntityIDSafeVersion(ancestor), + ancestry: ancestryArray(ancestor), + alertsDataStream: opts.alertsDataStream, + }) ); return events; } @@ -973,6 +1050,7 @@ export class EndpointDocGenerator { parentEntityID: currentStateEntityID, ancestry, ancestryArrayLimit: opts.ancestryArraySize, + eventsDataStream: opts.eventsDataStream, }); maxChildren = this.randomN(opts.children + 1); @@ -996,16 +1074,23 @@ export class EndpointDocGenerator { eventType: ['end'], ancestry, ancestryArrayLimit: opts.ancestryArraySize, + eventsDataStream: opts.eventsDataStream, }); } if (this.randomN(100) < opts.percentWithRelated) { - yield* this.relatedEventsGenerator( - child, - opts.relatedEvents, + yield* this.relatedEventsGenerator({ + node: child, + relatedEvents: opts.relatedEvents, processDuration, - opts.relatedEventsOrdered - ); - yield* this.relatedAlertsGenerator(child, opts.relatedAlerts, processDuration); + ordered: opts.relatedEventsOrdered, + eventsDataStream: opts.eventsDataStream, + }); + yield* this.relatedAlertsGenerator({ + node: child, + relatedAlerts: opts.relatedAlerts, + alertCreationTime: processDuration, + alertsDataStream: opts.alertsDataStream, + }); } } } @@ -1019,12 +1104,19 @@ export class EndpointDocGenerator { * @param ordered - if true the events will have an increasing timestamp, otherwise their timestamp will be random but * guaranteed to be greater than or equal to the originating event */ - public *relatedEventsGenerator( - node: Event, - relatedEvents: RelatedEventInfo[] | number = 10, - processDuration: number = 6 * 3600, - ordered: boolean = false - ) { + public *relatedEventsGenerator({ + node, + relatedEvents = 10, + processDuration = 6 * 3600, + ordered = false, + eventsDataStream = eventsDefaultDataStream, + }: { + node: Event; + relatedEvents?: RelatedEventInfo[] | number; + processDuration?: number; + ordered?: boolean; + eventsDataStream?: DataStream; + }) { let relatedEventsInfo: RelatedEventInfo[]; const nodeTimestamp = timestampSafeVersion(node) ?? 0; let ts = nodeTimestamp + 1; @@ -1056,6 +1148,7 @@ export class EndpointDocGenerator { eventCategory: eventInfo.category, eventType: eventInfo.creationType, ancestry: ancestryArray(node), + eventsDataStream, }); } } @@ -1067,19 +1160,26 @@ export class EndpointDocGenerator { * @param relatedAlerts - number which defines the number of related alerts to create * @param alertCreationTime - maximum number of seconds after process event that related alert timestamp can be */ - public *relatedAlertsGenerator( - node: Event, - relatedAlerts: number = 3, - alertCreationTime: number = 6 * 3600 - ) { + public *relatedAlertsGenerator({ + node, + relatedAlerts = 3, + alertCreationTime = 6 * 3600, + alertsDataStream = alertsDefaultDataStream, + }: { + node: Event; + relatedAlerts: number; + alertCreationTime: number; + alertsDataStream: DataStream; + }) { for (let i = 0; i < relatedAlerts; i++) { const ts = (timestampSafeVersion(node) ?? 0) + this.randomN(alertCreationTime) * 1000; - yield this.generateAlert( + yield this.generateAlert({ ts, - entityIDSafeVersion(node), - parentEntityIDSafeVersion(node), - ancestryArray(node) - ); + entityID: entityIDSafeVersion(node), + parentEntityID: parentEntityIDSafeVersion(node), + ancestry: ancestryArray(node), + alertsDataStream, + }); } } @@ -1227,15 +1327,21 @@ export class EndpointDocGenerator { /** * Generates a Host Policy response message */ - public generatePolicyResponse( + public generatePolicyResponse({ ts = new Date().getTime(), - allStatus?: HostPolicyResponseActionStatus - ): HostPolicyResponse { + allStatus, + policyDataStream = policyDefaultDataStream, + }: { + ts?: number; + allStatus?: HostPolicyResponseActionStatus; + policyDataStream?: DataStream; + } = {}): HostPolicyResponse { const policyVersion = this.seededUUIDv4(); const status = () => { return allStatus || this.randomHostPolicyResponseActionStatus(); }; return { + data_stream: policyDataStream, '@timestamp': ts, agent: { id: this.commonInfo.agent.id, diff --git a/x-pack/plugins/security_solution/common/endpoint/index_data.ts b/x-pack/plugins/security_solution/common/endpoint/index_data.ts index bf3d12f231c86..c0c70f9ca11af 100644 --- a/x-pack/plugins/security_solution/common/endpoint/index_data.ts +++ b/x-pack/plugins/security_solution/common/endpoint/index_data.ts @@ -52,10 +52,9 @@ export async function indexHostsAndAlerts( const epmEndpointPackage = await getEndpointPackageInfo(kbnClient); // Keep a map of host applied policy ids (fake) to real ingest package configs (policy record) const realPolicies: Record = {}; - for (let i = 0; i < numHosts; i++) { const generator = new EndpointDocGenerator(random); - await indexHostDocs( + await indexHostDocs({ numDocs, client, kbnClient, @@ -63,10 +62,17 @@ export async function indexHostsAndAlerts( epmEndpointPackage, metadataIndex, policyResponseIndex, - fleet, - generator - ); - await indexAlerts(client, eventIndex, alertIndex, generator, alertsPerHost, options); + enrollFleet: fleet, + generator, + }); + await indexAlerts({ + client, + eventIndex, + alertIndex, + generator, + numAlerts: alertsPerHost, + options, + }); } await client.indices.refresh({ index: eventIndex, @@ -81,17 +87,27 @@ function delay(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } -async function indexHostDocs( - numDocs: number, - client: Client, - kbnClient: KbnClientWithApiKeySupport, - realPolicies: Record, - epmEndpointPackage: GetPackagesResponse['response'][0], - metadataIndex: string, - policyResponseIndex: string, - enrollFleet: boolean, - generator: EndpointDocGenerator -) { +async function indexHostDocs({ + numDocs, + client, + kbnClient, + realPolicies, + epmEndpointPackage, + metadataIndex, + policyResponseIndex, + enrollFleet, + generator, +}: { + numDocs: number; + client: Client; + kbnClient: KbnClientWithApiKeySupport; + realPolicies: Record; + epmEndpointPackage: GetPackagesResponse['response'][0]; + metadataIndex: string; + policyResponseIndex: string; + enrollFleet: boolean; + generator: EndpointDocGenerator; +}) { const timeBetweenDocs = 6 * 3600 * 1000; // 6 hours between metadata documents const timestamp = new Date().getTime(); let hostMetadata: HostMetadata; @@ -102,7 +118,10 @@ async function indexHostDocs( generator.updateHostData(); generator.updateHostPolicyData(); - hostMetadata = generator.generateHostMetadata(timestamp - timeBetweenDocs * (numDocs - j - 1)); + hostMetadata = generator.generateHostMetadata( + timestamp - timeBetweenDocs * (numDocs - j - 1), + EndpointDocGenerator.createDataStreamFromIndex(metadataIndex) + ); if (enrollFleet) { const { id: appliedPolicyId, name: appliedPolicyName } = hostMetadata.Endpoint.policy.applied; @@ -156,20 +175,30 @@ async function indexHostDocs( }); await client.index({ index: policyResponseIndex, - body: generator.generatePolicyResponse(timestamp - timeBetweenDocs * (numDocs - j - 1)), + body: generator.generatePolicyResponse({ + ts: timestamp - timeBetweenDocs * (numDocs - j - 1), + policyDataStream: EndpointDocGenerator.createDataStreamFromIndex(policyResponseIndex), + }), op_type: 'create', }); } } -async function indexAlerts( - client: Client, - eventIndex: string, - alertIndex: string, - generator: EndpointDocGenerator, - numAlerts: number, - options: TreeOptions = {} -) { +async function indexAlerts({ + client, + eventIndex, + alertIndex, + generator, + numAlerts, + options = {}, +}: { + client: Client; + eventIndex: string; + alertIndex: string; + generator: EndpointDocGenerator; + numAlerts: number; + options: TreeOptions; +}) { const alertGenerator = generator.alertsGenerator(numAlerts, options); let result = alertGenerator.next(); while (!result.done) { diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index 510f1833b793b..f2033e064ef72 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -300,6 +300,15 @@ export interface HostResultList { query_strategy_version: MetadataQueryStrategyVersions; } +/** + * The data_stream fields in an elasticsearch document. + */ +export interface DataStream { + dataset: string; + namespace: string; + type: string; +} + /** * Operating System metadata. */ @@ -556,6 +565,7 @@ export type HostMetadata = Immutable<{ version: string; }; host: Host; + data_stream: DataStream; }>; export interface LegacyEndpointEvent { @@ -675,6 +685,11 @@ export type SafeEndpointEvent = Partial<{ version: ECSField; type: ECSField; }>; + data_stream: Partial<{ + type: ECSField; + dataset: ECSField; + namespace: ECSField; + }>; ecs: Partial<{ version: ECSField; }>; @@ -1002,6 +1017,7 @@ interface HostPolicyResponseAppliedArtifact { */ export interface HostPolicyResponse { '@timestamp': number; + data_stream: DataStream; elastic: { agent: { id: string; diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx index 12a76ae0772a3..d785e3b3a131a 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.test.tsx @@ -702,10 +702,10 @@ describe('when on the list page', () => { }); it('should not show any numbered badges if all actions are successful', () => { - const policyResponse = docGenerator.generatePolicyResponse( - new Date().getTime(), - HostPolicyResponseActionStatus.success - ); + const policyResponse = docGenerator.generatePolicyResponse({ + ts: new Date().getTime(), + allStatus: HostPolicyResponseActionStatus.success, + }); reactTestingLibrary.act(() => { store.dispatch({ type: 'serverReturnedEndpointPolicyResponse', diff --git a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts index 9fa2257afd411..c513c4576d890 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/resolver_generator_script.ts @@ -10,7 +10,7 @@ import { ResponseError } from '@elastic/elasticsearch/lib/errors'; import { KbnClient, ToolingLog } from '@kbn/dev-utils'; import { AxiosResponse } from 'axios'; import { indexHostsAndAlerts } from '../../common/endpoint/index_data'; -import { ANCESTRY_LIMIT } from '../../common/endpoint/generate_data'; +import { ANCESTRY_LIMIT, EndpointDocGenerator } from '../../common/endpoint/generate_data'; import { AGENTS_SETUP_API_ROUTES, SETUP_API_ROUTE } from '../../../ingest_manager/common/constants'; import { CreateFleetSetupResponse, @@ -250,6 +250,8 @@ async function main() { percentTerminated: argv.percentTerminated, alwaysGenMaxChildrenPerNode: argv.maxChildrenPerNode, ancestryArraySize: argv.ancestryArraySize, + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(argv.eventIndex), + alertsDataStream: EndpointDocGenerator.createDataStreamFromIndex(argv.alertIndex), } ); console.log(`Creating and indexing documents took: ${new Date().getTime() - startTime}ms`); diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.test.ts index 55965e5a9c70e..d5297072388e7 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/utils/pagination.test.ts @@ -20,7 +20,7 @@ describe('Pagination', () => { }; describe('cursor', () => { const root = generator.generateEvent(); - const events = Array.from(generator.relatedEventsGenerator(root, 5)); + const events = Array.from(generator.relatedEventsGenerator({ node: root, relatedEvents: 5 })); it('does build a cursor when received the same number of events as was requested', () => { expect(PaginationBuilder.buildCursorRequestLimit(4, events)).not.toBeNull(); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/package.ts b/x-pack/test/security_solution_endpoint_api_int/apis/package.ts index afbf0dcd7bd13..8dc78ed71d0b6 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/package.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/package.ts @@ -74,7 +74,9 @@ export default function ({ getService }: FtrProviderContext) { }); it('handles events without the `network.protocol` field being defined', async () => { - const eventWithoutNetworkObject = generator.generateEvent(); + const eventWithoutNetworkObject = generator.generateEvent({ + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(networkIndex), + }); // ensure that `network.protocol` does not exist in the event to test that the pipeline handles those type of events delete eventWithoutNetworkObject.network; @@ -137,8 +139,10 @@ export default function ({ getService }: FtrProviderContext) { let genData: InsertedEvents; before(async () => { - event = generator.generateEvent(); - genData = await resolver.insertEvents([event]); + event = generator.generateEvent({ + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), + }); + genData = await resolver.insertEvents([event], processEventsIndex); }); after(async () => { @@ -158,20 +162,29 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { // 46.239.193.5 should be in Iceland // 8.8.8.8 should be in the US - const eventWithBothIPs = generator.generateEvent({ + const eventWithBothIPsNetwork = generator.generateEvent({ extensions: { source: { ip: '8.8.8.8' }, destination: { ip: '46.239.193.5' } }, + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(networkIndex), }); - const eventWithSourceOnly = generator.generateEvent({ + const eventWithSourceOnlyNetwork = generator.generateEvent({ extensions: { source: { ip: '8.8.8.8' } }, + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(networkIndex), }); networkIndexData = await resolver.insertEvents( - [eventWithBothIPs, eventWithSourceOnly], + [eventWithBothIPsNetwork, eventWithSourceOnlyNetwork], networkIndex ); - processIndexData = await resolver.insertEvents([eventWithBothIPs], processEventsIndex); + const eventWithBothIPsProcess = generator.generateEvent({ + extensions: { source: { ip: '8.8.8.8' }, destination: { ip: '46.239.193.5' } }, + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), + }); + processIndexData = await resolver.insertEvents( + [eventWithBothIPsProcess], + processEventsIndex + ); }); after(async () => { diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/children.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/children.ts index 49e24ff67fa77..b56dea94ab569 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/children.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/children.ts @@ -22,7 +22,7 @@ import { Event, EndpointDocGenerator, } from '../../../../plugins/security_solution/common/endpoint/generate_data'; -import { InsertedEvents } from '../../services/resolver'; +import { InsertedEvents, processEventsIndex } from '../../services/resolver'; import { createAncestryArray } from './common'; export default function resolverAPIIntegrationTests({ getService }: FtrProviderContext) { @@ -42,25 +42,33 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC before(async () => { // Construct the following tree: // Origin -> infoEvent -> startEvent -> execEvent - origin = generator.generateEvent(); + origin = generator.generateEvent({ + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), + }); infoEvent = generator.generateEvent({ parentEntityID: entityIDSafeVersion(origin), ancestry: createAncestryArray([origin]), eventType: ['info'], + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); startEvent = generator.generateEvent({ parentEntityID: entityIDSafeVersion(infoEvent), ancestry: createAncestryArray([infoEvent, origin]), eventType: ['start'], + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); execEvent = generator.generateEvent({ parentEntityID: entityIDSafeVersion(startEvent), ancestry: createAncestryArray([startEvent, infoEvent]), eventType: ['change'], + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); - genData = await resolver.insertEvents([origin, infoEvent, startEvent, execEvent]); + genData = await resolver.insertEvents( + [origin, infoEvent, startEvent, execEvent], + processEventsIndex + ); }); after(async () => { @@ -88,11 +96,14 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC before(async () => { // Construct the following tree: // Origin -> (infoEvent, startEvent, execEvent are all for the same node) - origin = generator.generateEvent(); + origin = generator.generateEvent({ + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), + }); startEvent = generator.generateEvent({ parentEntityID: entityIDSafeVersion(origin), ancestry: createAncestryArray([origin]), eventType: ['start'], + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); infoEvent = generator.generateEvent({ @@ -100,6 +111,7 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC ancestry: createAncestryArray([origin]), entityID: entityIDSafeVersion(startEvent), eventType: ['info'], + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); execEvent = generator.generateEvent({ @@ -107,8 +119,12 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC ancestry: createAncestryArray([origin]), eventType: ['change'], entityID: entityIDSafeVersion(startEvent), + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); - genData = await resolver.insertEvents([origin, infoEvent, startEvent, execEvent]); + genData = await resolver.insertEvents( + [origin, infoEvent, startEvent, execEvent], + processEventsIndex + ); }); after(async () => { @@ -141,11 +157,14 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC before(async () => { // Construct the following tree: // Origin -> (infoEvent, startEvent, execEvent are all for the same node) - origin = generator.generateEvent(); + origin = generator.generateEvent({ + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), + }); startEvent = generator.generateEvent({ parentEntityID: entityIDSafeVersion(origin), ancestry: createAncestryArray([origin]), eventType: ['start'], + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); infoEvent = generator.generateEvent({ @@ -154,6 +173,7 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC ancestry: createAncestryArray([origin]), entityID: entityIDSafeVersion(startEvent), eventType: ['info'], + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); execEvent = generator.generateEvent({ @@ -162,8 +182,12 @@ export default function resolverAPIIntegrationTests({ getService }: FtrProviderC ancestry: createAncestryArray([origin]), eventType: ['change'], entityID: entityIDSafeVersion(startEvent), + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); - genData = await resolver.insertEvents([origin, infoEvent, startEvent, execEvent]); + genData = await resolver.insertEvents( + [origin, infoEvent, startEvent, execEvent], + processEventsIndex + ); }); after(async () => { diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/entity_id.ts b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/entity_id.ts index e6d5e8fccd00d..f9492e6291684 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/resolver/entity_id.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/resolver/entity_id.ts @@ -15,7 +15,7 @@ import { EndpointDocGenerator, Event, } from '../../../../plugins/security_solution/common/endpoint/generate_data'; -import { InsertedEvents } from '../../services/resolver'; +import { InsertedEvents, processEventsIndex } from '../../services/resolver'; import { createAncestryArray } from './common'; export default function ({ getService }: FtrProviderContext) { @@ -34,9 +34,12 @@ export default function ({ getService }: FtrProviderContext) { let origin: Event; let genData: InsertedEvents; before(async () => { - origin = generator.generateEvent({ parentEntityID: 'a' }); + origin = generator.generateEvent({ + parentEntityID: 'a', + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), + }); setEntityIDEmptyString(origin); - genData = await resolver.insertEvents([origin]); + genData = await resolver.insertEvents([origin], processEventsIndex); }); after(async () => { @@ -63,10 +66,14 @@ export default function ({ getService }: FtrProviderContext) { before(async () => { // construct a tree with an origin and two direct children. One child will not have an entity_id. That child // should not be returned by the backend. - origin = generator.generateEvent({ entityID: 'a' }); + origin = generator.generateEvent({ + entityID: 'a', + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), + }); childNoEntityID = generator.generateEvent({ parentEntityID: entityIDSafeVersion(origin), ancestry: createAncestryArray([origin]), + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); // force it to be empty setEntityIDEmptyString(childNoEntityID); @@ -75,9 +82,10 @@ export default function ({ getService }: FtrProviderContext) { entityID: 'b', parentEntityID: entityIDSafeVersion(origin), ancestry: createAncestryArray([origin]), + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); events = [origin, childNoEntityID, childWithEntityID]; - genData = await resolver.insertEvents(events); + genData = await resolver.insertEvents(events, processEventsIndex); }); after(async () => { @@ -106,17 +114,20 @@ export default function ({ getService }: FtrProviderContext) { // entity_ids in the ancestry array. This is to make sure that the backend will not query for that event. ancestor2 = generator.generateEvent({ entityID: '2', + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); ancestor1 = generator.generateEvent({ entityID: '1', parentEntityID: entityIDSafeVersion(ancestor2), ancestry: createAncestryArray([ancestor2]), + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); // we'll insert an event that doesn't have an entity id so if the backend does search for it, it should be // returned and our test should fail ancestorNoEntityID = generator.generateEvent({ ancestry: createAncestryArray([ancestor2]), + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); setEntityIDEmptyString(ancestorNoEntityID); @@ -124,10 +135,11 @@ export default function ({ getService }: FtrProviderContext) { entityID: 'a', parentEntityID: entityIDSafeVersion(ancestor1), ancestry: ['', ...createAncestryArray([ancestor2])], + eventsDataStream: EndpointDocGenerator.createDataStreamFromIndex(processEventsIndex), }); events = [origin, ancestor1, ancestor2, ancestorNoEntityID]; - genData = await resolver.insertEvents(events); + genData = await resolver.insertEvents(events, processEventsIndex); }); after(async () => { From 07c1284e9db2a180fc660302141455e3c33dae6d Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 15 Oct 2020 09:56:10 -0700 Subject: [PATCH 098/128] [kbn/bootstrap] validate that certain deps don't ship in production (#80549) Co-authored-by: spalger --- package.json | 6 +- .../elastic-eslint-config-kibana/package.json | 3 + packages/kbn-babel-preset/package.json | 3 + packages/kbn-config/package.json | 4 +- packages/kbn-dev-utils/package.json | 3 + packages/kbn-es-archiver/package.json | 3 + packages/kbn-es/package.json | 3 + .../package.json | 3 + .../kbn-eslint-plugin-eslint/package.json | 3 + packages/kbn-expect/package.json | 5 +- packages/kbn-pm/dist/index.js | 1414 +++++++++-------- packages/kbn-pm/package.json | 3 + packages/kbn-pm/src/commands/bootstrap.ts | 4 +- packages/kbn-pm/src/utils/project.ts | 4 + packages/kbn-pm/src/utils/projects_tree.ts | 4 +- ..._yarn_lock.ts => validate_dependencies.ts} | 44 +- packages/kbn-release-notes/package.json | 3 + packages/kbn-std/package.json | 8 +- packages/kbn-storybook/package.json | 3 + packages/kbn-telemetry-tools/package.json | 3 + packages/kbn-test-subj-selector/package.json | 5 +- packages/kbn-test/package.json | 3 + packages/kbn-utility-types/package.json | 3 + 23 files changed, 835 insertions(+), 702 deletions(-) rename packages/kbn-pm/src/utils/{validate_yarn_lock.ts => validate_dependencies.ts} (77%) diff --git a/package.json b/package.json index 732ee1fd3038b..951c73dc94021 100644 --- a/package.json +++ b/package.json @@ -131,10 +131,7 @@ "@kbn/i18n": "1.0.0", "@kbn/interpreter": "1.0.0", "@kbn/logging": "1.0.0", - "@kbn/pm": "1.0.0", "@kbn/std": "1.0.0", - "@kbn/telemetry-tools": "1.0.0", - "@kbn/test-subj-selector": "0.2.1", "@kbn/ui-framework": "1.0.0", "@kbn/ace": "1.0.0", "@kbn/monaco": "1.0.0", @@ -247,8 +244,11 @@ "@kbn/expect": "1.0.0", "@kbn/optimizer": "1.0.0", "@kbn/plugin-generator": "1.0.0", + "@kbn/pm": "1.0.0", "@kbn/release-notes": "1.0.0", + "@kbn/telemetry-tools": "1.0.0", "@kbn/test": "1.0.0", + "@kbn/test-subj-selector": "0.2.1", "@kbn/utility-types": "1.0.0", "@microsoft/api-documenter": "7.7.2", "@microsoft/api-extractor": "7.7.0", diff --git a/packages/elastic-eslint-config-kibana/package.json b/packages/elastic-eslint-config-kibana/package.json index 3f2c6e9edb261..9d0d579086543 100644 --- a/packages/elastic-eslint-config-kibana/package.json +++ b/packages/elastic-eslint-config-kibana/package.json @@ -7,6 +7,9 @@ "type": "git", "url": "git+https://github.com/elastic/kibana.git" }, + "kibana": { + "devOnly": true + }, "keywords": [], "author": "Spencer Alger ", "license": "Apache-2.0", diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index 79d2fd8687dae..2fab970c5c71f 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "private": true, "license": "Apache-2.0", + "kibana": { + "devOnly": true + }, "dependencies": { "@babel/plugin-proposal-class-properties": "^7.10.4", "@babel/plugin-proposal-export-namespace-from": "^7.10.4", diff --git a/packages/kbn-config/package.json b/packages/kbn-config/package.json index 6d2d56b929ead..f994836af8847 100644 --- a/packages/kbn-config/package.json +++ b/packages/kbn-config/package.json @@ -12,10 +12,8 @@ "dependencies": { "@elastic/safer-lodash-set": "0.0.0", "@kbn/config-schema": "1.0.0", - "@kbn/dev-utils": "1.0.0", "@kbn/logging": "1.0.0", "@kbn/std": "1.0.0", - "@kbn/utility-types": "1.0.0", "js-yaml": "^3.14.0", "load-json-file": "^6.2.0", "lodash": "^4.17.20", @@ -24,6 +22,8 @@ "type-detect": "^4.0.8" }, "devDependencies": { + "@kbn/dev-utils": "1.0.0", + "@kbn/utility-types": "1.0.0", "typescript": "4.0.2", "tsd": "^0.13.1" } diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index a51734168cf76..7fd9a9e7d67e1 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -9,6 +9,9 @@ "kbn:bootstrap": "yarn build", "kbn:watch": "yarn build --watch" }, + "kibana": { + "devOnly": true + }, "dependencies": { "@babel/core": "^7.11.6", "@kbn/utils": "1.0.0", diff --git a/packages/kbn-es-archiver/package.json b/packages/kbn-es-archiver/package.json index 81c1747bb2727..645abd6195909 100644 --- a/packages/kbn-es-archiver/package.json +++ b/packages/kbn-es-archiver/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "license": "Apache-2.0", "main": "target/index.js", + "kibana": { + "devOnly": true + }, "scripts": { "kbn:bootstrap": "rm -rf target && tsc", "kbn:watch": "rm -rf target && tsc --watch" diff --git a/packages/kbn-es/package.json b/packages/kbn-es/package.json index c3733094350be..6ed3ae2eb2fb7 100644 --- a/packages/kbn-es/package.json +++ b/packages/kbn-es/package.json @@ -4,6 +4,9 @@ "version": "1.0.0", "license": "Apache-2.0", "private": true, + "kibana": { + "devOnly": true + }, "scripts": { "kbn:bootstrap": "node scripts/build", "kbn:watch": "node scripts/build --watch" diff --git a/packages/kbn-eslint-import-resolver-kibana/package.json b/packages/kbn-eslint-import-resolver-kibana/package.json index 223c73e97908e..ffbd94810a405 100755 --- a/packages/kbn-eslint-import-resolver-kibana/package.json +++ b/packages/kbn-eslint-import-resolver-kibana/package.json @@ -5,6 +5,9 @@ "version": "2.0.0", "main": "import_resolver_kibana.js", "license": "Apache-2.0", + "kibana": { + "devOnly": true + }, "repository": { "type": "git", "url": "https://github.com/elastic/kibana/tree/master/packages/kbn-eslint-import-resolver-kibana" diff --git a/packages/kbn-eslint-plugin-eslint/package.json b/packages/kbn-eslint-plugin-eslint/package.json index 026938213ac83..72b8577cb0945 100644 --- a/packages/kbn-eslint-plugin-eslint/package.json +++ b/packages/kbn-eslint-plugin-eslint/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "private": true, "license": "Apache-2.0", + "kibana": { + "devOnly": true + }, "peerDependencies": { "eslint": "6.8.0", "babel-eslint": "^10.0.3" diff --git a/packages/kbn-expect/package.json b/packages/kbn-expect/package.json index 0975f5762fa1c..8ca37c7c88673 100644 --- a/packages/kbn-expect/package.json +++ b/packages/kbn-expect/package.json @@ -3,5 +3,8 @@ "main": "./expect.js", "version": "1.0.0", "license": "MIT", - "private": true + "private": true, + "kibana": { + "devOnly": true + } } diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 2e50f4214beb4..c053445285c03 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -150,7 +150,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5); /* harmony import */ var _kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_kbn_dev_utils_tooling_log__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _commands__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(128); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(500); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(501); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(144); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -8897,9 +8897,9 @@ exports.ToolingLogCollectingWriter = ToolingLogCollectingWriter; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "commands", function() { return commands; }); /* harmony import */ var _bootstrap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(129); -/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(280); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(399); -/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(400); +/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(281); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(400); +/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(401); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -8943,7 +8943,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_project_checksums__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(273); /* harmony import */ var _utils_bootstrap_cache_file__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(278); /* harmony import */ var _utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(275); -/* harmony import */ var _utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(279); +/* harmony import */ var _utils_validate_dependencies__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(279); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -9002,7 +9002,7 @@ const BootstrapCommand = { const yarnLock = await Object(_utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__["readYarnLock"])(kbn); if (options.validate) { - await Object(_utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__["validateYarnLock"])(kbn, yarnLock); + await Object(_utils_validate_dependencies__WEBPACK_IMPORTED_MODULE_7__["validateDependencies"])(kbn, yarnLock); } await Object(_utils_link_project_executables__WEBPACK_IMPORTED_MODULE_0__["linkProjectExecutables"])(projects, projectGraph); @@ -14713,6 +14713,10 @@ class Project { return this.json.kibana && this.json.kibana.clean || {}; } + isFlaggedAsDevOnly() { + return !!(this.json.kibana && this.json.kibana.devOnly); + } + hasScript(name) { return name in this.scripts; } @@ -37954,13 +37958,16 @@ class BootstrapCacheFile { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "validateYarnLock", function() { return validateYarnLock; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "validateDependencies", function() { return validateDependencies; }); /* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); /* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2); /* harmony import */ var dedent__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(dedent__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(131); -/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(144); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(113); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(131); +/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(144); +/* harmony import */ var _projects_tree__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(280); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -37984,7 +37991,9 @@ __webpack_require__.r(__webpack_exports__); -async function validateYarnLock(kbn, yarnLock) { + + +async function validateDependencies(kbn, yarnLock) { // look through all of the packages in the yarn.lock file to see if // we have accidentally installed multiple lodash v4 versions const lodash4Versions = new Set(); @@ -38005,8 +38014,8 @@ async function validateYarnLock(kbn, yarnLock) { delete yarnLock[req]; } - await Object(_fs__WEBPACK_IMPORTED_MODULE_2__["writeFile"])(kbn.getAbsolute('yarn.lock'), Object(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__["stringify"])(yarnLock), 'utf8'); - _log__WEBPACK_IMPORTED_MODULE_3__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` + await Object(_fs__WEBPACK_IMPORTED_MODULE_3__["writeFile"])(kbn.getAbsolute('yarn.lock'), Object(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__["stringify"])(yarnLock), 'utf8'); + _log__WEBPACK_IMPORTED_MODULE_4__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` Multiple version of lodash v4 were detected, so they have been removed from the yarn.lock file. Please rerun yarn kbn bootstrap to coalese the @@ -38025,7 +38034,7 @@ async function validateYarnLock(kbn, yarnLock) { // of lodash v3 in the distributable - const prodDependencies = kbn.resolveAllProductionDependencies(yarnLock, _log__WEBPACK_IMPORTED_MODULE_3__["log"]); + const prodDependencies = kbn.resolveAllProductionDependencies(yarnLock, _log__WEBPACK_IMPORTED_MODULE_4__["log"]); const lodash3Versions = new Set(); for (const dep of prodDependencies.values()) { @@ -38036,7 +38045,7 @@ async function validateYarnLock(kbn, yarnLock) { if (lodash3Versions.size) { - _log__WEBPACK_IMPORTED_MODULE_3__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` + _log__WEBPACK_IMPORTED_MODULE_4__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` Due to changes in the yarn.lock file and/or package.json files a version of lodash 3 is now included in the production dependencies. To reduce the size of @@ -38088,7 +38097,7 @@ async function validateYarnLock(kbn, yarnLock) { }) => ` ${range} => ${projects.map(p => p.name).join(', ')}`)], []).join('\n '); if (duplicateRanges) { - _log__WEBPACK_IMPORTED_MODULE_3__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` + _log__WEBPACK_IMPORTED_MODULE_4__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` [single_version_dependencies] Multiple version ranges for the same dependency were found declared across different package.json files. Please consolidate @@ -38102,21 +38111,207 @@ async function validateYarnLock(kbn, yarnLock) { ${duplicateRanges} `); process.exit(1); + } // look for packages that have the the `kibana.devOnly` flag in their package.json + // and make sure they aren't included in the production dependencies of Kibana + + + const devOnlyProjectsInProduction = getDevOnlyProductionDepsTree(kbn, 'kibana'); + + if (devOnlyProjectsInProduction) { + _log__WEBPACK_IMPORTED_MODULE_4__["log"].error(dedent__WEBPACK_IMPORTED_MODULE_1___default.a` + Some of the packages in the production dependency chain for Kibana and X-Pack are + flagged with "kibana.devOnly" in their package.json. Please check changes made to + packages and their dependencies to ensure they don't end up in production. + + The devOnly dependencies that are being dependend on in production are: + + ${Object(_projects_tree__WEBPACK_IMPORTED_MODULE_5__["treeToString"])(devOnlyProjectsInProduction).split('\n').join('\n ')} + `); + process.exit(1); } - _log__WEBPACK_IMPORTED_MODULE_3__["log"].success('yarn.lock analysis completed without any issues'); + _log__WEBPACK_IMPORTED_MODULE_4__["log"].success('yarn.lock analysis completed without any issues'); +} + +function getDevOnlyProductionDepsTree(kbn, projectName) { + const project = kbn.getProject(projectName); + const childProjectNames = [...Object.keys(project.productionDependencies).filter(name => kbn.hasProject(name)), ...(projectName === 'kibana' ? ['x-pack'] : [])]; + const children = childProjectNames.map(n => getDevOnlyProductionDepsTree(kbn, n)).filter(t => !!t); + + if (!children.length && !project.isFlaggedAsDevOnly()) { + return; + } + + const tree = { + name: project.isFlaggedAsDevOnly() ? chalk__WEBPACK_IMPORTED_MODULE_2___default.a.red.bold(projectName) : projectName, + children + }; + return tree; } /***/ }), /* 280 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "renderProjectsTree", function() { return renderProjectsTree; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "treeToString", function() { return treeToString; }); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(113); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); +/* + * 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 projectKey = Symbol('__project'); +function renderProjectsTree(rootPath, projects) { + const projectsTree = buildProjectsTree(rootPath, projects); + return treeToString(createTreeStructure(projectsTree)); +} +function treeToString(tree) { + return [tree.name].concat(childrenToStrings(tree.children, '')).join('\n'); +} + +function childrenToStrings(tree, treePrefix) { + if (tree === undefined) { + return []; + } + + let strings = []; + tree.forEach((node, index) => { + const isLastNode = tree.length - 1 === index; + const nodePrefix = isLastNode ? '└── ' : '├── '; + const childPrefix = isLastNode ? ' ' : '│ '; + const childrenPrefix = treePrefix + childPrefix; + strings.push(`${treePrefix}${nodePrefix}${node.name}`); + strings = strings.concat(childrenToStrings(node.children, childrenPrefix)); + }); + return strings; +} + +function createTreeStructure(tree) { + let name; + const children = []; + + for (const [dir, project] of tree.entries()) { + // This is a leaf node (aka a project) + if (typeof project === 'string') { + name = chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(project); + continue; + } // If there's only one project and the key indicates it's a leaf node, we + // know that we're at a package folder that contains a package.json, so we + // "inline it" so we don't get unnecessary levels, i.e. we'll just see + // `foo` instead of `foo -> foo`. + + + if (project.size === 1 && project.has(projectKey)) { + const projectName = project.get(projectKey); + children.push({ + children: [], + name: dirOrProjectName(dir, projectName) + }); + continue; + } + + const subtree = createTreeStructure(project); // If the name is specified, we know there's a package at the "root" of the + // subtree itself. + + if (subtree.name !== undefined) { + const projectName = subtree.name; + children.push({ + children: subtree.children, + name: dirOrProjectName(dir, projectName) + }); + continue; + } // Special-case whenever we have one child, so we don't get unnecessary + // folders in the output. E.g. instead of `foo -> bar -> baz` we get + // `foo/bar/baz` instead. + + + if (subtree.children && subtree.children.length === 1) { + const child = subtree.children[0]; + const newName = chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(path__WEBPACK_IMPORTED_MODULE_1___default.a.join(dir.toString(), child.name)); + children.push({ + children: child.children, + name: newName + }); + continue; + } + + children.push({ + children: subtree.children, + name: chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(dir.toString()) + }); + } + + return { + name, + children + }; +} + +function dirOrProjectName(dir, projectName) { + return dir === projectName ? chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(dir) : chalk__WEBPACK_IMPORTED_MODULE_0___default.a`{dim ${dir.toString()} ({reset.green ${projectName}})}`; +} + +function buildProjectsTree(rootPath, projects) { + const tree = new Map(); + + for (const project of projects.values()) { + if (rootPath === project.path) { + tree.set(projectKey, project.name); + } else { + const relativeProjectPath = path__WEBPACK_IMPORTED_MODULE_1___default.a.relative(rootPath, project.path); + addProjectToTree(tree, relativeProjectPath.split(path__WEBPACK_IMPORTED_MODULE_1___default.a.sep), project); + } + } + + return tree; +} + +function addProjectToTree(tree, pathParts, project) { + if (pathParts.length === 0) { + tree.set(projectKey, project.name); + } else { + const [currentDir, ...rest] = pathParts; + + if (!tree.has(currentDir)) { + tree.set(currentDir, new Map()); + } + + const subtree = tree.get(currentDir); + addProjectToTree(subtree, rest, project); + } +} + +/***/ }), +/* 281 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CleanCommand", function() { return CleanCommand; }); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(281); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(282); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(367); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(368); /* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); @@ -38216,21 +38411,21 @@ const CleanCommand = { }; /***/ }), -/* 281 */ +/* 282 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(112); const path = __webpack_require__(4); -const globby = __webpack_require__(282); -const isGlob = __webpack_require__(294); -const slash = __webpack_require__(358); +const globby = __webpack_require__(283); +const isGlob = __webpack_require__(295); +const slash = __webpack_require__(359); const gracefulFs = __webpack_require__(133); -const isPathCwd = __webpack_require__(360); -const isPathInside = __webpack_require__(361); -const rimraf = __webpack_require__(362); -const pMap = __webpack_require__(363); +const isPathCwd = __webpack_require__(361); +const isPathInside = __webpack_require__(362); +const rimraf = __webpack_require__(363); +const pMap = __webpack_require__(364); const rimrafP = promisify(rimraf); @@ -38344,19 +38539,19 @@ module.exports.sync = (patterns, {force, dryRun, cwd = process.cwd(), ...options /***/ }), -/* 282 */ +/* 283 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(134); -const arrayUnion = __webpack_require__(283); -const merge2 = __webpack_require__(284); +const arrayUnion = __webpack_require__(284); +const merge2 = __webpack_require__(285); const glob = __webpack_require__(147); -const fastGlob = __webpack_require__(285); -const dirGlob = __webpack_require__(354); -const gitignore = __webpack_require__(356); -const {FilterStream, UniqueStream} = __webpack_require__(359); +const fastGlob = __webpack_require__(286); +const dirGlob = __webpack_require__(355); +const gitignore = __webpack_require__(357); +const {FilterStream, UniqueStream} = __webpack_require__(360); const DEFAULT_FILTER = () => false; @@ -38529,7 +38724,7 @@ module.exports.gitignore = gitignore; /***/ }), -/* 283 */ +/* 284 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38541,7 +38736,7 @@ module.exports = (...arguments_) => { /***/ }), -/* 284 */ +/* 285 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38692,17 +38887,17 @@ function pauseStreams (streams, options) { /***/ }), -/* 285 */ +/* 286 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const taskManager = __webpack_require__(286); -const async_1 = __webpack_require__(315); -const stream_1 = __webpack_require__(350); -const sync_1 = __webpack_require__(351); -const settings_1 = __webpack_require__(353); -const utils = __webpack_require__(287); +const taskManager = __webpack_require__(287); +const async_1 = __webpack_require__(316); +const stream_1 = __webpack_require__(351); +const sync_1 = __webpack_require__(352); +const settings_1 = __webpack_require__(354); +const utils = __webpack_require__(288); async function FastGlob(source, options) { assertPatternsInput(source); const works = getWorks(source, async_1.default, options); @@ -38766,13 +38961,13 @@ module.exports = FastGlob; /***/ }), -/* 286 */ +/* 287 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(287); +const utils = __webpack_require__(288); function generate(patterns, settings) { const positivePatterns = getPositivePatterns(patterns); const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); @@ -38837,30 +39032,30 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 287 */ +/* 288 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const array = __webpack_require__(288); +const array = __webpack_require__(289); exports.array = array; -const errno = __webpack_require__(289); +const errno = __webpack_require__(290); exports.errno = errno; -const fs = __webpack_require__(290); +const fs = __webpack_require__(291); exports.fs = fs; -const path = __webpack_require__(291); +const path = __webpack_require__(292); exports.path = path; -const pattern = __webpack_require__(292); +const pattern = __webpack_require__(293); exports.pattern = pattern; -const stream = __webpack_require__(313); +const stream = __webpack_require__(314); exports.stream = stream; -const string = __webpack_require__(314); +const string = __webpack_require__(315); exports.string = string; /***/ }), -/* 288 */ +/* 289 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38888,7 +39083,7 @@ exports.splitWhen = splitWhen; /***/ }), -/* 289 */ +/* 290 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38901,7 +39096,7 @@ exports.isEnoentCodeError = isEnoentCodeError; /***/ }), -/* 290 */ +/* 291 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38926,7 +39121,7 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 291 */ +/* 292 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38965,16 +39160,16 @@ exports.removeLeadingDotSegment = removeLeadingDotSegment; /***/ }), -/* 292 */ +/* 293 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const globParent = __webpack_require__(293); -const micromatch = __webpack_require__(296); -const picomatch = __webpack_require__(307); +const globParent = __webpack_require__(294); +const micromatch = __webpack_require__(297); +const picomatch = __webpack_require__(308); const GLOBSTAR = '**'; const ESCAPE_SYMBOL = '\\'; const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; @@ -39084,13 +39279,13 @@ exports.matchAny = matchAny; /***/ }), -/* 293 */ +/* 294 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isGlob = __webpack_require__(294); +var isGlob = __webpack_require__(295); var pathPosixDirname = __webpack_require__(4).posix.dirname; var isWin32 = __webpack_require__(121).platform() === 'win32'; @@ -39132,7 +39327,7 @@ module.exports = function globParent(str, opts) { /***/ }), -/* 294 */ +/* 295 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -39142,7 +39337,7 @@ module.exports = function globParent(str, opts) { * Released under the MIT License. */ -var isExtglob = __webpack_require__(295); +var isExtglob = __webpack_require__(296); var chars = { '{': '}', '(': ')', '[': ']'}; var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; @@ -39186,7 +39381,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 295 */ +/* 296 */ /***/ (function(module, exports) { /*! @@ -39212,16 +39407,16 @@ module.exports = function isExtglob(str) { /***/ }), -/* 296 */ +/* 297 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const util = __webpack_require__(112); -const braces = __webpack_require__(297); -const picomatch = __webpack_require__(307); -const utils = __webpack_require__(310); +const braces = __webpack_require__(298); +const picomatch = __webpack_require__(308); +const utils = __webpack_require__(311); const isEmptyString = val => typeof val === 'string' && (val === '' || val === './'); /** @@ -39686,16 +39881,16 @@ module.exports = micromatch; /***/ }), -/* 297 */ +/* 298 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(298); -const compile = __webpack_require__(300); -const expand = __webpack_require__(304); -const parse = __webpack_require__(305); +const stringify = __webpack_require__(299); +const compile = __webpack_require__(301); +const expand = __webpack_require__(305); +const parse = __webpack_require__(306); /** * Expand the given pattern or create a regex-compatible string. @@ -39863,13 +40058,13 @@ module.exports = braces; /***/ }), -/* 298 */ +/* 299 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(299); +const utils = __webpack_require__(300); module.exports = (ast, options = {}) => { let stringify = (node, parent = {}) => { @@ -39902,7 +40097,7 @@ module.exports = (ast, options = {}) => { /***/ }), -/* 299 */ +/* 300 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40021,14 +40216,14 @@ exports.flatten = (...args) => { /***/ }), -/* 300 */ +/* 301 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(301); -const utils = __webpack_require__(299); +const fill = __webpack_require__(302); +const utils = __webpack_require__(300); const compile = (ast, options = {}) => { let walk = (node, parent = {}) => { @@ -40085,7 +40280,7 @@ module.exports = compile; /***/ }), -/* 301 */ +/* 302 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40099,7 +40294,7 @@ module.exports = compile; const util = __webpack_require__(112); -const toRegexRange = __webpack_require__(302); +const toRegexRange = __webpack_require__(303); const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); @@ -40341,7 +40536,7 @@ module.exports = fill; /***/ }), -/* 302 */ +/* 303 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40354,7 +40549,7 @@ module.exports = fill; -const isNumber = __webpack_require__(303); +const isNumber = __webpack_require__(304); const toRegexRange = (min, max, options) => { if (isNumber(min) === false) { @@ -40636,7 +40831,7 @@ module.exports = toRegexRange; /***/ }), -/* 303 */ +/* 304 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40661,15 +40856,15 @@ module.exports = function(num) { /***/ }), -/* 304 */ +/* 305 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(301); -const stringify = __webpack_require__(298); -const utils = __webpack_require__(299); +const fill = __webpack_require__(302); +const stringify = __webpack_require__(299); +const utils = __webpack_require__(300); const append = (queue = '', stash = '', enclose = false) => { let result = []; @@ -40781,13 +40976,13 @@ module.exports = expand; /***/ }), -/* 305 */ +/* 306 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(298); +const stringify = __webpack_require__(299); /** * Constants @@ -40809,7 +41004,7 @@ const { CHAR_SINGLE_QUOTE, /* ' */ CHAR_NO_BREAK_SPACE, CHAR_ZERO_WIDTH_NOBREAK_SPACE -} = __webpack_require__(306); +} = __webpack_require__(307); /** * parse @@ -41121,7 +41316,7 @@ module.exports = parse; /***/ }), -/* 306 */ +/* 307 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41185,27 +41380,27 @@ module.exports = { /***/ }), -/* 307 */ +/* 308 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = __webpack_require__(308); +module.exports = __webpack_require__(309); /***/ }), -/* 308 */ +/* 309 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const scan = __webpack_require__(309); -const parse = __webpack_require__(312); -const utils = __webpack_require__(310); -const constants = __webpack_require__(311); +const scan = __webpack_require__(310); +const parse = __webpack_require__(313); +const utils = __webpack_require__(311); +const constants = __webpack_require__(312); const isObject = val => val && typeof val === 'object' && !Array.isArray(val); /** @@ -41541,13 +41736,13 @@ module.exports = picomatch; /***/ }), -/* 309 */ +/* 310 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(310); +const utils = __webpack_require__(311); const { CHAR_ASTERISK, /* * */ CHAR_AT, /* @ */ @@ -41564,7 +41759,7 @@ const { CHAR_RIGHT_CURLY_BRACE, /* } */ CHAR_RIGHT_PARENTHESES, /* ) */ CHAR_RIGHT_SQUARE_BRACKET /* ] */ -} = __webpack_require__(311); +} = __webpack_require__(312); const isPathSeparator = code => { return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; @@ -41931,7 +42126,7 @@ module.exports = scan; /***/ }), -/* 310 */ +/* 311 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41944,7 +42139,7 @@ const { REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL -} = __webpack_require__(311); +} = __webpack_require__(312); exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); @@ -42002,7 +42197,7 @@ exports.wrapOutput = (input, state = {}, options = {}) => { /***/ }), -/* 311 */ +/* 312 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -42188,14 +42383,14 @@ module.exports = { /***/ }), -/* 312 */ +/* 313 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const constants = __webpack_require__(311); -const utils = __webpack_require__(310); +const constants = __webpack_require__(312); +const utils = __webpack_require__(311); /** * Constants @@ -43273,13 +43468,13 @@ module.exports = parse; /***/ }), -/* 313 */ +/* 314 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const merge2 = __webpack_require__(284); +const merge2 = __webpack_require__(285); function merge(streams) { const mergedStream = merge2(streams); streams.forEach((stream) => { @@ -43296,7 +43491,7 @@ function propagateCloseEventToSources(streams) { /***/ }), -/* 314 */ +/* 315 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43313,14 +43508,14 @@ exports.isEmpty = isEmpty; /***/ }), -/* 315 */ +/* 316 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(316); -const provider_1 = __webpack_require__(343); +const stream_1 = __webpack_require__(317); +const provider_1 = __webpack_require__(344); class ProviderAsync extends provider_1.default { constructor() { super(...arguments); @@ -43348,16 +43543,16 @@ exports.default = ProviderAsync; /***/ }), -/* 316 */ +/* 317 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(138); -const fsStat = __webpack_require__(317); -const fsWalk = __webpack_require__(322); -const reader_1 = __webpack_require__(342); +const fsStat = __webpack_require__(318); +const fsWalk = __webpack_require__(323); +const reader_1 = __webpack_require__(343); class ReaderStream extends reader_1.default { constructor() { super(...arguments); @@ -43410,15 +43605,15 @@ exports.default = ReaderStream; /***/ }), -/* 317 */ +/* 318 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(318); -const sync = __webpack_require__(319); -const settings_1 = __webpack_require__(320); +const async = __webpack_require__(319); +const sync = __webpack_require__(320); +const settings_1 = __webpack_require__(321); exports.Settings = settings_1.default; function stat(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -43441,7 +43636,7 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 318 */ +/* 319 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43479,7 +43674,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 319 */ +/* 320 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43508,13 +43703,13 @@ exports.read = read; /***/ }), -/* 320 */ +/* 321 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(321); +const fs = __webpack_require__(322); class Settings { constructor(_options = {}) { this._options = _options; @@ -43531,7 +43726,7 @@ exports.default = Settings; /***/ }), -/* 321 */ +/* 322 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43554,16 +43749,16 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 322 */ +/* 323 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(323); -const stream_1 = __webpack_require__(338); -const sync_1 = __webpack_require__(339); -const settings_1 = __webpack_require__(341); +const async_1 = __webpack_require__(324); +const stream_1 = __webpack_require__(339); +const sync_1 = __webpack_require__(340); +const settings_1 = __webpack_require__(342); exports.Settings = settings_1.default; function walk(directory, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -43593,13 +43788,13 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 323 */ +/* 324 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(324); +const async_1 = __webpack_require__(325); class AsyncProvider { constructor(_root, _settings) { this._root = _root; @@ -43630,17 +43825,17 @@ function callSuccessCallback(callback, entries) { /***/ }), -/* 324 */ +/* 325 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = __webpack_require__(156); -const fsScandir = __webpack_require__(325); -const fastq = __webpack_require__(334); -const common = __webpack_require__(336); -const reader_1 = __webpack_require__(337); +const fsScandir = __webpack_require__(326); +const fastq = __webpack_require__(335); +const common = __webpack_require__(337); +const reader_1 = __webpack_require__(338); class AsyncReader extends reader_1.default { constructor(_root, _settings) { super(_root, _settings); @@ -43730,15 +43925,15 @@ exports.default = AsyncReader; /***/ }), -/* 325 */ +/* 326 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(326); -const sync = __webpack_require__(331); -const settings_1 = __webpack_require__(332); +const async = __webpack_require__(327); +const sync = __webpack_require__(332); +const settings_1 = __webpack_require__(333); exports.Settings = settings_1.default; function scandir(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -43761,16 +43956,16 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 326 */ +/* 327 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(317); -const rpl = __webpack_require__(327); -const constants_1 = __webpack_require__(328); -const utils = __webpack_require__(329); +const fsStat = __webpack_require__(318); +const rpl = __webpack_require__(328); +const constants_1 = __webpack_require__(329); +const utils = __webpack_require__(330); function read(directory, settings, callback) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings, callback); @@ -43858,7 +44053,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 327 */ +/* 328 */ /***/ (function(module, exports) { module.exports = runParallel @@ -43912,7 +44107,7 @@ function runParallel (tasks, cb) { /***/ }), -/* 328 */ +/* 329 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43932,18 +44127,18 @@ exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_B /***/ }), -/* 329 */ +/* 330 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(330); +const fs = __webpack_require__(331); exports.fs = fs; /***/ }), -/* 330 */ +/* 331 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43968,15 +44163,15 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 331 */ +/* 332 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(317); -const constants_1 = __webpack_require__(328); -const utils = __webpack_require__(329); +const fsStat = __webpack_require__(318); +const constants_1 = __webpack_require__(329); +const utils = __webpack_require__(330); function read(directory, settings) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings); @@ -44027,15 +44222,15 @@ exports.readdir = readdir; /***/ }), -/* 332 */ +/* 333 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(317); -const fs = __webpack_require__(333); +const fsStat = __webpack_require__(318); +const fs = __webpack_require__(334); class Settings { constructor(_options = {}) { this._options = _options; @@ -44058,7 +44253,7 @@ exports.default = Settings; /***/ }), -/* 333 */ +/* 334 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44083,13 +44278,13 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 334 */ +/* 335 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var reusify = __webpack_require__(335) +var reusify = __webpack_require__(336) function fastqueue (context, worker, concurrency) { if (typeof context === 'function') { @@ -44263,7 +44458,7 @@ module.exports = fastqueue /***/ }), -/* 335 */ +/* 336 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44303,7 +44498,7 @@ module.exports = reusify /***/ }), -/* 336 */ +/* 337 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44334,13 +44529,13 @@ exports.joinPathSegments = joinPathSegments; /***/ }), -/* 337 */ +/* 338 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const common = __webpack_require__(336); +const common = __webpack_require__(337); class Reader { constructor(_root, _settings) { this._root = _root; @@ -44352,14 +44547,14 @@ exports.default = Reader; /***/ }), -/* 338 */ +/* 339 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(138); -const async_1 = __webpack_require__(324); +const async_1 = __webpack_require__(325); class StreamProvider { constructor(_root, _settings) { this._root = _root; @@ -44389,13 +44584,13 @@ exports.default = StreamProvider; /***/ }), -/* 339 */ +/* 340 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(340); +const sync_1 = __webpack_require__(341); class SyncProvider { constructor(_root, _settings) { this._root = _root; @@ -44410,15 +44605,15 @@ exports.default = SyncProvider; /***/ }), -/* 340 */ +/* 341 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsScandir = __webpack_require__(325); -const common = __webpack_require__(336); -const reader_1 = __webpack_require__(337); +const fsScandir = __webpack_require__(326); +const common = __webpack_require__(337); +const reader_1 = __webpack_require__(338); class SyncReader extends reader_1.default { constructor() { super(...arguments); @@ -44476,14 +44671,14 @@ exports.default = SyncReader; /***/ }), -/* 341 */ +/* 342 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsScandir = __webpack_require__(325); +const fsScandir = __webpack_require__(326); class Settings { constructor(_options = {}) { this._options = _options; @@ -44509,15 +44704,15 @@ exports.default = Settings; /***/ }), -/* 342 */ +/* 343 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(317); -const utils = __webpack_require__(287); +const fsStat = __webpack_require__(318); +const utils = __webpack_require__(288); class Reader { constructor(_settings) { this._settings = _settings; @@ -44549,17 +44744,17 @@ exports.default = Reader; /***/ }), -/* 343 */ +/* 344 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const deep_1 = __webpack_require__(344); -const entry_1 = __webpack_require__(347); -const error_1 = __webpack_require__(348); -const entry_2 = __webpack_require__(349); +const deep_1 = __webpack_require__(345); +const entry_1 = __webpack_require__(348); +const error_1 = __webpack_require__(349); +const entry_2 = __webpack_require__(350); class Provider { constructor(_settings) { this._settings = _settings; @@ -44604,14 +44799,14 @@ exports.default = Provider; /***/ }), -/* 344 */ +/* 345 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(287); -const partial_1 = __webpack_require__(345); +const utils = __webpack_require__(288); +const partial_1 = __webpack_require__(346); class DeepFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -44665,13 +44860,13 @@ exports.default = DeepFilter; /***/ }), -/* 345 */ +/* 346 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(346); +const matcher_1 = __webpack_require__(347); class PartialMatcher extends matcher_1.default { match(filepath) { const parts = filepath.split('/'); @@ -44710,13 +44905,13 @@ exports.default = PartialMatcher; /***/ }), -/* 346 */ +/* 347 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(287); +const utils = __webpack_require__(288); class Matcher { constructor(_patterns, _settings, _micromatchOptions) { this._patterns = _patterns; @@ -44767,13 +44962,13 @@ exports.default = Matcher; /***/ }), -/* 347 */ +/* 348 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(287); +const utils = __webpack_require__(288); class EntryFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -44829,13 +45024,13 @@ exports.default = EntryFilter; /***/ }), -/* 348 */ +/* 349 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(287); +const utils = __webpack_require__(288); class ErrorFilter { constructor(_settings) { this._settings = _settings; @@ -44851,13 +45046,13 @@ exports.default = ErrorFilter; /***/ }), -/* 349 */ +/* 350 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(287); +const utils = __webpack_require__(288); class EntryTransformer { constructor(_settings) { this._settings = _settings; @@ -44884,15 +45079,15 @@ exports.default = EntryTransformer; /***/ }), -/* 350 */ +/* 351 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(138); -const stream_2 = __webpack_require__(316); -const provider_1 = __webpack_require__(343); +const stream_2 = __webpack_require__(317); +const provider_1 = __webpack_require__(344); class ProviderStream extends provider_1.default { constructor() { super(...arguments); @@ -44922,14 +45117,14 @@ exports.default = ProviderStream; /***/ }), -/* 351 */ +/* 352 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(352); -const provider_1 = __webpack_require__(343); +const sync_1 = __webpack_require__(353); +const provider_1 = __webpack_require__(344); class ProviderSync extends provider_1.default { constructor() { super(...arguments); @@ -44952,15 +45147,15 @@ exports.default = ProviderSync; /***/ }), -/* 352 */ +/* 353 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(317); -const fsWalk = __webpack_require__(322); -const reader_1 = __webpack_require__(342); +const fsStat = __webpack_require__(318); +const fsWalk = __webpack_require__(323); +const reader_1 = __webpack_require__(343); class ReaderSync extends reader_1.default { constructor() { super(...arguments); @@ -45002,7 +45197,7 @@ exports.default = ReaderSync; /***/ }), -/* 353 */ +/* 354 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45061,13 +45256,13 @@ exports.default = Settings; /***/ }), -/* 354 */ +/* 355 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(355); +const pathType = __webpack_require__(356); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -45143,7 +45338,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 355 */ +/* 356 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45193,7 +45388,7 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 356 */ +/* 357 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45201,9 +45396,9 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); const {promisify} = __webpack_require__(112); const fs = __webpack_require__(134); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(285); -const gitIgnore = __webpack_require__(357); -const slash = __webpack_require__(358); +const fastGlob = __webpack_require__(286); +const gitIgnore = __webpack_require__(358); +const slash = __webpack_require__(359); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -45317,7 +45512,7 @@ module.exports.sync = options => { /***/ }), -/* 357 */ +/* 358 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -45920,7 +46115,7 @@ if ( /***/ }), -/* 358 */ +/* 359 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45938,7 +46133,7 @@ module.exports = path => { /***/ }), -/* 359 */ +/* 360 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45991,7 +46186,7 @@ module.exports = { /***/ }), -/* 360 */ +/* 361 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46013,7 +46208,7 @@ module.exports = path_ => { /***/ }), -/* 361 */ +/* 362 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46041,7 +46236,7 @@ module.exports = (childPath, parentPath) => { /***/ }), -/* 362 */ +/* 363 */ /***/ (function(module, exports, __webpack_require__) { const assert = __webpack_require__(140) @@ -46407,12 +46602,12 @@ rimraf.sync = rimrafSync /***/ }), -/* 363 */ +/* 364 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const AggregateError = __webpack_require__(364); +const AggregateError = __webpack_require__(365); module.exports = async ( iterable, @@ -46495,13 +46690,13 @@ module.exports = async ( /***/ }), -/* 364 */ +/* 365 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const indentString = __webpack_require__(365); -const cleanStack = __webpack_require__(366); +const indentString = __webpack_require__(366); +const cleanStack = __webpack_require__(367); const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); @@ -46549,7 +46744,7 @@ module.exports = AggregateError; /***/ }), -/* 365 */ +/* 366 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46591,7 +46786,7 @@ module.exports = (string, count = 1, options) => { /***/ }), -/* 366 */ +/* 367 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46638,20 +46833,20 @@ module.exports = (stack, options) => { /***/ }), -/* 367 */ +/* 368 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readline = __webpack_require__(368); -const chalk = __webpack_require__(369); -const cliCursor = __webpack_require__(376); -const cliSpinners = __webpack_require__(380); -const logSymbols = __webpack_require__(382); -const stripAnsi = __webpack_require__(391); -const wcwidth = __webpack_require__(393); -const isInteractive = __webpack_require__(397); -const MuteStream = __webpack_require__(398); +const readline = __webpack_require__(369); +const chalk = __webpack_require__(370); +const cliCursor = __webpack_require__(377); +const cliSpinners = __webpack_require__(381); +const logSymbols = __webpack_require__(383); +const stripAnsi = __webpack_require__(392); +const wcwidth = __webpack_require__(394); +const isInteractive = __webpack_require__(398); +const MuteStream = __webpack_require__(399); const TEXT = Symbol('text'); const PREFIX_TEXT = Symbol('prefixText'); @@ -47004,23 +47199,23 @@ module.exports.promise = (action, options) => { /***/ }), -/* 368 */ +/* 369 */ /***/ (function(module, exports) { module.exports = require("readline"); /***/ }), -/* 369 */ +/* 370 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiStyles = __webpack_require__(370); +const ansiStyles = __webpack_require__(371); const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(120); const { stringReplaceAll, stringEncaseCRLFWithFirstIndex -} = __webpack_require__(374); +} = __webpack_require__(375); // `supportsColor.level` → `ansiStyles.color[name]` mapping const levelMapping = [ @@ -47221,7 +47416,7 @@ const chalkTag = (chalk, ...strings) => { } if (template === undefined) { - template = __webpack_require__(375); + template = __webpack_require__(376); } return template(chalk, parts.join('')); @@ -47250,7 +47445,7 @@ module.exports = chalk; /***/ }), -/* 370 */ +/* 371 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -47296,7 +47491,7 @@ const setLazyProperty = (object, property, get) => { let colorConvert; const makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => { if (colorConvert === undefined) { - colorConvert = __webpack_require__(371); + colorConvert = __webpack_require__(372); } const offset = isBackground ? 10 : 0; @@ -47421,11 +47616,11 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(115)(module))) /***/ }), -/* 371 */ +/* 372 */ /***/ (function(module, exports, __webpack_require__) { -const conversions = __webpack_require__(372); -const route = __webpack_require__(373); +const conversions = __webpack_require__(373); +const route = __webpack_require__(374); const convert = {}; @@ -47508,7 +47703,7 @@ module.exports = convert; /***/ }), -/* 372 */ +/* 373 */ /***/ (function(module, exports, __webpack_require__) { /* MIT license */ @@ -48353,10 +48548,10 @@ convert.rgb.gray = function (rgb) { /***/ }), -/* 373 */ +/* 374 */ /***/ (function(module, exports, __webpack_require__) { -const conversions = __webpack_require__(372); +const conversions = __webpack_require__(373); /* This function routes a model to all other models. @@ -48456,7 +48651,7 @@ module.exports = function (fromModel) { /***/ }), -/* 374 */ +/* 375 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48502,7 +48697,7 @@ module.exports = { /***/ }), -/* 375 */ +/* 376 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48643,12 +48838,12 @@ module.exports = (chalk, temporary) => { /***/ }), -/* 376 */ +/* 377 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const restoreCursor = __webpack_require__(377); +const restoreCursor = __webpack_require__(378); let isHidden = false; @@ -48685,12 +48880,12 @@ exports.toggle = (force, writableStream) => { /***/ }), -/* 377 */ +/* 378 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const onetime = __webpack_require__(378); +const onetime = __webpack_require__(379); const signalExit = __webpack_require__(218); module.exports = onetime(() => { @@ -48701,12 +48896,12 @@ module.exports = onetime(() => { /***/ }), -/* 378 */ +/* 379 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const mimicFn = __webpack_require__(379); +const mimicFn = __webpack_require__(380); const calledFunctions = new WeakMap(); @@ -48758,7 +48953,7 @@ module.exports.callCount = fn => { /***/ }), -/* 379 */ +/* 380 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48778,13 +48973,13 @@ module.exports.default = mimicFn; /***/ }), -/* 380 */ +/* 381 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const spinners = Object.assign({}, __webpack_require__(381)); +const spinners = Object.assign({}, __webpack_require__(382)); const spinnersList = Object.keys(spinners); @@ -48802,18 +48997,18 @@ module.exports.default = spinners; /***/ }), -/* 381 */ +/* 382 */ /***/ (function(module) { module.exports = JSON.parse("{\"dots\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠹\",\"⠸\",\"⠼\",\"⠴\",\"⠦\",\"⠧\",\"⠇\",\"⠏\"]},\"dots2\":{\"interval\":80,\"frames\":[\"⣾\",\"⣽\",\"⣻\",\"⢿\",\"⡿\",\"⣟\",\"⣯\",\"⣷\"]},\"dots3\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠞\",\"⠖\",\"⠦\",\"⠴\",\"⠲\",\"⠳\",\"⠓\"]},\"dots4\":{\"interval\":80,\"frames\":[\"⠄\",\"⠆\",\"⠇\",\"⠋\",\"⠙\",\"⠸\",\"⠰\",\"⠠\",\"⠰\",\"⠸\",\"⠙\",\"⠋\",\"⠇\",\"⠆\"]},\"dots5\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\"]},\"dots6\":{\"interval\":80,\"frames\":[\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠴\",\"⠲\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠚\",\"⠙\",\"⠉\",\"⠁\"]},\"dots7\":{\"interval\":80,\"frames\":[\"⠈\",\"⠉\",\"⠋\",\"⠓\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠖\",\"⠦\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\"]},\"dots8\":{\"interval\":80,\"frames\":[\"⠁\",\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\",\"⠈\"]},\"dots9\":{\"interval\":80,\"frames\":[\"⢹\",\"⢺\",\"⢼\",\"⣸\",\"⣇\",\"⡧\",\"⡗\",\"⡏\"]},\"dots10\":{\"interval\":80,\"frames\":[\"⢄\",\"⢂\",\"⢁\",\"⡁\",\"⡈\",\"⡐\",\"⡠\"]},\"dots11\":{\"interval\":100,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⡀\",\"⢀\",\"⠠\",\"⠐\",\"⠈\"]},\"dots12\":{\"interval\":80,\"frames\":[\"⢀⠀\",\"⡀⠀\",\"⠄⠀\",\"⢂⠀\",\"⡂⠀\",\"⠅⠀\",\"⢃⠀\",\"⡃⠀\",\"⠍⠀\",\"⢋⠀\",\"⡋⠀\",\"⠍⠁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⢈⠩\",\"⡀⢙\",\"⠄⡙\",\"⢂⠩\",\"⡂⢘\",\"⠅⡘\",\"⢃⠨\",\"⡃⢐\",\"⠍⡐\",\"⢋⠠\",\"⡋⢀\",\"⠍⡁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⠈⠩\",\"⠀⢙\",\"⠀⡙\",\"⠀⠩\",\"⠀⢘\",\"⠀⡘\",\"⠀⠨\",\"⠀⢐\",\"⠀⡐\",\"⠀⠠\",\"⠀⢀\",\"⠀⡀\"]},\"dots8Bit\":{\"interval\":80,\"frames\":[\"⠀\",\"⠁\",\"⠂\",\"⠃\",\"⠄\",\"⠅\",\"⠆\",\"⠇\",\"⡀\",\"⡁\",\"⡂\",\"⡃\",\"⡄\",\"⡅\",\"⡆\",\"⡇\",\"⠈\",\"⠉\",\"⠊\",\"⠋\",\"⠌\",\"⠍\",\"⠎\",\"⠏\",\"⡈\",\"⡉\",\"⡊\",\"⡋\",\"⡌\",\"⡍\",\"⡎\",\"⡏\",\"⠐\",\"⠑\",\"⠒\",\"⠓\",\"⠔\",\"⠕\",\"⠖\",\"⠗\",\"⡐\",\"⡑\",\"⡒\",\"⡓\",\"⡔\",\"⡕\",\"⡖\",\"⡗\",\"⠘\",\"⠙\",\"⠚\",\"⠛\",\"⠜\",\"⠝\",\"⠞\",\"⠟\",\"⡘\",\"⡙\",\"⡚\",\"⡛\",\"⡜\",\"⡝\",\"⡞\",\"⡟\",\"⠠\",\"⠡\",\"⠢\",\"⠣\",\"⠤\",\"⠥\",\"⠦\",\"⠧\",\"⡠\",\"⡡\",\"⡢\",\"⡣\",\"⡤\",\"⡥\",\"⡦\",\"⡧\",\"⠨\",\"⠩\",\"⠪\",\"⠫\",\"⠬\",\"⠭\",\"⠮\",\"⠯\",\"⡨\",\"⡩\",\"⡪\",\"⡫\",\"⡬\",\"⡭\",\"⡮\",\"⡯\",\"⠰\",\"⠱\",\"⠲\",\"⠳\",\"⠴\",\"⠵\",\"⠶\",\"⠷\",\"⡰\",\"⡱\",\"⡲\",\"⡳\",\"⡴\",\"⡵\",\"⡶\",\"⡷\",\"⠸\",\"⠹\",\"⠺\",\"⠻\",\"⠼\",\"⠽\",\"⠾\",\"⠿\",\"⡸\",\"⡹\",\"⡺\",\"⡻\",\"⡼\",\"⡽\",\"⡾\",\"⡿\",\"⢀\",\"⢁\",\"⢂\",\"⢃\",\"⢄\",\"⢅\",\"⢆\",\"⢇\",\"⣀\",\"⣁\",\"⣂\",\"⣃\",\"⣄\",\"⣅\",\"⣆\",\"⣇\",\"⢈\",\"⢉\",\"⢊\",\"⢋\",\"⢌\",\"⢍\",\"⢎\",\"⢏\",\"⣈\",\"⣉\",\"⣊\",\"⣋\",\"⣌\",\"⣍\",\"⣎\",\"⣏\",\"⢐\",\"⢑\",\"⢒\",\"⢓\",\"⢔\",\"⢕\",\"⢖\",\"⢗\",\"⣐\",\"⣑\",\"⣒\",\"⣓\",\"⣔\",\"⣕\",\"⣖\",\"⣗\",\"⢘\",\"⢙\",\"⢚\",\"⢛\",\"⢜\",\"⢝\",\"⢞\",\"⢟\",\"⣘\",\"⣙\",\"⣚\",\"⣛\",\"⣜\",\"⣝\",\"⣞\",\"⣟\",\"⢠\",\"⢡\",\"⢢\",\"⢣\",\"⢤\",\"⢥\",\"⢦\",\"⢧\",\"⣠\",\"⣡\",\"⣢\",\"⣣\",\"⣤\",\"⣥\",\"⣦\",\"⣧\",\"⢨\",\"⢩\",\"⢪\",\"⢫\",\"⢬\",\"⢭\",\"⢮\",\"⢯\",\"⣨\",\"⣩\",\"⣪\",\"⣫\",\"⣬\",\"⣭\",\"⣮\",\"⣯\",\"⢰\",\"⢱\",\"⢲\",\"⢳\",\"⢴\",\"⢵\",\"⢶\",\"⢷\",\"⣰\",\"⣱\",\"⣲\",\"⣳\",\"⣴\",\"⣵\",\"⣶\",\"⣷\",\"⢸\",\"⢹\",\"⢺\",\"⢻\",\"⢼\",\"⢽\",\"⢾\",\"⢿\",\"⣸\",\"⣹\",\"⣺\",\"⣻\",\"⣼\",\"⣽\",\"⣾\",\"⣿\"]},\"line\":{\"interval\":130,\"frames\":[\"-\",\"\\\\\",\"|\",\"/\"]},\"line2\":{\"interval\":100,\"frames\":[\"⠂\",\"-\",\"–\",\"—\",\"–\",\"-\"]},\"pipe\":{\"interval\":100,\"frames\":[\"┤\",\"┘\",\"┴\",\"└\",\"├\",\"┌\",\"┬\",\"┐\"]},\"simpleDots\":{\"interval\":400,\"frames\":[\". \",\".. \",\"...\",\" \"]},\"simpleDotsScrolling\":{\"interval\":200,\"frames\":[\". \",\".. \",\"...\",\" ..\",\" .\",\" \"]},\"star\":{\"interval\":70,\"frames\":[\"✶\",\"✸\",\"✹\",\"✺\",\"✹\",\"✷\"]},\"star2\":{\"interval\":80,\"frames\":[\"+\",\"x\",\"*\"]},\"flip\":{\"interval\":70,\"frames\":[\"_\",\"_\",\"_\",\"-\",\"`\",\"`\",\"'\",\"´\",\"-\",\"_\",\"_\",\"_\"]},\"hamburger\":{\"interval\":100,\"frames\":[\"☱\",\"☲\",\"☴\"]},\"growVertical\":{\"interval\":120,\"frames\":[\"▁\",\"▃\",\"▄\",\"▅\",\"▆\",\"▇\",\"▆\",\"▅\",\"▄\",\"▃\"]},\"growHorizontal\":{\"interval\":120,\"frames\":[\"▏\",\"▎\",\"▍\",\"▌\",\"▋\",\"▊\",\"▉\",\"▊\",\"▋\",\"▌\",\"▍\",\"▎\"]},\"balloon\":{\"interval\":140,\"frames\":[\" \",\".\",\"o\",\"O\",\"@\",\"*\",\" \"]},\"balloon2\":{\"interval\":120,\"frames\":[\".\",\"o\",\"O\",\"°\",\"O\",\"o\",\".\"]},\"noise\":{\"interval\":100,\"frames\":[\"▓\",\"▒\",\"░\"]},\"bounce\":{\"interval\":120,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⠂\"]},\"boxBounce\":{\"interval\":120,\"frames\":[\"▖\",\"▘\",\"▝\",\"▗\"]},\"boxBounce2\":{\"interval\":100,\"frames\":[\"▌\",\"▀\",\"▐\",\"▄\"]},\"triangle\":{\"interval\":50,\"frames\":[\"◢\",\"◣\",\"◤\",\"◥\"]},\"arc\":{\"interval\":100,\"frames\":[\"◜\",\"◠\",\"◝\",\"◞\",\"◡\",\"◟\"]},\"circle\":{\"interval\":120,\"frames\":[\"◡\",\"⊙\",\"◠\"]},\"squareCorners\":{\"interval\":180,\"frames\":[\"◰\",\"◳\",\"◲\",\"◱\"]},\"circleQuarters\":{\"interval\":120,\"frames\":[\"◴\",\"◷\",\"◶\",\"◵\"]},\"circleHalves\":{\"interval\":50,\"frames\":[\"◐\",\"◓\",\"◑\",\"◒\"]},\"squish\":{\"interval\":100,\"frames\":[\"╫\",\"╪\"]},\"toggle\":{\"interval\":250,\"frames\":[\"⊶\",\"⊷\"]},\"toggle2\":{\"interval\":80,\"frames\":[\"▫\",\"▪\"]},\"toggle3\":{\"interval\":120,\"frames\":[\"□\",\"■\"]},\"toggle4\":{\"interval\":100,\"frames\":[\"■\",\"□\",\"▪\",\"▫\"]},\"toggle5\":{\"interval\":100,\"frames\":[\"▮\",\"▯\"]},\"toggle6\":{\"interval\":300,\"frames\":[\"ဝ\",\"၀\"]},\"toggle7\":{\"interval\":80,\"frames\":[\"⦾\",\"⦿\"]},\"toggle8\":{\"interval\":100,\"frames\":[\"◍\",\"◌\"]},\"toggle9\":{\"interval\":100,\"frames\":[\"◉\",\"◎\"]},\"toggle10\":{\"interval\":100,\"frames\":[\"㊂\",\"㊀\",\"㊁\"]},\"toggle11\":{\"interval\":50,\"frames\":[\"⧇\",\"⧆\"]},\"toggle12\":{\"interval\":120,\"frames\":[\"☗\",\"☖\"]},\"toggle13\":{\"interval\":80,\"frames\":[\"=\",\"*\",\"-\"]},\"arrow\":{\"interval\":100,\"frames\":[\"←\",\"↖\",\"↑\",\"↗\",\"→\",\"↘\",\"↓\",\"↙\"]},\"arrow2\":{\"interval\":80,\"frames\":[\"⬆️ \",\"↗️ \",\"➡️ \",\"↘️ \",\"⬇️ \",\"↙️ \",\"⬅️ \",\"↖️ \"]},\"arrow3\":{\"interval\":120,\"frames\":[\"▹▹▹▹▹\",\"▸▹▹▹▹\",\"▹▸▹▹▹\",\"▹▹▸▹▹\",\"▹▹▹▸▹\",\"▹▹▹▹▸\"]},\"bouncingBar\":{\"interval\":80,\"frames\":[\"[ ]\",\"[= ]\",\"[== ]\",\"[=== ]\",\"[ ===]\",\"[ ==]\",\"[ =]\",\"[ ]\",\"[ =]\",\"[ ==]\",\"[ ===]\",\"[====]\",\"[=== ]\",\"[== ]\",\"[= ]\"]},\"bouncingBall\":{\"interval\":80,\"frames\":[\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ●)\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"(● )\"]},\"smiley\":{\"interval\":200,\"frames\":[\"😄 \",\"😝 \"]},\"monkey\":{\"interval\":300,\"frames\":[\"🙈 \",\"🙈 \",\"🙉 \",\"🙊 \"]},\"hearts\":{\"interval\":100,\"frames\":[\"💛 \",\"💙 \",\"💜 \",\"💚 \",\"❤️ \"]},\"clock\":{\"interval\":100,\"frames\":[\"🕛 \",\"🕐 \",\"🕑 \",\"🕒 \",\"🕓 \",\"🕔 \",\"🕕 \",\"🕖 \",\"🕗 \",\"🕘 \",\"🕙 \",\"🕚 \"]},\"earth\":{\"interval\":180,\"frames\":[\"🌍 \",\"🌎 \",\"🌏 \"]},\"material\":{\"interval\":17,\"frames\":[\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"███████▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"██████████▁▁▁▁▁▁▁▁▁▁\",\"███████████▁▁▁▁▁▁▁▁▁\",\"█████████████▁▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁▁██████████████▁▁▁▁\",\"▁▁▁██████████████▁▁▁\",\"▁▁▁▁█████████████▁▁▁\",\"▁▁▁▁██████████████▁▁\",\"▁▁▁▁██████████████▁▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁██████████████▁\",\"▁▁▁▁▁▁██████████████\",\"▁▁▁▁▁▁██████████████\",\"▁▁▁▁▁▁▁█████████████\",\"▁▁▁▁▁▁▁█████████████\",\"▁▁▁▁▁▁▁▁████████████\",\"▁▁▁▁▁▁▁▁████████████\",\"▁▁▁▁▁▁▁▁▁███████████\",\"▁▁▁▁▁▁▁▁▁███████████\",\"▁▁▁▁▁▁▁▁▁▁██████████\",\"▁▁▁▁▁▁▁▁▁▁██████████\",\"▁▁▁▁▁▁▁▁▁▁▁▁████████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁██████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"██▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"███▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"████▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"█████▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"██████▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"████████▁▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"█████████▁▁▁▁▁▁▁▁▁▁▁\",\"███████████▁▁▁▁▁▁▁▁▁\",\"████████████▁▁▁▁▁▁▁▁\",\"████████████▁▁▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"██████████████▁▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁██████████████▁▁▁▁▁\",\"▁▁▁█████████████▁▁▁▁\",\"▁▁▁▁▁████████████▁▁▁\",\"▁▁▁▁▁████████████▁▁▁\",\"▁▁▁▁▁▁███████████▁▁▁\",\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\"▁▁▁▁▁▁▁▁█████████▁▁▁\",\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\"▁▁▁▁▁▁▁▁▁█████████▁▁\",\"▁▁▁▁▁▁▁▁▁▁█████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁████████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁███████▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁███████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁████\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁███\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁██\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\",\"▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁\"]},\"moon\":{\"interval\":80,\"frames\":[\"🌑 \",\"🌒 \",\"🌓 \",\"🌔 \",\"🌕 \",\"🌖 \",\"🌗 \",\"🌘 \"]},\"runner\":{\"interval\":140,\"frames\":[\"🚶 \",\"🏃 \"]},\"pong\":{\"interval\":80,\"frames\":[\"▐⠂ ▌\",\"▐⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂▌\",\"▐ ⠠▌\",\"▐ ⡀▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐⠠ ▌\"]},\"shark\":{\"interval\":120,\"frames\":[\"▐|\\\\____________▌\",\"▐_|\\\\___________▌\",\"▐__|\\\\__________▌\",\"▐___|\\\\_________▌\",\"▐____|\\\\________▌\",\"▐_____|\\\\_______▌\",\"▐______|\\\\______▌\",\"▐_______|\\\\_____▌\",\"▐________|\\\\____▌\",\"▐_________|\\\\___▌\",\"▐__________|\\\\__▌\",\"▐___________|\\\\_▌\",\"▐____________|\\\\▌\",\"▐____________/|▌\",\"▐___________/|_▌\",\"▐__________/|__▌\",\"▐_________/|___▌\",\"▐________/|____▌\",\"▐_______/|_____▌\",\"▐______/|______▌\",\"▐_____/|_______▌\",\"▐____/|________▌\",\"▐___/|_________▌\",\"▐__/|__________▌\",\"▐_/|___________▌\",\"▐/|____________▌\"]},\"dqpb\":{\"interval\":100,\"frames\":[\"d\",\"q\",\"p\",\"b\"]},\"weather\":{\"interval\":100,\"frames\":[\"☀️ \",\"☀️ \",\"☀️ \",\"🌤 \",\"⛅️ \",\"🌥 \",\"☁️ \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"⛈ \",\"🌨 \",\"🌧 \",\"🌨 \",\"☁️ \",\"🌥 \",\"⛅️ \",\"🌤 \",\"☀️ \",\"☀️ \"]},\"christmas\":{\"interval\":400,\"frames\":[\"🌲\",\"🎄\"]},\"grenade\":{\"interval\":80,\"frames\":[\"، \",\"′ \",\" ´ \",\" ‾ \",\" ⸌\",\" ⸊\",\" |\",\" ⁎\",\" ⁕\",\" ෴ \",\" ⁓\",\" \",\" \",\" \"]},\"point\":{\"interval\":125,\"frames\":[\"∙∙∙\",\"●∙∙\",\"∙●∙\",\"∙∙●\",\"∙∙∙\"]},\"layer\":{\"interval\":150,\"frames\":[\"-\",\"=\",\"≡\"]},\"betaWave\":{\"interval\":80,\"frames\":[\"ρββββββ\",\"βρβββββ\",\"ββρββββ\",\"βββρβββ\",\"ββββρββ\",\"βββββρβ\",\"ββββββρ\"]}}"); /***/ }), -/* 382 */ +/* 383 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const chalk = __webpack_require__(383); +const chalk = __webpack_require__(384); const isSupported = process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color'; @@ -48835,16 +49030,16 @@ module.exports = isSupported ? main : fallbacks; /***/ }), -/* 383 */ +/* 384 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const escapeStringRegexp = __webpack_require__(179); -const ansiStyles = __webpack_require__(384); -const stdoutColor = __webpack_require__(389).stdout; +const ansiStyles = __webpack_require__(385); +const stdoutColor = __webpack_require__(390).stdout; -const template = __webpack_require__(390); +const template = __webpack_require__(391); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); @@ -49070,12 +49265,12 @@ module.exports.default = module.exports; // For TypeScript /***/ }), -/* 384 */ +/* 385 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(module) { -const colorConvert = __webpack_require__(385); +const colorConvert = __webpack_require__(386); const wrapAnsi16 = (fn, offset) => function () { const code = fn.apply(colorConvert, arguments); @@ -49243,11 +49438,11 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(115)(module))) /***/ }), -/* 385 */ +/* 386 */ /***/ (function(module, exports, __webpack_require__) { -var conversions = __webpack_require__(386); -var route = __webpack_require__(388); +var conversions = __webpack_require__(387); +var route = __webpack_require__(389); var convert = {}; @@ -49327,11 +49522,11 @@ module.exports = convert; /***/ }), -/* 386 */ +/* 387 */ /***/ (function(module, exports, __webpack_require__) { /* MIT license */ -var cssKeywords = __webpack_require__(387); +var cssKeywords = __webpack_require__(388); // NOTE: conversions should only return primitive values (i.e. arrays, or // values that give correct `typeof` results). @@ -50201,7 +50396,7 @@ convert.rgb.gray = function (rgb) { /***/ }), -/* 387 */ +/* 388 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50360,10 +50555,10 @@ module.exports = { /***/ }), -/* 388 */ +/* 389 */ /***/ (function(module, exports, __webpack_require__) { -var conversions = __webpack_require__(386); +var conversions = __webpack_require__(387); /* this function routes a model to all other models. @@ -50463,7 +50658,7 @@ module.exports = function (fromModel) { /***/ }), -/* 389 */ +/* 390 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50601,7 +50796,7 @@ module.exports = { /***/ }), -/* 390 */ +/* 391 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50736,18 +50931,18 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 391 */ +/* 392 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiRegex = __webpack_require__(392); +const ansiRegex = __webpack_require__(393); module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; /***/ }), -/* 392 */ +/* 393 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -50764,14 +50959,14 @@ module.exports = ({onlyFirst = false} = {}) => { /***/ }), -/* 393 */ +/* 394 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var defaults = __webpack_require__(394) -var combining = __webpack_require__(396) +var defaults = __webpack_require__(395) +var combining = __webpack_require__(397) var DEFAULTS = { nul: 0, @@ -50870,10 +51065,10 @@ function bisearch(ucs) { /***/ }), -/* 394 */ +/* 395 */ /***/ (function(module, exports, __webpack_require__) { -var clone = __webpack_require__(395); +var clone = __webpack_require__(396); module.exports = function(options, defaults) { options = options || {}; @@ -50888,7 +51083,7 @@ module.exports = function(options, defaults) { }; /***/ }), -/* 395 */ +/* 396 */ /***/ (function(module, exports, __webpack_require__) { var clone = (function() { @@ -51060,7 +51255,7 @@ if ( true && module.exports) { /***/ }), -/* 396 */ +/* 397 */ /***/ (function(module, exports) { module.exports = [ @@ -51116,7 +51311,7 @@ module.exports = [ /***/ }), -/* 397 */ +/* 398 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -51132,7 +51327,7 @@ module.exports = ({stream = process.stdout} = {}) => { /***/ }), -/* 398 */ +/* 399 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(138) @@ -51283,7 +51478,7 @@ MuteStream.prototype.close = proxy('close') /***/ }), -/* 399 */ +/* 400 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51344,7 +51539,7 @@ const RunCommand = { }; /***/ }), -/* 400 */ +/* 401 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51354,7 +51549,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(144); /* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(145); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(146); -/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(401); +/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(402); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -51439,14 +51634,14 @@ const WatchCommand = { }; /***/ }), -/* 401 */ +/* 402 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "waitUntilWatchIsReady", function() { return waitUntilWatchIsReady; }); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(8); -/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(402); +/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(403); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -51513,141 +51708,141 @@ function waitUntilWatchIsReady(stream, opts = {}) { } /***/ }), -/* 402 */ +/* 403 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(403); +/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(404); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__["audit"]; }); -/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(404); +/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(405); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__["auditTime"]; }); -/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(405); +/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(406); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__["buffer"]; }); -/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(406); +/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(407); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferCount", function() { return _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__["bufferCount"]; }); -/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(407); +/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(408); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__["bufferTime"]; }); -/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(408); +/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(409); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__["bufferToggle"]; }); -/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(409); +/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(410); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferWhen", function() { return _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__["bufferWhen"]; }); -/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(410); +/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(411); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__["catchError"]; }); -/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(411); +/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(412); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__["combineAll"]; }); -/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(412); +/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(413); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__["combineLatest"]; }); -/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(413); +/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(414); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__["concat"]; }); /* harmony import */ var _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(80); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatAll", function() { return _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__["concatAll"]; }); -/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(414); +/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(415); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__["concatMap"]; }); -/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(415); +/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(416); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__["concatMapTo"]; }); -/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(416); +/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(417); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "count", function() { return _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__["count"]; }); -/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(417); +/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(418); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__["debounce"]; }); -/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(418); +/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(419); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__["debounceTime"]; }); -/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(419); +/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(420); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "defaultIfEmpty", function() { return _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__["defaultIfEmpty"]; }); -/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(420); +/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(421); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__["delay"]; }); -/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(422); +/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(423); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delayWhen", function() { return _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__["delayWhen"]; }); -/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(423); +/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(424); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "dematerialize", function() { return _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__["dematerialize"]; }); -/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(424); +/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(425); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__["distinct"]; }); -/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(425); +/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(426); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__["distinctUntilChanged"]; }); -/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(426); +/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(427); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__["distinctUntilKeyChanged"]; }); -/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(427); +/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(428); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__["elementAt"]; }); -/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(430); +/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(431); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__["endWith"]; }); -/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(431); +/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(432); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "every", function() { return _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__["every"]; }); -/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(432); +/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(433); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__["exhaust"]; }); -/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(433); +/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(434); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__["exhaustMap"]; }); -/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(434); +/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(435); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "expand", function() { return _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__["expand"]; }); /* harmony import */ var _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(105); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "filter", function() { return _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__["filter"]; }); -/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(435); +/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(436); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "finalize", function() { return _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__["finalize"]; }); -/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(436); +/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(437); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "find", function() { return _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__["find"]; }); -/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(437); +/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(438); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__["findIndex"]; }); -/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(438); +/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(439); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "first", function() { return _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__["first"]; }); /* harmony import */ var _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(31); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "groupBy", function() { return _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__["groupBy"]; }); -/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(439); +/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(440); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ignoreElements", function() { return _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__["ignoreElements"]; }); -/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(440); +/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(441); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isEmpty", function() { return _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__["isEmpty"]; }); -/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(441); +/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(442); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "last", function() { return _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__["last"]; }); /* harmony import */ var _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(66); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "map", function() { return _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__["map"]; }); -/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(443); +/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(444); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mapTo", function() { return _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__["mapTo"]; }); -/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(444); +/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(445); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__["materialize"]; }); -/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(445); +/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(446); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "max", function() { return _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__["max"]; }); -/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(448); +/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(449); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__["merge"]; }); /* harmony import */ var _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(81); @@ -51658,175 +51853,175 @@ __webpack_require__.r(__webpack_exports__); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "flatMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["flatMap"]; }); -/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(449); +/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(450); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__["mergeMapTo"]; }); -/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(450); +/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(451); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeScan", function() { return _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__["mergeScan"]; }); -/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(451); +/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(452); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "min", function() { return _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__["min"]; }); -/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(452); +/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(453); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "multicast", function() { return _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__["multicast"]; }); /* harmony import */ var _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(41); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "observeOn", function() { return _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__["observeOn"]; }); -/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(453); +/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(454); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__["onErrorResumeNext"]; }); -/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(454); +/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(455); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pairwise", function() { return _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__["pairwise"]; }); -/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(455); +/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(456); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__["partition"]; }); -/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(456); +/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(457); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pluck", function() { return _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__["pluck"]; }); -/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(457); +/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(458); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__["publish"]; }); -/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(458); +/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(459); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__["publishBehavior"]; }); -/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(459); +/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(460); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__["publishLast"]; }); -/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(460); +/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(461); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__["publishReplay"]; }); -/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(461); +/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(462); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__["race"]; }); -/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(446); +/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(447); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__["reduce"]; }); -/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(462); +/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(463); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__["repeat"]; }); -/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(463); +/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(464); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__["repeatWhen"]; }); -/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(464); +/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(465); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__["retry"]; }); -/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(465); +/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(466); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__["retryWhen"]; }); /* harmony import */ var _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(30); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "refCount", function() { return _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__["refCount"]; }); -/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(466); +/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(467); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__["sample"]; }); -/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(467); +/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(468); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__["sampleTime"]; }); -/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(447); +/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(448); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "scan", function() { return _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__["scan"]; }); -/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(468); +/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(469); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__["sequenceEqual"]; }); -/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(469); +/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(470); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "share", function() { return _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__["share"]; }); -/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(470); +/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(471); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__["shareReplay"]; }); -/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(471); +/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(472); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "single", function() { return _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__["single"]; }); -/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(472); +/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(473); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skip", function() { return _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__["skip"]; }); -/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(473); +/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(474); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__["skipLast"]; }); -/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(474); +/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(475); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__["skipUntil"]; }); -/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(475); +/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(476); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__["skipWhile"]; }); -/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(476); +/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(477); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__["startWith"]; }); -/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(477); +/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(478); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__["subscribeOn"]; }); -/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(479); +/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(480); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__["switchAll"]; }); -/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(480); +/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(481); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__["switchMap"]; }); -/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(481); +/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(482); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__["switchMapTo"]; }); -/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(429); +/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(430); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "take", function() { return _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__["take"]; }); -/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(442); +/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(443); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__["takeLast"]; }); -/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(482); +/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(483); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__["takeUntil"]; }); -/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(483); +/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(484); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeWhile", function() { return _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__["takeWhile"]; }); -/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(484); +/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(485); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__["tap"]; }); -/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(485); +/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(486); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttle", function() { return _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__["throttle"]; }); -/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(486); +/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(487); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__["throttleTime"]; }); -/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(428); +/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(429); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__["throwIfEmpty"]; }); -/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(487); +/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(488); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__["timeInterval"]; }); -/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(488); +/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(489); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__["timeout"]; }); -/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(489); +/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(490); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__["timeoutWith"]; }); -/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(490); +/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(491); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__["timestamp"]; }); -/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(491); +/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(492); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__["toArray"]; }); -/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(492); +/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(493); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "window", function() { return _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__["window"]; }); -/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(493); +/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(494); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__["windowCount"]; }); -/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(494); +/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(495); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__["windowTime"]; }); -/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(495); +/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(496); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__["windowToggle"]; }); -/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(496); +/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(497); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__["windowWhen"]; }); -/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(497); +/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(498); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "withLatestFrom", function() { return _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__["withLatestFrom"]; }); -/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(498); +/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(499); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__["zip"]; }); -/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(499); +/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(500); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zipAll", function() { return _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__["zipAll"]; }); /** PURE_IMPORTS_START PURE_IMPORTS_END */ @@ -51937,7 +52132,7 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 403 */ +/* 404 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52016,14 +52211,14 @@ var AuditSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 404 */ +/* 405 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return auditTime; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(55); -/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(403); +/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(404); /* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(108); /** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ @@ -52039,7 +52234,7 @@ function auditTime(duration, scheduler) { /***/ }), -/* 405 */ +/* 406 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52086,7 +52281,7 @@ var BufferSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 406 */ +/* 407 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52187,7 +52382,7 @@ var BufferSkipCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 407 */ +/* 408 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52348,7 +52543,7 @@ function dispatchBufferClose(arg) { /***/ }), -/* 408 */ +/* 409 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52467,7 +52662,7 @@ var BufferToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 409 */ +/* 410 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52560,7 +52755,7 @@ var BufferWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 410 */ +/* 411 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52620,7 +52815,7 @@ var CatchSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 411 */ +/* 412 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52636,7 +52831,7 @@ function combineAll(project) { /***/ }), -/* 412 */ +/* 413 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52668,7 +52863,7 @@ function combineLatest() { /***/ }), -/* 413 */ +/* 414 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52688,7 +52883,7 @@ function concat() { /***/ }), -/* 414 */ +/* 415 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52704,13 +52899,13 @@ function concatMap(project, resultSelector) { /***/ }), -/* 415 */ +/* 416 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return concatMapTo; }); -/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(414); +/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(415); /** PURE_IMPORTS_START _concatMap PURE_IMPORTS_END */ function concatMapTo(innerObservable, resultSelector) { @@ -52720,7 +52915,7 @@ function concatMapTo(innerObservable, resultSelector) { /***/ }), -/* 416 */ +/* 417 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52785,7 +52980,7 @@ var CountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 417 */ +/* 418 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52870,7 +53065,7 @@ var DebounceSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 418 */ +/* 419 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52946,7 +53141,7 @@ function dispatchNext(subscriber) { /***/ }), -/* 419 */ +/* 420 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52996,7 +53191,7 @@ var DefaultIfEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 420 */ +/* 421 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53004,7 +53199,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return delay; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(421); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(422); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); /* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(42); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ @@ -53103,7 +53298,7 @@ var DelayMessage = /*@__PURE__*/ (function () { /***/ }), -/* 421 */ +/* 422 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53117,7 +53312,7 @@ function isDate(value) { /***/ }), -/* 422 */ +/* 423 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53263,7 +53458,7 @@ var SubscriptionDelaySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 423 */ +/* 424 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53301,7 +53496,7 @@ var DeMaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 424 */ +/* 425 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53377,7 +53572,7 @@ var DistinctSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 425 */ +/* 426 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53448,13 +53643,13 @@ var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 426 */ +/* 427 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return distinctUntilKeyChanged; }); -/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(425); +/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(426); /** PURE_IMPORTS_START _distinctUntilChanged PURE_IMPORTS_END */ function distinctUntilKeyChanged(key, compare) { @@ -53464,7 +53659,7 @@ function distinctUntilKeyChanged(key, compare) { /***/ }), -/* 427 */ +/* 428 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53472,9 +53667,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return elementAt; }); /* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(62); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(105); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(428); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(419); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(429); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(429); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(420); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(430); /** PURE_IMPORTS_START _util_ArgumentOutOfRangeError,_filter,_throwIfEmpty,_defaultIfEmpty,_take PURE_IMPORTS_END */ @@ -53496,7 +53691,7 @@ function elementAt(index, defaultValue) { /***/ }), -/* 428 */ +/* 429 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53562,7 +53757,7 @@ function defaultErrorFactory() { /***/ }), -/* 429 */ +/* 430 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53624,7 +53819,7 @@ var TakeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 430 */ +/* 431 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53646,7 +53841,7 @@ function endWith() { /***/ }), -/* 431 */ +/* 432 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53708,7 +53903,7 @@ var EverySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 432 */ +/* 433 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53762,7 +53957,7 @@ var SwitchFirstSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 433 */ +/* 434 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53856,7 +54051,7 @@ var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 434 */ +/* 435 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53968,7 +54163,7 @@ var ExpandSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 435 */ +/* 436 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54006,7 +54201,7 @@ var FinallySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 436 */ +/* 437 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54078,13 +54273,13 @@ var FindValueSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 437 */ +/* 438 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return findIndex; }); -/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(436); +/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(437); /** PURE_IMPORTS_START _operators_find PURE_IMPORTS_END */ function findIndex(predicate, thisArg) { @@ -54094,7 +54289,7 @@ function findIndex(predicate, thisArg) { /***/ }), -/* 438 */ +/* 439 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54102,9 +54297,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "first", function() { return first; }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(63); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(105); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(429); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(419); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(428); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(430); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(420); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(429); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25); /** PURE_IMPORTS_START _util_EmptyError,_filter,_take,_defaultIfEmpty,_throwIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -54121,7 +54316,7 @@ function first(predicate, defaultValue) { /***/ }), -/* 439 */ +/* 440 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54158,7 +54353,7 @@ var IgnoreElementsSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 440 */ +/* 441 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54202,7 +54397,7 @@ var IsEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 441 */ +/* 442 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54210,9 +54405,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "last", function() { return last; }); /* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(63); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(105); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(442); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(428); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(419); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(443); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(429); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(420); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25); /** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -54229,7 +54424,7 @@ function last(predicate, defaultValue) { /***/ }), -/* 442 */ +/* 443 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54306,7 +54501,7 @@ var TakeLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 443 */ +/* 444 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54345,7 +54540,7 @@ var MapToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 444 */ +/* 445 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54395,13 +54590,13 @@ var MaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 445 */ +/* 446 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "max", function() { return max; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(446); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(447); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function max(comparer) { @@ -54414,15 +54609,15 @@ function max(comparer) { /***/ }), -/* 446 */ +/* 447 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return reduce; }); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(447); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(442); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(419); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(448); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(443); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(420); /* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(24); /** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ @@ -54443,7 +54638,7 @@ function reduce(accumulator, seed) { /***/ }), -/* 447 */ +/* 448 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54525,7 +54720,7 @@ var ScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 448 */ +/* 449 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54545,7 +54740,7 @@ function merge() { /***/ }), -/* 449 */ +/* 450 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54570,7 +54765,7 @@ function mergeMapTo(innerObservable, resultSelector, concurrent) { /***/ }), -/* 450 */ +/* 451 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54679,13 +54874,13 @@ var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 451 */ +/* 452 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "min", function() { return min; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(446); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(447); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function min(comparer) { @@ -54698,7 +54893,7 @@ function min(comparer) { /***/ }), -/* 452 */ +/* 453 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54747,7 +54942,7 @@ var MulticastOperator = /*@__PURE__*/ (function () { /***/ }), -/* 453 */ +/* 454 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54837,7 +55032,7 @@ var OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 454 */ +/* 455 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54885,7 +55080,7 @@ var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 455 */ +/* 456 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54908,7 +55103,7 @@ function partition(predicate, thisArg) { /***/ }), -/* 456 */ +/* 457 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54948,14 +55143,14 @@ function plucker(props, length) { /***/ }), -/* 457 */ +/* 458 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return publish; }); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(27); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(452); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(453); /** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ @@ -54968,14 +55163,14 @@ function publish(selector) { /***/ }), -/* 458 */ +/* 459 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return publishBehavior; }); /* harmony import */ var _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(32); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(452); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(453); /** PURE_IMPORTS_START _BehaviorSubject,_multicast PURE_IMPORTS_END */ @@ -54986,14 +55181,14 @@ function publishBehavior(value) { /***/ }), -/* 459 */ +/* 460 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return publishLast; }); /* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(50); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(452); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(453); /** PURE_IMPORTS_START _AsyncSubject,_multicast PURE_IMPORTS_END */ @@ -55004,14 +55199,14 @@ function publishLast() { /***/ }), -/* 460 */ +/* 461 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return publishReplay; }); /* harmony import */ var _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(33); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(452); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(453); /** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ @@ -55027,7 +55222,7 @@ function publishReplay(bufferSize, windowTime, selectorOrScheduler, scheduler) { /***/ }), -/* 461 */ +/* 462 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55054,7 +55249,7 @@ function race() { /***/ }), -/* 462 */ +/* 463 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55119,7 +55314,7 @@ var RepeatSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 463 */ +/* 464 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55213,7 +55408,7 @@ var RepeatWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 464 */ +/* 465 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55266,7 +55461,7 @@ var RetrySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 465 */ +/* 466 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55352,7 +55547,7 @@ var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 466 */ +/* 467 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55407,7 +55602,7 @@ var SampleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 467 */ +/* 468 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55467,7 +55662,7 @@ function dispatchNotification(state) { /***/ }), -/* 468 */ +/* 469 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55590,13 +55785,13 @@ var SequenceEqualCompareToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 469 */ +/* 470 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "share", function() { return share; }); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(452); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(453); /* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(30); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(27); /** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ @@ -55613,7 +55808,7 @@ function share() { /***/ }), -/* 470 */ +/* 471 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55682,7 +55877,7 @@ function shareReplayOperator(_a) { /***/ }), -/* 471 */ +/* 472 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55762,7 +55957,7 @@ var SingleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 472 */ +/* 473 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55804,7 +55999,7 @@ var SkipSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 473 */ +/* 474 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55866,7 +56061,7 @@ var SkipLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 474 */ +/* 475 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55923,7 +56118,7 @@ var SkipUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 475 */ +/* 476 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55979,7 +56174,7 @@ var SkipWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 476 */ +/* 477 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56008,13 +56203,13 @@ function startWith() { /***/ }), -/* 477 */ +/* 478 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return subscribeOn; }); -/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(478); +/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(479); /** PURE_IMPORTS_START _observable_SubscribeOnObservable PURE_IMPORTS_END */ function subscribeOn(scheduler, delay) { @@ -56039,7 +56234,7 @@ var SubscribeOnOperator = /*@__PURE__*/ (function () { /***/ }), -/* 478 */ +/* 479 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56103,13 +56298,13 @@ var SubscribeOnObservable = /*@__PURE__*/ (function (_super) { /***/ }), -/* 479 */ +/* 480 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return switchAll; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(480); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(481); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(25); /** PURE_IMPORTS_START _switchMap,_util_identity PURE_IMPORTS_END */ @@ -56121,7 +56316,7 @@ function switchAll() { /***/ }), -/* 480 */ +/* 481 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56209,13 +56404,13 @@ var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 481 */ +/* 482 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return switchMapTo; }); -/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(480); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(481); /** PURE_IMPORTS_START _switchMap PURE_IMPORTS_END */ function switchMapTo(innerObservable, resultSelector) { @@ -56225,7 +56420,7 @@ function switchMapTo(innerObservable, resultSelector) { /***/ }), -/* 482 */ +/* 483 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56273,7 +56468,7 @@ var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 483 */ +/* 484 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56341,7 +56536,7 @@ var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 484 */ +/* 485 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56429,7 +56624,7 @@ var TapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 485 */ +/* 486 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56531,7 +56726,7 @@ var ThrottleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 486 */ +/* 487 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56540,7 +56735,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); -/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(485); +/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(486); /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ @@ -56629,7 +56824,7 @@ function dispatchNext(arg) { /***/ }), -/* 487 */ +/* 488 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56637,7 +56832,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return timeInterval; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TimeInterval", function() { return TimeInterval; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(55); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(447); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(448); /* harmony import */ var _observable_defer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(91); /* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(66); /** PURE_IMPORTS_START _scheduler_async,_scan,_observable_defer,_map PURE_IMPORTS_END */ @@ -56673,7 +56868,7 @@ var TimeInterval = /*@__PURE__*/ (function () { /***/ }), -/* 488 */ +/* 489 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56681,7 +56876,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return timeout; }); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(55); /* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(64); -/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(489); +/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(490); /* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(49); /** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ @@ -56698,7 +56893,7 @@ function timeout(due, scheduler) { /***/ }), -/* 489 */ +/* 490 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56706,7 +56901,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return timeoutWith; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(421); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(422); /* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(90); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_innerSubscribe PURE_IMPORTS_END */ @@ -56777,7 +56972,7 @@ var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 490 */ +/* 491 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56807,13 +57002,13 @@ var Timestamp = /*@__PURE__*/ (function () { /***/ }), -/* 491 */ +/* 492 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return toArray; }); -/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(446); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(447); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function toArrayReducer(arr, item, index) { @@ -56830,7 +57025,7 @@ function toArray() { /***/ }), -/* 492 */ +/* 493 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56908,7 +57103,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 493 */ +/* 494 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56998,7 +57193,7 @@ var WindowCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 494 */ +/* 495 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57168,7 +57363,7 @@ function dispatchWindowClose(state) { /***/ }), -/* 495 */ +/* 496 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57311,7 +57506,7 @@ var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 496 */ +/* 497 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57408,7 +57603,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 497 */ +/* 498 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57503,7 +57698,7 @@ var WithLatestFromSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 498 */ +/* 499 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57525,7 +57720,7 @@ function zip() { /***/ }), -/* 499 */ +/* 500 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57541,7 +57736,7 @@ function zipAll(project) { /***/ }), -/* 500 */ +/* 501 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -57550,7 +57745,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(163); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(144); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(146); -/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(501); +/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(280); /* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(502); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } @@ -57632,159 +57827,6 @@ function toArray(value) { return Array.isArray(value) ? value : [value]; } -/***/ }), -/* 501 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "renderProjectsTree", function() { return renderProjectsTree; }); -/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(113); -/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); -/* - * 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 projectKey = Symbol('__project'); -function renderProjectsTree(rootPath, projects) { - const projectsTree = buildProjectsTree(rootPath, projects); - return treeToString(createTreeStructure(projectsTree)); -} - -function treeToString(tree) { - return [tree.name].concat(childrenToStrings(tree.children, '')).join('\n'); -} - -function childrenToStrings(tree, treePrefix) { - if (tree === undefined) { - return []; - } - - let strings = []; - tree.forEach((node, index) => { - const isLastNode = tree.length - 1 === index; - const nodePrefix = isLastNode ? '└── ' : '├── '; - const childPrefix = isLastNode ? ' ' : '│ '; - const childrenPrefix = treePrefix + childPrefix; - strings.push(`${treePrefix}${nodePrefix}${node.name}`); - strings = strings.concat(childrenToStrings(node.children, childrenPrefix)); - }); - return strings; -} - -function createTreeStructure(tree) { - let name; - const children = []; - - for (const [dir, project] of tree.entries()) { - // This is a leaf node (aka a project) - if (typeof project === 'string') { - name = chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(project); - continue; - } // If there's only one project and the key indicates it's a leaf node, we - // know that we're at a package folder that contains a package.json, so we - // "inline it" so we don't get unnecessary levels, i.e. we'll just see - // `foo` instead of `foo -> foo`. - - - if (project.size === 1 && project.has(projectKey)) { - const projectName = project.get(projectKey); - children.push({ - children: [], - name: dirOrProjectName(dir, projectName) - }); - continue; - } - - const subtree = createTreeStructure(project); // If the name is specified, we know there's a package at the "root" of the - // subtree itself. - - if (subtree.name !== undefined) { - const projectName = subtree.name; - children.push({ - children: subtree.children, - name: dirOrProjectName(dir, projectName) - }); - continue; - } // Special-case whenever we have one child, so we don't get unnecessary - // folders in the output. E.g. instead of `foo -> bar -> baz` we get - // `foo/bar/baz` instead. - - - if (subtree.children && subtree.children.length === 1) { - const child = subtree.children[0]; - const newName = chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(path__WEBPACK_IMPORTED_MODULE_1___default.a.join(dir.toString(), child.name)); - children.push({ - children: child.children, - name: newName - }); - continue; - } - - children.push({ - children: subtree.children, - name: chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(dir.toString()) - }); - } - - return { - name, - children - }; -} - -function dirOrProjectName(dir, projectName) { - return dir === projectName ? chalk__WEBPACK_IMPORTED_MODULE_0___default.a.green(dir) : chalk__WEBPACK_IMPORTED_MODULE_0___default.a`{dim ${dir.toString()} ({reset.green ${projectName}})}`; -} - -function buildProjectsTree(rootPath, projects) { - const tree = new Map(); - - for (const project of projects.values()) { - if (rootPath === project.path) { - tree.set(projectKey, project.name); - } else { - const relativeProjectPath = path__WEBPACK_IMPORTED_MODULE_1___default.a.relative(rootPath, project.path); - addProjectToTree(tree, relativeProjectPath.split(path__WEBPACK_IMPORTED_MODULE_1___default.a.sep), project); - } - } - - return tree; -} - -function addProjectToTree(tree, pathParts, project) { - if (pathParts.length === 0) { - tree.set(projectKey, project.name); - } else { - const [currentDir, ...rest] = pathParts; - - if (!tree.has(currentDir)) { - tree.set(currentDir, new Map()); - } - - const subtree = tree.get(currentDir); - addProjectToTree(subtree, rest, project); - } -} - /***/ }), /* 502 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { @@ -57796,7 +57838,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(503); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(361); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(362); /* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(275); /* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(146); @@ -58088,7 +58130,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return buildProductionProjects; }); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(509); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(281); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(282); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); @@ -58239,7 +58281,7 @@ const os = __webpack_require__(121); const pAll = __webpack_require__(510); const arrify = __webpack_require__(512); const globby = __webpack_require__(513); -const isGlob = __webpack_require__(294); +const isGlob = __webpack_require__(295); const cpFile = __webpack_require__(713); const junk = __webpack_require__(723); const CpyError = __webpack_require__(724); @@ -58957,7 +58999,7 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); var globParent = __webpack_require__(521); -var isGlob = __webpack_require__(294); +var isGlob = __webpack_require__(295); var micromatch = __webpack_require__(524); var GLOBSTAR = '**'; /** @@ -59145,7 +59187,7 @@ module.exports = function globParent(str) { * Licensed under the MIT License. */ -var isExtglob = __webpack_require__(295); +var isExtglob = __webpack_require__(296); module.exports = function isGlob(str) { if (typeof str !== 'string' || str === '') { @@ -82800,7 +82842,7 @@ exports.flatten = flatten; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var merge2 = __webpack_require__(284); +var merge2 = __webpack_require__(285); /** * Merge multiple streams and propagate their errors into one stream in parallel. */ diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index 8ffd86b84bf76..944fcf5998637 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -4,6 +4,9 @@ "version": "1.0.0", "license": "Apache-2.0", "private": true, + "kibana": { + "devOnly": true + }, "scripts": { "build": "webpack", "kbn:watch": "webpack --watch --progress", diff --git a/packages/kbn-pm/src/commands/bootstrap.ts b/packages/kbn-pm/src/commands/bootstrap.ts index 0fa3f355ae9d6..9a8048d6fd106 100644 --- a/packages/kbn-pm/src/commands/bootstrap.ts +++ b/packages/kbn-pm/src/commands/bootstrap.ts @@ -26,7 +26,7 @@ import { ICommand } from './'; import { getAllChecksums } from '../utils/project_checksums'; import { BootstrapCacheFile } from '../utils/bootstrap_cache_file'; import { readYarnLock } from '../utils/yarn_lock'; -import { validateYarnLock } from '../utils/validate_yarn_lock'; +import { validateDependencies } from '../utils/validate_dependencies'; export const BootstrapCommand: ICommand = { description: 'Install dependencies and crosslink projects', @@ -59,7 +59,7 @@ export const BootstrapCommand: ICommand = { const yarnLock = await readYarnLock(kbn); if (options.validate) { - await validateYarnLock(kbn, yarnLock); + await validateDependencies(kbn, yarnLock); } await linkProjectExecutables(projects, projectGraph); diff --git a/packages/kbn-pm/src/utils/project.ts b/packages/kbn-pm/src/utils/project.ts index 8f45df52c7a2f..4e4d76544aa35 100644 --- a/packages/kbn-pm/src/utils/project.ts +++ b/packages/kbn-pm/src/utils/project.ts @@ -153,6 +153,10 @@ export class Project { return (this.json.kibana && this.json.kibana.clean) || {}; } + public isFlaggedAsDevOnly() { + return !!(this.json.kibana && this.json.kibana.devOnly); + } + public hasScript(name: string) { return name in this.scripts; } diff --git a/packages/kbn-pm/src/utils/projects_tree.ts b/packages/kbn-pm/src/utils/projects_tree.ts index c7a13ce2de348..4ba000bc1b158 100644 --- a/packages/kbn-pm/src/utils/projects_tree.ts +++ b/packages/kbn-pm/src/utils/projects_tree.ts @@ -29,7 +29,7 @@ export function renderProjectsTree(rootPath: string, projects: Map {} -function treeToString(tree: ITree) { +export function treeToString(tree: ITree) { return [tree.name].concat(childrenToStrings(tree.children, '')).join('\n'); } diff --git a/packages/kbn-pm/src/utils/validate_yarn_lock.ts b/packages/kbn-pm/src/utils/validate_dependencies.ts similarity index 77% rename from packages/kbn-pm/src/utils/validate_yarn_lock.ts rename to packages/kbn-pm/src/utils/validate_dependencies.ts index ec853a3a958fb..045d6332dcc29 100644 --- a/packages/kbn-pm/src/utils/validate_yarn_lock.ts +++ b/packages/kbn-pm/src/utils/validate_dependencies.ts @@ -20,14 +20,16 @@ // @ts-expect-error published types are useless import { stringify as stringifyLockfile } from '@yarnpkg/lockfile'; import dedent from 'dedent'; +import chalk from 'chalk'; import { writeFile } from './fs'; import { Kibana } from './kibana'; import { YarnLock } from './yarn_lock'; import { log } from './log'; import { Project } from './project'; +import { ITree, treeToString } from './projects_tree'; -export async function validateYarnLock(kbn: Kibana, yarnLock: YarnLock) { +export async function validateDependencies(kbn: Kibana, yarnLock: YarnLock) { // look through all of the packages in the yarn.lock file to see if // we have accidentally installed multiple lodash v4 versions const lodash4Versions = new Set(); @@ -157,5 +159,45 @@ export async function validateYarnLock(kbn: Kibana, yarnLock: YarnLock) { process.exit(1); } + // look for packages that have the the `kibana.devOnly` flag in their package.json + // and make sure they aren't included in the production dependencies of Kibana + const devOnlyProjectsInProduction = getDevOnlyProductionDepsTree(kbn, 'kibana'); + if (devOnlyProjectsInProduction) { + log.error(dedent` + Some of the packages in the production dependency chain for Kibana and X-Pack are + flagged with "kibana.devOnly" in their package.json. Please check changes made to + packages and their dependencies to ensure they don't end up in production. + + The devOnly dependencies that are being dependend on in production are: + + ${treeToString(devOnlyProjectsInProduction).split('\n').join('\n ')} + `); + + process.exit(1); + } + log.success('yarn.lock analysis completed without any issues'); } + +function getDevOnlyProductionDepsTree(kbn: Kibana, projectName: string) { + const project = kbn.getProject(projectName); + const childProjectNames = [ + ...Object.keys(project.productionDependencies).filter((name) => kbn.hasProject(name)), + ...(projectName === 'kibana' ? ['x-pack'] : []), + ]; + + const children = childProjectNames + .map((n) => getDevOnlyProductionDepsTree(kbn, n)) + .filter((t): t is ITree => !!t); + + if (!children.length && !project.isFlaggedAsDevOnly()) { + return; + } + + const tree: ITree = { + name: project.isFlaggedAsDevOnly() ? chalk.red.bold(projectName) : projectName, + children, + }; + + return tree; +} diff --git a/packages/kbn-release-notes/package.json b/packages/kbn-release-notes/package.json index 268530c22399a..e3306b7a54917 100644 --- a/packages/kbn-release-notes/package.json +++ b/packages/kbn-release-notes/package.json @@ -3,6 +3,9 @@ "version": "1.0.0", "license": "Apache-2.0", "main": "target/index.js", + "kibana": { + "devOnly": true + }, "scripts": { "kbn:bootstrap": "tsc", "kbn:watch": "tsc --watch" diff --git a/packages/kbn-std/package.json b/packages/kbn-std/package.json index a931dd3f3154d..8a5e885c456cd 100644 --- a/packages/kbn-std/package.json +++ b/packages/kbn-std/package.json @@ -9,12 +9,12 @@ "build": "tsc", "kbn:bootstrap": "yarn build" }, + "dependencies": { + "lodash": "^4.17.20" + }, "devDependencies": { + "@kbn/utility-types": "1.0.0", "typescript": "4.0.2", "tsd": "^0.13.1" - }, - "dependencies": { - "@kbn/utility-types": "1.0.0", - "lodash": "^4.17.20" } } diff --git a/packages/kbn-storybook/package.json b/packages/kbn-storybook/package.json index 58359159e950d..5c57f6893d0c8 100644 --- a/packages/kbn-storybook/package.json +++ b/packages/kbn-storybook/package.json @@ -4,6 +4,9 @@ "private": true, "license": "Apache-2.0", "main": "./target/index.js", + "kibana": { + "devOnly": true + }, "dependencies": { "@kbn/dev-utils": "1.0.0", "@storybook/addon-actions": "^6.0.16", diff --git a/packages/kbn-telemetry-tools/package.json b/packages/kbn-telemetry-tools/package.json index 4318cbcf2ec4e..cda2998901d56 100644 --- a/packages/kbn-telemetry-tools/package.json +++ b/packages/kbn-telemetry-tools/package.json @@ -4,6 +4,9 @@ "license": "Apache-2.0", "main": "./target/index.js", "private": true, + "kibana": { + "devOnly": true + }, "scripts": { "build": "babel src --out-dir target --delete-dir-on-start --extensions .ts --source-maps=inline", "kbn:bootstrap": "yarn build", diff --git a/packages/kbn-test-subj-selector/package.json b/packages/kbn-test-subj-selector/package.json index 82a26dc4807be..b823c68f9560b 100755 --- a/packages/kbn-test-subj-selector/package.json +++ b/packages/kbn-test-subj-selector/package.json @@ -5,5 +5,8 @@ "main": "index.js", "keywords": [], "author": "Spencer Alger ", - "license": "Apache-2.0" + "license": "Apache-2.0", + "kibana": { + "devOnly": true + } } diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index 8422c34c9ed08..24096a41a5fdd 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -9,6 +9,9 @@ "kbn:bootstrap": "yarn build", "kbn:watch": "yarn build --watch" }, + "kibana": { + "devOnly": true + }, "devDependencies": { "@babel/cli": "^7.10.5", "@jest/types": "^26.5.2", diff --git a/packages/kbn-utility-types/package.json b/packages/kbn-utility-types/package.json index d1d7a1c0397cf..6b531efcebace 100644 --- a/packages/kbn-utility-types/package.json +++ b/packages/kbn-utility-types/package.json @@ -5,6 +5,9 @@ "license": "Apache-2.0", "main": "target", "types": "target/index.d.ts", + "kibana": { + "devOnly": true + }, "scripts": { "build": "tsc", "kbn:bootstrap": "tsc", From d10d70b2ac67a9aa470bde0405192daacff277cf Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Thu, 15 Oct 2020 13:02:20 -0400 Subject: [PATCH 099/128] [Ingest Manager] Better validation of registry urls (#80685) --- x-pack/plugins/ingest_manager/server/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/index.ts b/x-pack/plugins/ingest_manager/server/index.ts index e13c023d0d11a..a8b986be048ae 100644 --- a/x-pack/plugins/ingest_manager/server/index.ts +++ b/x-pack/plugins/ingest_manager/server/index.ts @@ -30,8 +30,8 @@ export const config: PluginConfigDescriptor = { ], schema: schema.object({ enabled: schema.boolean({ defaultValue: true }), - registryUrl: schema.maybe(schema.uri()), - registryProxyUrl: schema.maybe(schema.uri()), + registryUrl: schema.maybe(schema.uri({ scheme: ['http', 'https'] })), + registryProxyUrl: schema.maybe(schema.uri({ scheme: ['http', 'https'] })), agents: schema.object({ enabled: schema.boolean({ defaultValue: true }), tlsCheckDisabled: schema.boolean({ defaultValue: false }), From 41db7d175c282a14d038b3718ad0150bd96cc1a3 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Thu, 15 Oct 2020 13:24:17 -0400 Subject: [PATCH 100/128] [SECURITY SOLUTION] bug styling (#80572) * remove beta * fix i18n * fix cypress tests * forget to save * x-pack test --- .../alerts_detection_rules_custom.spec.ts | 4 ++-- .../alerts_detection_rules_eql.spec.ts | 2 +- .../alerts_detection_rules_ml.spec.ts | 2 +- .../alerts_detection_rules_override.spec.ts | 2 +- .../alerts_detection_rules_threshold.spec.ts | 2 +- .../cypress/integration/cases.spec.ts | 2 +- .../components/case_header_page/index.tsx | 9 -------- .../case_header_page/translations.ts | 22 ------------------- .../public/common/store/sourcerer/model.ts | 2 +- .../detection_engine_header_page/index.tsx | 9 -------- .../translations.ts | 22 ------------------- .../pages/endpoint_hosts/view/index.tsx | 2 +- .../pages/policy/view/policy_list.tsx | 2 +- .../trusted_apps/view/trusted_apps_page.tsx | 2 +- .../translations/translations/ja-JP.json | 4 ---- .../translations/translations/zh-CN.json | 4 ---- .../apps/endpoint/endpoint_list.ts | 2 +- .../apps/endpoint/policy_list.ts | 2 +- .../apps/endpoint/trusted_apps_list.ts | 2 +- 19 files changed, 14 insertions(+), 84 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/cases/components/case_header_page/translations.ts delete mode 100644 x-pack/plugins/security_solution/public/detections/components/detection_engine_header_page/translations.ts diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts index 28889920e00e5..41665cf6d20a4 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts @@ -168,7 +168,7 @@ describe('Custom detection rules creation', () => { goToRuleDetails(); - cy.get(RULE_NAME_HEADER).should('have.text', `${newRule.name} Beta`); + cy.get(RULE_NAME_HEADER).should('have.text', `${newRule.name}`); cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newRule.description); cy.get(ABOUT_DETAILS).within(() => { getDetails(SEVERITY_DETAILS).should('have.text', newRule.severity); @@ -328,7 +328,7 @@ describe('Custom detection rules deletion and edition', () => { fillAboutRule(editedRule); saveEditedRule(); - cy.get(RULE_NAME_HEADER).should('have.text', `${editedRule.name} Beta`); + cy.get(RULE_NAME_HEADER).should('have.text', `${editedRule.name}`); cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', editedRule.description); cy.get(ABOUT_DETAILS).within(() => { getDetails(SEVERITY_DETAILS).should('have.text', editedRule.severity); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts index 252ffb6c8c660..5502f35d6f0f8 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts @@ -131,7 +131,7 @@ describe.skip('Detection rules, EQL', () => { goToRuleDetails(); - cy.get(RULE_NAME_HEADER).should('have.text', `${eqlRule.name} Beta`); + cy.get(RULE_NAME_HEADER).should('have.text', `${eqlRule.name}`); cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', eqlRule.description); cy.get(ABOUT_DETAILS).within(() => { getDetails(SEVERITY_DETAILS).should('have.text', eqlRule.severity); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts index 49ec6381cbc89..0f34e7d71e5fa 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_ml.spec.ts @@ -115,7 +115,7 @@ describe('Detection rules, machine learning', () => { goToRuleDetails(); - cy.get(RULE_NAME_HEADER).should('have.text', `${machineLearningRule.name} Beta`); + cy.get(RULE_NAME_HEADER).should('have.text', `${machineLearningRule.name}`); cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', machineLearningRule.description); cy.get(ABOUT_DETAILS).within(() => { getDetails(SEVERITY_DETAILS).should('have.text', machineLearningRule.severity); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts index abc873f2df0ee..edf7305f6916e 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_override.spec.ts @@ -132,7 +132,7 @@ describe('Detection rules, override', () => { goToRuleDetails(); - cy.get(RULE_NAME_HEADER).should('have.text', `${newOverrideRule.name} Beta`); + cy.get(RULE_NAME_HEADER).should('have.text', `${newOverrideRule.name}`); cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newOverrideRule.description); cy.get(ABOUT_DETAILS).within(() => { getDetails(SEVERITY_DETAILS).should('have.text', newOverrideRule.severity); diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts index 9d988a46662fa..5095e856e3f65 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_threshold.spec.ts @@ -129,7 +129,7 @@ describe('Detection rules, threshold', () => { goToRuleDetails(); - cy.get(RULE_NAME_HEADER).should('have.text', `${newThresholdRule.name} Beta`); + cy.get(RULE_NAME_HEADER).should('have.text', `${newThresholdRule.name}`); cy.get(ABOUT_RULE_DESCRIPTION).should('have.text', newThresholdRule.description); cy.get(ABOUT_DETAILS).within(() => { getDetails(SEVERITY_DETAILS).should('have.text', newThresholdRule.severity); diff --git a/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts b/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts index a45b1fd18a4b6..ec3887ad72625 100644 --- a/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/cases.spec.ts @@ -60,7 +60,7 @@ describe('Cases', () => { createNewCaseWithTimeline(case1); backToCases(); - cy.get(ALL_CASES_PAGE_TITLE).should('have.text', 'Cases Beta'); + cy.get(ALL_CASES_PAGE_TITLE).should('have.text', 'Cases'); cy.get(ALL_CASES_OPEN_CASES_STATS).should('have.text', 'Open cases1'); cy.get(ALL_CASES_CLOSED_CASES_STATS).should('have.text', 'Closed cases0'); cy.get(ALL_CASES_OPEN_CASES_COUNT).should('have.text', 'Open cases (1)'); diff --git a/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx b/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx index 4f7b17a730b6a..5e4db16d6d9cb 100644 --- a/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/case_header_page/index.tsx @@ -7,18 +7,9 @@ import React from 'react'; import { HeaderPage, HeaderPageProps } from '../../../common/components/header_page'; -import * as i18n from './translations'; const CaseHeaderPageComponent: React.FC = (props) => ( ); -CaseHeaderPageComponent.defaultProps = { - badgeOptions: { - beta: true, - text: i18n.PAGE_BADGE_LABEL, - tooltip: i18n.PAGE_BADGE_TOOLTIP, - }, -}; - export const CaseHeaderPage = React.memo(CaseHeaderPageComponent); diff --git a/x-pack/plugins/security_solution/public/cases/components/case_header_page/translations.ts b/x-pack/plugins/security_solution/public/cases/components/case_header_page/translations.ts deleted file mode 100644 index 8cdc287b1584c..0000000000000 --- a/x-pack/plugins/security_solution/public/cases/components/case_header_page/translations.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. - */ - -import { i18n } from '@kbn/i18n'; - -export const PAGE_BADGE_LABEL = i18n.translate( - 'xpack.securitySolution.case.caseView.pageBadgeLabel', - { - defaultMessage: 'Beta', - } -); - -export const PAGE_BADGE_TOOLTIP = i18n.translate( - 'xpack.securitySolution.case.caseView.pageBadgeTooltip', - { - defaultMessage: - 'Case Workflow is still in beta. Please help us improve by reporting issues or bugs in the Kibana repo.', - } -); diff --git a/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts b/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts index 93f7ff95dfb00..18aa4e65a03cf 100644 --- a/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts +++ b/x-pack/plugins/security_solution/public/common/store/sourcerer/model.ts @@ -56,7 +56,7 @@ export const initSourcererScope = { errorMessage: null, indexPattern: EMPTY_INDEX_PATTERN, indicesExist: true, - loading: true, + loading: false, selectedPatterns: [], }; diff --git a/x-pack/plugins/security_solution/public/detections/components/detection_engine_header_page/index.tsx b/x-pack/plugins/security_solution/public/detections/components/detection_engine_header_page/index.tsx index 1a2deb059ad4f..293ed4d488c7d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/detection_engine_header_page/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/detection_engine_header_page/index.tsx @@ -7,20 +7,11 @@ import React from 'react'; import { HeaderPage, HeaderPageProps } from '../../../common/components/header_page'; -import * as i18n from './translations'; const DetectionEngineHeaderPageComponent: React.FC = (props) => ( ); -DetectionEngineHeaderPageComponent.defaultProps = { - badgeOptions: { - beta: true, - text: i18n.PAGE_BADGE_LABEL, - tooltip: i18n.PAGE_BADGE_TOOLTIP, - }, -}; - export const DetectionEngineHeaderPage = React.memo(DetectionEngineHeaderPageComponent); DetectionEngineHeaderPage.displayName = 'DetectionEngineHeaderPage'; diff --git a/x-pack/plugins/security_solution/public/detections/components/detection_engine_header_page/translations.ts b/x-pack/plugins/security_solution/public/detections/components/detection_engine_header_page/translations.ts deleted file mode 100644 index f59be16923805..0000000000000 --- a/x-pack/plugins/security_solution/public/detections/components/detection_engine_header_page/translations.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. - */ - -import { i18n } from '@kbn/i18n'; - -export const PAGE_BADGE_LABEL = i18n.translate( - 'xpack.securitySolution.detectionEngine.headerPage.pageBadgeLabel', - { - defaultMessage: 'Beta', - } -); - -export const PAGE_BADGE_TOOLTIP = i18n.translate( - 'xpack.securitySolution.detectionEngine.headerPage.pageBadgeTooltip', - { - defaultMessage: - 'Alerts is still in beta. Please help us improve by reporting issues or bugs in the Kibana repo.', - } -); diff --git a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx index 36c5b0d1037e5..c5d3c3c25313d 100644 --- a/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/endpoint_hosts/view/index.tsx @@ -561,7 +561,7 @@ export const EndpointList = () => { return ( { )} { return ( { it('finds page title', async () => { const title = await testSubjects.getVisibleText('header-page-title'); - expect(title).to.equal('Endpoints BETA'); + expect(title).to.equal('Endpoints'); }); it('displays table data', async () => { diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts index 49a7a2155a700..70958d7ca7631 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_list.ts @@ -30,7 +30,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); it('displays page title', async () => { const policyTitle = await testSubjects.getVisibleText('header-page-title'); - expect(policyTitle).to.equal('Policies BETA'); + expect(policyTitle).to.equal('Policies'); }); it('shows header create policy button', async () => { const createButtonTitle = await testSubjects.getVisibleText('headerCreateNewPolicyButton'); diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts index 78ef1bc894e0b..3a0f0b91bddb3 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/trusted_apps_list.ts @@ -20,7 +20,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('should show page title', async () => { expect(await testSubjects.getVisibleText('header-page-title')).to.equal( - 'Trusted Applications BETA' + 'Trusted Applications' ); }); From 6b8e8a5b468746f14beb6e17264e5b9c5bebc67b Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Thu, 15 Oct 2020 18:46:11 +0100 Subject: [PATCH 101/128] [Security Solution] Update button text according to status (#80389) * update button text according to status * remove unused translations * fix functional test * fixup * fix unit test * update unit tests * update unit test --- .../load_empty_prompt.test.tsx | 96 ++++++++++- .../pre_packaged_rules/load_empty_prompt.tsx | 42 +++-- .../rules/pre_packaged_rules/translations.ts | 7 - .../detection_engine/rules/translations.ts | 54 ++++++ .../rules/use_pre_packaged_rules.test.tsx | 12 ++ .../rules/use_pre_packaged_rules.tsx | 128 +++++++++++++- .../detection_engine/rules/index.test.tsx | 163 +++++++++++++++++- .../pages/detection_engine/rules/index.tsx | 68 +++----- .../detection_engine/rules/translations.ts | 40 ----- .../translations/translations/ja-JP.json | 3 +- .../translations/translations/zh-CN.json | 3 +- 11 files changed, 492 insertions(+), 124 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/load_empty_prompt.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/load_empty_prompt.test.tsx index a41da908085bc..75ab1524c5c06 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/load_empty_prompt.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/load_empty_prompt.test.tsx @@ -5,10 +5,12 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { waitFor } from '@testing-library/react'; +import { shallow, mount, ReactWrapper } from 'enzyme'; import '../../../../common/mock/match_media'; import { PrePackagedRulesPrompt } from './load_empty_prompt'; +import { getPrePackagedRulesStatus } from '../../../containers/detection_engine/rules/api'; jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); @@ -23,16 +25,94 @@ jest.mock('react-router-dom', () => { jest.mock('../../../../common/components/link_to'); +jest.mock('../../../containers/detection_engine/rules/api', () => ({ + getPrePackagedRulesStatus: jest.fn().mockResolvedValue({ + rules_not_installed: 0, + rules_installed: 0, + rules_not_updated: 0, + timelines_not_installed: 0, + timelines_installed: 0, + timelines_not_updated: 0, + }), + createPrepackagedRules: jest.fn(), +})); + +const props = { + createPrePackagedRules: jest.fn(), + loading: false, + userHasNoPermissions: false, + 'data-test-subj': 'load-prebuilt-rules', +}; + describe('PrePackagedRulesPrompt', () => { it('renders correctly', () => { - const wrapper = shallow( - - ); + const wrapper = shallow(); expect(wrapper.find('EmptyPrompt')).toHaveLength(1); }); }); + +describe('LoadPrebuiltRulesAndTemplatesButton', () => { + it('renders correct button with correct text - Load Elastic prebuilt rules and timeline templates', async () => { + (getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({ + rules_not_installed: 3, + rules_installed: 0, + rules_not_updated: 0, + timelines_not_installed: 3, + timelines_installed: 0, + timelines_not_updated: 0, + }); + + const wrapper: ReactWrapper = mount(); + await waitFor(() => { + wrapper.update(); + + expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').exists()).toEqual(true); + expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').last().text()).toEqual( + 'Load Elastic prebuilt rules and timeline templates' + ); + }); + }); + + it('renders correct button with correct text - Load Elastic prebuilt rules', async () => { + (getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({ + rules_not_installed: 3, + rules_installed: 0, + rules_not_updated: 0, + timelines_not_installed: 0, + timelines_installed: 0, + timelines_not_updated: 0, + }); + + const wrapper: ReactWrapper = mount(); + await waitFor(() => { + wrapper.update(); + + expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').exists()).toEqual(true); + expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').last().text()).toEqual( + 'Load Elastic prebuilt rules' + ); + }); + }); + + it('renders correct button with correct text - Load Elastic prebuilt timeline templates', async () => { + (getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({ + rules_not_installed: 0, + rules_installed: 0, + rules_not_updated: 0, + timelines_not_installed: 3, + timelines_installed: 0, + timelines_not_updated: 0, + }); + + const wrapper: ReactWrapper = mount(); + await waitFor(() => { + wrapper.update(); + + expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').exists()).toEqual(true); + expect(wrapper.find('[data-test-subj="load-prebuilt-rules"]').last().text()).toEqual( + 'Load Elastic prebuilt timeline templates' + ); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/load_empty_prompt.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/load_empty_prompt.tsx index 99968cd4d9fe8..64b3cfa3aa991 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/load_empty_prompt.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/pre_packaged_rules/load_empty_prompt.tsx @@ -4,8 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui'; -import React, { memo, useCallback } from 'react'; +import { EuiEmptyPrompt, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import React, { memo, useCallback, useMemo } from 'react'; import styled from 'styled-components'; import { useHistory } from 'react-router-dom'; @@ -14,6 +14,8 @@ import * as i18n from './translations'; import { LinkButton } from '../../../../common/components/links'; import { SecurityPageName } from '../../../../app/types'; import { useFormatUrl } from '../../../../common/components/link_to'; +import { usePrePackagedRules } from '../../../containers/detection_engine/rules'; +import { useUserData } from '../../user_info'; const EmptyPrompt = styled(EuiEmptyPrompt)` align-self: center; /* Corrects horizontal centering in IE11 */ @@ -46,24 +48,36 @@ const PrePackagedRulesPromptComponent: React.FC = ( [history] ); + const [ + { isSignalIndexExists, isAuthenticated, hasEncryptionKey, canUserCRUD, hasIndexWrite }, + ] = useUserData(); + + const { getLoadPrebuiltRulesAndTemplatesButton } = usePrePackagedRules({ + canUserCRUD, + hasIndexWrite, + isSignalIndexExists, + isAuthenticated, + hasEncryptionKey, + }); + + const loadPrebuiltRulesAndTemplatesButton = useMemo( + () => + getLoadPrebuiltRulesAndTemplatesButton({ + isDisabled: userHasNoPermissions, + onClick: handlePreBuiltCreation, + fill: true, + 'data-test-subj': 'load-prebuilt-rules', + }), + [getLoadPrebuiltRulesAndTemplatesButton, handlePreBuiltCreation, userHasNoPermissions] + ); + return ( {i18n.PRE_BUILT_TITLE}} body={

{i18n.PRE_BUILT_MSG}

} actions={ - - - {i18n.PRE_BUILT_ACTION} - - + {loadPrebuiltRulesAndTemplatesButton} + i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton', + { + values: { missingRules }, + defaultMessage: + 'Install {missingRules} Elastic prebuilt {missingRules, plural, =1 {rule} other {rules}} ', + } + ); + +export const RELOAD_MISSING_PREPACKAGED_TIMELINES = (missingTimelines: number) => + i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedTimelinesButton', + { + values: { missingTimelines }, + defaultMessage: + 'Install {missingTimelines} Elastic prebuilt {missingTimelines, plural, =1 {timeline} other {timelines}} ', + } + ); + +export const RELOAD_MISSING_PREPACKAGED_RULES_AND_TIMELINES = ( + missingRules: number, + missingTimelines: number +) => + i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton', + { + values: { missingRules, missingTimelines }, + defaultMessage: + 'Install {missingRules} Elastic prebuilt {missingRules, plural, =1 {rule} other {rules}} and {missingTimelines} Elastic prebuilt {missingTimelines, plural, =1 {timeline} other {timelines}} ', + } + ); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx index 92d46a785b034..7f74e92584494 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.test.tsx @@ -32,6 +32,10 @@ describe('usePrePackagedRules', () => { await waitForNextUpdate(); expect(result.current).toEqual({ + getLoadPrebuiltRulesAndTemplatesButton: + result.current.getLoadPrebuiltRulesAndTemplatesButton, + getReloadPrebuiltRulesAndTemplatesButton: + result.current.getReloadPrebuiltRulesAndTemplatesButton, createPrePackagedRules: null, loading: true, loadingCreatePrePackagedRules: false, @@ -63,6 +67,10 @@ describe('usePrePackagedRules', () => { await waitForNextUpdate(); expect(result.current).toEqual({ + getLoadPrebuiltRulesAndTemplatesButton: + result.current.getLoadPrebuiltRulesAndTemplatesButton, + getReloadPrebuiltRulesAndTemplatesButton: + result.current.getReloadPrebuiltRulesAndTemplatesButton, createPrePackagedRules: result.current.createPrePackagedRules, loading: false, loadingCreatePrePackagedRules: false, @@ -100,6 +108,10 @@ describe('usePrePackagedRules', () => { expect(resp).toEqual(true); expect(spyOnCreatePrepackagedRules).toHaveBeenCalled(); expect(result.current).toEqual({ + getLoadPrebuiltRulesAndTemplatesButton: + result.current.getLoadPrebuiltRulesAndTemplatesButton, + getReloadPrebuiltRulesAndTemplatesButton: + result.current.getReloadPrebuiltRulesAndTemplatesButton, createPrePackagedRules: result.current.createPrePackagedRules, loading: false, loadingCreatePrePackagedRules: false, diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.tsx index d82d97883a3d0..4d19f44bcfc84 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_pre_packaged_rules.tsx @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { useEffect, useState } from 'react'; +import React, { useCallback, useMemo, useState, useEffect } from 'react'; +import { EuiButton } from '@elastic/eui'; import { errorToToaster, @@ -14,6 +15,11 @@ import { import { getPrePackagedRulesStatus, createPrepackagedRules } from './api'; import * as i18n from './translations'; +import { + getPrePackagedRuleStatus, + getPrePackagedTimelineStatus, +} from '../../../pages/detection_engine/rules/helpers'; + type Func = () => void; export type CreatePreBuiltRules = () => Promise; @@ -23,6 +29,23 @@ interface ReturnPrePackagedTimelines { timelinesNotUpdated: number | null; } +type GetLoadPrebuiltRulesAndTemplatesButton = (args: { + isDisabled: boolean; + onClick: () => void; + fill?: boolean; + 'data-test-subj'?: string; +}) => React.ReactNode | null; + +type GetReloadPrebuiltRulesAndTemplatesButton = ({ + isDisabled, + onClick, + fill, +}: { + isDisabled: boolean; + onClick: () => void; + fill?: boolean; +}) => React.ReactNode | null; + interface ReturnPrePackagedRules { createPrePackagedRules: null | CreatePreBuiltRules; loading: boolean; @@ -32,6 +55,8 @@ interface ReturnPrePackagedRules { rulesInstalled: number | null; rulesNotInstalled: number | null; rulesNotUpdated: number | null; + getLoadPrebuiltRulesAndTemplatesButton: GetLoadPrebuiltRulesAndTemplatesButton; + getReloadPrebuiltRulesAndTemplatesButton: GetReloadPrebuiltRulesAndTemplatesButton; } export type ReturnPrePackagedRulesAndTimelines = ReturnPrePackagedRules & @@ -89,7 +114,6 @@ export const usePrePackagedRules = ({ const [loadingCreatePrePackagedRules, setLoadingCreatePrePackagedRules] = useState(false); const [loading, setLoading] = useState(true); const [, dispatchToaster] = useStateToaster(); - useEffect(() => { let isSubscribed = true; const abortCtrl = new AbortController(); @@ -100,7 +124,6 @@ export const usePrePackagedRules = ({ const prePackagedRuleStatusResponse = await getPrePackagedRulesStatus({ signal: abortCtrl.signal, }); - if (isSubscribed) { setPrepackagedDataStatus({ createPrePackagedRules: createElasticRules, @@ -225,9 +248,108 @@ export const usePrePackagedRules = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [canUserCRUD, hasIndexWrite, isAuthenticated, hasEncryptionKey, isSignalIndexExists]); + const prePackagedRuleStatus = useMemo( + () => + getPrePackagedRuleStatus( + prepackagedDataStatus.rulesInstalled, + prepackagedDataStatus.rulesNotInstalled, + prepackagedDataStatus.rulesNotUpdated + ), + [ + prepackagedDataStatus.rulesInstalled, + prepackagedDataStatus.rulesNotInstalled, + prepackagedDataStatus.rulesNotUpdated, + ] + ); + + const prePackagedTimelineStatus = useMemo( + () => + getPrePackagedTimelineStatus( + prepackagedDataStatus.timelinesInstalled, + prepackagedDataStatus.timelinesNotInstalled, + prepackagedDataStatus.timelinesNotUpdated + ), + [ + prepackagedDataStatus.timelinesInstalled, + prepackagedDataStatus.timelinesNotInstalled, + prepackagedDataStatus.timelinesNotUpdated, + ] + ); + const getLoadPrebuiltRulesAndTemplatesButton = useCallback( + ({ isDisabled, onClick, fill, 'data-test-subj': dataTestSubj = 'loadPrebuiltRulesBtn' }) => { + return prePackagedRuleStatus === 'ruleNotInstalled' || + prePackagedTimelineStatus === 'timelinesNotInstalled' ? ( + + {prePackagedRuleStatus === 'ruleNotInstalled' && + prePackagedTimelineStatus === 'timelinesNotInstalled' && + i18n.LOAD_PREPACKAGED_RULES_AND_TEMPLATES} + + {prePackagedRuleStatus === 'ruleNotInstalled' && + prePackagedTimelineStatus !== 'timelinesNotInstalled' && + i18n.LOAD_PREPACKAGED_RULES} + + {prePackagedRuleStatus !== 'ruleNotInstalled' && + prePackagedTimelineStatus === 'timelinesNotInstalled' && + i18n.LOAD_PREPACKAGED_TIMELINE_TEMPLATES} + + ) : null; + }, + [loadingCreatePrePackagedRules, prePackagedRuleStatus, prePackagedTimelineStatus] + ); + + const getMissingRulesOrTimelinesButtonTitle = useCallback( + (missingRules: number, missingTimelines: number) => { + if (missingRules > 0 && missingTimelines === 0) + return i18n.RELOAD_MISSING_PREPACKAGED_RULES(missingRules); + else if (missingRules === 0 && missingTimelines > 0) + return i18n.RELOAD_MISSING_PREPACKAGED_TIMELINES(missingTimelines); + else if (missingRules > 0 && missingTimelines > 0) + return i18n.RELOAD_MISSING_PREPACKAGED_RULES_AND_TIMELINES(missingRules, missingTimelines); + }, + [] + ); + + const getReloadPrebuiltRulesAndTemplatesButton = useCallback( + ({ isDisabled, onClick, fill = false }) => { + return prePackagedRuleStatus === 'someRuleUninstall' || + prePackagedTimelineStatus === 'someTimelineUninstall' ? ( + + {getMissingRulesOrTimelinesButtonTitle( + prepackagedDataStatus.rulesNotInstalled ?? 0, + prepackagedDataStatus.timelinesNotInstalled ?? 0 + )} + + ) : null; + }, + [ + getMissingRulesOrTimelinesButtonTitle, + loadingCreatePrePackagedRules, + prePackagedRuleStatus, + prePackagedTimelineStatus, + prepackagedDataStatus.rulesNotInstalled, + prepackagedDataStatus.timelinesNotInstalled, + ] + ); + return { loading, loadingCreatePrePackagedRules, ...prepackagedDataStatus, + getLoadPrebuiltRulesAndTemplatesButton, + getReloadPrebuiltRulesAndTemplatesButton, }; }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.test.tsx index 886a24dd7cbe8..58e61c5b47486 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.test.tsx @@ -5,13 +5,14 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { shallow, mount, ReactWrapper } from 'enzyme'; import '../../../../common/mock/match_media'; import { RulesPage } from './index'; import { useUserData } from '../../../components/user_info'; -import { usePrePackagedRules } from '../../../containers/detection_engine/rules'; - +import { waitFor } from '@testing-library/react'; +import { TestProviders } from '../../../../common/mock'; +import { getPrePackagedRulesStatus } from '../../../containers/detection_engine/rules/api'; jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); @@ -26,16 +27,164 @@ jest.mock('react-router-dom', () => { jest.mock('../../../containers/detection_engine/lists/use_lists_config'); jest.mock('../../../../common/components/link_to'); jest.mock('../../../components/user_info'); -jest.mock('../../../containers/detection_engine/rules'); +jest.mock('../../../../common/components/toasters', () => { + const actual = jest.requireActual('../../../../common/components/toasters'); + return { + ...actual, + errorToToaster: jest.fn(), + useStateToaster: jest.fn().mockReturnValue([jest.fn(), jest.fn()]), + displaySuccessToast: jest.fn(), + }; +}); + +jest.mock('../../../containers/detection_engine/rules/api', () => ({ + getPrePackagedRulesStatus: jest.fn().mockResolvedValue({ + rules_not_installed: 0, + rules_installed: 0, + rules_not_updated: 0, + timelines_not_installed: 0, + timelines_installed: 0, + timelines_not_updated: 0, + }), + createPrepackagedRules: jest.fn(), +})); + +jest.mock('../../../../common/lib/kibana', () => { + return { + useToast: jest.fn(), + useHttp: jest.fn(), + }; +}); + +jest.mock('../../../components/value_lists_management_modal', () => { + return { + ValueListsModal: jest.fn().mockReturnValue(
), + }; +}); + +jest.mock('./all', () => { + return { + AllRules: jest.fn().mockReturnValue(
), + }; +}); + +jest.mock('../../../../common/utils/route/spy_routes', () => { + return { + SpyRoute: jest.fn().mockReturnValue(
), + }; +}); + +jest.mock('../../../components/rules/pre_packaged_rules/update_callout', () => { + return { + UpdatePrePackagedRulesCallOut: jest.fn().mockReturnValue(
), + }; +}); describe('RulesPage', () => { beforeAll(() => { (useUserData as jest.Mock).mockReturnValue([{}]); - (usePrePackagedRules as jest.Mock).mockReturnValue({}); }); - it('renders correctly', () => { + + it('renders AllRules', () => { const wrapper = shallow(); + expect(wrapper.find('[data-test-subj="all-rules"]').exists()).toEqual(true); + }); + + it('renders correct button with correct text - Load Elastic prebuilt rules and timeline templates', async () => { + (getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({ + rules_not_installed: 3, + rules_installed: 0, + rules_not_updated: 0, + timelines_not_installed: 3, + timelines_installed: 0, + timelines_not_updated: 0, + }); + + const wrapper: ReactWrapper = mount( + + + + ); + await waitFor(() => { + wrapper.update(); + + expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').exists()).toEqual(true); + expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').last().text()).toEqual( + 'Load Elastic prebuilt rules and timeline templates' + ); + }); + }); + + it('renders correct button with correct text - Load Elastic prebuilt rules', async () => { + (getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({ + rules_not_installed: 3, + rules_installed: 0, + rules_not_updated: 0, + timelines_not_installed: 0, + timelines_installed: 0, + timelines_not_updated: 0, + }); + + const wrapper: ReactWrapper = mount( + + + + ); + + await waitFor(() => { + wrapper.update(); + + expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').exists()).toEqual(true); + expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').last().text()).toEqual( + 'Load Elastic prebuilt rules' + ); + }); + }); + + it('renders correct button with correct text - Load Elastic prebuilt timeline templates', async () => { + (getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({ + rules_not_installed: 0, + rules_installed: 0, + rules_not_updated: 0, + timelines_not_installed: 3, + timelines_installed: 0, + timelines_not_updated: 0, + }); + + const wrapper: ReactWrapper = mount( + + + + ); + + await waitFor(() => { + wrapper.update(); + expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').exists()).toEqual(true); + expect(wrapper.find('[data-test-subj="loadPrebuiltRulesBtn"]').last().text()).toEqual( + 'Load Elastic prebuilt timeline templates' + ); + }); + }); + + it('renders a callout - Update Elastic prebuilt rules', async () => { + (getPrePackagedRulesStatus as jest.Mock).mockResolvedValue({ + rules_not_installed: 2, + rules_installed: 1, + rules_not_updated: 1, + timelines_not_installed: 0, + timelines_installed: 0, + timelines_not_updated: 0, + }); + + const wrapper: ReactWrapper = mount( + + + + ); - expect(wrapper.find('AllRules')).toHaveLength(1); + await waitFor(() => { + wrapper.update(); + expect(wrapper.find('[data-test-subj="update-callout-button"]').exists()).toEqual(true); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx index 53c82569f94ae..8c7cb6a0d9284 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/index.tsx @@ -5,7 +5,7 @@ */ import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; -import React, { useCallback, useRef, useState } from 'react'; +import React, { useCallback, useMemo, useRef, useState } from 'react'; import { useHistory } from 'react-router-dom'; import { usePrePackagedRules, importRules } from '../../../containers/detection_engine/rules'; @@ -70,6 +70,8 @@ const RulesPageComponent: React.FC = () => { timelinesInstalled, timelinesNotInstalled, timelinesNotUpdated, + getLoadPrebuiltRulesAndTemplatesButton, + getReloadPrebuiltRulesAndTemplatesButton, } = usePrePackagedRules({ canUserCRUD, hasIndexWrite, @@ -113,18 +115,6 @@ const RulesPageComponent: React.FC = () => { refreshRulesData.current = refreshRule; }, []); - const getMissingRulesOrTimelinesButtonTitle = useCallback( - (missingRules: number, missingTimelines: number) => { - if (missingRules > 0 && missingTimelines === 0) - return i18n.RELOAD_MISSING_PREPACKAGED_RULES(missingRules); - else if (missingRules === 0 && missingTimelines > 0) - return i18n.RELOAD_MISSING_PREPACKAGED_TIMELINES(missingTimelines); - else if (missingRules > 0 && missingTimelines > 0) - return i18n.RELOAD_MISSING_PREPACKAGED_RULES_AND_TIMELINES(missingRules, missingTimelines); - }, - [] - ); - const goToNewRule = useCallback( (ev) => { ev.preventDefault(); @@ -133,6 +123,24 @@ const RulesPageComponent: React.FC = () => { [history] ); + const loadPrebuiltRulesAndTemplatesButton = useMemo( + () => + getLoadPrebuiltRulesAndTemplatesButton({ + isDisabled: userHasNoPermissions(canUserCRUD) || loading, + onClick: handleCreatePrePackagedRules, + }), + [canUserCRUD, getLoadPrebuiltRulesAndTemplatesButton, handleCreatePrePackagedRules, loading] + ); + + const reloadPrebuiltRulesAndTemplatesButton = useMemo( + () => + getReloadPrebuiltRulesAndTemplatesButton({ + isDisabled: userHasNoPermissions(canUserCRUD) || loading, + onClick: handleCreatePrePackagedRules, + }), + [canUserCRUD, getReloadPrebuiltRulesAndTemplatesButton, handleCreatePrePackagedRules, loading] + ); + if ( redirectToDetections( isSignalIndexExists, @@ -177,35 +185,11 @@ const RulesPageComponent: React.FC = () => { title={i18n.PAGE_TITLE} > - {(prePackagedRuleStatus === 'ruleNotInstalled' || - prePackagedTimelineStatus === 'timelinesNotInstalled') && ( - - - {i18n.LOAD_PREPACKAGED_RULES} - - + {loadPrebuiltRulesAndTemplatesButton && ( + {loadPrebuiltRulesAndTemplatesButton} )} - {(prePackagedRuleStatus === 'someRuleUninstall' || - prePackagedTimelineStatus === 'someTimelineUninstall') && ( - - - {getMissingRulesOrTimelinesButtonTitle( - rulesNotInstalled ?? 0, - timelinesNotInstalled ?? 0 - )} - - + {reloadPrebuiltRulesAndTemplatesButton && ( + {reloadPrebuiltRulesAndTemplatesButton} )} @@ -247,6 +231,7 @@ const RulesPageComponent: React.FC = () => { {(prePackagedRuleStatus === 'ruleNeedUpdate' || prePackagedTimelineStatus === 'timelineNeedUpdate') && ( { )} - i18n.translate( - 'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton', - { - values: { missingRules }, - defaultMessage: - 'Install {missingRules} Elastic prebuilt {missingRules, plural, =1 {rule} other {rules}} ', - } - ); - -export const RELOAD_MISSING_PREPACKAGED_TIMELINES = (missingTimelines: number) => - i18n.translate( - 'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedTimelinesButton', - { - values: { missingTimelines }, - defaultMessage: - 'Install {missingTimelines} Elastic prebuilt {missingTimelines, plural, =1 {timeline} other {timelines}} ', - } - ); - -export const RELOAD_MISSING_PREPACKAGED_RULES_AND_TIMELINES = ( - missingRules: number, - missingTimelines: number -) => - i18n.translate( - 'xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton', - { - values: { missingRules, missingTimelines }, - defaultMessage: - 'Install {missingRules} Elastic prebuilt {missingRules, plural, =1 {rule} other {rules}} and {missingTimelines} Elastic prebuilt {missingTimelines, plural, =1 {timeline} other {timelines}} ', - } - ); - export const IMPORT_RULE_BTN_TITLE = i18n.translate( 'xpack.securitySolution.detectionEngine.components.importRuleModal.importRuleTitle', { diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 763b26de00940..d0c24b0239b62 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -15574,13 +15574,12 @@ "xpack.securitySolution.detectionEngine.rules.deleteDescription": "削除", "xpack.securitySolution.detectionEngine.rules.editPageTitle": "編集", "xpack.securitySolution.detectionEngine.rules.importRuleTitle": "ルールのインポート...", - "xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesButton": "Elastic事前構築済みルールおよびタイムラインテンプレートを読み込む", + "xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesAndTemplatesButton": "Elastic事前構築済みルールおよびタイムラインテンプレートを読み込む", "xpack.securitySolution.detectionEngine.rules.optionalFieldDescription": "オプション", "xpack.securitySolution.detectionEngine.rules.pageTitle": "検出ルール", "xpack.securitySolution.detectionEngine.rules.prePackagedRules.createOwnRuletButton": "独自のルールの作成", "xpack.securitySolution.detectionEngine.rules.prePackagedRules.emptyPromptMessage": "Elasticセキュリティには、バックグラウンドで実行され、条件が合うとアラートを作成する事前構築済み検出ルールがあります。デフォルトでは、Elastic Endpoint Securityルールを除くすべての事前構築済みルールが無効になっています。有効にする追加のルールを選択することができます。", "xpack.securitySolution.detectionEngine.rules.prePackagedRules.emptyPromptTitle": "Elastic事前構築済み検出ルールを読み込む", - "xpack.securitySolution.detectionEngine.rules.prePackagedRules.loadPreBuiltButton": "事前構築済み検出ルールおよびタイムラインテンプレートを読み込む", "xpack.securitySolution.detectionEngine.rules.releaseNotesHelp": "リリースノート", "xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton": "{missingRules} Elastic事前構築済み{missingRules, plural, =1 {ルール} other {ルール}}と{missingTimelines} Elastic事前構築済み{missingTimelines, plural, =1 {タイムライン} other {タイムライン}}をインストール ", "xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton": "{missingRules} Elasticの事前構築済みの{missingRules, plural, =1 {個のルール} other {個のルール}}をインストール ", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 4245b446c5b93..db73cd8043e7e 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -15583,13 +15583,12 @@ "xpack.securitySolution.detectionEngine.rules.deleteDescription": "删除", "xpack.securitySolution.detectionEngine.rules.editPageTitle": "编辑", "xpack.securitySolution.detectionEngine.rules.importRuleTitle": "导入规则……", - "xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesButton": "加载 Elastic 预构建规则和时间线模板", + "xpack.securitySolution.detectionEngine.rules.loadPrePackagedRulesAndTemplatesButton": "加载 Elastic 预构建规则和时间线模板", "xpack.securitySolution.detectionEngine.rules.optionalFieldDescription": "可选", "xpack.securitySolution.detectionEngine.rules.pageTitle": "检测规则", "xpack.securitySolution.detectionEngine.rules.prePackagedRules.createOwnRuletButton": "创建自己的规则", "xpack.securitySolution.detectionEngine.rules.prePackagedRules.emptyPromptMessage": "Elastic Security 附带预置检测规则,这些规则在后台运行,并在条件满足时创建告警。默认情况下,除 Elastic Endpoint Security 规则外,所有预置规则都处于禁用状态。您可以选择其他要激活的规则。", "xpack.securitySolution.detectionEngine.rules.prePackagedRules.emptyPromptTitle": "加载 Elastic 预构建检测规则", - "xpack.securitySolution.detectionEngine.rules.prePackagedRules.loadPreBuiltButton": "加载预置检测规则和时间线模板", "xpack.securitySolution.detectionEngine.rules.releaseNotesHelp": "发行说明", "xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesAndTimelinesButton": "安装 {missingRules} 个 Elastic 预构建{missingRules, plural, =1 {规则} other {规则}}以及 {missingTimelines} 个 Elastic 预构建{missingTimelines, plural, =1 {时间线} other {时间线}} ", "xpack.securitySolution.detectionEngine.rules.reloadMissingPrePackagedRulesButton": "安装 {missingRules} 个 Elastic 预构建{missingRules, plural, =1 {规则} other {规则}} ", From b1af4ba9ae74188306f22d00c7e36ecce8886772 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Thu, 15 Oct 2020 13:58:54 -0400 Subject: [PATCH 102/128] [Maps] Add support for envelope (#80614) --- .../elasticsearch_geo_utils.js | 10 ++++++++ .../elasticsearch_geo_utils.test.js | 24 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js index be214e3b01e67..813d01ff90861 100644 --- a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js +++ b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.js @@ -175,6 +175,16 @@ export function convertESShapeToGeojsonGeometry(value) { geoJson.type = GEO_JSON_TYPE.GEOMETRY_COLLECTION; break; case 'envelope': + // format defined here https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-shape.html#_envelope + const polygon = formatEnvelopeAsPolygon({ + minLon: geoJson.coordinates[0][0], + maxLon: geoJson.coordinates[1][0], + minLat: geoJson.coordinates[1][1], + maxLat: geoJson.coordinates[0][1], + }); + geoJson.type = polygon.type; + geoJson.coordinates = polygon.coordinates; + break; case 'circle': const errorMessage = i18n.translate( 'xpack.maps.es_geo_utils.convert.unsupportedGeometryTypeErrorMessage', diff --git a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js index a8d5d650740cd..ccab57dd18339 100644 --- a/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js +++ b/x-pack/plugins/maps/common/elasticsearch_util/elasticsearch_geo_utils.test.js @@ -250,6 +250,30 @@ describe('geoShapeToGeometry', () => { expect(shapes[0].coordinates).toEqual(coordinates); }); + it('Should convert envelope to geojson', () => { + const coordinates = [ + [100.0, 1.0], + [101.0, 0.0], + ]; + const value = { + type: 'envelope', + coordinates: coordinates, + }; + const shapes = []; + geoShapeToGeometry(value, shapes); + expect(shapes.length).toBe(1); + expect(shapes[0].type).toBe('Polygon'); + expect(shapes[0].coordinates).toEqual([ + [ + [100, 1], + [100, 0], + [101, 0], + [101, 1], + [100, 1], + ], + ]); + }); + it('Should convert array of values', () => { const linestringCoordinates = [ [-77.03653, 38.897676], From 5dc91229f290aeacffb3d4979234764b48eeb704 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 15 Oct 2020 14:20:54 -0400 Subject: [PATCH 103/128] Fix role mappings test for ESS (#80604) Co-authored-by: Joe Portner <5295965+jportner@users.noreply.github.com> --- test/common/services/security/role_mappings.ts | 18 ++++++++++++++++-- .../functional/apps/security/role_mappings.ts | 4 ++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/test/common/services/security/role_mappings.ts b/test/common/services/security/role_mappings.ts index 7951d4b5b47b2..267294991f30e 100644 --- a/test/common/services/security/role_mappings.ts +++ b/test/common/services/security/role_mappings.ts @@ -23,10 +23,24 @@ import { KbnClient, ToolingLog } from '@kbn/dev-utils'; export class RoleMappings { constructor(private log: ToolingLog, private kbnClient: KbnClient) {} + public async getAll() { + this.log.debug(`Getting role mappings`); + const { data, status, statusText } = await this.kbnClient.request>({ + path: `/internal/security/role_mapping`, + method: 'GET', + }); + if (status !== 200) { + throw new Error( + `Expected status code of 200, received ${status} ${statusText}: ${util.inspect(data)}` + ); + } + return data; + } + public async create(name: string, roleMapping: Record) { this.log.debug(`creating role mapping ${name}`); const { data, status, statusText } = await this.kbnClient.request({ - path: `/internal/security/role_mapping/${name}`, + path: `/internal/security/role_mapping/${encodeURIComponent(name)}`, method: 'POST', body: roleMapping, }); @@ -41,7 +55,7 @@ export class RoleMappings { public async delete(name: string) { this.log.debug(`deleting role mapping ${name}`); const { data, status, statusText } = await this.kbnClient.request({ - path: `/internal/security/role_mapping/${name}`, + path: `/internal/security/role_mapping/${encodeURIComponent(name)}`, method: 'DELETE', }); if (status !== 200 && status !== 404) { diff --git a/x-pack/test/functional/apps/security/role_mappings.ts b/x-pack/test/functional/apps/security/role_mappings.ts index 60c166d837933..96f16aebd11b9 100644 --- a/x-pack/test/functional/apps/security/role_mappings.ts +++ b/x-pack/test/functional/apps/security/role_mappings.ts @@ -17,6 +17,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('Role Mappings', function () { before(async () => { + // Delete any existing role mappings. ESS commonly sets up a role mapping automatically. + const existingMappings = await security.roleMappings.getAll(); + await Promise.all(existingMappings.map((m) => security.roleMappings.delete(m.name))); + await pageObjects.common.navigateToApp('roleMappings'); }); From 9d50c17fa683c49f3a89c2af9ac8d2c4eee64269 Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Thu, 15 Oct 2020 20:45:11 +0200 Subject: [PATCH 104/128] [APM] Hide service if only data is from ML (#80145) Closes #79998. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../get_services/get_services_items.ts | 29 ++++++++++++++----- .../trial/tests/services/top_services.ts | 19 ++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts index 4cb0c4c750dd1..5ea3714e81b6f 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts @@ -58,14 +58,27 @@ export async function getServicesItems({ }), ]); - const allMetrics = [ - ...transactionDurationAverages, - ...agentNames, - ...transactionRates, - ...transactionErrorRates, - ...environments, - ...healthStatuses, - ]; + const apmServiceMetrics = joinByKey( + [ + ...transactionDurationAverages, + ...agentNames, + ...transactionRates, + ...transactionErrorRates, + ...environments, + ], + 'serviceName' + ); + + const apmServices = apmServiceMetrics.map(({ serviceName }) => serviceName); + + // make sure to exclude health statuses from services + // that are not found in APM data + + const matchedHealthStatuses = healthStatuses.filter(({ serviceName }) => + apmServices.includes(serviceName) + ); + + const allMetrics = [...apmServiceMetrics, ...matchedHealthStatuses]; return joinByKey(allMetrics, 'serviceName'); } diff --git a/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts b/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts index 6fd5e7e0c3ea7..9a6c6f94dbb60 100644 --- a/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts +++ b/x-pack/test/apm_api_integration/trial/tests/services/top_services.ts @@ -99,6 +99,25 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(definedHealthStatuses.length).to.be(0); }); }); + + describe('and fetching a list of services with a filter', () => { + let response: PromiseReturnType; + before(async () => { + response = await supertest.get( + `/api/apm/services?start=${start}&end=${end}&uiFilters=${encodeURIComponent( + `{"kuery":"service.name:opbeans-java","environment":"ENVIRONMENT_ALL"}` + )}` + ); + }); + + it('does not return health statuses for services that are not found in APM data', () => { + expect(response.status).to.be(200); + + expect(response.body.items.length).to.be(1); + + expect(response.body.items[0].serviceName).to.be('opbeans-java'); + }); + }); }); }); } From 8d053fdc8eff158d35f96053d295936734c9535c Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Thu, 15 Oct 2020 19:46:20 +0100 Subject: [PATCH 105/128] [Security Solution] Cypress template creation (#80180) * init tests * fix cypress test * remove console * fix functional test * update functional test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../integration/timeline_creation.spec.ts | 3 +-- .../timeline_template_creation.spec.ts | 20 +++++++++---------- .../cypress/screens/timeline.ts | 5 +++++ .../cypress/tasks/timeline.ts | 11 ++++++++-- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts index 8ce60450671b9..9f61d11b7ac0f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_creation.spec.ts @@ -45,8 +45,7 @@ import { openTimeline } from '../tasks/timelines'; import { OVERVIEW_URL } from '../urls/navigation'; -// FLAKY: https://github.com/elastic/kibana/issues/79389 -describe.skip('Timelines', () => { +describe('Timelines', () => { before(() => { cy.server(); cy.route('PATCH', '**/api/timeline').as('timeline'); diff --git a/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts index 91255d6110d59..377b2100b36cd 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timeline_template_creation.spec.ts @@ -12,8 +12,8 @@ import { NOTES_BUTTON, NOTES_COUNT, NOTES_TEXT_AREA, + PIN_EVENT, TIMELINE_DESCRIPTION, - // TIMELINE_FILTER, TIMELINE_QUERY, TIMELINE_TITLE, } from '../screens/timeline'; @@ -35,7 +35,7 @@ import { closeTimeline, createNewTimelineTemplate, markAsFavorite, - openTimelineFromSettings, + openTimelineTemplateFromSettings, populateTimeline, waitForTimelineChanges, } from '../tasks/timeline'; @@ -43,8 +43,7 @@ import { openTimeline } from '../tasks/timelines'; import { OVERVIEW_URL } from '../urls/navigation'; -// FLAKY: https://github.com/elastic/kibana/issues/79967 -describe.skip('Timeline Templates', () => { +describe('Timeline Templates', () => { before(() => { cy.server(); cy.route('PATCH', '**/api/timeline').as('timeline'); @@ -56,12 +55,11 @@ describe.skip('Timeline Templates', () => { createNewTimelineTemplate(); populateTimeline(); addFilter(timeline.filter); - // To fix - // cy.get(PIN_EVENT).should( - // 'have.attr', - // 'aria-label', - // 'This event may not be pinned while editing a template timeline' - // ); + cy.get(PIN_EVENT).should( + 'have.attr', + 'aria-label', + 'This event may not be pinned while editing a template timeline' + ); cy.get(LOCKED_ICON).should('be.visible'); addNameToTimeline(timeline.title); @@ -77,7 +75,7 @@ describe.skip('Timeline Templates', () => { waitForTimelineChanges(); createNewTimelineTemplate(); closeTimeline(); - openTimelineFromSettings(); + openTimelineTemplateFromSettings(timelineId); cy.contains(timeline.title).should('exist'); cy.get(TIMELINES_DESCRIPTION).first().should('have.text', timeline.description); diff --git a/x-pack/plugins/security_solution/cypress/screens/timeline.ts b/x-pack/plugins/security_solution/cypress/screens/timeline.ts index e397dd9b5a41a..98e6502ffe94f 100644 --- a/x-pack/plugins/security_solution/cypress/screens/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/screens/timeline.ts @@ -58,6 +58,9 @@ export const NOTES_COUNT = '[data-test-subj="timeline-notes-count"]'; export const OPEN_TIMELINE_ICON = '[data-test-subj="open-timeline-button"]'; +export const OPEN_TIMELINE_TEMPLATE_ICON = + '[data-test-subj="open-timeline-modal-body-filter-template"]'; + export const PIN_EVENT = '[data-test-subj="pin"]'; export const PROVIDER_BADGE = '[data-test-subj="providerBadge"]'; @@ -98,6 +101,8 @@ export const TIMELINE_FILTER = (filter: TimelineFilter) => { export const TIMELINE_FILTER_FIELD = '[data-test-subj="filterFieldSuggestionList"]'; +export const TIMELINE_TITLE_BY_ID = (id: string) => `[data-test-subj="title-${id}"]`; + export const TIMELINE_FILTER_OPERATOR = '[data-test-subj="filterOperatorList"]'; export const TIMELINE_FILTER_VALUE = diff --git a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts index 7c9c95427a4d0..b101793385488 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/timeline.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/timeline.ts @@ -41,9 +41,11 @@ import { TIMELINE_INSPECT_BUTTON, TIMELINE_SETTINGS_ICON, TIMELINE_TITLE, + TIMELINE_TITLE_BY_ID, TIMESTAMP_TOGGLE_FIELD, TOGGLE_TIMELINE_EXPAND_EVENT, CREATE_NEW_TIMELINE_TEMPLATE, + OPEN_TIMELINE_TEMPLATE_ICON, } from '../screens/timeline'; import { TIMELINES_TABLE } from '../screens/timelines'; @@ -69,8 +71,7 @@ export const addNotesToTimeline = (notes: string) => { export const addFilter = (filter: TimelineFilter) => { cy.get(ADD_FILTER).click(); - cy.get(TIMELINE_FILTER_FIELD).type(filter.field); - cy.get(COMBO_BOX).contains(filter.field).click(); + cy.get(TIMELINE_FILTER_FIELD).type(`${filter.field}{downarrow}{enter}`); cy.get(TIMELINE_FILTER_OPERATOR).type(filter.operator); cy.get(COMBO_BOX).contains(filter.operator).click(); if (filter.operator !== 'exists') { @@ -146,6 +147,12 @@ export const openTimelineFromSettings = () => { cy.get(OPEN_TIMELINE_ICON).click({ force: true }); }; +export const openTimelineTemplateFromSettings = (id: string) => { + openTimelineFromSettings(); + cy.get(OPEN_TIMELINE_TEMPLATE_ICON).click({ force: true }); + cy.get(TIMELINE_TITLE_BY_ID(id)).click({ force: true }); +}; + export const openTimelineSettings = () => { cy.get(TIMELINE_SETTINGS_ICON).trigger('click', { force: true }); }; From 3ad698d6a0293adb7f56bf570f05c08e2aa59548 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Thu, 15 Oct 2020 14:47:46 -0400 Subject: [PATCH 106/128] Licensed feature usage for connectors (#77679) * Initial work * Fix type check and jest failures * Add unit tests * No need to notifyUsage from alert execution handler * Fix ESLint * Log action usage from alerts * Add integration tests * Fix jest test * Skip feature usage of basic action types * Fix types * Fix ESLint issue * Clarify comment Co-authored-by: Elastic Machine --- .../server/action_type_registry.test.ts | 80 ++++++++++++++++++- .../actions/server/action_type_registry.ts | 38 +++++++-- .../actions/server/actions_client.mock.ts | 1 + .../actions/server/actions_client.test.ts | 33 +++++++- .../plugins/actions/server/actions_client.ts | 7 ++ .../server/builtin_action_types/index.test.ts | 2 + .../server/create_execute_function.test.ts | 6 +- .../actions/server/create_execute_function.ts | 2 +- .../server/lib/action_executor.test.ts | 3 + .../actions/server/lib/action_executor.ts | 2 +- .../lib/get_action_type_feature_usage_name.ts | 11 +++ x-pack/plugins/actions/server/lib/index.ts | 1 + .../actions/server/lib/license_state.mock.ts | 1 + .../actions/server/lib/license_state.test.ts | 43 ++++++++++ .../actions/server/lib/license_state.ts | 23 +++++- x-pack/plugins/actions/server/plugin.test.ts | 45 +++++++++++ x-pack/plugins/actions/server/plugin.ts | 27 +++++-- .../server/alerts_client/alerts_client.ts | 5 ++ .../server/alerts_client/tests/create.test.ts | 14 +++- .../server/alerts_client/tests/update.test.ts | 7 +- .../task_runner/create_execution_handler.ts | 4 +- .../spaces_only/tests/actions/create.ts | 26 ++++++ .../spaces_only/tests/actions/execute.ts | 34 ++++++++ .../spaces_only/tests/actions/update.ts | 37 +++++++++ .../spaces_only/tests/alerting/alerts_base.ts | 25 ++++++ 25 files changed, 451 insertions(+), 26 deletions(-) create mode 100644 x-pack/plugins/actions/server/lib/get_action_type_feature_usage_name.ts diff --git a/x-pack/plugins/actions/server/action_type_registry.test.ts b/x-pack/plugins/actions/server/action_type_registry.test.ts index b25e33400df5d..e641b81189b93 100644 --- a/x-pack/plugins/actions/server/action_type_registry.test.ts +++ b/x-pack/plugins/actions/server/action_type_registry.test.ts @@ -11,6 +11,7 @@ import { ActionExecutor, ExecutorError, ILicenseState, TaskRunnerFactory } from import { actionsConfigMock } from './actions_config.mock'; import { licenseStateMock } from './lib/license_state.mock'; import { ActionsConfigurationUtilities } from './actions_config'; +import { licensingMock } from '../../licensing/server/mocks'; const mockTaskManager = taskManagerMock.setup(); let mockedLicenseState: jest.Mocked; @@ -22,6 +23,7 @@ beforeEach(() => { mockedLicenseState = licenseStateMock.create(); mockedActionsConfig = actionsConfigMock.create(); actionTypeRegistryParams = { + licensing: licensingMock.createSetup(), taskManager: mockTaskManager, taskRunnerFactory: new TaskRunnerFactory( new ActionExecutor({ isESOUsingEphemeralEncryptionKey: false }) @@ -51,7 +53,7 @@ describe('register()', () => { actionTypeRegistry.register({ id: 'my-action-type', name: 'My action type', - minimumLicenseRequired: 'basic', + minimumLicenseRequired: 'gold', executor, }); expect(actionTypeRegistry.has('my-action-type')).toEqual(true); @@ -69,6 +71,10 @@ describe('register()', () => { }, ] `); + expect(actionTypeRegistryParams.licensing.featureUsage.register).toHaveBeenCalledWith( + 'Connector: My action type', + 'gold' + ); }); test('shallow clones the given action type', () => { @@ -123,6 +129,31 @@ describe('register()', () => { expect(getRetry(0, new ExecutorError('my message', {}, undefined))).toEqual(false); expect(getRetry(0, new ExecutorError('my message', {}, retryTime))).toEqual(retryTime); }); + + test('registers gold+ action types to the licensing feature usage API', () => { + const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + actionTypeRegistry.register({ + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'gold', + executor, + }); + expect(actionTypeRegistryParams.licensing.featureUsage.register).toHaveBeenCalledWith( + 'Connector: My action type', + 'gold' + ); + }); + + test(`doesn't register basic action types to the licensing feature usage API`, () => { + const actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + actionTypeRegistry.register({ + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'basic', + executor, + }); + expect(actionTypeRegistryParams.licensing.featureUsage.register).not.toHaveBeenCalled(); + }); }); describe('get()', () => { @@ -232,10 +263,20 @@ describe('isActionTypeEnabled', () => { expect(actionTypeRegistry.isActionExecutable('my-slack1', 'foo')).toEqual(true); }); - test('should call isLicenseValidForActionType of the license state', async () => { + test('should call isLicenseValidForActionType of the license state with notifyUsage false by default', async () => { mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); actionTypeRegistry.isActionTypeEnabled('foo'); - expect(mockedLicenseState.isLicenseValidForActionType).toHaveBeenCalledWith(fooActionType); + expect(mockedLicenseState.isLicenseValidForActionType).toHaveBeenCalledWith(fooActionType, { + notifyUsage: false, + }); + }); + + test('should call isLicenseValidForActionType of the license state with notifyUsage true when specified', async () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + actionTypeRegistry.isActionTypeEnabled('foo', { notifyUsage: true }); + expect(mockedLicenseState.isLicenseValidForActionType).toHaveBeenCalledWith(fooActionType, { + notifyUsage: true, + }); }); test('should return false when isActionTypeEnabled is false and isLicenseValidForActionType is true', async () => { @@ -298,3 +339,36 @@ describe('ensureActionTypeEnabled', () => { ).toThrowErrorMatchingInlineSnapshot(`"Fail"`); }); }); + +describe('isActionExecutable()', () => { + let actionTypeRegistry: ActionTypeRegistry; + const fooActionType: ActionType = { + id: 'foo', + name: 'Foo', + minimumLicenseRequired: 'basic', + executor: async (options) => { + return { status: 'ok', actionId: options.actionId }; + }, + }; + + beforeEach(() => { + actionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams); + actionTypeRegistry.register(fooActionType); + }); + + test('should call isLicenseValidForActionType of the license state with notifyUsage false by default', async () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + actionTypeRegistry.isActionExecutable('123', 'foo'); + expect(mockedLicenseState.isLicenseValidForActionType).toHaveBeenCalledWith(fooActionType, { + notifyUsage: false, + }); + }); + + test('should call isLicenseValidForActionType of the license state with notifyUsage true when specified', async () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + actionTypeRegistry.isActionExecutable('123', 'foo', { notifyUsage: true }); + expect(mockedLicenseState.isLicenseValidForActionType).toHaveBeenCalledWith(fooActionType, { + notifyUsage: true, + }); + }); +}); diff --git a/x-pack/plugins/actions/server/action_type_registry.ts b/x-pack/plugins/actions/server/action_type_registry.ts index 4015381ff9502..b93d4a6e78ac6 100644 --- a/x-pack/plugins/actions/server/action_type_registry.ts +++ b/x-pack/plugins/actions/server/action_type_registry.ts @@ -7,9 +7,15 @@ import Boom from 'boom'; import { i18n } from '@kbn/i18n'; import { RunContext, TaskManagerSetupContract } from '../../task_manager/server'; -import { ExecutorError, TaskRunnerFactory, ILicenseState } from './lib'; import { ActionType as CommonActionType } from '../common'; import { ActionsConfigurationUtilities } from './actions_config'; +import { LicensingPluginSetup } from '../../licensing/server'; +import { + ExecutorError, + getActionTypeFeatureUsageName, + TaskRunnerFactory, + ILicenseState, +} from './lib'; import { ActionType, PreConfiguredAction, @@ -19,6 +25,7 @@ import { } from './types'; export interface ActionTypeRegistryOpts { + licensing: LicensingPluginSetup; taskManager: TaskManagerSetupContract; taskRunnerFactory: TaskRunnerFactory; actionsConfigUtils: ActionsConfigurationUtilities; @@ -33,6 +40,7 @@ export class ActionTypeRegistry { private readonly actionsConfigUtils: ActionsConfigurationUtilities; private readonly licenseState: ILicenseState; private readonly preconfiguredActions: PreConfiguredAction[]; + private readonly licensing: LicensingPluginSetup; constructor(constructorParams: ActionTypeRegistryOpts) { this.taskManager = constructorParams.taskManager; @@ -40,6 +48,7 @@ export class ActionTypeRegistry { this.actionsConfigUtils = constructorParams.actionsConfigUtils; this.licenseState = constructorParams.licenseState; this.preconfiguredActions = constructorParams.preconfiguredActions; + this.licensing = constructorParams.licensing; } /** @@ -54,26 +63,36 @@ export class ActionTypeRegistry { */ public ensureActionTypeEnabled(id: string) { this.actionsConfigUtils.ensureActionTypeEnabled(id); + // Important to happen last because the function will notify of feature usage at the + // same time and it shouldn't notify when the action type isn't enabled this.licenseState.ensureLicenseForActionType(this.get(id)); } /** * Returns true if action type is enabled in the config and a valid license is used. */ - public isActionTypeEnabled(id: string) { + public isActionTypeEnabled( + id: string, + options: { notifyUsage: boolean } = { notifyUsage: false } + ) { return ( this.actionsConfigUtils.isActionTypeEnabled(id) && - this.licenseState.isLicenseValidForActionType(this.get(id)).isValid === true + this.licenseState.isLicenseValidForActionType(this.get(id), options).isValid === true ); } /** * Returns true if action type is enabled or it is a preconfigured action type. */ - public isActionExecutable(actionId: string, actionTypeId: string) { + public isActionExecutable( + actionId: string, + actionTypeId: string, + options: { notifyUsage: boolean } = { notifyUsage: false } + ) { + const actionTypeEnabled = this.isActionTypeEnabled(actionTypeId, options); return ( - this.isActionTypeEnabled(actionTypeId) || - (!this.isActionTypeEnabled(actionTypeId) && + actionTypeEnabled || + (!actionTypeEnabled && this.preconfiguredActions.find( (preconfiguredAction) => preconfiguredAction.id === actionId ) !== undefined) @@ -118,6 +137,13 @@ export class ActionTypeRegistry { createTaskRunner: (context: RunContext) => this.taskRunnerFactory.create(context), }, }); + // No need to notify usage on basic action types + if (actionType.minimumLicenseRequired !== 'basic') { + this.licensing.featureUsage.register( + getActionTypeFeatureUsageName(actionType as ActionType), + actionType.minimumLicenseRequired + ); + } } /** diff --git a/x-pack/plugins/actions/server/actions_client.mock.ts b/x-pack/plugins/actions/server/actions_client.mock.ts index 48122a5ce4e0f..0c16c88ad7a89 100644 --- a/x-pack/plugins/actions/server/actions_client.mock.ts +++ b/x-pack/plugins/actions/server/actions_client.mock.ts @@ -20,6 +20,7 @@ const createActionsClientMock = () => { execute: jest.fn(), enqueueExecution: jest.fn(), listTypes: jest.fn(), + isActionTypeEnabled: jest.fn(), }; return mocked; }; diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index adef12454f2d5..2b6aec42e0d21 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -8,12 +8,13 @@ import { schema } from '@kbn/config-schema'; import { ActionTypeRegistry, ActionTypeRegistryOpts } from './action_type_registry'; import { ActionsClient } from './actions_client'; -import { ExecutorType } from './types'; +import { ExecutorType, ActionType } from './types'; import { ActionExecutor, TaskRunnerFactory, ILicenseState } from './lib'; import { taskManagerMock } from '../../task_manager/server/task_manager.mock'; import { actionsConfigMock } from './actions_config.mock'; import { getActionsConfigurationUtilities } from './actions_config'; import { licenseStateMock } from './lib/license_state.mock'; +import { licensingMock } from '../../licensing/server/mocks'; import { elasticsearchServiceMock, @@ -47,6 +48,7 @@ beforeEach(() => { jest.resetAllMocks(); mockedLicenseState = licenseStateMock.create(); actionTypeRegistryParams = { + licensing: licensingMock.createSetup(), taskManager: mockTaskManager, taskRunnerFactory: new TaskRunnerFactory( new ActionExecutor({ isESOUsingEphemeralEncryptionKey: false }) @@ -299,6 +301,7 @@ describe('create()', () => { }); const localActionTypeRegistryParams = { + licensing: licensingMock.createSetup(), taskManager: mockTaskManager, taskRunnerFactory: new TaskRunnerFactory( new ActionExecutor({ isESOUsingEphemeralEncryptionKey: false }) @@ -1244,3 +1247,31 @@ describe('enqueueExecution()', () => { expect(executionEnqueuer).toHaveBeenCalledWith(unsecuredSavedObjectsClient, opts); }); }); + +describe('isActionTypeEnabled()', () => { + const fooActionType: ActionType = { + id: 'foo', + name: 'Foo', + minimumLicenseRequired: 'gold', + executor: jest.fn(), + }; + beforeEach(() => { + actionTypeRegistry.register(fooActionType); + }); + + test('should call isLicenseValidForActionType of the license state with notifyUsage false by default', () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + actionsClient.isActionTypeEnabled('foo'); + expect(mockedLicenseState.isLicenseValidForActionType).toHaveBeenCalledWith(fooActionType, { + notifyUsage: false, + }); + }); + + test('should call isLicenseValidForActionType of the license state with notifyUsage true when specified', () => { + mockedLicenseState.isLicenseValidForActionType.mockReturnValue({ isValid: true }); + actionsClient.isActionTypeEnabled('foo', { notifyUsage: true }); + expect(mockedLicenseState.isLicenseValidForActionType).toHaveBeenCalledWith(fooActionType, { + notifyUsage: true, + }); + }); +}); diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 4079a6ddeeb8a..e565d420d772e 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -343,6 +343,13 @@ export class ActionsClient { public async listTypes(): Promise { return this.actionTypeRegistry.list(); } + + public isActionTypeEnabled( + actionTypeId: string, + options: { notifyUsage: boolean } = { notifyUsage: false } + ) { + return this.actionTypeRegistry.isActionTypeEnabled(actionTypeId, options); + } } function actionFromSavedObject(savedObject: SavedObject): ActionResult { diff --git a/x-pack/plugins/actions/server/builtin_action_types/index.test.ts b/x-pack/plugins/actions/server/builtin_action_types/index.test.ts index acab6dd41b4b3..f7882849708e5 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/index.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/index.test.ts @@ -12,6 +12,7 @@ import { Logger } from '../../../../../src/core/server'; import { loggingSystemMock } from '../../../../../src/core/server/mocks'; import { actionsConfigMock } from '../actions_config.mock'; import { licenseStateMock } from '../lib/license_state.mock'; +import { licensingMock } from '../../../licensing/server/mocks'; const ACTION_TYPE_IDS = ['.index', '.email', '.pagerduty', '.server-log', '.slack', '.webhook']; @@ -21,6 +22,7 @@ export function createActionTypeRegistry(): { } { const logger = loggingSystemMock.create().get() as jest.Mocked; const actionTypeRegistry = new ActionTypeRegistry({ + licensing: licensingMock.createSetup(), taskManager: taskManagerMock.setup(), taskRunnerFactory: new TaskRunnerFactory( new ActionExecutor({ isESOUsingEphemeralEncryptionKey: false }) diff --git a/x-pack/plugins/actions/server/create_execute_function.test.ts b/x-pack/plugins/actions/server/create_execute_function.test.ts index 7682f01ed769d..33e78ee444cd0 100644 --- a/x-pack/plugins/actions/server/create_execute_function.test.ts +++ b/x-pack/plugins/actions/server/create_execute_function.test.ts @@ -23,9 +23,10 @@ beforeEach(() => jest.resetAllMocks()); describe('execute()', () => { test('schedules the action with all given parameters', async () => { + const actionTypeRegistry = actionTypeRegistryMock.create(); const executeFn = createExecutionEnqueuerFunction({ taskManager: mockTaskManager, - actionTypeRegistry: actionTypeRegistryMock.create(), + actionTypeRegistry, isESOUsingEphemeralEncryptionKey: false, preconfiguredActions: [], }); @@ -76,6 +77,9 @@ describe('execute()', () => { }, {} ); + expect(actionTypeRegistry.isActionExecutable).toHaveBeenCalledWith('123', 'mock-action', { + notifyUsage: true, + }); }); test('schedules the action with all given parameters with a preconfigured action', async () => { diff --git a/x-pack/plugins/actions/server/create_execute_function.ts b/x-pack/plugins/actions/server/create_execute_function.ts index b226583fade52..f0a22c642cf61 100644 --- a/x-pack/plugins/actions/server/create_execute_function.ts +++ b/x-pack/plugins/actions/server/create_execute_function.ts @@ -51,7 +51,7 @@ export function createExecutionEnqueuerFunction({ id ); - if (!actionTypeRegistry.isActionExecutable(id, actionTypeId)) { + if (!actionTypeRegistry.isActionExecutable(id, actionTypeId, { notifyUsage: true })) { actionTypeRegistry.ensureActionTypeEnabled(actionTypeId); } diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index 692d14e859b34..4ff56536e3867 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -90,6 +90,9 @@ test('successfully executes', async () => { ); expect(actionTypeRegistry.get).toHaveBeenCalledWith('test'); + expect(actionTypeRegistry.isActionExecutable).toHaveBeenCalledWith('1', 'test', { + notifyUsage: true, + }); expect(actionType.executor).toHaveBeenCalledWith({ actionId: '1', diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index 0d4d6de3be1f9..0015b417d72ce 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -102,7 +102,7 @@ export class ActionExecutor { namespace.namespace ); - if (!actionTypeRegistry.isActionExecutable(actionId, actionTypeId)) { + if (!actionTypeRegistry.isActionExecutable(actionId, actionTypeId, { notifyUsage: true })) { actionTypeRegistry.ensureActionTypeEnabled(actionTypeId); } const actionType = actionTypeRegistry.get(actionTypeId); diff --git a/x-pack/plugins/actions/server/lib/get_action_type_feature_usage_name.ts b/x-pack/plugins/actions/server/lib/get_action_type_feature_usage_name.ts new file mode 100644 index 0000000000000..75919442b2ce6 --- /dev/null +++ b/x-pack/plugins/actions/server/lib/get_action_type_feature_usage_name.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 { ActionType } from '../types'; + +export function getActionTypeFeatureUsageName(actionType: ActionType) { + return `Connector: ${actionType.name}`; +} diff --git a/x-pack/plugins/actions/server/lib/index.ts b/x-pack/plugins/actions/server/lib/index.ts index e97875b91cf33..4c8e7ab17db68 100644 --- a/x-pack/plugins/actions/server/lib/index.ts +++ b/x-pack/plugins/actions/server/lib/index.ts @@ -10,6 +10,7 @@ export { TaskRunnerFactory } from './task_runner_factory'; export { ActionExecutor, ActionExecutorContract } from './action_executor'; export { ILicenseState, LicenseState } from './license_state'; export { verifyApiAccess } from './verify_api_access'; +export { getActionTypeFeatureUsageName } from './get_action_type_feature_usage_name'; export { ActionTypeDisabledError, ActionTypeDisabledReason, diff --git a/x-pack/plugins/actions/server/lib/license_state.mock.ts b/x-pack/plugins/actions/server/lib/license_state.mock.ts index d59e9dbdc540f..e5bd9fc9d16cd 100644 --- a/x-pack/plugins/actions/server/lib/license_state.mock.ts +++ b/x-pack/plugins/actions/server/lib/license_state.mock.ts @@ -12,6 +12,7 @@ export const createLicenseStateMock = () => { getLicenseInformation: jest.fn(), ensureLicenseForActionType: jest.fn(), isLicenseValidForActionType: jest.fn(), + setNotifyUsage: jest.fn(), checkLicense: jest.fn().mockResolvedValue({ state: 'valid', }), diff --git a/x-pack/plugins/actions/server/lib/license_state.test.ts b/x-pack/plugins/actions/server/lib/license_state.test.ts index 32c3c54faf007..06148b1825e73 100644 --- a/x-pack/plugins/actions/server/lib/license_state.test.ts +++ b/x-pack/plugins/actions/server/lib/license_state.test.ts @@ -55,6 +55,7 @@ describe('checkLicense()', () => { describe('isLicenseValidForActionType', () => { let license: Subject; let licenseState: ILicenseState; + const mockNotifyUsage = jest.fn(); const fooActionType: ActionType = { id: 'foo', name: 'Foo', @@ -67,6 +68,7 @@ describe('isLicenseValidForActionType', () => { beforeEach(() => { license = new Subject(); licenseState = new LicenseState(license); + licenseState.setNotifyUsage(mockNotifyUsage); }); test('should return false when license not defined', () => { @@ -113,11 +115,42 @@ describe('isLicenseValidForActionType', () => { isValid: true, }); }); + + test('should not call notifyUsage by default', () => { + const goldLicense = licensingMock.createLicense({ + license: { status: 'active', type: 'gold' }, + }); + license.next(goldLicense); + licenseState.isLicenseValidForActionType(fooActionType); + expect(mockNotifyUsage).not.toHaveBeenCalled(); + }); + + test('should not call notifyUsage on basic action types', () => { + const basicLicense = licensingMock.createLicense({ + license: { status: 'active', type: 'basic' }, + }); + license.next(basicLicense); + licenseState.isLicenseValidForActionType({ + ...fooActionType, + minimumLicenseRequired: 'basic', + }); + expect(mockNotifyUsage).not.toHaveBeenCalled(); + }); + + test('should call notifyUsage when specified', () => { + const goldLicense = licensingMock.createLicense({ + license: { status: 'active', type: 'gold' }, + }); + license.next(goldLicense); + licenseState.isLicenseValidForActionType(fooActionType, { notifyUsage: true }); + expect(mockNotifyUsage).toHaveBeenCalledWith('Connector: Foo'); + }); }); describe('ensureLicenseForActionType()', () => { let license: Subject; let licenseState: ILicenseState; + const mockNotifyUsage = jest.fn(); const fooActionType: ActionType = { id: 'foo', name: 'Foo', @@ -130,6 +163,7 @@ describe('ensureLicenseForActionType()', () => { beforeEach(() => { license = new Subject(); licenseState = new LicenseState(license); + licenseState.setNotifyUsage(mockNotifyUsage); }); test('should throw when license not defined', () => { @@ -178,6 +212,15 @@ describe('ensureLicenseForActionType()', () => { license.next(goldLicense); licenseState.ensureLicenseForActionType(fooActionType); }); + + test('should call notifyUsage', () => { + const goldLicense = licensingMock.createLicense({ + license: { status: 'active', type: 'gold' }, + }); + license.next(goldLicense); + licenseState.ensureLicenseForActionType(fooActionType); + expect(mockNotifyUsage).toHaveBeenCalledWith('Connector: Foo'); + }); }); function createUnavailableLicense() { diff --git a/x-pack/plugins/actions/server/lib/license_state.ts b/x-pack/plugins/actions/server/lib/license_state.ts index 1686d0201e96c..902fadb3da170 100644 --- a/x-pack/plugins/actions/server/lib/license_state.ts +++ b/x-pack/plugins/actions/server/lib/license_state.ts @@ -11,6 +11,8 @@ import { ILicense } from '../../../licensing/common/types'; import { PLUGIN } from '../constants/plugin'; import { ActionType } from '../types'; import { ActionTypeDisabledError } from './errors'; +import { LicensingPluginStart } from '../../../licensing/server'; +import { getActionTypeFeatureUsageName } from './get_action_type_feature_usage_name'; export type ILicenseState = PublicMethodsOf; @@ -24,6 +26,7 @@ export class LicenseState { private licenseInformation: ActionsLicenseInformation = this.checkLicense(undefined); private subscription: Subscription; private license?: ILicense; + private _notifyUsage: LicensingPluginStart['featureUsage']['notifyUsage'] | null = null; constructor(license$: Observable) { this.subscription = license$.subscribe(this.updateInformation.bind(this)); @@ -34,6 +37,10 @@ export class LicenseState { this.licenseInformation = this.checkLicense(license); } + public setNotifyUsage(notifyUsage: LicensingPluginStart['featureUsage']['notifyUsage']) { + this._notifyUsage = notifyUsage; + } + public clean() { this.subscription.unsubscribe(); } @@ -43,8 +50,13 @@ export class LicenseState { } public isLicenseValidForActionType( - actionType: ActionType + actionType: ActionType, + { notifyUsage }: { notifyUsage: boolean } = { notifyUsage: false } ): { isValid: true } | { isValid: false; reason: 'unavailable' | 'expired' | 'invalid' } { + if (notifyUsage) { + this.notifyUsage(actionType); + } + if (!this.license?.isAvailable) { return { isValid: false, reason: 'unavailable' }; } @@ -65,7 +77,16 @@ export class LicenseState { } } + private notifyUsage(actionType: ActionType) { + // No need to notify usage on basic action types + if (this._notifyUsage && actionType.minimumLicenseRequired !== 'basic') { + this._notifyUsage(getActionTypeFeatureUsageName(actionType)); + } + } + public ensureLicenseForActionType(actionType: ActionType) { + this.notifyUsage(actionType); + const check = this.isLicenseValidForActionType(actionType); if (check.isValid) { diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index 9d545600e61ee..7f7f9e196da07 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -211,6 +211,7 @@ describe('Actions Plugin', () => { features: featuresPluginMock.createSetup(), }; pluginsStart = { + licensing: licensingMock.createStart(), taskManager: taskManagerMock.createStart(), encryptedSavedObjects: encryptedSavedObjectsMock.createStart(), }; @@ -255,5 +256,49 @@ describe('Actions Plugin', () => { ); }); }); + + describe('isActionTypeEnabled()', () => { + const actionType: ActionType = { + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'gold', + executor: jest.fn(), + }; + + it('passes through the notifyUsage option when set to true', async () => { + // coreMock.createSetup doesn't support Plugin generics + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup); + pluginSetup.registerType(actionType); + const pluginStart = plugin.start(coreStart, pluginsStart); + + pluginStart.isActionTypeEnabled('my-action-type', { notifyUsage: true }); + expect(pluginsStart.licensing.featureUsage.notifyUsage).toHaveBeenCalledWith( + 'Connector: My action type' + ); + }); + }); + + describe('isActionExecutable()', () => { + const actionType: ActionType = { + id: 'my-action-type', + name: 'My action type', + minimumLicenseRequired: 'gold', + executor: jest.fn(), + }; + + it('passes through the notifyUsage option when set to true', async () => { + // coreMock.createSetup doesn't support Plugin generics + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup); + pluginSetup.registerType(actionType); + const pluginStart = plugin.start(coreStart, pluginsStart); + + pluginStart.isActionExecutable('123', 'my-action-type', { notifyUsage: true }); + expect(pluginsStart.licensing.featureUsage.notifyUsage).toHaveBeenCalledWith( + 'Connector: My action type' + ); + }); + }); }); }); diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 1a15a5a815195..ef20ffbb9ee68 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -26,7 +26,7 @@ import { EncryptedSavedObjectsPluginStart, } from '../../encrypted_saved_objects/server'; import { TaskManagerSetupContract, TaskManagerStartContract } from '../../task_manager/server'; -import { LicensingPluginSetup } from '../../licensing/server'; +import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/server'; import { LICENSE_TYPE } from '../../licensing/common/types'; import { SpacesPluginSetup, SpacesServiceSetup } from '../../spaces/server'; import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server'; @@ -93,8 +93,12 @@ export interface PluginSetupContract { } export interface PluginStartContract { - isActionTypeEnabled(id: string): boolean; - isActionExecutable(actionId: string, actionTypeId: string): boolean; + isActionTypeEnabled(id: string, options?: { notifyUsage: boolean }): boolean; + isActionExecutable( + actionId: string, + actionTypeId: string, + options?: { notifyUsage: boolean } + ): boolean; getActionsClientWithRequest(request: KibanaRequest): Promise>; getActionsAuthorizationWithRequest(request: KibanaRequest): PublicMethodsOf; preconfiguredActions: PreConfiguredAction[]; @@ -113,6 +117,7 @@ export interface ActionsPluginsSetup { export interface ActionsPluginsStart { encryptedSavedObjects: EncryptedSavedObjectsPluginStart; taskManager: TaskManagerStartContract; + licensing: LicensingPluginStart; } const includedHiddenTypes = [ @@ -196,6 +201,7 @@ export class ActionsPlugin implements Plugin, Plugi } const actionTypeRegistry = new ActionTypeRegistry({ + licensing: plugins.licensing, taskRunnerFactory, taskManager: plugins.taskManager, actionsConfigUtils, @@ -268,6 +274,7 @@ export class ActionsPlugin implements Plugin, Plugi public start(core: CoreStart, plugins: ActionsPluginsStart): PluginStartContract { const { logger, + licenseState, actionExecutor, actionTypeRegistry, taskRunnerFactory, @@ -278,6 +285,8 @@ export class ActionsPlugin implements Plugin, Plugi getUnsecuredSavedObjectsClient, } = this; + licenseState?.setNotifyUsage(plugins.licensing.featureUsage.notifyUsage); + const encryptedSavedObjectsClient = plugins.encryptedSavedObjects.getClient({ includedHiddenTypes, }); @@ -368,11 +377,15 @@ export class ActionsPlugin implements Plugin, Plugi scheduleActionsTelemetry(this.telemetryLogger, plugins.taskManager); return { - isActionTypeEnabled: (id) => { - return this.actionTypeRegistry!.isActionTypeEnabled(id); + isActionTypeEnabled: (id, options = { notifyUsage: false }) => { + return this.actionTypeRegistry!.isActionTypeEnabled(id, options); }, - isActionExecutable: (actionId: string, actionTypeId: string) => { - return this.actionTypeRegistry!.isActionExecutable(actionId, actionTypeId); + isActionExecutable: ( + actionId: string, + actionTypeId: string, + options = { notifyUsage: false } + ) => { + return this.actionTypeRegistry!.isActionExecutable(actionId, actionTypeId, options); }, getActionsAuthorizationWithRequest(request: KibanaRequest) { return instantiateAuthorization(request); diff --git a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts index ef3a9e42b983f..88abce7298622 100644 --- a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts @@ -1038,6 +1038,11 @@ export class AlertsClient { const actionsClient = await this.getActionsClient(); const actionIds = [...new Set(alertActions.map((alertAction) => alertAction.id))]; const actionResults = await actionsClient.getBulk(actionIds); + const actionTypeIds = [...new Set(actionResults.map((action) => action.actionTypeId))]; + actionTypeIds.forEach((id) => { + // Notify action type usage via "isActionTypeEnabled" function + actionsClient.isActionTypeEnabled(id, { notifyUsage: true }); + }); alertActions.forEach(({ id, ...alertAction }, i) => { const actionResultValue = actionResults.find((action) => action.id === id); if (actionResultValue) { diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts index 65a30d1750149..56e868732e3fb 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/create.test.ts @@ -10,9 +10,9 @@ import { taskManagerMock } from '../../../../task_manager/server/task_manager.mo import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; -import { actionsClientMock, actionsAuthorizationMock } from '../../../../actions/server/mocks'; +import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; import { AlertsAuthorization } from '../../authorization/alerts_authorization'; -import { ActionsAuthorization } from '../../../../actions/server'; +import { ActionsAuthorization, ActionsClient } from '../../../../actions/server'; import { TaskStatus } from '../../../../task_manager/server'; import { getBeforeSetup, setGlobalDate } from './lib'; @@ -374,6 +374,10 @@ describe('create()', () => { "scheduledTaskId": "task-123", } `); + const actionsClient = (await alertsClientParams.getActionsClient()) as jest.Mocked< + ActionsClient + >; + expect(actionsClient.isActionTypeEnabled).toHaveBeenCalledWith('test', { notifyUsage: true }); }); test('creates an alert with multiple actions', async () => { @@ -690,7 +694,11 @@ describe('create()', () => { test('throws error if loading actions fails', async () => { const data = getMockData(); - const actionsClient = actionsClientMock.create(); + // Reset from default behaviour + const actionsClient = (await alertsClientParams.getActionsClient()) as jest.Mocked< + ActionsClient + >; + actionsClient.getBulk.mockReset(); actionsClient.getBulk.mockRejectedValueOnce(new Error('Test Error')); alertsClientParams.getActionsClient.mockResolvedValue(actionsClient); await expect(alertsClient.create({ data })).rejects.toThrowErrorMatchingInlineSnapshot( diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/update.test.ts b/x-pack/plugins/alerts/server/alerts_client/tests/update.test.ts index 14275575f75f4..60b5b62954f05 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/update.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client/tests/update.test.ts @@ -15,7 +15,7 @@ import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/s import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; import { AlertsAuthorization } from '../../authorization/alerts_authorization'; import { resolvable } from '../../test_utils'; -import { ActionsAuthorization } from '../../../../actions/server'; +import { ActionsAuthorization, ActionsClient } from '../../../../actions/server'; import { TaskStatus } from '../../../../task_manager/server'; import { getBeforeSetup, setGlobalDate } from './lib'; @@ -319,6 +319,11 @@ describe('update()', () => { "version": "123", } `); + const actionsClient = (await alertsClientParams.getActionsClient()) as jest.Mocked< + ActionsClient + >; + expect(actionsClient.isActionTypeEnabled).toHaveBeenCalledWith('test', { notifyUsage: true }); + expect(actionsClient.isActionTypeEnabled).toHaveBeenCalledWith('test2', { notifyUsage: true }); }); it('calls the createApiKey function', async () => { diff --git a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts index aca447b6adedd..21e642d228b4d 100644 --- a/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerts/server/task_runner/create_execution_handler.ts @@ -86,7 +86,9 @@ export function createExecutionHandler({ const alertLabel = `${alertType.id}:${alertId}: '${alertName}'`; for (const action of actions) { - if (!actionsPlugin.isActionExecutable(action.id, action.actionTypeId)) { + if ( + !actionsPlugin.isActionExecutable(action.id, action.actionTypeId, { notifyUsage: true }) + ) { logger.warn( `Alert "${alertId}" skipped scheduling action "${action.id}" because it is disabled` ); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts index f3542c728845d..2fa9fbe18730c 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts @@ -54,5 +54,31 @@ export default function createActionTests({ getService }: FtrProviderContext) { id: response.body.id, }); }); + + it('should notify feature usage when creating a gold action type', async () => { + const testStart = new Date(); + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'Noop action type', + actionTypeId: 'test.noop', + secrets: {}, + config: {}, + }) + .expect(200); + objectRemover.add(Spaces.space1.id, response.body.id, 'action', 'actions'); + + const { + body: { features }, + } = await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/licensing/feature_usage`); + expect(features).to.be.an(Array); + const noopFeature = features.find( + (feature: { name: string }) => feature.name === 'Connector: Test: Noop' + ); + expect(noopFeature).to.be.ok(); + expect(noopFeature.last_used).to.be.a('string'); + expect(new Date(noopFeature.last_used).getTime()).to.be.greaterThan(testStart.getTime()); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts index f74c6eaa3298a..2316585d2d0f4 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/execute.ts @@ -216,6 +216,40 @@ export default function ({ getService }: FtrProviderContext) { }, }); }); + + it('should notify feature usage when executing a gold action type', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'Noop action type', + actionTypeId: 'test.noop', + secrets: {}, + config: {}, + }) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions'); + + const executionStart = new Date(); + await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}/_execute`) + .set('kbn-xsrf', 'foo') + .send({ + params: {}, + }) + .expect(200); + + const { + body: { features }, + } = await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/licensing/feature_usage`); + expect(features).to.be.an(Array); + const noopFeature = features.find( + (feature: { name: string }) => feature.name === 'Connector: Test: Noop' + ); + expect(noopFeature).to.be.ok(); + expect(noopFeature.last_used).to.be.a('string'); + expect(new Date(noopFeature.last_used).getTime()).to.be.greaterThan(executionStart.getTime()); + }); }); interface ValidateEventLogParams { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts index 81db8177b2c11..e06aec72f1874 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import expect from '@kbn/expect'; import { Spaces } from '../../scenarios'; import { checkAAD, getUrlPrefix, ObjectRemover } from '../../../common/lib'; import { FtrProviderContext } from '../../../common/ftr_provider_context'; @@ -120,5 +121,41 @@ export default function updateActionTests({ getService }: FtrProviderContext) { message: `Preconfigured action custom-system-abc-connector is not allowed to update.`, }); }); + + it('should notify feature usage when editing a gold action type', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'Noop action type', + actionTypeId: 'test.noop', + secrets: {}, + config: {}, + }) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAction.id, 'action', 'actions'); + + const updateStart = new Date(); + await supertest + .put(`${getUrlPrefix(Spaces.space1.id)}/api/actions/action/${createdAction.id}`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'Noop action type updated', + secrets: {}, + config: {}, + }) + .expect(200); + + const { + body: { features }, + } = await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/licensing/feature_usage`); + expect(features).to.be.an(Array); + const noopFeature = features.find( + (feature: { name: string }) => feature.name === 'Connector: Test: Noop' + ); + expect(noopFeature).to.be.ok(); + expect(noopFeature.last_used).to.be.a('string'); + expect(new Date(noopFeature.last_used).getTime()).to.be.greaterThan(updateStart.getTime()); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts index b94a547452377..40d88a6bface5 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/alerts_base.ts @@ -343,5 +343,30 @@ instanceStateValue: true }, }); }); + + it('should notify feature usage when using a gold action type', async () => { + const testStart = new Date(); + const reference = alertUtils.generateReference(); + const response = await alertUtils.createAlwaysFiringAction({ reference }); + expect(response.statusCode).to.eql(200); + + // Wait for alert to run + await esTestIndexTool.waitForDocs('action:test.index-record', reference); + + const { + body: { features }, + } = await supertestWithoutAuth.get(`${getUrlPrefix(space.id)}/api/licensing/feature_usage`); + expect(features).to.be.an(Array); + const indexRecordFeature = features.find( + (feature: { name: string }) => feature.name === 'Connector: Test: Index Record' + ); + expect(indexRecordFeature).to.be.ok(); + expect(indexRecordFeature.last_used).to.be.a('string'); + expect(new Date(indexRecordFeature.last_used).getTime()).to.be.greaterThan( + testStart.getTime() + ); + + await taskManagerUtils.waitForActionTaskParamsToBeCleanedUp(testStart); + }); }); } From 20edc752765efe7ca720d908f778ae08e85db466 Mon Sep 17 00:00:00 2001 From: Luke Elmers Date: Thu, 15 Oct 2020 12:59:28 -0600 Subject: [PATCH 107/128] [data.ui] Fix flaky test & lazy loading rendering artifacts. (#80612) --- .../apply_filters/apply_filters_popover.tsx | 7 +- .../data/public/ui/filter_bar/index.tsx | 7 +- .../public/ui/index_pattern_select/index.tsx | 7 +- .../public/ui/query_string_input/index.tsx | 7 +- .../query_string_input/query_bar_top_row.tsx | 6 +- .../data/public/ui/search_bar/index.tsx | 7 +- .../public/ui/search_bar/search_bar.test.tsx | 104 +++++++----------- .../data/public/ui/search_bar/search_bar.tsx | 2 +- .../public/ui/shard_failure_modal/index.tsx | 7 +- .../data/public/ui/typeahead/index.tsx | 7 +- 10 files changed, 54 insertions(+), 107 deletions(-) diff --git a/src/plugins/data/public/ui/apply_filters/apply_filters_popover.tsx b/src/plugins/data/public/ui/apply_filters/apply_filters_popover.tsx index 80e1a26163b72..19606cafc5c8a 100644 --- a/src/plugins/data/public/ui/apply_filters/apply_filters_popover.tsx +++ b/src/plugins/data/public/ui/apply_filters/apply_filters_popover.tsx @@ -18,17 +18,12 @@ */ import React from 'react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import { IIndexPattern, Filter } from '../..'; type CancelFnType = () => void; type SubmitFnType = (filters: Filter[]) => void; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazyApplyFiltersPopoverContent = React.lazy(() => import('./apply_filter_popover_content')); diff --git a/src/plugins/data/public/ui/filter_bar/index.tsx b/src/plugins/data/public/ui/filter_bar/index.tsx index b4296bb6615d4..4d9ba69afd48e 100644 --- a/src/plugins/data/public/ui/filter_bar/index.tsx +++ b/src/plugins/data/public/ui/filter_bar/index.tsx @@ -18,14 +18,9 @@ */ import React from 'react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import type { FilterLabelProps } from './filter_editor/lib/filter_label'; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazyFilterLabel = React.lazy(() => import('./filter_editor/lib/filter_label')); export const FilterLabel = (props: FilterLabelProps) => ( diff --git a/src/plugins/data/public/ui/index_pattern_select/index.tsx b/src/plugins/data/public/ui/index_pattern_select/index.tsx index f0db37eb963fd..c909b202a4094 100644 --- a/src/plugins/data/public/ui/index_pattern_select/index.tsx +++ b/src/plugins/data/public/ui/index_pattern_select/index.tsx @@ -18,14 +18,9 @@ */ import React from 'react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import type { IndexPatternSelectInternalProps } from './index_pattern_select'; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazyIndexPatternSelect = React.lazy(() => import('./index_pattern_select')); export const IndexPatternSelect = (props: IndexPatternSelectInternalProps) => ( diff --git a/src/plugins/data/public/ui/query_string_input/index.tsx b/src/plugins/data/public/ui/query_string_input/index.tsx index 5bc5bd5097969..eb6641bf3661e 100644 --- a/src/plugins/data/public/ui/query_string_input/index.tsx +++ b/src/plugins/data/public/ui/query_string_input/index.tsx @@ -18,16 +18,11 @@ */ import React from 'react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import { withKibana } from '../../../../kibana_react/public'; import type { QueryBarTopRowProps } from './query_bar_top_row'; import type { QueryStringInputProps } from './query_string_input'; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazyQueryBarTopRow = React.lazy(() => import('./query_bar_top_row')); export const QueryBarTopRow = (props: QueryBarTopRowProps) => ( diff --git a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx index e01fbedbe38de..7a44b924870f0 100644 --- a/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx +++ b/src/plugins/data/public/ui/query_string_input/query_bar_top_row.tsx @@ -36,12 +36,14 @@ import { EuiSuperUpdateButton, OnRefreshProps } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { Toast } from 'src/core/public'; import { IDataPluginServices, IIndexPattern, TimeRange, TimeHistoryContract, Query } from '../..'; -import { useKibana, toMountPoint } from '../../../../kibana_react/public'; -import { QueryStringInput } from './'; +import { useKibana, toMountPoint, withKibana } from '../../../../kibana_react/public'; +import QueryStringInputUI from './query_string_input'; import { doesKueryExpressionHaveLuceneSyntaxError, UI_SETTINGS } from '../../../common'; import { PersistedLog, getQueryLog } from '../../query'; import { NoDataPopover } from './no_data_popover'; +const QueryStringInput = withKibana(QueryStringInputUI); + // @internal export interface QueryBarTopRowProps { query?: Query; diff --git a/src/plugins/data/public/ui/search_bar/index.tsx b/src/plugins/data/public/ui/search_bar/index.tsx index d81ed7333655d..310542f4b12bd 100644 --- a/src/plugins/data/public/ui/search_bar/index.tsx +++ b/src/plugins/data/public/ui/search_bar/index.tsx @@ -19,15 +19,10 @@ import React from 'react'; import { injectI18n } from '@kbn/i18n/react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import { withKibana } from '../../../../kibana_react/public'; import type { SearchBarProps } from './search_bar'; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazySearchBar = React.lazy(() => import('./search_bar')); const WrappedSearchBar = (props: SearchBarProps) => ( diff --git a/src/plugins/data/public/ui/search_bar/search_bar.test.tsx b/src/plugins/data/public/ui/search_bar/search_bar.test.tsx index a89b9bb7f91ef..74992f35fffc8 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.test.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.test.tsx @@ -18,10 +18,7 @@ */ import React from 'react'; -import { waitFor } from '@testing-library/dom'; -import { render } from '@testing-library/react'; - -import { SearchBar } from './'; +import SearchBar from './search_bar'; import { KibanaContextProvider } from 'src/plugins/kibana_react/public'; import { I18nProvider } from '@kbn/i18n/react'; @@ -29,6 +26,7 @@ import { I18nProvider } from '@kbn/i18n/react'; import { coreMock } from '../../../../../core/public/mocks'; const startMock = coreMock.createStart(); +import { mount } from 'enzyme'; import { IIndexPattern } from '../..'; const mockTimeHistory = { @@ -37,16 +35,14 @@ const mockTimeHistory = { }, }; -jest.mock('..', () => { +jest.mock('../filter_bar/filter_bar', () => { return { FilterBar: () =>
, }; }); -jest.mock('../query_string_input', () => { - return { - QueryBarTopRow: () =>
, - }; +jest.mock('../query_string_input/query_bar_top_row', () => { + return () =>
; }); const noop = jest.fn(); @@ -117,48 +113,42 @@ function wrapSearchBarInContext(testProps: any) { ); } -// FLAKY: https://github.com/elastic/kibana/issues/79910 -describe.skip('SearchBar', () => { - const SEARCH_BAR_TEST_ID = 'globalQueryBar'; +describe('SearchBar', () => { const SEARCH_BAR_ROOT = '.globalQueryBar'; - const FILTER_BAR = '.globalFilterBar'; + const FILTER_BAR = '.filterBar'; const QUERY_BAR = '.queryBar'; beforeEach(() => { jest.clearAllMocks(); }); - it('Should render query bar when no options provided (in reality - timepicker)', async () => { - const { container, getByTestId } = render( + it('Should render query bar when no options provided (in reality - timepicker)', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(0); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(1); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(0); + expect(component.find(QUERY_BAR).length).toBe(1); }); - it('Should render empty when timepicker is off and no options provided', async () => { - const { container, getByTestId } = render( + it('Should render empty when timepicker is off and no options provided', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], showDatePicker: false, }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(0); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(0); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(0); + expect(component.find(QUERY_BAR).length).toBe(0); }); - it('Should render filter bar, when required fields are provided', async () => { - const { container, getByTestId } = render( + it('Should render filter bar, when required fields are provided', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], showDatePicker: false, @@ -167,15 +157,13 @@ describe.skip('SearchBar', () => { }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(1); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(0); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(1); + expect(component.find(QUERY_BAR).length).toBe(0); }); - it('Should NOT render filter bar, if disabled', async () => { - const { container, getByTestId } = render( + it('Should NOT render filter bar, if disabled', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], showFilterBar: false, @@ -185,15 +173,13 @@ describe.skip('SearchBar', () => { }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(0); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(0); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(0); + expect(component.find(QUERY_BAR).length).toBe(0); }); - it('Should render query bar, when required fields are provided', async () => { - const { container, getByTestId } = render( + it('Should render query bar, when required fields are provided', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], screenTitle: 'test screen', @@ -202,15 +188,13 @@ describe.skip('SearchBar', () => { }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(0); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(1); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(0); + expect(component.find(QUERY_BAR).length).toBe(1); }); - it('Should NOT render query bar, if disabled', async () => { - const { container, getByTestId } = render( + it('Should NOT render query bar, if disabled', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], screenTitle: 'test screen', @@ -220,15 +204,13 @@ describe.skip('SearchBar', () => { }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(0); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(0); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(0); + expect(component.find(QUERY_BAR).length).toBe(0); }); - it('Should render query bar and filter bar', async () => { - const { container, getByTestId } = render( + it('Should render query bar and filter bar', () => { + const component = mount( wrapSearchBarInContext({ indexPatterns: [mockIndexPattern], screenTitle: 'test screen', @@ -239,10 +221,8 @@ describe.skip('SearchBar', () => { }) ); - await waitFor(() => getByTestId(SEARCH_BAR_TEST_ID)); - - expect(container.querySelectorAll(SEARCH_BAR_ROOT).length).toBe(1); - expect(container.querySelectorAll(FILTER_BAR).length).toBe(1); - expect(container.querySelectorAll(QUERY_BAR).length).toBe(1); + expect(component.find(SEARCH_BAR_ROOT).length).toBe(1); + expect(component.find(FILTER_BAR).length).toBe(1); + expect(component.find(QUERY_BAR).length).toBe(1); }); }); diff --git a/src/plugins/data/public/ui/search_bar/search_bar.tsx b/src/plugins/data/public/ui/search_bar/search_bar.tsx index 95651ac9ed8b3..daa6fa0dd80ab 100644 --- a/src/plugins/data/public/ui/search_bar/search_bar.tsx +++ b/src/plugins/data/public/ui/search_bar/search_bar.tsx @@ -26,7 +26,7 @@ import { get, isEqual } from 'lodash'; import { withKibana, KibanaReactContextValue } from '../../../../kibana_react/public'; -import { QueryBarTopRow } from '../query_string_input'; +import QueryBarTopRow from '../query_string_input/query_bar_top_row'; import { SavedQueryAttributes, TimeHistoryContract, SavedQuery } from '../../query'; import { IDataPluginServices } from '../../types'; import { TimeRange, Query, Filter, IIndexPattern } from '../../../common'; diff --git a/src/plugins/data/public/ui/shard_failure_modal/index.tsx b/src/plugins/data/public/ui/shard_failure_modal/index.tsx index cea882deff365..2ac470573c422 100644 --- a/src/plugins/data/public/ui/shard_failure_modal/index.tsx +++ b/src/plugins/data/public/ui/shard_failure_modal/index.tsx @@ -18,14 +18,9 @@ */ import React from 'react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import type { ShardFailureOpenModalButtonProps } from './shard_failure_open_modal_button'; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazyShardFailureOpenModalButton = React.lazy( () => import('./shard_failure_open_modal_button') diff --git a/src/plugins/data/public/ui/typeahead/index.tsx b/src/plugins/data/public/ui/typeahead/index.tsx index aa3c2d71300df..58547cd2ccbec 100644 --- a/src/plugins/data/public/ui/typeahead/index.tsx +++ b/src/plugins/data/public/ui/typeahead/index.tsx @@ -18,14 +18,9 @@ */ import React from 'react'; -import { EuiLoadingContent, EuiDelayRender } from '@elastic/eui'; import type { SuggestionsComponentProps } from './suggestions_component'; -const Fallback = () => ( - - - -); +const Fallback = () =>
; const LazySuggestionsComponent = React.lazy(() => import('./suggestions_component')); export const SuggestionsComponent = (props: SuggestionsComponentProps) => ( From e1aec1791026e0deb9773536fd4c677cb80ffead Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Thu, 15 Oct 2020 13:21:03 -0600 Subject: [PATCH 108/128] [Security Solutions][Detection Engine] Fixes pre-packaged rules which contain exception lists to not overwrite user defined lists (#80592) ## Summary Fixes a bug where when you update your pre-packaged rules you could end up removing any existing exception lists the user might have already added. See: https://github.com/elastic/kibana/issues/80417 * Fixes the merge logic so that any exception lists from pre-packaged rules will be additive if they do not already exist on the rule. User based exception lists will not be lost. * Added new backend integration tests for exception lists that did not exist before including ones that test the functionality of exception lists * Refactored some of the code in the `get_rules_to_update.ts` * Refactored some of the integration tests to use helper utils of `countDownES`, and `countDownTest` which simplify the retry logic within the integration tests * Added unit tests to exercise the bug and then the fix. * Added integration tests that fail this logic and then fixed the logic ### Checklist Delete any items that are not applicable to this PR. - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --- .../rules/get_rules_to_update.test.ts | 516 +++++++++++-- .../rules/get_rules_to_update.ts | 64 +- .../tests/create_exceptions.ts | 704 ++++++++++++++++++ .../tests/create_threat_matching.ts | 2 +- .../security_and_spaces/tests/index.ts | 1 + .../detection_engine_api_integration/utils.ts | 218 ++++-- x-pack/test/lists_api_integration/utils.ts | 88 +-- 7 files changed, 1377 insertions(+), 216 deletions(-) create mode 100644 x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_update.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_update.test.ts index 5b2eb24bcdcd2..a21f861c1f348 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_update.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_update.test.ts @@ -4,102 +4,470 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getRulesToUpdate } from './get_rules_to_update'; +import { filterInstalledRules, getRulesToUpdate, mergeExceptionLists } from './get_rules_to_update'; import { getResult } from '../routes/__mocks__/request_responses'; import { getAddPrepackagedRulesSchemaDecodedMock } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema.mock'; describe('get_rules_to_update', () => { - test('should return empty array if both rule sets are empty', () => { - const update = getRulesToUpdate([], []); - expect(update).toEqual([]); - }); + describe('get_rules_to_update', () => { + test('should return empty array if both rule sets are empty', () => { + const update = getRulesToUpdate([], []); + expect(update).toEqual([]); + }); - test('should return empty array if the id of the two rules do not match', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; + test('should return empty array if the rule_id of the two rules do not match', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; - const installedRule = getResult(); - installedRule.params.ruleId = 'rule-2'; - installedRule.params.version = 1; - const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); - expect(update).toEqual([]); - }); + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-2'; + installedRule.params.version = 1; + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([]); + }); - test('should return empty array if the id of file system rule is less than the installed version', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 1; + test('should return empty array if the version of file system rule is less than the installed version', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 1; - const installedRule = getResult(); - installedRule.params.ruleId = 'rule-1'; - installedRule.params.version = 2; - const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); - expect(update).toEqual([]); - }); + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 2; + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([]); + }); - test('should return empty array if the id of file system rule is the same as the installed version', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 1; + test('should return empty array if the version of file system rule is the same as the installed version', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 1; - const installedRule = getResult(); - installedRule.params.ruleId = 'rule-1'; - installedRule.params.version = 1; - const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); - expect(update).toEqual([]); - }); + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 1; + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([]); + }); + + test('should return the rule to update if the version of file system rule is greater than the installed version', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; + + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 1; + installedRule.params.exceptionsList = []; + + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); + expect(update).toEqual([ruleFromFileSystem]); + }); + + test('should return 1 rule out of 2 to update if the version of file system rule is greater than the installed version of just one', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = []; + + const installedRule2 = getResult(); + installedRule2.params.ruleId = 'rule-2'; + installedRule2.params.version = 1; + installedRule2.params.exceptionsList = []; + + const update = getRulesToUpdate([ruleFromFileSystem], [installedRule1, installedRule2]); + expect(update).toEqual([ruleFromFileSystem]); + }); + + test('should return 2 rules out of 2 to update if the version of file system rule is greater than the installed version of both', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem2.rule_id = 'rule-2'; + ruleFromFileSystem2.version = 2; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = []; + + const installedRule2 = getResult(); + installedRule2.params.ruleId = 'rule-2'; + installedRule2.params.version = 1; + installedRule2.params.exceptionsList = []; + + const update = getRulesToUpdate( + [ruleFromFileSystem1, ruleFromFileSystem2], + [installedRule1, installedRule2] + ); + expect(update).toEqual([ruleFromFileSystem1, ruleFromFileSystem2]); + }); + + test('should add back an exception_list if it was removed by the end user on an immutable rule during an upgrade', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = []; + + const [update] = getRulesToUpdate([ruleFromFileSystem1], [installedRule1]); + expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); + }); + + test('should not remove an additional exception_list if an additional one was added by the end user on an immutable rule during an upgrade', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'second_exception_list', + list_id: 'some-other-id', + namespace_type: 'single', + type: 'detection', + }, + ]; + + const [update] = getRulesToUpdate([ruleFromFileSystem1], [installedRule1]); + expect(update.exceptions_list).toEqual([ + ...ruleFromFileSystem1.exceptions_list, + ...installedRule1.params.exceptionsList, + ]); + }); + + test('should not remove an existing exception_list if they are the same between the current installed one and the upgraded one', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; - test('should return the rule to update if the id of file system rule is greater than the installed version', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; + const [update] = getRulesToUpdate([ruleFromFileSystem1], [installedRule1]); + expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); + }); - const installedRule = getResult(); - installedRule.params.ruleId = 'rule-1'; - installedRule.params.version = 1; - const update = getRulesToUpdate([ruleFromFileSystem], [installedRule]); - expect(update).toEqual([ruleFromFileSystem]); + test('should not remove an existing exception_list if the rule has an empty exceptions list', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = []; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + + const [update] = getRulesToUpdate([ruleFromFileSystem1], [installedRule1]); + expect(update.exceptions_list).toEqual(installedRule1.params.exceptionsList); + }); + + test('should not remove an existing exception_list if the rule has an empty exceptions list for multiple rules', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = []; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem2.exceptions_list = []; + ruleFromFileSystem2.rule_id = 'rule-2'; + ruleFromFileSystem2.version = 2; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + const installedRule2 = getResult(); + installedRule2.params.ruleId = 'rule-2'; + installedRule2.params.version = 1; + installedRule2.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + + const [update1, update2] = getRulesToUpdate( + [ruleFromFileSystem1, ruleFromFileSystem2], + [installedRule1, installedRule2] + ); + expect(update1.exceptions_list).toEqual(installedRule1.params.exceptionsList); + expect(update2.exceptions_list).toEqual(installedRule2.params.exceptionsList); + }); + + test('should not remove an existing exception_list if the rule has an empty exceptions list for mixed rules', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = []; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem2.exceptions_list = []; + ruleFromFileSystem2.rule_id = 'rule-2'; + ruleFromFileSystem2.version = 2; + ruleFromFileSystem2.exceptions_list = [ + { + id: 'second_list', + list_id: 'second_list', + namespace_type: 'single', + type: 'detection', + }, + ]; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + + const installedRule2 = getResult(); + installedRule2.params.ruleId = 'rule-2'; + installedRule2.params.version = 1; + installedRule2.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + + const [update1, update2] = getRulesToUpdate( + [ruleFromFileSystem1, ruleFromFileSystem2], + [installedRule1, installedRule2] + ); + expect(update1.exceptions_list).toEqual(installedRule1.params.exceptionsList); + expect(update2.exceptions_list).toEqual([ + ...ruleFromFileSystem2.exceptions_list, + ...installedRule2.params.exceptionsList, + ]); + }); }); - test('should return 1 rule out of 2 to update if the id of file system rule is greater than the installed version of just one', () => { - const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem.rule_id = 'rule-1'; - ruleFromFileSystem.version = 2; + describe('filterInstalledRules', () => { + test('should return "false" if the id of the two rules do not match', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; + + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-2'; + installedRule.params.version = 1; + const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]); + expect(shouldUpdate).toEqual(false); + }); - const installedRule1 = getResult(); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; + test('should return "false" if the version of file system rule is less than the installed version', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 1; - const installedRule2 = getResult(); - installedRule2.params.ruleId = 'rule-2'; - installedRule2.params.version = 1; + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 2; + const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]); + expect(shouldUpdate).toEqual(false); + }); - const update = getRulesToUpdate([ruleFromFileSystem], [installedRule1, installedRule2]); - expect(update).toEqual([ruleFromFileSystem]); + test('should return "false" if the version of file system rule is the same as the installed version', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 1; + + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 1; + const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]); + expect(shouldUpdate).toEqual(false); + }); + + test('should return "true" to update if the version of file system rule is greater than the installed version', () => { + const ruleFromFileSystem = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem.rule_id = 'rule-1'; + ruleFromFileSystem.version = 2; + + const installedRule = getResult(); + installedRule.params.ruleId = 'rule-1'; + installedRule.params.version = 1; + installedRule.params.exceptionsList = []; + + const shouldUpdate = filterInstalledRules(ruleFromFileSystem, [installedRule]); + expect(shouldUpdate).toEqual(true); + }); }); - test('should return 2 rules out of 2 to update if the id of file system rule is greater than the installed version of both', () => { - const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem1.rule_id = 'rule-1'; - ruleFromFileSystem1.version = 2; + describe('mergeExceptionLists', () => { + test('should add back an exception_list if it was removed by the end user on an immutable rule during an upgrade', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = []; + + const update = mergeExceptionLists(ruleFromFileSystem1, [installedRule1]); + expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); + }); + + test('should not remove an additional exception_list if an additional one was added by the end user on an immutable rule during an upgrade', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'second_exception_list', + list_id: 'some-other-id', + namespace_type: 'single', + type: 'detection', + }, + ]; + + const update = mergeExceptionLists(ruleFromFileSystem1, [installedRule1]); + expect(update.exceptions_list).toEqual([ + ...ruleFromFileSystem1.exceptions_list, + ...installedRule1.params.exceptionsList, + ]); + }); + + test('should not remove an existing exception_list if they are the same between the current installed one and the upgraded one', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; + + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; - const ruleFromFileSystem2 = getAddPrepackagedRulesSchemaDecodedMock(); - ruleFromFileSystem2.rule_id = 'rule-2'; - ruleFromFileSystem2.version = 2; + const update = mergeExceptionLists(ruleFromFileSystem1, [installedRule1]); + expect(update.exceptions_list).toEqual(ruleFromFileSystem1.exceptions_list); + }); - const installedRule1 = getResult(); - installedRule1.params.ruleId = 'rule-1'; - installedRule1.params.version = 1; + test('should not remove an existing exception_list if the rule has an empty exceptions list', () => { + const ruleFromFileSystem1 = getAddPrepackagedRulesSchemaDecodedMock(); + ruleFromFileSystem1.exceptions_list = []; + ruleFromFileSystem1.rule_id = 'rule-1'; + ruleFromFileSystem1.version = 2; - const installedRule2 = getResult(); - installedRule2.params.ruleId = 'rule-2'; - installedRule2.params.version = 1; + const installedRule1 = getResult(); + installedRule1.params.ruleId = 'rule-1'; + installedRule1.params.version = 1; + installedRule1.params.exceptionsList = [ + { + id: 'endpoint_list', + list_id: 'endpoint_list', + namespace_type: 'agnostic', + type: 'endpoint', + }, + ]; - const update = getRulesToUpdate( - [ruleFromFileSystem1, ruleFromFileSystem2], - [installedRule1, installedRule2] - ); - expect(update).toEqual([ruleFromFileSystem1, ruleFromFileSystem2]); + const update = mergeExceptionLists(ruleFromFileSystem1, [installedRule1]); + expect(update.exceptions_list).toEqual(installedRule1.params.exceptionsList); + }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_update.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_update.ts index 577ad44789bdc..28a58ea49b903 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_update.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_rules_to_update.ts @@ -7,15 +7,67 @@ import { AddPrepackagedRulesSchemaDecoded } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema'; import { RuleAlertType } from './types'; +/** + * Returns the rules to update by doing a compare to the rules from the file system against + * the installed rules already. This also merges exception list items between the two since + * exception list items can exist on both rules to update and already installed rules. + * @param rulesFromFileSystem The rules on the file system to check against installed + * @param installedRules The installed rules + */ export const getRulesToUpdate = ( rulesFromFileSystem: AddPrepackagedRulesSchemaDecoded[], installedRules: RuleAlertType[] ): AddPrepackagedRulesSchemaDecoded[] => { - return rulesFromFileSystem.filter((rule) => - installedRules.some((installedRule) => { - return ( - rule.rule_id === installedRule.params.ruleId && rule.version > installedRule.params.version + return rulesFromFileSystem + .filter((ruleFromFileSystem) => filterInstalledRules(ruleFromFileSystem, installedRules)) + .map((ruleFromFileSystem) => mergeExceptionLists(ruleFromFileSystem, installedRules)); +}; + +/** + * Filters rules from the file system that do not match the installed rules so you only + * get back rules that are going to be updated + * @param ruleFromFileSystem The rules from the file system to check if any are updates + * @param installedRules The installed rules to compare against for updates + */ +export const filterInstalledRules = ( + ruleFromFileSystem: AddPrepackagedRulesSchemaDecoded, + installedRules: RuleAlertType[] +): boolean => { + return installedRules.some((installedRule) => { + return ( + ruleFromFileSystem.rule_id === installedRule.params.ruleId && + ruleFromFileSystem.version > installedRule.params.version + ); + }); +}; + +/** + * Given a rule from the file system and the set of installed rules this will merge the exception lists + * from the installed rules onto the rules from the file system. + * @param ruleFromFileSystem The rules from the file system that might have exceptions_lists + * @param installedRules The installed rules which might have user driven exceptions_lists + */ +export const mergeExceptionLists = ( + ruleFromFileSystem: AddPrepackagedRulesSchemaDecoded, + installedRules: RuleAlertType[] +): AddPrepackagedRulesSchemaDecoded => { + if (ruleFromFileSystem.exceptions_list != null) { + const installedRule = installedRules.find( + (ruleToFind) => ruleToFind.params.ruleId === ruleFromFileSystem.rule_id + ); + if (installedRule != null && installedRule.params.exceptionsList != null) { + const installedExceptionList = installedRule.params.exceptionsList; + const fileSystemExceptions = ruleFromFileSystem.exceptions_list.filter((potentialDuplicate) => + installedExceptionList.every((item) => item.list_id !== potentialDuplicate.list_id) ); - }) - ); + return { + ...ruleFromFileSystem, + exceptions_list: [...fileSystemExceptions, ...installedRule.params.exceptionsList], + }; + } else { + return ruleFromFileSystem; + } + } else { + return ruleFromFileSystem; + } }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts new file mode 100644 index 0000000000000..42d4b86119bb0 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_exceptions.ts @@ -0,0 +1,704 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* eslint-disable @typescript-eslint/naming-convention */ + +import expect from '@kbn/expect'; +import { SearchResponse } from 'elasticsearch'; +import { Signal } from '../../../../plugins/security_solution/server/lib/detection_engine/signals/types'; +import { getCreateExceptionListItemMinimalSchemaMock } from '../../../../plugins/lists/common/schemas/request/create_exception_list_item_schema.mock'; +import { deleteAllExceptions } from '../../../lists_api_integration/utils'; +import { RulesSchema } from '../../../../plugins/security_solution/common/detection_engine/schemas/response'; +import { CreateRulesSchema } from '../../../../plugins/security_solution/common/detection_engine/schemas/request'; +import { getCreateExceptionListMinimalSchemaMock } from '../../../../plugins/lists/common/schemas/request/create_exception_list_schema.mock'; +import { CreateExceptionListItemSchema } from '../../../../plugins/lists/common'; +import { + EXCEPTION_LIST_ITEM_URL, + EXCEPTION_LIST_URL, +} from '../../../../plugins/lists/common/constants'; + +import { + DETECTION_ENGINE_RULES_URL, + DETECTION_ENGINE_RULES_STATUS_URL, + DETECTION_ENGINE_QUERY_SIGNALS_URL, + DETECTION_ENGINE_PREPACKAGED_URL, +} from '../../../../plugins/security_solution/common/constants'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + createSignalsIndex, + deleteAllAlerts, + deleteSignalsIndex, + getSimpleRule, + getSimpleRuleOutput, + removeServerGeneratedProperties, + waitFor, + getQueryAllSignals, + downgradeImmutableRule, +} from '../../utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const es = getService('es'); + + describe('create_rules_with_exceptions', () => { + describe('creating rules with exceptions', () => { + beforeEach(async () => { + await createSignalsIndex(supertest); + }); + + afterEach(async () => { + await deleteSignalsIndex(supertest); + await deleteAllAlerts(es); + await deleteAllExceptions(es); + }); + + it('should create a single rule with a rule_id and add an exception list to the rule', async () => { + const { + body: { id, list_id, namespace_type, type }, + } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListMinimalSchemaMock()) + .expect(200); + + const ruleWithException: CreateRulesSchema = { + ...getSimpleRule(), + exceptions_list: [ + { + id, + list_id, + namespace_type, + type, + }, + ], + }; + + const { body } = await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(ruleWithException) + .expect(200); + + const expected: Partial = { + ...getSimpleRuleOutput(), + exceptions_list: [ + { + id, + list_id, + namespace_type, + type, + }, + ], + }; + const bodyToCompare = removeServerGeneratedProperties(body); + expect(bodyToCompare).to.eql(expected); + }); + + it('should create a single rule with an exception list and validate it ran successfully', async () => { + const { + body: { id, list_id, namespace_type, type }, + } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListMinimalSchemaMock()) + .expect(200); + const ruleWithException: CreateRulesSchema = { + ...getSimpleRule(), + exceptions_list: [ + { + id, + list_id, + namespace_type, + type, + }, + ], + }; + + const { body } = await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(ruleWithException) + .expect(200); + + // wait for Task Manager to execute the rule and update status + await waitFor(async () => { + const { body: statusBody } = await supertest + .post(DETECTION_ENGINE_RULES_STATUS_URL) + .set('kbn-xsrf', 'true') + .send({ ids: [body.id] }) + .expect(200); + + return statusBody[body.id]?.current_status?.status === 'succeeded'; + }); + + const { body: statusBody } = await supertest + .post(DETECTION_ENGINE_RULES_STATUS_URL) + .set('kbn-xsrf', 'true') + .send({ ids: [body.id] }) + .expect(200); + + const bodyToCompare = removeServerGeneratedProperties(body); + const expected: Partial = { + ...getSimpleRuleOutput(), + exceptions_list: [ + { + id, + list_id, + namespace_type, + type, + }, + ], + }; + expect(bodyToCompare).to.eql(expected); + expect(statusBody[body.id].current_status.status).to.eql('succeeded'); + }); + + it('should allow removing an exception list from an immutable rule through patch', async () => { + // add all the immutable rules from the pre-packaged url + await supertest + .put(DETECTION_ENGINE_PREPACKAGED_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Rule id of "9a1a2dae-0b5f-4c3d-8305-a268d404c306" is from the file: + // x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json + // This rule has an existing exceptions_list that we are going to use + const { body: immutableRule } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=9a1a2dae-0b5f-4c3d-8305-a268d404c306`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one + + // remove the exceptions list as a user is allowed to remove it from an immutable rule + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306', exceptions_list: [] }) + .expect(200); + + const { body } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=9a1a2dae-0b5f-4c3d-8305-a268d404c306`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + + expect(body.exceptions_list.length).to.eql(0); + }); + + it('should allow adding a second exception list to an immutable rule through patch', async () => { + // add all the immutable rules from the pre-packaged url + await supertest + .put(DETECTION_ENGINE_PREPACKAGED_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Create a new exception list + const { + body: { id, list_id, namespace_type, type }, + } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListMinimalSchemaMock()) + .expect(200); + + // Rule id of "9a1a2dae-0b5f-4c3d-8305-a268d404c306" is from the file: + // x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json + // This rule has an existing exceptions_list that we are going to use + const { body: immutableRule } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=9a1a2dae-0b5f-4c3d-8305-a268d404c306`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one + + // add a second exceptions list as a user is allowed to add a second list to an immutable rule + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ + rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306', + exceptions_list: [ + ...immutableRule.exceptions_list, + { + id, + list_id, + namespace_type, + type, + }, + ], + }) + .expect(200); + + const { body } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=9a1a2dae-0b5f-4c3d-8305-a268d404c306`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + + expect(body.exceptions_list.length).to.eql(2); + }); + + it('should override any updates to pre-packaged rules if the user removes the exception list through the API but the new version of a rule has an exception list again', async () => { + await supertest + .put(DETECTION_ENGINE_PREPACKAGED_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Rule id of "9a1a2dae-0b5f-4c3d-8305-a268d404c306" is from the file: + // x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json + // This rule has an existing exceptions_list that we are going to use + const { body: immutableRule } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=9a1a2dae-0b5f-4c3d-8305-a268d404c306`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + expect(immutableRule.exceptions_list.length).greaterThan(0); // make sure we have at least one exception list + + // Rule id of "9a1a2dae-0b5f-4c3d-8305-a268d404c306" is from the file: + // x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json + // This rule has an existing exceptions_list that we are going to ensure does not stomp on our existing rule + // remove the exceptions list as a user is allowed to remove it + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306', exceptions_list: [] }) + .expect(200); + + // downgrade the version number of the rule + await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306'); + + // re-add the pre-packaged rule to get the single upgrade to happen + await supertest + .put(DETECTION_ENGINE_PREPACKAGED_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // get the pre-packaged rule after we upgraded it + const { body } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=9a1a2dae-0b5f-4c3d-8305-a268d404c306`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + + // We should have a length of 1 and it should be the same as our original before we tried to remove it using patch + expect(body.exceptions_list.length).to.eql(1); + expect(body.exceptions_list).to.eql(immutableRule.exceptions_list); + }); + + it('should merge back an exceptions_list if it was removed from the immutable rule through PATCH', async () => { + await supertest + .put(DETECTION_ENGINE_PREPACKAGED_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Create a new exception list + const { + body: { id, list_id, namespace_type, type }, + } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListMinimalSchemaMock()) + .expect(200); + + // Rule id of "9a1a2dae-0b5f-4c3d-8305-a268d404c306" is from the file: + // x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json + // This rule has an existing exceptions_list that we are going to ensure does not stomp on our existing rule + const { body: immutableRule } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=9a1a2dae-0b5f-4c3d-8305-a268d404c306`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + + // remove the exception list and only have a single list that is not an endpoint_list + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ + rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306', + exceptions_list: [ + { + id, + list_id, + namespace_type, + type, + }, + ], + }) + .expect(200); + + // downgrade the version number of the rule + await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306'); + + // re-add the pre-packaged rule to get the single upgrade to happen + await supertest + .put(DETECTION_ENGINE_PREPACKAGED_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // get the immutable rule after we installed it a second time + const { body } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=9a1a2dae-0b5f-4c3d-8305-a268d404c306`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + + // The installed rule should have both the original immutable exceptions list back and the + // new list the user added. + expect(body.exceptions_list).to.eql([ + ...immutableRule.exceptions_list, + { + id, + list_id, + namespace_type, + type, + }, + ]); + }); + + it('should NOT add an extra exceptions_list that already exists on a rule during an upgrade', async () => { + await supertest + .put(DETECTION_ENGINE_PREPACKAGED_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Rule id of "9a1a2dae-0b5f-4c3d-8305-a268d404c306" is from the file: + // x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json + // This rule has an existing exceptions_list that we are going to ensure does not stomp on our existing rule + const { body: immutableRule } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=9a1a2dae-0b5f-4c3d-8305-a268d404c306`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + + // downgrade the version number of the rule + await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306'); + + // re-add the pre-packaged rule to get the single upgrade to happen + await supertest + .put(DETECTION_ENGINE_PREPACKAGED_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // get the immutable rule after we installed it a second time + const { body } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=9a1a2dae-0b5f-4c3d-8305-a268d404c306`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + + // The installed rule should have both the original immutable exceptions list back and the + // new list the user added. + expect(body.exceptions_list).to.eql([...immutableRule.exceptions_list]); + }); + + it('should NOT allow updates to pre-packaged rules to overwrite existing exception based rules when the user adds an additional exception list', async () => { + await supertest + .put(DETECTION_ENGINE_PREPACKAGED_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Create a new exception list + const { + body: { id, list_id, namespace_type, type }, + } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListMinimalSchemaMock()) + .expect(200); + + // Rule id of "9a1a2dae-0b5f-4c3d-8305-a268d404c306" is from the file: + // x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint.json + // This rule has an existing exceptions_list that we are going to ensure does not stomp on our existing rule + const { body: immutableRule } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=9a1a2dae-0b5f-4c3d-8305-a268d404c306`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + + // add a second exceptions list as a user is allowed to add a second list to an immutable rule + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ + rule_id: '9a1a2dae-0b5f-4c3d-8305-a268d404c306', + exceptions_list: [ + ...immutableRule.exceptions_list, + { + id, + list_id, + namespace_type, + type, + }, + ], + }) + .expect(200); + + // downgrade the version number of the rule + await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306'); + + // re-add the pre-packaged rule to get the single upgrade to happen + await supertest + .put(DETECTION_ENGINE_PREPACKAGED_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + const { body } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=9a1a2dae-0b5f-4c3d-8305-a268d404c306`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + + // It should be the same as what the user added originally + expect(body.exceptions_list).to.eql([ + ...immutableRule.exceptions_list, + { + id, + list_id, + namespace_type, + type, + }, + ]); + }); + + it('should not remove any exceptions added to a pre-packaged/immutable rule during an update if that rule has no existing exception lists', async () => { + // add all the immutable rules from the pre-packaged url + await supertest + .put(DETECTION_ENGINE_PREPACKAGED_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // Create a new exception list + const { + body: { id, list_id, namespace_type, type }, + } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListMinimalSchemaMock()) + .expect(200); + + // Rule id of "6d3456a5-4a42-49d1-aaf2-7b1fd475b2c6" is from the file: + // x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules/c2_reg_beacon.json + // since this rule does not have existing exceptions_list that we are going to use for tests + const { body: immutableRule } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=6d3456a5-4a42-49d1-aaf2-7b1fd475b2c6`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + expect(immutableRule.exceptions_list.length).eql(0); // make sure we have no exceptions_list + + // add a second exceptions list as a user is allowed to add a second list to an immutable rule + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send({ + rule_id: '6d3456a5-4a42-49d1-aaf2-7b1fd475b2c6', + exceptions_list: [ + { + id, + list_id, + namespace_type, + type, + }, + ], + }) + .expect(200); + + // downgrade the version number of the rule + await downgradeImmutableRule(es, '9a1a2dae-0b5f-4c3d-8305-a268d404c306'); + + // re-add the pre-packaged rule to get the single upgrade of the rule to happen + await supertest + .put(DETECTION_ENGINE_PREPACKAGED_URL) + .set('kbn-xsrf', 'true') + .send() + .expect(200); + + // ensure that the same exception is still on the rule + const { body } = await supertest + .get(`${DETECTION_ENGINE_RULES_URL}?rule_id=6d3456a5-4a42-49d1-aaf2-7b1fd475b2c6`) + .set('kbn-xsrf', 'true') + .send(getSimpleRule()) + .expect(200); + + expect(body.exceptions_list).to.eql([ + { + id, + list_id, + namespace_type, + type, + }, + ]); + }); + + describe('tests with auditbeat data', () => { + beforeEach(async () => { + await createSignalsIndex(supertest); + await esArchiver.load('auditbeat/hosts'); + }); + + afterEach(async () => { + await deleteSignalsIndex(supertest); + await deleteAllAlerts(es); + await deleteAllExceptions(es); + await esArchiver.unload('auditbeat/hosts'); + }); + + it('should be able to execute against an exception list that does not include valid entries and get back 10 signals', async () => { + const { + body: { id, list_id, namespace_type, type }, + } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListMinimalSchemaMock()) + .expect(200); + + const exceptionListItem: CreateExceptionListItemSchema = { + ...getCreateExceptionListItemMinimalSchemaMock(), + entries: [ + { + field: 'some.none.existent.field', // non-existent field where we should not exclude anything + operator: 'included', + type: 'match', + value: 'some value', + }, + ], + }; + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send(exceptionListItem) + .expect(200); + + const ruleWithException: CreateRulesSchema = { + ...getSimpleRule(), + from: '1900-01-01T00:00:00.000Z', + query: 'host.name: "suricata-sensor-amsterdam"', + exceptions_list: [ + { + id, + list_id, + namespace_type, + type, + }, + ], + }; + + await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(ruleWithException) + .expect(200); + + // wait until rules show up and are present + await waitFor(async () => { + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + return signalsOpen.hits.hits.length > 0; + }); + + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + + // expect there to be 10 + expect(signalsOpen.hits.hits.length).equal(10); + }); + + it('should be able to execute against an exception list that does include valid entries and get back 0 signals', async () => { + const { + body: { id, list_id, namespace_type, type }, + } = await supertest + .post(EXCEPTION_LIST_URL) + .set('kbn-xsrf', 'true') + .send(getCreateExceptionListMinimalSchemaMock()) + .expect(200); + + const exceptionListItem: CreateExceptionListItemSchema = { + ...getCreateExceptionListItemMinimalSchemaMock(), + entries: [ + { + field: 'host.name', // This matches the query below which will exclude everything + operator: 'included', + type: 'match', + value: 'suricata-sensor-amsterdam', + }, + ], + }; + await supertest + .post(EXCEPTION_LIST_ITEM_URL) + .set('kbn-xsrf', 'true') + .send(exceptionListItem) + .expect(200); + + const ruleWithException: CreateRulesSchema = { + ...getSimpleRule(), + from: '1900-01-01T00:00:00.000Z', + query: 'host.name: "suricata-sensor-amsterdam"', // this matches all the exceptions we should exclude + exceptions_list: [ + { + id, + list_id, + namespace_type, + type, + }, + ], + }; + + const { body: resBody } = await supertest + .post(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .send(ruleWithException) + .expect(200); + + // wait for Task Manager to finish executing the rule + await waitFor(async () => { + const { body } = await supertest + .post(`${DETECTION_ENGINE_RULES_URL}/_find_statuses`) + .set('kbn-xsrf', 'true') + .send({ ids: [resBody.id] }) + .expect(200); + return body[resBody.id]?.current_status?.status === 'succeeded'; + }); + + // Get the signals now that we are done running and expect the result to always be zero + const { + body: signalsOpen, + }: { body: SearchResponse<{ signal: Signal }> } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getQueryAllSignals()) + .expect(200); + + // expect there to be 10 + expect(signalsOpen.hits.hits.length).equal(0); + }); + }); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts index 620e771b3446d..6d3a0ce683cda 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_threat_matching.ts @@ -170,7 +170,7 @@ export default ({ getService }: FtrProviderContext) => { expect(signalsOpen.hits.hits.length).equal(10); }); - it('should be return zero matches if the mapping does not match against anything in the mapping', async () => { + it('should return zero matches if the mapping does not match against anything in the mapping', async () => { const rule: CreateRulesSchema = { ...getCreateThreatMatchRulesSchemaMock(), from: '1900-01-01T00:00:00.000Z', diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts index cc0eb04075b77..24b76853164f2 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts @@ -16,6 +16,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./create_rules')); loadTestFile(require.resolve('./create_rules_bulk')); loadTestFile(require.resolve('./create_threat_matching')); + loadTestFile(require.resolve('./create_exceptions')); loadTestFile(require.resolve('./delete_rules')); loadTestFile(require.resolve('./delete_rules_bulk')); loadTestFile(require.resolve('./export_rules')); diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index 5d82eed41d3c5..db91529b8a2c3 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -4,9 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Client } from '@elastic/elasticsearch'; +import { ApiResponse, Client } from '@elastic/elasticsearch'; import { SuperTest } from 'supertest'; import supertestAsPromised from 'supertest-as-promised'; +import { Context } from '@elastic/elasticsearch/lib/Transport'; import { Status, SignalIds, @@ -14,7 +15,10 @@ import { import { CreateRulesSchema } from '../../plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema'; import { UpdateRulesSchema } from '../../plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema'; import { RulesSchema } from '../../plugins/security_solution/common/detection_engine/schemas/response/rules_schema'; -import { DETECTION_ENGINE_INDEX_URL } from '../../plugins/security_solution/common/constants'; +import { + DETECTION_ENGINE_INDEX_URL, + INTERNAL_RULE_ID_KEY, +} from '../../plugins/security_solution/common/constants'; /** * This will remove server generated properties such as date times, etc... @@ -245,34 +249,38 @@ export const getSimpleMlRuleOutput = (ruleId = 'rule-1'): Partial = * This will retry 20 times before giving up and hopefully still not interfere with other tests * @param es The ElasticSearch handle */ -export const deleteAllAlerts = async (es: Client, retryCount = 20): Promise => { - if (retryCount > 0) { - try { - const result = await es.deleteByQuery({ - index: '.kibana', - q: 'type:alert', - wait_for_completion: true, - refresh: true, - conflicts: 'proceed', - body: {}, - }); - // deleteByQuery will cause version conflicts as alerts are being updated - // by background processes; the code below accounts for that - if (result.body.version_conflicts !== 0) { - throw new Error(`Version conflicts for ${result.body.version_conflicts} alerts`); - } - } catch (err) { - // eslint-disable-next-line no-console - console.log(`Error in deleteAllAlerts(), retries left: ${retryCount - 1}`, err); +export const deleteAllAlerts = async (es: Client): Promise => { + return countDownES(async () => { + return es.deleteByQuery({ + index: '.kibana', + q: 'type:alert', + wait_for_completion: true, + refresh: true, + conflicts: 'proceed', + body: {}, + }); + }, 'deleteAllAlerts'); +}; - // retry, counting down, and delay a bit before - await new Promise((resolve) => setTimeout(resolve, 250)); - await deleteAllAlerts(es, retryCount - 1); - } - } else { - // eslint-disable-next-line no-console - console.log('Could not deleteAllAlerts, no retries are left'); - } +export const downgradeImmutableRule = async (es: Client, ruleId: string): Promise => { + return countDownES(async () => { + return es.updateByQuery({ + index: '.kibana', + refresh: true, + wait_for_completion: true, + body: { + script: { + lang: 'painless', + source: 'ctx._source.alert.params.version--', + }, + query: { + term: { + 'alert.tags': `${INTERNAL_RULE_ID_KEY}:${ruleId}`, + }, + }, + }, + }); + }, 'downgradeImmutableRule'); }; /** @@ -295,27 +303,15 @@ export const deleteAllTimelines = async (es: Client): Promise => { * @param es The ElasticSearch handle */ export const deleteAllRulesStatuses = async (es: Client, retryCount = 20): Promise => { - if (retryCount > 0) { - try { - await es.deleteByQuery({ - index: '.kibana', - q: 'type:siem-detection-engine-rule-status', - wait_for_completion: true, - refresh: true, - body: {}, - }); - } catch (err) { - // eslint-disable-next-line no-console - console.log( - `Failure trying to deleteAllRulesStatuses, retries left are: ${retryCount - 1}`, - err - ); - await deleteAllRulesStatuses(es, retryCount - 1); - } - } else { - // eslint-disable-next-line no-console - console.log('Could not deleteAllRulesStatuses, no retries are left'); - } + return countDownES(async () => { + return es.deleteByQuery({ + index: '.kibana', + q: 'type:siem-detection-engine-rule-status', + wait_for_completion: true, + refresh: true, + body: {}, + }); + }, 'deleteAllRulesStatuses'); }; /** @@ -324,24 +320,12 @@ export const deleteAllRulesStatuses = async (es: Client, retryCount = 20): Promi * @param supertest The supertest client library */ export const createSignalsIndex = async ( - supertest: SuperTest, - retryCount = 20 + supertest: SuperTest ): Promise => { - if (retryCount > 0) { - try { - await supertest.post(DETECTION_ENGINE_INDEX_URL).set('kbn-xsrf', 'true').send(); - } catch (err) { - // eslint-disable-next-line no-console - console.log( - `Failure trying to create the signals index, retries left are: ${retryCount - 1}`, - err - ); - await createSignalsIndex(supertest, retryCount - 1); - } - } else { - // eslint-disable-next-line no-console - console.log('Could not createSignalsIndex, no retries are left'); - } + await countDownTest(async () => { + await supertest.post(DETECTION_ENGINE_INDEX_URL).set('kbn-xsrf', 'true').send(); + return true; + }, 'createSignalsIndex'); }; /** @@ -349,21 +333,12 @@ export const createSignalsIndex = async ( * @param supertest The supertest client library */ export const deleteSignalsIndex = async ( - supertest: SuperTest, - retryCount = 20 + supertest: SuperTest ): Promise => { - if (retryCount > 0) { - try { - await supertest.delete(DETECTION_ENGINE_INDEX_URL).set('kbn-xsrf', 'true').send(); - } catch (err) { - // eslint-disable-next-line no-console - console.log(`Failure trying to deleteSignalsIndex, retries left are: ${retryCount - 1}`, err); - await deleteSignalsIndex(supertest, retryCount - 1); - } - } else { - // eslint-disable-next-line no-console - console.log('Could not deleteSignalsIndex, no retries are left'); - } + await countDownTest(async () => { + await supertest.delete(DETECTION_ENGINE_INDEX_URL).set('kbn-xsrf', 'true').send(); + return true; + }, 'deleteSignalsIndex'); }; /** @@ -616,7 +591,7 @@ export const waitFor = async ( functionToTest: () => Promise, maxTimeout: number = 5000, timeoutWait: number = 10 -) => { +): Promise => { await new Promise(async (resolve, reject) => { let found = false; let numberOfTries = 0; @@ -636,3 +611,82 @@ export const waitFor = async ( } }); }; + +/** + * Does a plain countdown and checks against es queries for either conflicts in the error + * or for any over the wire issues such as timeouts or temp 404's to make the tests more + * reliant. + * @param esFunction The function to test against + * @param esFunctionName The name of the function to print if we encounter errors + * @param retryCount The number of times to retry before giving up (has default) + * @param timeoutWait Time to wait before trying again (has default) + */ +export const countDownES = async ( + esFunction: () => Promise, Context>>, + esFunctionName: string, + retryCount: number = 20, + timeoutWait = 250 +): Promise => { + await countDownTest( + async () => { + const result = await esFunction(); + if (result.body.version_conflicts !== 0) { + // eslint-disable-next-line no-console + console.log(`Version conflicts for ${result.body.version_conflicts}`); + return false; + } else { + return true; + } + }, + esFunctionName, + retryCount, + timeoutWait + ); +}; + +/** + * Does a plain countdown and checks against a boolean to determine if to wait and try again. + * This is useful for over the wire things that can cause issues such as conflict or timeouts + * for testing resiliency. + * @param functionToTest The function to test against + * @param name The name of the function to print if we encounter errors + * @param retryCount The number of times to retry before giving up (has default) + * @param timeoutWait Time to wait before trying again (has default) + */ +export const countDownTest = async ( + functionToTest: () => Promise, + name: string, + retryCount: number = 20, + timeoutWait = 250, + ignoreThrow: boolean = false +) => { + if (retryCount > 0) { + try { + const passed = await functionToTest(); + if (!passed) { + // eslint-disable-next-line no-console + console.log(`Failure trying to ${name}, retries left are: ${retryCount - 1}`); + // retry, counting down, and delay a bit before + await new Promise((resolve) => setTimeout(resolve, timeoutWait)); + await countDownTest(functionToTest, name, retryCount - 1, timeoutWait, ignoreThrow); + } + } catch (err) { + if (ignoreThrow) { + throw err; + } else { + // eslint-disable-next-line no-console + console.log( + `Failure trying to ${name}, with exception message of:`, + err.message, + `retries left are: ${retryCount - 1}` + ); + // retry, counting down, and delay a bit before + await new Promise((resolve) => setTimeout(resolve, timeoutWait)); + await countDownTest(functionToTest, name, retryCount - 1, timeoutWait, ignoreThrow); + } + } + } else { + // eslint-disable-next-line no-console + console.log(`Could not ${name}, no retries are left`); + } +}; diff --git a/x-pack/test/lists_api_integration/utils.ts b/x-pack/test/lists_api_integration/utils.ts index 54a13fc027c99..5870239b73ed1 100644 --- a/x-pack/test/lists_api_integration/utils.ts +++ b/x-pack/test/lists_api_integration/utils.ts @@ -15,6 +15,7 @@ import { } from '../../plugins/lists/common/schemas'; import { ListSchema } from '../../plugins/lists/common'; import { LIST_INDEX } from '../../plugins/lists/common/constants'; +import { countDownES, countDownTest } from '../detection_engine_api_integration/utils'; /** * Creates the lists and lists items index for use inside of beforeEach blocks of tests @@ -22,24 +23,12 @@ import { LIST_INDEX } from '../../plugins/lists/common/constants'; * @param supertest The supertest client library */ export const createListsIndex = async ( - supertest: SuperTest, - retryCount = 20 + supertest: SuperTest ): Promise => { - if (retryCount > 0) { - try { - await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true').send(); - } catch (err) { - // eslint-disable-next-line no-console - console.log( - `Failure trying to create the lists index, retries left are: ${retryCount - 1}`, - err - ); - await createListsIndex(supertest, retryCount - 1); - } - } else { - // eslint-disable-next-line no-console - console.log('Could not createListsIndex, no retries are left'); - } + return countDownTest(async () => { + await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true').send(); + return true; + }, 'createListsIndex'); }; /** @@ -47,21 +36,26 @@ export const createListsIndex = async ( * @param supertest The supertest client library */ export const deleteListsIndex = async ( - supertest: SuperTest, - retryCount = 20 + supertest: SuperTest ): Promise => { - if (retryCount > 0) { - try { - await supertest.delete(LIST_INDEX).set('kbn-xsrf', 'true').send(); - } catch (err) { - // eslint-disable-next-line no-console - console.log(`Failure trying to deleteListsIndex, retries left are: ${retryCount - 1}`, err); - await deleteListsIndex(supertest, retryCount - 1); - } - } else { - // eslint-disable-next-line no-console - console.log('Could not deleteListsIndex, no retries are left'); - } + return countDownTest(async () => { + await supertest.delete(LIST_INDEX).set('kbn-xsrf', 'true').send(); + return true; + }, 'deleteListsIndex'); +}; + +/** + * Creates the exception lists and lists items index for use inside of beforeEach blocks of tests + * This will retry 20 times before giving up and hopefully still not interfere with other tests + * @param supertest The supertest client library + */ +export const createExceptionListsIndex = async ( + supertest: SuperTest +): Promise => { + return countDownTest(async () => { + await supertest.post(LIST_INDEX).set('kbn-xsrf', 'true').send(); + return true; + }, 'createListsIndex'); }; /** @@ -159,26 +153,14 @@ export const binaryToString = (res: any, callback: any): void => { * This will retry 20 times before giving up and hopefully still not interfere with other tests * @param es The ElasticSearch handle */ -export const deleteAllExceptions = async (es: Client, retryCount = 20): Promise => { - if (retryCount > 0) { - try { - await es.deleteByQuery({ - index: '.kibana', - q: 'type:exception-list or type:exception-list-agnostic', - wait_for_completion: true, - refresh: true, - body: {}, - }); - } catch (err) { - // eslint-disable-next-line no-console - console.log( - `Failure trying to deleteAllExceptions, retries left are: ${retryCount - 1}`, - err - ); - await deleteAllExceptions(es, retryCount - 1); - } - } else { - // eslint-disable-next-line no-console - console.log('Could not deleteAllExceptions, no retries are left'); - } +export const deleteAllExceptions = async (es: Client): Promise => { + return countDownES(async () => { + return es.deleteByQuery({ + index: '.kibana', + q: 'type:exception-list or type:exception-list-agnostic', + wait_for_completion: true, + refresh: true, + body: {}, + }); + }, 'deleteAllExceptions'); }; From 8f3ec3a73d8ff2d4ad5b3cd3d37c5f91133424fe Mon Sep 17 00:00:00 2001 From: Jason Rhodes Date: Thu, 15 Oct 2020 15:22:24 -0400 Subject: [PATCH 109/128] Adjusts observability alerting perms to require "all" (#79896) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- x-pack/plugins/apm/server/feature.ts | 2 +- x-pack/plugins/infra/server/features.ts | 4 ++-- x-pack/plugins/uptime/server/kibana.index.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/apm/server/feature.ts b/x-pack/plugins/apm/server/feature.ts index 75d8842d4843b..d597b65040ce6 100644 --- a/x-pack/plugins/apm/server/feature.ts +++ b/x-pack/plugins/apm/server/feature.ts @@ -55,7 +55,7 @@ export const APM_FEATURE = { read: [], }, alerting: { - all: Object.values(AlertType), + read: Object.values(AlertType), }, management: { insightsAndAlerting: ['triggersActions'], diff --git a/x-pack/plugins/infra/server/features.ts b/x-pack/plugins/infra/server/features.ts index 444530c4d79f0..3767144a1b798 100644 --- a/x-pack/plugins/infra/server/features.ts +++ b/x-pack/plugins/infra/server/features.ts @@ -51,7 +51,7 @@ export const METRICS_FEATURE = { read: ['infrastructure-ui-source', 'index-pattern'], }, alerting: { - all: [METRIC_THRESHOLD_ALERT_TYPE_ID, METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID], + read: [METRIC_THRESHOLD_ALERT_TYPE_ID, METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID], }, management: { insightsAndAlerting: ['triggersActions'], @@ -92,7 +92,7 @@ export const LOGS_FEATURE = { catalogue: ['infralogging', 'logs'], api: ['infra'], alerting: { - all: [LOG_DOCUMENT_COUNT_ALERT_TYPE_ID], + read: [LOG_DOCUMENT_COUNT_ALERT_TYPE_ID], }, savedObject: { all: [], diff --git a/x-pack/plugins/uptime/server/kibana.index.ts b/x-pack/plugins/uptime/server/kibana.index.ts index cd2dc5018e110..730bb2277227e 100644 --- a/x-pack/plugins/uptime/server/kibana.index.ts +++ b/x-pack/plugins/uptime/server/kibana.index.ts @@ -67,7 +67,7 @@ export const initServerWithKibana = (server: UptimeCoreSetup, plugins: UptimeCor read: [umDynamicSettings.name], }, alerting: { - all: ['xpack.uptime.alerts.tls', 'xpack.uptime.alerts.monitorStatus'], + read: ['xpack.uptime.alerts.tls', 'xpack.uptime.alerts.monitorStatus'], }, management: { insightsAndAlerting: ['triggersActions'], From aec1eb04dfde2bfb284bde00cef5aa30dcd8efc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Thu, 15 Oct 2020 21:49:19 +0200 Subject: [PATCH 110/128] [Security Solution] Fix the Field dropdown in Timeline data providers resets when scrolled (#80718) --- .../components/edit_data_provider/index.tsx | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/security_solution/public/timelines/components/edit_data_provider/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/edit_data_provider/index.tsx index 72386a2b287f1..2bc202c65f6ab 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/edit_data_provider/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/edit_data_provider/index.tsx @@ -15,7 +15,6 @@ import { EuiFormRow, EuiPanel, EuiSpacer, - EuiToolTip, } from '@elastic/eui'; import React, { useEffect, useMemo, useState, useCallback } from 'react'; import styled from 'styled-components'; @@ -193,18 +192,16 @@ export const StatefulEditDataProvider = React.memo( - 0 ? updatedField[0].label : null}> - - + From 2b380b658309e0022461a92c7bd4c63ba93cf1e4 Mon Sep 17 00:00:00 2001 From: Charlie Pichette <56399229+charlie-pichette@users.noreply.github.com> Date: Thu, 15 Oct 2020 14:49:53 -0600 Subject: [PATCH 111/128] update template to use the new team label (#80748) --- .github/ISSUE_TEMPLATE/security_solution_bug_report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/security_solution_bug_report.md b/.github/ISSUE_TEMPLATE/security_solution_bug_report.md index 0c24eb2f973f5..a7a12a6eb379d 100644 --- a/.github/ISSUE_TEMPLATE/security_solution_bug_report.md +++ b/.github/ISSUE_TEMPLATE/security_solution_bug_report.md @@ -1,8 +1,8 @@ --- -name: Security Solution Bug Report +name: Bug report for Security Solution about: Help us identify bugs in Elastic Security, SIEM, and Endpoint so we can fix them! title: '[Security Solution]' -labels: Team:Security Solution +labels: 'Team: Security Solution' --- **Describe the bug:** From ff32bb1716dba3bf1cf84169250d12b81b8fe2bf Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 15 Oct 2020 14:24:29 -0700 Subject: [PATCH 112/128] [kbn/optimizer] tweak split chunks options (#80444) Co-authored-by: spalger --- packages/kbn-optimizer/src/worker/webpack.config.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 9678dd5de868b..f632b07dc6c7c 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -64,6 +64,14 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: optimization: { noEmitOnErrors: true, + splitChunks: { + maxAsyncRequests: 10, + cacheGroups: { + default: { + reuseExistingChunk: false, + }, + }, + }, }, externals: [UiSharedDeps.externals], From 10271c50eeea164c5ed3244fae63d0f1c470720d Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Thu, 15 Oct 2020 15:50:31 -0600 Subject: [PATCH 113/128] [Security Solution] [Maps] Kibana index pattern, comma bug fix (#80208) --- .../components/embeddables/__mocks__/mock.ts | 27 ++++++++++++ .../embeddables/embedded_map_helpers.test.tsx | 44 ++++++++++++++++++- .../embeddables/embedded_map_helpers.tsx | 10 ++++- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/network/components/embeddables/__mocks__/mock.ts index 6f8c3e1123854..d2f482c320002 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/__mocks__/mock.ts +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/__mocks__/mock.ts @@ -484,3 +484,30 @@ export const mockCCSGlobIndexPattern: IndexPatternSavedObject = { title: '*:*', }, }; + +export const mockCommaFilebeatAuditbeatGlobIndexPattern: IndexPatternSavedObject = { + id: 'filebeat-*,auditbeat-*', + type: 'index-pattern', + _version: 'abc', + attributes: { + title: 'filebeat-*,auditbeat-*', + }, +}; + +export const mockCommaFilebeatAuditbeatCCSGlobIndexPattern: IndexPatternSavedObject = { + id: '*:filebeat-*,*:auditbeat-*', + type: 'index-pattern', + _version: 'abc', + attributes: { + title: '*:filebeat-*,*:auditbeat-*', + }, +}; + +export const mockCommaFilebeatExclusionGlobIndexPattern: IndexPatternSavedObject = { + id: 'filebeat-*,-filebeat-7.6.0*', + type: 'index-pattern', + _version: 'abc', + attributes: { + title: 'filebeat-*,-filebeat-7.6.0*', + }, +}; diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.test.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.test.tsx index 0c6b90ec2b9dd..c503690e776af 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.test.tsx @@ -12,9 +12,12 @@ import { mockAPMRegexIndexPattern, mockAPMTransactionIndexPattern, mockAuditbeatIndexPattern, + mockCCSGlobIndexPattern, + mockCommaFilebeatAuditbeatCCSGlobIndexPattern, + mockCommaFilebeatAuditbeatGlobIndexPattern, + mockCommaFilebeatExclusionGlobIndexPattern, mockFilebeatIndexPattern, mockGlobIndexPattern, - mockCCSGlobIndexPattern, } from './__mocks__/mock'; const mockEmbeddable = embeddablePluginMock.createStartContract(); @@ -122,5 +125,44 @@ describe('embedded_map_helpers', () => { }); expect(matchingIndexPatterns).toEqual([mockFilebeatIndexPattern]); }); + + test('matches on comma separated Kibana index pattern', () => { + const matchingIndexPatterns = findMatchingIndexPatterns({ + kibanaIndexPatterns: [ + mockCommaFilebeatAuditbeatGlobIndexPattern, + mockAuditbeatIndexPattern, + ], + siemDefaultIndices, + }); + expect(matchingIndexPatterns).toEqual([ + mockCommaFilebeatAuditbeatGlobIndexPattern, + mockAuditbeatIndexPattern, + ]); + }); + + test('matches on excluded comma separated Kibana index pattern', () => { + const matchingIndexPatterns = findMatchingIndexPatterns({ + kibanaIndexPatterns: [ + mockCommaFilebeatExclusionGlobIndexPattern, + mockAuditbeatIndexPattern, + ], + siemDefaultIndices, + }); + expect(matchingIndexPatterns).toEqual([ + mockCommaFilebeatExclusionGlobIndexPattern, + mockAuditbeatIndexPattern, + ]); + }); + + test('matches on CCS comma separated Kibana index pattern', () => { + const matchingIndexPatterns = findMatchingIndexPatterns({ + kibanaIndexPatterns: [ + mockCommaFilebeatAuditbeatCCSGlobIndexPattern, + mockAuditbeatIndexPattern, + ], + siemDefaultIndices: ['cluster2:filebeat-*', 'cluster1:auditbeat-*'], + }); + expect(matchingIndexPatterns).toEqual([mockCommaFilebeatAuditbeatCCSGlobIndexPattern]); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.tsx index 25928197590ea..4ac759ea534ec 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/embedded_map_helpers.tsx @@ -150,7 +150,15 @@ export const findMatchingIndexPatterns = ({ const pattern = kip.attributes.title; return ( !ignoredIndexPatterns.includes(pattern) && - siemDefaultIndices.some((sdi) => minimatch(sdi, pattern)) + siemDefaultIndices.some((sdi) => { + const splitPattern = pattern.split(',') ?? []; + return splitPattern.length > 1 + ? splitPattern.some((p) => { + const isMatch = minimatch(sdi, p); + return isMatch && p.charAt(0) === '-' ? false : isMatch; + }) + : minimatch(sdi, pattern); + }) ); }); } catch { From 29491ac3e69c6705aeaafd5a25c975b838ed8351 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Thu, 15 Oct 2020 17:21:33 -0500 Subject: [PATCH 114/128] Fix anomaly alert selection text (#80746) Was using the incorrect value. Add a case to leave out "and above" when on "critical." Add a test and rename files to snake case. Fixes #80441. --- .../index.tsx | 2 +- .../select_anomaly_severity.test.tsx | 43 +++++++++++++++++++ ...verity.tsx => select_anomaly_severity.tsx} | 9 ++-- 3 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/select_anomaly_severity.test.tsx rename x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/{SelectAnomalySeverity.tsx => select_anomaly_severity.tsx} (86%) diff --git a/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/index.tsx b/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/index.tsx index f910f34d258fd..4f87e13104371 100644 --- a/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/index.tsx +++ b/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/index.tsx @@ -17,7 +17,7 @@ import { PopoverExpression } from '../ServiceAlertTrigger/PopoverExpression'; import { AnomalySeverity, SelectAnomalySeverity, -} from './SelectAnomalySeverity'; +} from './select_anomaly_severity'; import { ENVIRONMENT_ALL } from '../../../../common/environment_filter_values'; import { EnvironmentField, diff --git a/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/select_anomaly_severity.test.tsx b/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/select_anomaly_severity.test.tsx new file mode 100644 index 0000000000000..0db8fa6c9d0d4 --- /dev/null +++ b/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/select_anomaly_severity.test.tsx @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { render } from '@testing-library/react'; +import React, { ReactNode } from 'react'; +import { IntlProvider } from 'react-intl'; +import { ANOMALY_SEVERITY } from '../../../../../ml/common'; +import { SelectAnomalySeverity } from './select_anomaly_severity'; + +function Wrapper({ children }: { children?: ReactNode }) { + return {children}; +} + +describe('SelectAnomalySeverity', () => { + it('shows the correct text for each item', async () => { + const result = render( + {}} + value={ANOMALY_SEVERITY.CRITICAL} + />, + { wrapper: Wrapper } + ); + const button = (await result.findAllByText('critical'))[1]; + + button.click(); + + const options = await result.findAllByTestId( + 'SelectAnomalySeverity option text' + ); + + expect( + options.map((option) => (option.firstChild as HTMLElement)?.innerHTML) + ).toEqual([ + 'score critical ', // Trailing space is intentional here, to keep the i18n simple + 'score major and above', + 'score minor and above', + 'score warning and above', + ]); + }); +}); diff --git a/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/SelectAnomalySeverity.tsx b/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/select_anomaly_severity.tsx similarity index 86% rename from x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/SelectAnomalySeverity.tsx rename to x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/select_anomaly_severity.tsx index 468d08339431c..b0513c3b59579 100644 --- a/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/SelectAnomalySeverity.tsx +++ b/x-pack/plugins/apm/public/components/alerting/TransactionDurationAnomalyAlertTrigger/select_anomaly_severity.tsx @@ -45,11 +45,14 @@ export function SelectAnomalySeverity({ onChange, value }: Props) { -

+

From 5f3e2c05e826c77e7ffa30e03fbfecc2fe974d7e Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Thu, 15 Oct 2020 17:25:34 -0500 Subject: [PATCH 115/128] Add Storybook a11y addon (#80069) --- package.json | 9 + .../src/worker/webpack.config.ts | 1 - packages/kbn-storybook/lib/default_config.ts | 7 +- packages/kbn-storybook/package.json | 34 +- packages/kbn-storybook/yarn.lock | 1 - x-pack/package.json | 6 - x-pack/plugins/canvas/scripts/storybook.js | 2 + .../canvas/storybook/storyshots.test.tsx | 1 + yarn.lock | 528 +++++++++--------- 9 files changed, 286 insertions(+), 303 deletions(-) delete mode 120000 packages/kbn-storybook/yarn.lock diff --git a/package.json b/package.json index 951c73dc94021..8a87598aec56d 100644 --- a/package.json +++ b/package.json @@ -253,6 +253,13 @@ "@microsoft/api-documenter": "7.7.2", "@microsoft/api-extractor": "7.7.0", "@percy/agent": "^0.26.0", + "@storybook/addon-a11y": "^6.0.26", + "@storybook/addon-actions": "^6.0.26", + "@storybook/addon-essentials": "^6.0.26", + "@storybook/addon-knobs": "^6.0.26", + "@storybook/addon-storyshots": "^6.0.26", + "@storybook/react": "^6.0.26", + "@storybook/theming": "^6.0.26", "@testing-library/dom": "^7.24.2", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.0.4", @@ -301,6 +308,7 @@ "@types/json5": "^0.0.30", "@types/license-checker": "15.0.0", "@types/listr": "^0.14.0", + "@types/loader-utils": "^1.1.3", "@types/lodash": "^4.14.159", "@types/lru-cache": "^5.1.0", "@types/markdown-it": "^0.0.7", @@ -346,6 +354,7 @@ "@types/vinyl-fs": "^2.4.11", "@types/webpack": "^4.41.3", "@types/webpack-env": "^1.15.2", + "@types/webpack-merge": "^4.1.5", "@types/zen-observable": "^0.8.0", "@typescript-eslint/eslint-plugin": "^3.10.0", "@typescript-eslint/parser": "^3.10.0", diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index f632b07dc6c7c..7987dd71f765c 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -23,7 +23,6 @@ import { stringifyRequest } from 'loader-utils'; import webpack from 'webpack'; // @ts-expect-error import TerserPlugin from 'terser-webpack-plugin'; -// @ts-expect-error import webpackMerge from 'webpack-merge'; import { CleanWebpackPlugin } from 'clean-webpack-plugin'; import CompressionPlugin from 'compression-webpack-plugin'; diff --git a/packages/kbn-storybook/lib/default_config.ts b/packages/kbn-storybook/lib/default_config.ts index 1fad9e2a3e087..dc2647b7b5757 100644 --- a/packages/kbn-storybook/lib/default_config.ts +++ b/packages/kbn-storybook/lib/default_config.ts @@ -20,7 +20,12 @@ import { StorybookConfig } from '@storybook/core/types'; export const defaultConfig: StorybookConfig = { - addons: ['@kbn/storybook/preset', '@storybook/addon-knobs', '@storybook/addon-essentials'], + addons: [ + '@kbn/storybook/preset', + '@storybook/addon-a11y', + '@storybook/addon-knobs', + '@storybook/addon-essentials', + ], stories: ['../**/*.stories.tsx'], typescript: { reactDocgen: false, diff --git a/packages/kbn-storybook/package.json b/packages/kbn-storybook/package.json index 5c57f6893d0c8..cf0bb1262ea73 100644 --- a/packages/kbn-storybook/package.json +++ b/packages/kbn-storybook/package.json @@ -4,37 +4,13 @@ "private": true, "license": "Apache-2.0", "main": "./target/index.js", - "kibana": { - "devOnly": true - }, - "dependencies": { + "devDependencies": { "@kbn/dev-utils": "1.0.0", - "@storybook/addon-actions": "^6.0.16", - "@storybook/addon-essentials": "^6.0.16", - "@storybook/addon-knobs": "^6.0.16", - "@storybook/addon-storyshots": "^6.0.16", - "@storybook/core": "^6.0.16", - "@storybook/react": "^6.0.16", - "@storybook/theming": "^6.0.16", "@types/loader-utils": "^1.1.3", - "@types/webpack": "^4.41.3", - "@types/webpack-env": "^1.15.2", - "@types/webpack-merge": "^4.1.5", - "@kbn/utils": "1.0.0", - "babel-loader": "^8.0.6", - "copy-webpack-plugin": "^6.0.2", - "fast-glob": "2.2.7", - "glob-watcher": "5.0.3", - "jest-specific-snapshot": "2.0.0", - "jest-styled-components": "^7.0.2", - "mkdirp": "0.5.1", - "mini-css-extract-plugin": "0.8.0", - "normalize-path": "^3.0.0", - "react-docgen-typescript-loader": "^3.1.1", - "rxjs": "^6.5.5", - "serve-static": "1.14.1", - "styled-components": "^5.1.0", - "webpack": "^4.41.5" + "@types/webpack-merge": "^4.1.5" + }, + "kibana": { + "devOnly": true }, "scripts": { "build": "tsc", diff --git a/packages/kbn-storybook/yarn.lock b/packages/kbn-storybook/yarn.lock deleted file mode 120000 index 3f82ebc9cdbae..0000000000000 --- a/packages/kbn-storybook/yarn.lock +++ /dev/null @@ -1 +0,0 @@ -../../yarn.lock \ No newline at end of file diff --git a/x-pack/package.json b/x-pack/package.json index 484a64fdc2628..91fecff094110 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -44,12 +44,6 @@ "@mapbox/mapbox-gl-draw": "^1.2.0", "@mapbox/mapbox-gl-rtl-text": "^0.2.3", "@scant/router": "^0.1.0", - "@storybook/addon-actions": "^6.0.16", - "@storybook/addon-essentials": "^6.0.16", - "@storybook/addon-knobs": "^6.0.16", - "@storybook/addon-storyshots": "^6.0.16", - "@storybook/react": "^6.0.16", - "@storybook/theming": "^6.0.16", "@testing-library/dom": "^7.24.2", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.0.4", diff --git a/x-pack/plugins/canvas/scripts/storybook.js b/x-pack/plugins/canvas/scripts/storybook.js index 23703810569b6..3b06b7e7e2112 100644 --- a/x-pack/plugins/canvas/scripts/storybook.js +++ b/x-pack/plugins/canvas/scripts/storybook.js @@ -8,6 +8,8 @@ const path = require('path'); const fs = require('fs'); const del = require('del'); const { run } = require('@kbn/dev-utils'); +// This is included in the main Kibana package.json +// eslint-disable-next-line import/no-extraneous-dependencies const storybook = require('@storybook/react/standalone'); const execa = require('execa'); const { DLL_OUTPUT } = require('./../storybook/constants'); diff --git a/x-pack/plugins/canvas/storybook/storyshots.test.tsx b/x-pack/plugins/canvas/storybook/storyshots.test.tsx index 1e993f9c54617..420923972ed6a 100644 --- a/x-pack/plugins/canvas/storybook/storyshots.test.tsx +++ b/x-pack/plugins/canvas/storybook/storyshots.test.tsx @@ -111,6 +111,7 @@ addSerializer(styleSheetSerializer); // Initialize Storyshots and build the Jest Snapshots initStoryshots({ configPath: path.resolve(__dirname, './../storybook'), + framework: 'react', test: multiSnapshotWithOptions({}), // Don't snapshot tests that start with 'redux' storyNameRegex: /^((?!.*?redux).)*$/, diff --git a/yarn.lock b/yarn.lock index 200896a9ce1a6..4ed8f513da7da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2759,17 +2759,39 @@ "@types/node" ">=8.9.0" axios "^0.18.0" -"@storybook/addon-actions@6.0.16", "@storybook/addon-actions@^6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.0.16.tgz#869c90291fdfec4a0644e8415f5004cc57e59145" - integrity sha512-kyPGMP2frdhUgJAm6ChqvndaUawwQE9Vx7pN1pk/Q4qnyVlWCneZVojQf0iAgL45q0az0XI1tOPr4ooroaniYg== - dependencies: - "@storybook/addons" "6.0.16" - "@storybook/api" "6.0.16" - "@storybook/client-api" "6.0.16" - "@storybook/components" "6.0.16" - "@storybook/core-events" "6.0.16" - "@storybook/theming" "6.0.16" +"@storybook/addon-a11y@^6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-6.0.26.tgz#b71761d9b8f8b340894eb9826d51ce319ce65116" + integrity sha512-sx1Ethl9W3Kfns0qB1v0CoAymKTC+odB+rCjUKM1h/ILS/n8ZzwkzAj0L7DU/6wA0nZwZDQ+1wL2ZN7r+vxr8A== + dependencies: + "@storybook/addons" "6.0.26" + "@storybook/api" "6.0.26" + "@storybook/channels" "6.0.26" + "@storybook/client-api" "6.0.26" + "@storybook/client-logger" "6.0.26" + "@storybook/components" "6.0.26" + "@storybook/core-events" "6.0.26" + "@storybook/theming" "6.0.26" + axe-core "^3.5.2" + core-js "^3.0.1" + global "^4.3.2" + lodash "^4.17.15" + react-sizeme "^2.5.2" + regenerator-runtime "^0.13.3" + ts-dedent "^1.1.1" + util-deprecate "^1.0.2" + +"@storybook/addon-actions@6.0.26", "@storybook/addon-actions@^6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.0.26.tgz#d0de9e4d78a8f8f5bf8730c04d0b6d1741c29273" + integrity sha512-9tWbAqSwzWWVz8zwAndZFusZYjIcRYgZUC0LqC8QlH79DgF3ASjw9y97+w1VTTAzdb6LYnsMuSpX6+8m5hrR4g== + dependencies: + "@storybook/addons" "6.0.26" + "@storybook/api" "6.0.26" + "@storybook/client-api" "6.0.26" + "@storybook/components" "6.0.26" + "@storybook/core-events" "6.0.26" + "@storybook/theming" "6.0.26" core-js "^3.0.1" fast-deep-equal "^3.1.1" global "^4.3.2" @@ -2783,40 +2805,40 @@ util-deprecate "^1.0.2" uuid "^8.0.0" -"@storybook/addon-backgrounds@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.0.16.tgz#cbf909992a86dbbdfea172d3950300e8c2a7de01" - integrity sha512-0sH7hlZh4bHt6zV6QyG3ryNGJsxD42iXVwWdwAShzfWJKGfLy5XwdvHUKkMEBbY9bOPeoI9oMli2RAfsD6juLQ== - dependencies: - "@storybook/addons" "6.0.16" - "@storybook/api" "6.0.16" - "@storybook/client-logger" "6.0.16" - "@storybook/components" "6.0.16" - "@storybook/core-events" "6.0.16" - "@storybook/theming" "6.0.16" +"@storybook/addon-backgrounds@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.0.26.tgz#97cea86cc4fe88b6c0ad8addb2d01712e535aa10" + integrity sha512-Y9t1s4N2PgTxLhO85w+WFnnclZNRdxGpsoiLDPkb63HajZvUa5/ogmIOrfFl5DX2zCpkgNLlskmDcEP6n835cA== + dependencies: + "@storybook/addons" "6.0.26" + "@storybook/api" "6.0.26" + "@storybook/client-logger" "6.0.26" + "@storybook/components" "6.0.26" + "@storybook/core-events" "6.0.26" + "@storybook/theming" "6.0.26" core-js "^3.0.1" memoizerific "^1.11.3" react "^16.8.3" regenerator-runtime "^0.13.3" -"@storybook/addon-controls@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.0.16.tgz#c7fc765a01cc3a0de397f8b55bfeda3f328e5495" - integrity sha512-RgBOply9o3PYoWI7TNKef2AQixw7l620pT1fCJbXykp/lu17eqKaIa5KYHRE9vEajun5RuEQxGnSzQOV3OZAsA== - dependencies: - "@storybook/addons" "6.0.16" - "@storybook/api" "6.0.16" - "@storybook/client-api" "6.0.16" - "@storybook/components" "6.0.16" - "@storybook/node-logger" "6.0.16" - "@storybook/theming" "6.0.16" +"@storybook/addon-controls@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.0.26.tgz#4cc4c30ee7bf89ab873158ead4d25d6f7e07ffba" + integrity sha512-K3Oik9ClpShv8Qc6JeNwtmd4yJJcnO2nyaAYYFiyNt+Vsg7zMaDtE2wfvViThNKjX7nUXIeh+OscseIkdWgLuA== + dependencies: + "@storybook/addons" "6.0.26" + "@storybook/api" "6.0.26" + "@storybook/client-api" "6.0.26" + "@storybook/components" "6.0.26" + "@storybook/node-logger" "6.0.26" + "@storybook/theming" "6.0.26" core-js "^3.0.1" ts-dedent "^1.1.1" -"@storybook/addon-docs@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.0.16.tgz#b24983a63c6c9469a418bb1478606626aff42dff" - integrity sha512-7gM/0lQ3mSybpOpQbgR8fjAU+u3zgAWyOM1a+LR7zVn5lNjgBhZD2pfHuwViTeAGG/IIpvmOsd57BKlFJw5TPA== +"@storybook/addon-docs@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.0.26.tgz#bd7fc1fcdc47bb7992fa8d3254367e8c3bba373d" + integrity sha512-3t8AOPkp8ZW74h7FnzxF3wAeb1wRyYjMmgJZxqzgi/x7K0i1inbCq8MuJnytuTcZ7+EK4HR6Ih7o9tJuAtIBLw== dependencies: "@babel/generator" "^7.9.6" "@babel/parser" "^7.9.6" @@ -2826,18 +2848,18 @@ "@mdx-js/loader" "^1.5.1" "@mdx-js/mdx" "^1.5.1" "@mdx-js/react" "^1.5.1" - "@storybook/addons" "6.0.16" - "@storybook/api" "6.0.16" - "@storybook/client-api" "6.0.16" - "@storybook/client-logger" "6.0.16" - "@storybook/components" "6.0.16" - "@storybook/core" "6.0.16" - "@storybook/core-events" "6.0.16" + "@storybook/addons" "6.0.26" + "@storybook/api" "6.0.26" + "@storybook/client-api" "6.0.26" + "@storybook/client-logger" "6.0.26" + "@storybook/components" "6.0.26" + "@storybook/core" "6.0.26" + "@storybook/core-events" "6.0.26" "@storybook/csf" "0.0.1" - "@storybook/node-logger" "6.0.16" - "@storybook/postinstall" "6.0.16" - "@storybook/source-loader" "6.0.16" - "@storybook/theming" "6.0.16" + "@storybook/node-logger" "6.0.26" + "@storybook/postinstall" "6.0.26" + "@storybook/source-loader" "6.0.26" + "@storybook/theming" "6.0.26" acorn "^7.1.0" acorn-jsx "^5.1.0" acorn-walk "^7.0.0" @@ -2857,36 +2879,36 @@ ts-dedent "^1.1.1" util-deprecate "^1.0.2" -"@storybook/addon-essentials@^6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.0.16.tgz#031b05f6a9947fd93a86f28767b1c354e8ea4237" - integrity sha512-tHH2B4cGYihaPytzIlcFlc/jDSu1PUMgaQM4uzIDOn6SCYZJMp5vygK97zF7hf41x/TXv+8i9ZMN5iUJ7l1+fw== - dependencies: - "@storybook/addon-actions" "6.0.16" - "@storybook/addon-backgrounds" "6.0.16" - "@storybook/addon-controls" "6.0.16" - "@storybook/addon-docs" "6.0.16" - "@storybook/addon-toolbars" "6.0.16" - "@storybook/addon-viewport" "6.0.16" - "@storybook/addons" "6.0.16" - "@storybook/api" "6.0.16" - "@storybook/node-logger" "6.0.16" +"@storybook/addon-essentials@^6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.0.26.tgz#1962f4fd19a9d9a1d1fad152bbfc3bba90f45216" + integrity sha512-AsKcPrVFksYNWq07jKXX/GRcdTa6Uo3sTEwuV5ZazWltlbOIcI0YdQs6mCFaCElB5Dqg1jqyxZ3vcd+VHiRSkA== + dependencies: + "@storybook/addon-actions" "6.0.26" + "@storybook/addon-backgrounds" "6.0.26" + "@storybook/addon-controls" "6.0.26" + "@storybook/addon-docs" "6.0.26" + "@storybook/addon-toolbars" "6.0.26" + "@storybook/addon-viewport" "6.0.26" + "@storybook/addons" "6.0.26" + "@storybook/api" "6.0.26" + "@storybook/node-logger" "6.0.26" core-js "^3.0.1" regenerator-runtime "^0.13.3" ts-dedent "^1.1.1" -"@storybook/addon-knobs@^6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-6.0.16.tgz#ef7b9a67c5f3f75579af1d3c2c1f36205f77f505" - integrity sha512-//4Fq70M7LLOghM6+eugL53QHVmlbBm5240u+Aq2nWQLUtaszrPW6/7Vj0XRwLyp/DQtEHetTE/fFfCLoGK+dw== - dependencies: - "@storybook/addons" "6.0.16" - "@storybook/api" "6.0.16" - "@storybook/channels" "6.0.16" - "@storybook/client-api" "6.0.16" - "@storybook/components" "6.0.16" - "@storybook/core-events" "6.0.16" - "@storybook/theming" "6.0.16" +"@storybook/addon-knobs@^6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-6.0.26.tgz#c574a817c8d791aced89a272eb0c6baaee9a0bdf" + integrity sha512-a25hOepctnsqX1nym80HLKrn8fvXFqsbcL3ZkAZWAIXZhf+zPYTJFrw25FxvbyhktmjQv6l89jteAfGfC0g8DA== + dependencies: + "@storybook/addons" "6.0.26" + "@storybook/api" "6.0.26" + "@storybook/channels" "6.0.26" + "@storybook/client-api" "6.0.26" + "@storybook/components" "6.0.26" + "@storybook/core-events" "6.0.26" + "@storybook/theming" "6.0.26" copy-to-clipboard "^3.0.8" core-js "^3.0.1" escape-html "^1.0.3" @@ -2900,15 +2922,15 @@ react-select "^3.0.8" regenerator-runtime "^0.13.3" -"@storybook/addon-storyshots@^6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/addon-storyshots/-/addon-storyshots-6.0.16.tgz#e912273966d4c7cba1a9053d6a76e8856e3b834f" - integrity sha512-wQhM6pnjUCLTr/6BMXTptGeqiMPnnTrvLeaRwG1cDChGK/qs3YqTsa2QqLXQ17IvNUDTHLUNQlYk5af+HrCGhg== +"@storybook/addon-storyshots@^6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/addon-storyshots/-/addon-storyshots-6.0.26.tgz#529a557b4a8e4558da22a8ce847b88f9fb3ab5fa" + integrity sha512-XLt7aqjp3lH9ye5zfgbcZIDe8B9riu9shOsJfhZ1DpzfXxb8NVgAcvsXyMW/7dJZ/paAadXAeZZtWnOBuqNnmw== dependencies: "@jest/transform" "^26.0.0" - "@storybook/addons" "6.0.16" - "@storybook/client-api" "6.0.16" - "@storybook/core" "6.0.16" + "@storybook/addons" "6.0.26" + "@storybook/client-api" "6.0.26" + "@storybook/core" "6.0.26" "@types/glob" "^7.1.1" "@types/jest" "^25.1.1" "@types/jest-specific-snapshot" "^0.5.3" @@ -2922,62 +2944,62 @@ regenerator-runtime "^0.13.3" ts-dedent "^1.1.1" -"@storybook/addon-toolbars@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.0.16.tgz#704a5d506b8d952eca6e5dca96c00b22aedf495f" - integrity sha512-6ulvPqe38NJRbQp0zajeNsDJQKZzGqbCMsSw3gtkFOMt8D/V625MF8YY/Y9UZ+xHWor17GUgE1k9hljdyZe1Nw== +"@storybook/addon-toolbars@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.0.26.tgz#650a1793caac6616f4481116f4dfb79f2d3c336b" + integrity sha512-f9OI7ix0lQWg4eEHheWYB3dz7kYO6qCGkzp+oIQkPpjnYmY8ZghyRM+ui6vfq+G8BwxrAKGR0CB8ttNxVsd/9A== dependencies: - "@storybook/addons" "6.0.16" - "@storybook/api" "6.0.16" - "@storybook/client-api" "6.0.16" - "@storybook/components" "6.0.16" + "@storybook/addons" "6.0.26" + "@storybook/api" "6.0.26" + "@storybook/client-api" "6.0.26" + "@storybook/components" "6.0.26" core-js "^3.0.1" -"@storybook/addon-viewport@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.0.16.tgz#574cc0a3f991ce405ba4a3540132fb05edf488f6" - integrity sha512-3vk6lBZrKJrK9rwxglLT1p579WkLvoJxgW5ddpvSsu31NPAKfDufkDqOZOQGyMmcgIFzZJEc9eKjoTcLiHxppw== - dependencies: - "@storybook/addons" "6.0.16" - "@storybook/api" "6.0.16" - "@storybook/client-logger" "6.0.16" - "@storybook/components" "6.0.16" - "@storybook/core-events" "6.0.16" - "@storybook/theming" "6.0.16" +"@storybook/addon-viewport@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.0.26.tgz#c913dadcb55b31d2df21a580e932b85b1a200a8b" + integrity sha512-LdVW61iZhUf2npNk3qPH9DTunVMhKcyiFq2CRlgxcA5FwjdkAbcPiYMc18rfyTvp/Zd2idartvwYalBYpJhAhw== + dependencies: + "@storybook/addons" "6.0.26" + "@storybook/api" "6.0.26" + "@storybook/client-logger" "6.0.26" + "@storybook/components" "6.0.26" + "@storybook/core-events" "6.0.26" + "@storybook/theming" "6.0.26" core-js "^3.0.1" global "^4.3.2" memoizerific "^1.11.3" prop-types "^15.7.2" regenerator-runtime "^0.13.3" -"@storybook/addons@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.0.16.tgz#a20a219bd5b1474ad02b92e79a74652898a684d9" - integrity sha512-jGMaOJYTM2yZeX1tI6whEn+4xpI1aAybZBrc+OD21CcGoQrbF/jplZMq7xKI0Y6vOMguuTGulpUNCezD3LbBjA== - dependencies: - "@storybook/api" "6.0.16" - "@storybook/channels" "6.0.16" - "@storybook/client-logger" "6.0.16" - "@storybook/core-events" "6.0.16" - "@storybook/router" "6.0.16" - "@storybook/theming" "6.0.16" +"@storybook/addons@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.0.26.tgz#343cbea3eee2d39413b80bc2d66535a7f61488fc" + integrity sha512-OhAApFKgsj9an7FLYfHI4cJQuZ4Zm6yoGOpaxhOvKQMw7dXUPsLvbCyw/6dZOLvaFhjJjQiXtbxtZG+UjR8nvA== + dependencies: + "@storybook/api" "6.0.26" + "@storybook/channels" "6.0.26" + "@storybook/client-logger" "6.0.26" + "@storybook/core-events" "6.0.26" + "@storybook/router" "6.0.26" + "@storybook/theming" "6.0.26" core-js "^3.0.1" global "^4.3.2" regenerator-runtime "^0.13.3" -"@storybook/api@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.0.16.tgz#56cdfc6f7a21d62d1a4ab06b4741c1560160d320" - integrity sha512-RTC4BKmH5i8bJUQejOHEtjebVKtOaHkmEagI2HQRalsokBc1GLAf84EGrO2TaZiRrItAPL5zZQgEnKUblsGJGw== +"@storybook/api@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.0.26.tgz#c45222c132eb8bc2e383536adfebbeb7a89867d0" + integrity sha512-aszDoz1c6T+eRtTUwWvySoyd3gRXmQxsingD084NnEp4VfFLA5H7VS/0sre0ZvU5GWh8d9COxY0DS2Ry/QSKvw== dependencies: "@reach/router" "^1.3.3" - "@storybook/channels" "6.0.16" - "@storybook/client-logger" "6.0.16" - "@storybook/core-events" "6.0.16" + "@storybook/channels" "6.0.26" + "@storybook/client-logger" "6.0.26" + "@storybook/core-events" "6.0.26" "@storybook/csf" "0.0.1" - "@storybook/router" "6.0.16" + "@storybook/router" "6.0.26" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.0.16" + "@storybook/theming" "6.0.26" "@types/reach__router" "^1.3.5" core-js "^3.0.1" fast-deep-equal "^3.1.1" @@ -2991,38 +3013,38 @@ ts-dedent "^1.1.1" util-deprecate "^1.0.2" -"@storybook/channel-postmessage@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.0.16.tgz#a617578c49543b0de9f53eb28daae2bd3c9e1754" - integrity sha512-66B4FH5R7k9i7LBhGsr/hYOxwE4UBM1JMPGV0rhAnFY8m91GiUWl4YWTRdbYIkeaZxf/0oT4sgPScqz44hnw6Q== +"@storybook/channel-postmessage@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.0.26.tgz#a98a0132d6bdf06741afac2607e9feabe34ab98b" + integrity sha512-FT6lC8M5JlNBxPT0rYfmF1yl9mBv04nfYs82TZpp1CzpLxf7wxdCBZ8SSRmvWIVBoNwGZPDhIk5+6JWyDEISBg== dependencies: - "@storybook/channels" "6.0.16" - "@storybook/client-logger" "6.0.16" - "@storybook/core-events" "6.0.16" + "@storybook/channels" "6.0.26" + "@storybook/client-logger" "6.0.26" + "@storybook/core-events" "6.0.26" core-js "^3.0.1" global "^4.3.2" qs "^6.6.0" telejson "^5.0.2" -"@storybook/channels@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.0.16.tgz#94e521b9eae535da80afb23feae593aa69bfe75d" - integrity sha512-TsI4GA7lKD4L2w6IjODMRfnEOkmvEp4eJDgf3MKm7+sMbxwi1y1d6yrW1UQbnmwoNJWk60ArMN2yqDBV+5MNJQ== +"@storybook/channels@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.0.26.tgz#3e8678b4b40085081257a39b9e85fab13a19943c" + integrity sha512-H0iUorayYqS+zfhbjd+cYRzAdRLGLWUeWFu2Aa+oJ4/zeAQNL+DafWboHc567RQ4Vb5KqE5QZoCFskWUUYqJYA== dependencies: core-js "^3.0.1" ts-dedent "^1.1.1" util-deprecate "^1.0.2" -"@storybook/client-api@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.0.16.tgz#4af47caccf92a31326ab77c5094dd4f90f888b91" - integrity sha512-fFsp53lt9W2QHSumqdfFRbh+DI9fvd7li0GDxqLeNESXaUVw48yg8lQiyRNK+j5Pl4VBS3AqytLugJ+0MGm2cA== +"@storybook/client-api@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.0.26.tgz#ac9334ba86834e5cb23fc4fb577de60bda66164d" + integrity sha512-Qd5wR5b5lio/EchuJMhAmmJAE1pfvnEyu+JnyFGwMZLV9mN9NSspz+YsqbSCCDZsYcP5ewvPEnumIWqmj/wagQ== dependencies: - "@storybook/addons" "6.0.16" - "@storybook/channel-postmessage" "6.0.16" - "@storybook/channels" "6.0.16" - "@storybook/client-logger" "6.0.16" - "@storybook/core-events" "6.0.16" + "@storybook/addons" "6.0.26" + "@storybook/channel-postmessage" "6.0.26" + "@storybook/channels" "6.0.26" + "@storybook/client-logger" "6.0.26" + "@storybook/core-events" "6.0.26" "@storybook/csf" "0.0.1" "@types/qs" "^6.9.0" "@types/webpack-env" "^1.15.2" @@ -3036,22 +3058,22 @@ ts-dedent "^1.1.1" util-deprecate "^1.0.2" -"@storybook/client-logger@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.0.16.tgz#6265d2b869a82be64538eaac39470e3845c9e069" - integrity sha512-xM61Aewxqoo8500UxV7iPpfqwikITojiCX3+w8ZiCJ2NizSaXkis95TEFAeHqyozfNym5CqG+6v2NWvGYV3ncQ== +"@storybook/client-logger@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.0.26.tgz#e3d28bd8dc02ec2c53a9d69773a68189590b746f" + integrity sha512-VNoL6/oehVhn3hZi9vrTNT+C/3oAZKV+smfZFnPtsCR/Fq7CKbmsBd0pGPL57f81RU8e8WygwrIlAGJTDSNIjw== dependencies: core-js "^3.0.1" global "^4.3.2" -"@storybook/components@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.0.16.tgz#d4c797f7897cefa11bbdb8dfd07bb3d4fa66b3e9" - integrity sha512-zpYGt3tWiN0yT7V0VhBl2T5Mr0COiNnTQUGCpA9Gl3pUBmAov2jCVf1sUxsIcBcMMZmDRcfo6NbJ/LqCFeUg+Q== +"@storybook/components@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.0.26.tgz#e1f6e16aae850a71c9ac7bdd1d44a068ec9cfdc1" + integrity sha512-8wigI1pDFJO1m1IQWPguOK+nOsaAVRWkVdu+2te/rDcIR9QNvMzzou0+Lhfp3zKSVT4E6mEoGB/TWXXF5Iq0sQ== dependencies: - "@storybook/client-logger" "6.0.16" + "@storybook/client-logger" "6.0.26" "@storybook/csf" "0.0.1" - "@storybook/theming" "6.0.16" + "@storybook/theming" "6.0.26" "@types/overlayscrollbars" "^1.9.0" "@types/react-color" "^3.0.1" "@types/react-syntax-highlighter" "11.0.4" @@ -3072,17 +3094,17 @@ react-textarea-autosize "^8.1.1" ts-dedent "^1.1.1" -"@storybook/core-events@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.0.16.tgz#3f8cd525c15fd80c9f327389851cce82a4b96850" - integrity sha512-ib+58N4OY8AOix2qcBH9ICRmVHUocpGaGRVlIo79WxJrpnB/HNQ8pEaniD+OAavDRq1B7uJqFlMkTXCC0GoFiQ== +"@storybook/core-events@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.0.26.tgz#61181c9a8610d26cc85d47f133a563879044ca2d" + integrity sha512-nWjS/+kMiw31OPgeJQaiFsJk9ZJJo3/d4c+kc6GOl2iC1H3Q4/5cm3NvJBn/7bUtKHmSFwfbDouj+XjUk5rZbQ== dependencies: core-js "^3.0.1" -"@storybook/core@6.0.16", "@storybook/core@^6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.0.16.tgz#ec9aa8c0fd1c23d29bf8401b650c0876c41d1b5f" - integrity sha512-dVgw03bB8rSMrYDw+v07Yiqyy4yas1olnXpytscWCWdbBuflSAQU+mtqcHMIH9YlhucIT2dYiErDDDNmqP+6tw== +"@storybook/core@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.0.26.tgz#ff587929d0f55cefa8405686e831e79aeeb6870e" + integrity sha512-2kmkxbzDJVrjzCjlseffoQJwZRH9bHZUumo5m8gpbN9kVnADER7yd6RUf2Zle5BK3ExC+0PPI1Whfg0qkiXvqw== dependencies: "@babel/plugin-proposal-class-properties" "^7.8.3" "@babel/plugin-proposal-decorators" "^7.8.3" @@ -3105,20 +3127,20 @@ "@babel/preset-react" "^7.8.3" "@babel/preset-typescript" "^7.9.0" "@babel/register" "^7.10.5" - "@storybook/addons" "6.0.16" - "@storybook/api" "6.0.16" - "@storybook/channel-postmessage" "6.0.16" - "@storybook/channels" "6.0.16" - "@storybook/client-api" "6.0.16" - "@storybook/client-logger" "6.0.16" - "@storybook/components" "6.0.16" - "@storybook/core-events" "6.0.16" + "@storybook/addons" "6.0.26" + "@storybook/api" "6.0.26" + "@storybook/channel-postmessage" "6.0.26" + "@storybook/channels" "6.0.26" + "@storybook/client-api" "6.0.26" + "@storybook/client-logger" "6.0.26" + "@storybook/components" "6.0.26" + "@storybook/core-events" "6.0.26" "@storybook/csf" "0.0.1" - "@storybook/node-logger" "6.0.16" - "@storybook/router" "6.0.16" + "@storybook/node-logger" "6.0.26" + "@storybook/router" "6.0.26" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.0.16" - "@storybook/ui" "6.0.16" + "@storybook/theming" "6.0.26" + "@storybook/ui" "6.0.26" "@types/glob-base" "^0.3.0" "@types/micromatch" "^4.0.1" "@types/node-fetch" "^2.5.4" @@ -3189,10 +3211,10 @@ dependencies: lodash "^4.17.15" -"@storybook/node-logger@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.0.16.tgz#805e0748355d13535c3295455f568ea94e57d1ad" - integrity sha512-mD6so/puFV5oByBkDp9rv2mV/WyGy21QdrwXpXdtLDKNgqPuJjHZuF1RA/+MmDK4P1CjvP1no2H5WDKg+aW4QQ== +"@storybook/node-logger@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.0.26.tgz#2ef95ea1e2defd4efcba6b23431ea5c5cbaa110b" + integrity sha512-mdILu91d/2ZgYfICoAMBjwBAYOgjk2URsPudrs5+23lFoPPIwf4CPWcfgs0f4GdfoICk3kV0W7+8bIARhRKp3g== dependencies: "@types/npmlog" "^4.1.2" chalk "^4.0.0" @@ -3200,23 +3222,23 @@ npmlog "^4.1.2" pretty-hrtime "^1.0.3" -"@storybook/postinstall@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.0.16.tgz#77c428534dd10074778dc669f7ffce9f387acc93" - integrity sha512-gZgPNJK/58VepIBodK0pSlD1jPQgIVTEFWot5/iDjxv9cnSl9V+LbIEW5jZp/lzoAONSj8AS646ZZjAM87S4RQ== +"@storybook/postinstall@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.0.26.tgz#3ba9f6fa598d92daf5823361186c4b1369f16ebe" + integrity sha512-B9Dh66MfserWw1J4KbLqfxpnanN//yeDjrrkowzqa3OFLqEPQCekv0ALocovnCkQ13+TcVGjPprxnWXfGhEMpg== dependencies: core-js "^3.0.1" -"@storybook/react@^6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.0.16.tgz#21464749f7bd90dc6026235b2ee47acf168d974a" - integrity sha512-cxnBwewx37rL1BjXo3TQFIvvCv9z26r3yuRRWh527/0QODfwGz8TT+/sJHeqBA5JIQzLwAHNqNJhLp6xzfr5Dw== +"@storybook/react@^6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.0.26.tgz#5d4b8f2c6d8003912d371298a6e5a945e24680b4" + integrity sha512-X02VpIEhpVc4avYiff861c015++tvMVSXJSrDP5J1xTAglVEiRFcU0Kn5h96o9N8FTup2n2xyj6Y7e8oC9yLXQ== dependencies: "@babel/preset-flow" "^7.0.0" "@babel/preset-react" "^7.0.0" - "@storybook/addons" "6.0.16" - "@storybook/core" "6.0.16" - "@storybook/node-logger" "6.0.16" + "@storybook/addons" "6.0.26" + "@storybook/core" "6.0.26" + "@storybook/node-logger" "6.0.26" "@storybook/semver" "^7.3.2" "@svgr/webpack" "^5.4.0" "@types/webpack-env" "^1.15.2" @@ -3233,10 +3255,10 @@ ts-dedent "^1.1.1" webpack "^4.43.0" -"@storybook/router@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.0.16.tgz#b18cc0b1bba477f16f9f2ae8f0eaa0d5ba4b0a0e" - integrity sha512-zijPJ3CR4ytHE0v+pGdaWT3H+es+mLHRkR6hkqcD0ABT5HVfwMlmXJ9FkQGCVpnnNeBOz7+QKCdE13HMelQpqg== +"@storybook/router@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.0.26.tgz#5b991001afa7d7eb5e40c53cd4c58266b6f9edfd" + integrity sha512-kQ1LF/2gX3IkjS1wX7CsoeBc9ptHQzOsyax16rUyJa769DT5vMNtFtQxjNXMqSiSapPg2yrXJFKQNaoWvKgQEQ== dependencies: "@reach/router" "^1.3.3" "@types/reach__router" "^1.3.5" @@ -3253,31 +3275,31 @@ core-js "^3.6.5" find-up "^4.1.0" -"@storybook/source-loader@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.0.16.tgz#a3eb2b0cbede7d9121387738a530d71df645db5d" - integrity sha512-Ub6bU7o2JJUigzu9MSrFH1RD2SmpZZnym+WEidWI9A1gseKp1Rd4KDq36AqJo/oL3hAzoAOirrv3ZixIwXLFMg== +"@storybook/source-loader@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.0.26.tgz#0c9a20b9e018c49d559c56e1bdae8350b8175371" + integrity sha512-axNYEHEj7c9oHUFTMKZ6xRyKZCEEP7Aa9sFPzV5Q3Vrq6/3qhih5fOPXhst6/s4XZC1eIoKKHb/Gk4hmjYOEYA== dependencies: - "@storybook/addons" "6.0.16" - "@storybook/client-logger" "6.0.16" + "@storybook/addons" "6.0.26" + "@storybook/client-logger" "6.0.26" "@storybook/csf" "0.0.1" core-js "^3.0.1" estraverse "^4.2.0" global "^4.3.2" loader-utils "^2.0.0" lodash "^4.17.15" - prettier "^2.0.5" + prettier "~2.0.5" regenerator-runtime "^0.13.3" -"@storybook/theming@6.0.16", "@storybook/theming@^6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.0.16.tgz#dd6de4f29316a6a2380018978b7b4a0ef9ea33c8" - integrity sha512-6D7oMEbeABYZdDY8e3i+O39XLrk6fvG3GBaSGp31BE30d269NcPkGPxMKY/nzc6MY30a+/LbBbM7b6gRKe6b4Q== +"@storybook/theming@6.0.26", "@storybook/theming@^6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.0.26.tgz#e5b545fb2653dfd1b043b567197d490b1c3c0da3" + integrity sha512-9yon2ofb9a+RT1pdvn8Njydy7XRw0qXcIsMqGsJRKoZecmRRozqB6DxH9Gbdf1vRSbM9gYUUDjbiMDFz7+4RiQ== dependencies: "@emotion/core" "^10.0.20" "@emotion/is-prop-valid" "^0.8.6" "@emotion/styled" "^10.0.17" - "@storybook/client-logger" "6.0.16" + "@storybook/client-logger" "6.0.26" core-js "^3.0.1" deep-object-diff "^1.1.0" emotion-theming "^10.0.19" @@ -3287,21 +3309,21 @@ resolve-from "^5.0.0" ts-dedent "^1.1.1" -"@storybook/ui@6.0.16": - version "6.0.16" - resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.0.16.tgz#448d2286404554afb13e27fecd9efb0861fa9286" - integrity sha512-4F21kwQVaMwgqoJmO+566j7MXmvPp+7jfWBMPAvyGsf5uIZ4q6V29h5mMLvTOFA4qHw0lHZk2k8V0g5gk/tjCA== +"@storybook/ui@6.0.26": + version "6.0.26" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.0.26.tgz#60e97d2044a3f63b489d7ad0b0529d93373b71ee" + integrity sha512-Jb7oUJs6uyW+rM4zA8xDn9T0/0XtUAOC/zBl6ofdhYU9rVjYKAQUJqmYgUHNOggq1NGS7BVp1RJIzDWGYEagsA== dependencies: "@emotion/core" "^10.0.20" - "@storybook/addons" "6.0.16" - "@storybook/api" "6.0.16" - "@storybook/channels" "6.0.16" - "@storybook/client-logger" "6.0.16" - "@storybook/components" "6.0.16" - "@storybook/core-events" "6.0.16" - "@storybook/router" "6.0.16" + "@storybook/addons" "6.0.26" + "@storybook/api" "6.0.26" + "@storybook/channels" "6.0.26" + "@storybook/client-logger" "6.0.26" + "@storybook/components" "6.0.26" + "@storybook/core-events" "6.0.26" + "@storybook/router" "6.0.26" "@storybook/semver" "^7.3.2" - "@storybook/theming" "6.0.16" + "@storybook/theming" "6.0.26" "@types/markdown-to-jsx" "^6.11.0" copy-to-clipboard "^3.0.8" core-js "^3.0.1" @@ -6735,6 +6757,11 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== +axe-core@^3.5.2: + version "3.5.5" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.5.tgz#84315073b53fa3c0c51676c588d59da09a192227" + integrity sha512-5P0QZ6J5xGikH780pghEdbEKijCTrruK9KxtPZCFWUpef0f6GipO+xEZ5GKCb020mmqgbiNO6TcA55CriL784Q== + axe-core@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.0.2.tgz#c7cf7378378a51fcd272d3c09668002a4990b1cb" @@ -7649,7 +7676,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browser-resolve@^1.11.3, browser-resolve@^1.8.1: +browser-resolve@^1.8.1: version "1.11.3" resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== @@ -11199,7 +11226,7 @@ elegant-spinner@^1.0.1: resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= -element-resize-detector@^1.1.12: +element-resize-detector@^1.1.12, element-resize-detector@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/element-resize-detector/-/element-resize-detector-1.2.1.tgz#b0305194447a4863155e58f13323a0aef30851d1" integrity sha512-BdFsPepnQr9fznNPF9nF4vQ457U/ZJXQDSNF1zBe7yaga8v9AdZf3/NElYxFdUh7SitSGt040QygiTo6dtatIw== @@ -12262,7 +12289,7 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^24.8.0, expect@^24.9.0: +expect@^24.8.0: version "24.9.0" resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== @@ -12462,7 +12489,7 @@ fast-equals@^2.0.0: resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-2.0.0.tgz#bef2c423af3939f2c54310df54c57e64cd2adefc" integrity sha512-u6RBd8cSiLLxAiC04wVsLV6GBFDOXcTCgWkd3wEoFXgidPSoAJENqC9m7Jb2vewSvjBIfXV6icKeh3GTKfIaXA== -fast-glob@2.2.7, fast-glob@^2.0.2, fast-glob@^2.2.6: +fast-glob@^2.0.2, fast-glob@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== @@ -13672,7 +13699,7 @@ glob-to-regexp@^0.4.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.0.tgz#49bd677b1671022bd10921c3788f23cdebf9c7e6" integrity sha512-fyPCII4vn9Gvjq2U/oDAfP433aiE64cyP/CJjRJcpVGjqqNdioUYn9+r0cSzT1XPwmGAHuTT7iv+rQT8u/YHKQ== -glob-watcher@5.0.3, glob-watcher@^5.0.3: +glob-watcher@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-5.0.3.tgz#88a8abf1c4d131eb93928994bc4a593c2e5dd626" integrity sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg== @@ -17092,7 +17119,7 @@ jest-mock@^26.3.0: "@jest/types" "^26.3.0" "@types/node" "*" -jest-pnp-resolver@^1.2.1, jest-pnp-resolver@^1.2.2: +jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== @@ -17121,17 +17148,6 @@ jest-resolve-dependencies@^26.4.2: jest-regex-util "^26.0.0" jest-snapshot "^26.4.2" -jest-resolve@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" - integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== - dependencies: - "@jest/types" "^24.9.0" - browser-resolve "^1.11.3" - chalk "^2.0.1" - jest-pnp-resolver "^1.2.1" - realpath-native "^1.1.0" - jest-resolve@^26.4.0, jest-resolve@^26.5.2: version "26.5.2" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.5.2.tgz#0d719144f61944a428657b755a0e5c6af4fc8602" @@ -17212,25 +17228,6 @@ jest-serializer@^26.5.0: "@types/node" "*" graceful-fs "^4.2.4" -jest-snapshot@^24.1.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" - integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== - dependencies: - "@babel/types" "^7.0.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" - expect "^24.9.0" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - pretty-format "^24.9.0" - semver "^6.2.0" - jest-snapshot@^26.3.0, jest-snapshot@^26.4.2: version "26.4.2" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.4.2.tgz#87d3ac2f2bd87ea8003602fbebd8fcb9e94104f6" @@ -17252,13 +17249,6 @@ jest-snapshot@^26.3.0, jest-snapshot@^26.4.2: pretty-format "^26.4.2" semver "^7.3.2" -jest-specific-snapshot@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/jest-specific-snapshot/-/jest-specific-snapshot-2.0.0.tgz#425fe524b25df154aa39f97fa6fe9726faaac273" - integrity sha512-aXaNqBg/svwEpY5iQEzEHc5I85cUBKgfeVka9KmpznxLnatpjiqjr7QLb/BYNYlsrZjZzgRHTjQJ+Svx+dbdvg== - dependencies: - jest-snapshot "^24.1.0" - jest-specific-snapshot@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jest-specific-snapshot/-/jest-specific-snapshot-4.0.0.tgz#a52a2e223e7576e610dbeaf341207c557ac20554" @@ -21921,11 +21911,16 @@ prettier@1.16.4: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717" integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g== -prettier@^2.0.5, prettier@^2.1.1: +prettier@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.1.tgz#d9485dd5e499daa6cb547023b87a6cf51bee37d6" integrity sha512-9bY+5ZWCfqj3ghYBLxApy2zf6m+NJo5GzmLTpr9FsApsfjriNnS2dahWReHMi7qNPhhHl9SYHJs2cHZLgexNIw== +prettier@~2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" + integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg== + pretty-bytes@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" @@ -23003,6 +22998,16 @@ react-sizeme@^2.3.6: invariant "^2.2.2" lodash "^4.17.4" +react-sizeme@^2.5.2: + version "2.6.12" + resolved "https://registry.yarnpkg.com/react-sizeme/-/react-sizeme-2.6.12.tgz#ed207be5476f4a85bf364e92042520499455453e" + integrity sha512-tL4sCgfmvapYRZ1FO2VmBmjPVzzqgHA7kI8lSJ6JS6L78jXFNRdOZFpXyK6P1NBZvKPPCZxReNgzZNUajAerZw== + dependencies: + element-resize-detector "^1.2.1" + invariant "^2.2.4" + shallowequal "^1.1.0" + throttle-debounce "^2.1.0" + react-sizeme@^2.6.7: version "2.6.10" resolved "https://registry.yarnpkg.com/react-sizeme/-/react-sizeme-2.6.10.tgz#9993dcb5e67fab94a8e5d078a0d3820609010f17" @@ -23373,13 +23378,6 @@ readline2@^1.0.1: is-fullwidth-code-point "^1.0.0" mute-stream "0.0.5" -realpath-native@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" - integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== - dependencies: - util.promisify "^1.0.0" - recast@^0.14.7: version "0.14.7" resolved "https://registry.yarnpkg.com/recast/-/recast-0.14.7.tgz#4f1497c2b5826d42a66e8e3c9d80c512983ff61d" @@ -27845,7 +27843,7 @@ util-extend@^1.0.1: resolved "https://registry.yarnpkg.com/util-extend/-/util-extend-1.0.3.tgz#a7c216d267545169637b3b6edc6ca9119e2ff93f" integrity sha1-p8IW0mdUUWljeztu3GypEZ4v+T8= -util.promisify@1.0.0, util.promisify@^1.0.0, util.promisify@~1.0.0: +util.promisify@1.0.0, util.promisify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== From 2fa083763b4a5092623ba7f511deec608a7715c8 Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Thu, 15 Oct 2020 16:14:23 -0700 Subject: [PATCH 116/128] [Reporting] Config Schema Validation for rules[N].protocol strings (#80766) --- x-pack/plugins/reporting/server/config/schema.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/reporting/server/config/schema.ts b/x-pack/plugins/reporting/server/config/schema.ts index 8276e8b49d348..7a21c5a1f6104 100644 --- a/x-pack/plugins/reporting/server/config/schema.ts +++ b/x-pack/plugins/reporting/server/config/schema.ts @@ -45,7 +45,15 @@ const QueueSchema = schema.object({ const RulesSchema = schema.object({ allow: schema.boolean(), host: schema.maybe(schema.string()), - protocol: schema.maybe(schema.string()), + protocol: schema.maybe( + schema.string({ + validate(value) { + if (!/:$/.test(value)) { + return 'must end in colon'; + } + }, + }) + ), }); const CaptureSchema = schema.object({ From da2f2db646a63b5568662ac47e413e91cd10df8d Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Thu, 15 Oct 2020 16:57:45 -0700 Subject: [PATCH 117/128] Emit info log when using custom registry URL (#80768) --- .../server/services/epm/registry/registry_url.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/ingest_manager/server/services/epm/registry/registry_url.ts b/x-pack/plugins/ingest_manager/server/services/epm/registry/registry_url.ts index ff9a7871a7db8..efc25cc2efb5d 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/registry/registry_url.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/registry/registry_url.ts @@ -28,12 +28,14 @@ const getDefaultRegistryUrl = (): string => { } }; -// Custom registry URL is currently only for internal Elastic development and is unsupported export const getRegistryUrl = (): string => { const customUrl = appContextService.getConfig()?.registryUrl; const isEnterprise = licenseService.isEnterprise(); if (customUrl && isEnterprise) { + appContextService + .getLogger() + .info('Custom registry url is an experimental feature and is unsupported.'); return customUrl; } From 4ff67805237a4e9ea97d7eb02a8bbbc32638cb3d Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Thu, 15 Oct 2020 19:43:39 -0500 Subject: [PATCH 118/128] Fix error rate sorting in services list (#80764) The field was incorrectly labeled `errorsPerMinute` instead of `transactionErrorRate` (probably left over from before when we switched to using error rate.) Use `-1` for the fallback sort so "N/A" appears after "0%" Fixes #80473. --- .../app/ServiceOverview/ServiceList/index.tsx | 12 +++++++----- .../ServiceList/service_list.test.tsx | 2 -- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx index 49319f167703c..0ce07a3c0ad27 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/index.tsx @@ -153,7 +153,7 @@ export const SERVICE_COLUMNS: Array> = [ width: px(unit * 10), }, { - field: 'errorsPerMinute', + field: 'transactionErrorRate', name: i18n.translate('xpack.apm.servicesTable.transactionErrorRate', { defaultMessage: 'Error rate %', }), @@ -222,13 +222,15 @@ export function ServiceList({ items, noItemsMessage }: Props) { itemsToSort, (item) => { switch (sortField) { + // Use `?? -1` here so `undefined` will appear after/before `0`. + // In the table this will make the "N/A" items always at the + // bottom/top. case 'avgResponseTime': - return item.avgResponseTime?.value ?? 0; + return item.avgResponseTime?.value ?? -1; case 'transactionsPerMinute': - return item.transactionsPerMinute?.value ?? 0; + return item.transactionsPerMinute?.value ?? -1; case 'transactionErrorRate': - return item.transactionErrorRate?.value ?? 0; - + return item.transactionErrorRate?.value ?? -1; default: return item[sortField as keyof typeof item]; } diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/service_list.test.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/service_list.test.tsx index daddd0a60fe1f..73777c2221a5b 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/service_list.test.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/service_list.test.tsx @@ -35,8 +35,6 @@ describe('ServiceList', () => { it('renders with data', () => { expect(() => - // Types of property 'avgResponseTime' are incompatible. - // Type 'null' is not assignable to type '{ value: number | null; timeseries: { x: number; y: number | null; }[]; } | undefined'.ts(2322) renderWithTheme( , { wrapper: Wrapper } From 06fa16d2a313d884111f6e7f217ab7207a4cfea5 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 16 Oct 2020 09:42:10 +0200 Subject: [PATCH 119/128] added brace import to vis editor (#80652) --- src/plugins/vis_default_editor/public/default_editor.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/vis_default_editor/public/default_editor.tsx b/src/plugins/vis_default_editor/public/default_editor.tsx index ed94e52ee2399..a7251acfdf75d 100644 --- a/src/plugins/vis_default_editor/public/default_editor.tsx +++ b/src/plugins/vis_default_editor/public/default_editor.tsx @@ -18,6 +18,7 @@ */ import './index.scss'; +import 'brace/mode/json'; import React, { useEffect, useRef, useState, useCallback } from 'react'; import { EventEmitter } from 'events'; From b582559bbd4d12d7c9afc4acd3e4ba55b40f54ec Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Fri, 16 Oct 2020 10:39:12 +0200 Subject: [PATCH 120/128] [ML] Transforms/DF Analytics: Fix data grid column sorting. (#80618) * [ML] Fix column sorting. * [ML] Tweak sorting. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/data_grid/use_data_grid.tsx | 35 +++++++++++++++---- .../exploration_query_bar.tsx | 4 +-- .../outlier_exploration/use_outlier_data.ts | 31 +++++++++------- 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx b/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx index b97ddb2690982..08b2d48a982d6 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { EuiDataGridSorting, EuiDataGridColumn } from '@elastic/eui'; @@ -93,10 +93,8 @@ export const useDataGrid = ( [columns] ); - return { - chartsVisible, - chartsButtonVisible: true, - columnsWithCharts: columns.map((c, index) => { + const columnsWithCharts = useMemo(() => { + const updatedColumns = columns.map((c, index) => { const chartData = columnCharts.find((cd) => cd.id === c.id); return { @@ -110,7 +108,32 @@ export const useDataGrid = ( /> ) : undefined, }; - }), + }); + + // Sort the columns to be in line with the current order of visible columns. + // EuiDataGrid misses a callback for the order of all available columns, so + // this only can retain the order of visible columns. + return updatedColumns.sort((a, b) => { + // This will always move visible columns above invisible ones. + if (visibleColumns.indexOf(a.id) === -1 && visibleColumns.indexOf(b.id) > -1) { + return 1; + } + if (visibleColumns.indexOf(b.id) === -1 && visibleColumns.indexOf(a.id) > -1) { + return -1; + } + if (visibleColumns.indexOf(a.id) === -1 && visibleColumns.indexOf(b.id) === -1) { + return a.id.localeCompare(b.id); + } + + // If both columns are visible sort by their visible sorting order. + return visibleColumns.indexOf(a.id) - visibleColumns.indexOf(b.id); + }); + }, [columns, columnCharts, chartsVisible, JSON.stringify(visibleColumns)]); + + return { + chartsVisible, + chartsButtonVisible: true, + columnsWithCharts, errorMessage, invalidSortingColumnns, noDataMessage, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx index 06bcdfd364d66..c837fcbacdd55 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_query_bar/exploration_query_bar.tsx @@ -149,11 +149,11 @@ export const ExplorationQueryBar: FC = ({ placeholder={ searchInput.language === SEARCH_QUERY_LANGUAGE.KUERY ? i18n.translate('xpack.ml.stepDefineForm.queryPlaceholderKql', { - defaultMessage: 'e.g. {example}', + defaultMessage: 'Search for e.g. {example}', values: { example: 'method : "GET" or status : "404"' }, }) : i18n.translate('xpack.ml.stepDefineForm.queryPlaceholderLucene', { - defaultMessage: 'e.g. {example}', + defaultMessage: 'Search for e.g. {example}', values: { example: 'method:GET OR status:404' }, }) } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts index eded8e82a7919..88aa06808e8a7 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts @@ -52,17 +52,21 @@ export const useOutlierData = ( const needsDestIndexFields = indexPattern !== undefined && indexPattern.title === jobConfig?.source.index[0]; - const columns: EuiDataGridColumn[] = []; - - if (jobConfig !== undefined && indexPattern !== undefined) { - const resultsField = jobConfig.dest.results_field; - const { fieldTypes } = getIndexFields(jobConfig, needsDestIndexFields); - columns.push( - ...getDataGridSchemasFromFieldTypes(fieldTypes, resultsField).sort((a: any, b: any) => - sortExplorationResultsFields(a.id, b.id, jobConfig) - ) - ); - } + const columns = useMemo(() => { + const newColumns: EuiDataGridColumn[] = []; + + if (jobConfig !== undefined && indexPattern !== undefined) { + const resultsField = jobConfig.dest.results_field; + const { fieldTypes } = getIndexFields(jobConfig, needsDestIndexFields); + newColumns.push( + ...getDataGridSchemasFromFieldTypes(fieldTypes, resultsField).sort((a: any, b: any) => + sortExplorationResultsFields(a.id, b.id, jobConfig) + ) + ); + } + + return newColumns; + }, [jobConfig, indexPattern]); const dataGrid = useDataGrid( columns, @@ -124,7 +128,10 @@ export const useOutlierData = ( }, [ dataGrid.chartsVisible, jobConfig?.dest.index, - JSON.stringify([searchQuery, dataGrid.visibleColumns]), + // Only trigger when search or the visible columns changes. + // We're only interested in the visible columns but not their order, that's + // why we sort for comparison (and copying it via spread to avoid sort in place). + JSON.stringify([searchQuery, [...dataGrid.visibleColumns].sort()]), ]); const colorRange = useColorRange( From fc5ad4d859185e344fb2e5c263f4a172d84d63ff Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Fri, 16 Oct 2020 10:54:46 +0200 Subject: [PATCH 121/128] Lazy load reporting (#80492) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: ⚡️ load dynamically reporting management section * refactor: 💡 remove JSX from main plugin entry file * perf: ⚡️ lazy-load CSV sharing panel React component * perf: ⚡️ lazy-load screen capture sharing panel React components * feat: 🎸 show spinner while shring panels are loading --- .../public/components/panel_spinner.tsx | 22 ++++++ .../components/reporting_panel_content.tsx | 2 +- .../reporting_panel_content_lazy.tsx | 24 ++++++ .../screen_capture_panel_content.tsx | 2 +- .../screen_capture_panel_content_lazy.tsx | 24 ++++++ .../public/mount_management_section.tsx | 42 +++++++++++ .../public/{plugin.tsx => plugin.ts} | 74 +++++++++---------- .../register_csv_reporting.tsx | 2 +- .../register_pdf_png_reporting.tsx | 2 +- 9 files changed, 152 insertions(+), 42 deletions(-) create mode 100644 x-pack/plugins/reporting/public/components/panel_spinner.tsx create mode 100644 x-pack/plugins/reporting/public/components/reporting_panel_content_lazy.tsx create mode 100644 x-pack/plugins/reporting/public/components/screen_capture_panel_content_lazy.tsx create mode 100644 x-pack/plugins/reporting/public/mount_management_section.tsx rename x-pack/plugins/reporting/public/{plugin.tsx => plugin.ts} (78%) diff --git a/x-pack/plugins/reporting/public/components/panel_spinner.tsx b/x-pack/plugins/reporting/public/components/panel_spinner.tsx new file mode 100644 index 0000000000000..841b7063361b9 --- /dev/null +++ b/x-pack/plugins/reporting/public/components/panel_spinner.tsx @@ -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 * as React from 'react'; +import { EuiSpacer, EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; + +export const PanelSpinner: React.FC = (props) => { + return ( + <> + + + + + + + + + ); +}; diff --git a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx index 22b97f45db186..18895f9e623eb 100644 --- a/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/reporting_panel_content.tsx @@ -13,7 +13,7 @@ import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; import { BaseParams } from '../../common/types'; import { ReportingAPIClient } from '../lib/reporting_api_client'; -interface Props { +export interface Props { apiClient: ReportingAPIClient; toasts: ToastsSetup; reportType: string; diff --git a/x-pack/plugins/reporting/public/components/reporting_panel_content_lazy.tsx b/x-pack/plugins/reporting/public/components/reporting_panel_content_lazy.tsx new file mode 100644 index 0000000000000..45a7d60a60966 --- /dev/null +++ b/x-pack/plugins/reporting/public/components/reporting_panel_content_lazy.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 * as React from 'react'; +import { lazy, Suspense, FC } from 'react'; +import { PanelSpinner } from './panel_spinner'; +import type { Props } from './reporting_panel_content'; + +const LazyComponent = lazy(() => + import('./reporting_panel_content').then(({ ReportingPanelContent }) => ({ + default: ReportingPanelContent, + })) +); + +export const ReportingPanelContent: FC> = (props) => { + return ( + }> + + + ); +}; diff --git a/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx index 4a62ab2b76508..ff81ced43e0b4 100644 --- a/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx +++ b/x-pack/plugins/reporting/public/components/screen_capture_panel_content.tsx @@ -12,7 +12,7 @@ import { BaseParams } from '../../common/types'; import { ReportingAPIClient } from '../lib/reporting_api_client'; import { ReportingPanelContent } from './reporting_panel_content'; -interface Props { +export interface Props { apiClient: ReportingAPIClient; toasts: ToastsSetup; reportType: string; diff --git a/x-pack/plugins/reporting/public/components/screen_capture_panel_content_lazy.tsx b/x-pack/plugins/reporting/public/components/screen_capture_panel_content_lazy.tsx new file mode 100644 index 0000000000000..52080e16dd6a3 --- /dev/null +++ b/x-pack/plugins/reporting/public/components/screen_capture_panel_content_lazy.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 * as React from 'react'; +import { lazy, Suspense, FC } from 'react'; +import { PanelSpinner } from './panel_spinner'; +import type { Props } from './screen_capture_panel_content'; + +const LazyComponent = lazy(() => + import('./screen_capture_panel_content').then(({ ScreenCapturePanelContent }) => ({ + default: ScreenCapturePanelContent, + })) +); + +export const ScreenCapturePanelContent: FC = (props) => { + return ( + }> + + + ); +}; diff --git a/x-pack/plugins/reporting/public/mount_management_section.tsx b/x-pack/plugins/reporting/public/mount_management_section.tsx new file mode 100644 index 0000000000000..ac737e4a318ac --- /dev/null +++ b/x-pack/plugins/reporting/public/mount_management_section.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 * as React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { I18nProvider } from '@kbn/i18n/react'; +import { CoreSetup, CoreStart } from 'src/core/public'; +import { Observable } from 'rxjs'; +import { ReportListing } from './components/report_listing'; +import { ManagementAppMountParams } from '../../../../src/plugins/management/public'; +import { ILicense } from '../../licensing/public'; +import { ClientConfigType } from './plugin'; +import { ReportingAPIClient } from './lib/reporting_api_client'; + +export async function mountManagementSection( + coreSetup: CoreSetup, + coreStart: CoreStart, + license$: Observable, + pollConfig: ClientConfigType['poll'], + apiClient: ReportingAPIClient, + params: ManagementAppMountParams +) { + render( + + + , + params.element + ); + + return () => { + unmountComponentAtNode(params.element); + }; +} diff --git a/x-pack/plugins/reporting/public/plugin.tsx b/x-pack/plugins/reporting/public/plugin.ts similarity index 78% rename from x-pack/plugins/reporting/public/plugin.tsx rename to x-pack/plugins/reporting/public/plugin.ts index cc5964f737988..33f4fd4abf72c 100644 --- a/x-pack/plugins/reporting/public/plugin.tsx +++ b/x-pack/plugins/reporting/public/plugin.ts @@ -5,9 +5,6 @@ */ import { i18n } from '@kbn/i18n'; -import { I18nProvider } from '@kbn/i18n/react'; -import React from 'react'; -import ReactDOM from 'react-dom'; import * as Rx from 'rxjs'; import { catchError, filter, map, mergeMap, takeUntil } from 'rxjs/operators'; import { @@ -17,21 +14,21 @@ import { Plugin, PluginInitializerContext, } from 'src/core/public'; -import { UiActionsSetup } from 'src/plugins/ui_actions/public'; +import { UiActionsSetup, UiActionsStart } from 'src/plugins/ui_actions/public'; import { CONTEXT_MENU_TRIGGER } from '../../../../src/plugins/embeddable/public'; import { FeatureCatalogueCategory, HomePublicPluginSetup, + HomePublicPluginStart, } from '../../../../src/plugins/home/public'; -import { ManagementSetup } from '../../../../src/plugins/management/public'; -import { SharePluginSetup } from '../../../../src/plugins/share/public'; -import { LicensingPluginSetup } from '../../licensing/public'; +import { ManagementSetup, ManagementStart } from '../../../../src/plugins/management/public'; +import { SharePluginSetup, SharePluginStart } from '../../../../src/plugins/share/public'; +import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public'; import { durationToNumber } from '../common/schema_utils'; import { JobId, ReportingConfigType } from '../common/types'; import { JOB_COMPLETION_NOTIFICATIONS_SESSION_KEY } from '../constants'; import { JobSummarySet } from './'; import { getGeneralErrorToast } from './components'; -import { ReportListing } from './components/report_listing'; import { ReportingAPIClient } from './lib/reporting_api_client'; import { ReportingNotifierStreamHandler as StreamHandler } from './lib/stream_handler'; import { GetCsvReportPanelAction } from './panel_actions/get_csv_panel_action'; @@ -60,7 +57,25 @@ function handleError(notifications: NotificationsSetup, err: Error): Rx.Observab return Rx.of({ completed: [], failed: [] }); } -export class ReportingPublicPlugin implements Plugin { +export interface ReportingPublicPluginSetupDendencies { + home: HomePublicPluginSetup; + management: ManagementSetup; + licensing: LicensingPluginSetup; + uiActions: UiActionsSetup; + share: SharePluginSetup; +} + +export interface ReportingPublicPluginStartDendencies { + home: HomePublicPluginStart; + management: ManagementStart; + licensing: LicensingPluginStart; + uiActions: UiActionsStart; + share: SharePluginStart; +} + +export class ReportingPublicPlugin + implements + Plugin { private config: ClientConfigType; private readonly stop$ = new Rx.ReplaySubject(1); private readonly title = i18n.translate('xpack.reporting.management.reportingTitle', { @@ -76,19 +91,7 @@ export class ReportingPublicPlugin implements Plugin { public setup( core: CoreSetup, - { - home, - management, - licensing, - uiActions, - share, - }: { - home: HomePublicPluginSetup; - management: ManagementSetup; - licensing: LicensingPluginSetup; - uiActions: UiActionsSetup; - share: SharePluginSetup; - } + { home, management, licensing, uiActions, share }: ReportingPublicPluginSetupDendencies ) { const { http, @@ -119,24 +122,19 @@ export class ReportingPublicPlugin implements Plugin { title: this.title, order: 1, mount: async (params) => { - const [start] = await getStartServices(); params.setBreadcrumbs([{ text: this.breadcrumbText }]); - ReactDOM.render( - - - , - params.element + const [[start], { mountManagementSection }] = await Promise.all([ + getStartServices(), + import('./mount_management_section'), + ]); + return await mountManagementSection( + core, + start, + license$, + this.config.poll, + apiClient, + params ); - - return () => { - ReactDOM.unmountComponentAtNode(params.element); - }; }, }); diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx index 451d907199c4c..e90d6786b58f2 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_csv_reporting.tsx @@ -11,7 +11,7 @@ import { IUiSettingsClient, ToastsSetup } from 'src/core/public'; import { ShareContext } from '../../../../../src/plugins/share/public'; import { LicensingPluginSetup } from '../../../licensing/public'; import { JobParamsCSV, SearchRequest } from '../../server/export_types/csv/types'; -import { ReportingPanelContent } from '../components/reporting_panel_content'; +import { ReportingPanelContent } from '../components/reporting_panel_content_lazy'; import { checkLicense } from '../lib/license_check'; import { ReportingAPIClient } from '../lib/reporting_api_client'; diff --git a/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx b/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx index 2dab66187bb25..d17d4af3c0102 100644 --- a/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx +++ b/x-pack/plugins/reporting/public/share_context_menu/register_pdf_png_reporting.tsx @@ -13,7 +13,7 @@ import { LicensingPluginSetup } from '../../../licensing/public'; import { LayoutParams } from '../../common/types'; import { JobParamsPNG } from '../../server/export_types/png/types'; import { JobParamsPDF } from '../../server/export_types/printable_pdf/types'; -import { ScreenCapturePanelContent } from '../components/screen_capture_panel_content'; +import { ScreenCapturePanelContent } from '../components/screen_capture_panel_content_lazy'; import { checkLicense } from '../lib/license_check'; import { ReportingAPIClient } from '../lib/reporting_api_client'; From 9b540f0bc75cd1d483c07a88ad0959cae369acaa Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Fri, 16 Oct 2020 11:07:50 +0200 Subject: [PATCH 122/128] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20separator?= =?UTF-8?q?=20for=20different=20context=20menu=20groups=20(#80498)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../panel_edit_with_drilldowns_and_context_actions.tsx | 2 +- .../public/context_menu/build_eui_context_menu_panels.tsx | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/ui_actions_explorer/public/context_menu_examples/panel_edit_with_drilldowns_and_context_actions.tsx b/examples/ui_actions_explorer/public/context_menu_examples/panel_edit_with_drilldowns_and_context_actions.tsx index e9543814ff015..5ef2cb73b5937 100644 --- a/examples/ui_actions_explorer/public/context_menu_examples/panel_edit_with_drilldowns_and_context_actions.tsx +++ b/examples/ui_actions_explorer/public/context_menu_examples/panel_edit_with_drilldowns_and_context_actions.tsx @@ -39,7 +39,7 @@ export const PanelEditWithDrilldownsAndContextActions: React.FC = () => { const customActionGrouping: Action['grouping'] = [ { id: 'actions', - getDisplayName: () => 'Custom actions', + getDisplayName: () => 'API actions', getIconType: () => 'cloudStormy', order: 20, }, diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx index 1fdddfc272e94..c7efb6dad326d 100644 --- a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx +++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx @@ -201,8 +201,10 @@ export async function buildContextMenuForActions({ for (const panel of Object.values(panels)) { if (panel._level === 0) { - // TODO: Add separator line here once it is available in EUI. - // See https://github.com/elastic/eui/pull/4018 + panels.mainMenu.items.push({ + isSeparator: true, + key: panel.id + '__separator', + }); if (panel.items.length > 3) { panels.mainMenu.items.push({ name: panel.title || panel.id, From a1831a6d9dd7318631470aa9a065a1a94e9c04eb Mon Sep 17 00:00:00 2001 From: Liza Katz Date: Fri, 16 Oct 2020 12:30:46 +0300 Subject: [PATCH 123/128] [Search] Client side session service (#76889) * Add a session service and use it in discover and dashboard * check unefined * Start session in visualize * Fix tests * docs * OSS error alignemnt * Adjust error messages in xpack * Add getErrorMessage * Use showError in vizualize Add original error to expression exception * Cleanup * ts, doc and i18n fixes * Fix jest tests * Fix functional test * functional test * ts * Update functional tests * Add unit tests to interceptor and timeout error * expose toasts test function * doc * typos * lint * Cleanup * review 1 * Code review * doc * doc fix * visualization type fix * fix jest * Fix xpack functional test * fix xpack test * code review * Add tracking methods to session service * remove chromium * Fix ts and jest tests * jest + docs * ts fix * siem test * Use session service to show a timeout notification per session + more unit tests * ts and docs * Remove session service from search source (not needed) * Code review * ts * Single active session in FE session service * Cleanup * Don't integrate with dashboard \ visualize Add functional tests for session toast plugin * Typescript * ts * Improve functional tests * es * simplify filter test * wait until loadedw * filter test * delete crypto for now * Select the correct index :facepalm: * timerange * Adjust functional test logic * improved test format @dosant * Handle exceptions * Don't close sessions automatically, warn instead * jest * Adjust functional test * Remove unused code * delete export Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- ...ugin-plugins-data-public.isearchoptions.md | 1 + ...ns-data-public.isearchoptions.sessionid.md | 13 + ...plugin-plugins-data-public.isearchsetup.md | 1 + ...lugins-data-public.isearchsetup.session.md | 13 + ...plugin-plugins-data-public.isearchstart.md | 1 + ...lugins-data-public.isearchstart.session.md | 13 + ...lic.searchinterceptor.handlesearcherror.md | 4 +- ...n-plugins-data-public.searchinterceptor.md | 2 +- ...ugins-data-public.searchinterceptordeps.md | 1 + ...ta-public.searchinterceptordeps.session.md | 11 + ...ugin-plugins-data-server.isearchoptions.md | 1 + ...ns-data-server.isearchoptions.sessionid.md | 13 + .../application/dashboard_app_controller.tsx | 4 - src/plugins/data/common/mocks.ts | 20 ++ .../data/common/search/es_search/types.ts | 5 + src/plugins/data/common/search/index.ts | 1 + .../data/common/search/session/index.ts | 20 ++ .../data/common/search/session/mocks.ts | 29 ++ .../data/common/search/session/types.ts | 41 +++ src/plugins/data/public/public.api.md | 12 +- .../data/public/search/expressions/esaggs.ts | 4 +- src/plugins/data/public/search/mocks.ts | 3 + .../public/search/search_interceptor.test.ts | 327 +++++++++++------- .../data/public/search/search_interceptor.ts | 54 +-- .../data/public/search/search_service.ts | 7 + .../public/search/session_service.test.ts | 43 +++ .../data/public/search/session_service.ts | 80 +++++ src/plugins/data/public/search/types.ts | 12 +- src/plugins/data/server/server.api.md | 1 + .../public/application/angular/discover.js | 4 + test/functional/page_objects/settings_page.ts | 11 + test/functional/services/toasts.ts | 6 + .../plugins/session_notifications/kibana.json | 9 + .../session_notifications/package.json | 18 + .../session_notifications/public/index.ts | 23 ++ .../session_notifications/public/plugin.tsx | 66 ++++ .../session_notifications/public/types.ts | 28 ++ .../session_notifications/tsconfig.json | 18 + .../test_suites/data_plugin/index.ts | 4 +- .../test_suites/data_plugin/index_patterns.ts | 4 +- .../test_suites/data_plugin/session.ts | 83 +++++ x-pack/plugins/data_enhanced/public/plugin.ts | 1 + .../public/search/search_interceptor.test.ts | 3 + .../public/search/search_interceptor.ts | 2 +- .../components/alerts_table/actions.test.tsx | 5 +- 45 files changed, 858 insertions(+), 164 deletions(-) create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.sessionid.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.session.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.session.md create mode 100644 docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.session.md create mode 100644 docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.sessionid.md create mode 100644 src/plugins/data/common/mocks.ts create mode 100644 src/plugins/data/common/search/session/index.ts create mode 100644 src/plugins/data/common/search/session/mocks.ts create mode 100644 src/plugins/data/common/search/session/types.ts create mode 100644 src/plugins/data/public/search/session_service.test.ts create mode 100644 src/plugins/data/public/search/session_service.ts create mode 100644 test/plugin_functional/plugins/session_notifications/kibana.json create mode 100644 test/plugin_functional/plugins/session_notifications/package.json create mode 100644 test/plugin_functional/plugins/session_notifications/public/index.ts create mode 100644 test/plugin_functional/plugins/session_notifications/public/plugin.tsx create mode 100644 test/plugin_functional/plugins/session_notifications/public/types.ts create mode 100644 test/plugin_functional/plugins/session_notifications/tsconfig.json create mode 100644 test/plugin_functional/test_suites/data_plugin/session.ts diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md index c9018b0048aa3..76d0914173447 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.md @@ -15,5 +15,6 @@ export interface ISearchOptions | Property | Type | Description | | --- | --- | --- | | [abortSignal](./kibana-plugin-plugins-data-public.isearchoptions.abortsignal.md) | AbortSignal | An AbortSignal that allows the caller of search to abort a search request. | +| [sessionId](./kibana-plugin-plugins-data-public.isearchoptions.sessionid.md) | string | A session ID, grouping multiple search requests into a single session. | | [strategy](./kibana-plugin-plugins-data-public.isearchoptions.strategy.md) | string | Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.sessionid.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.sessionid.md new file mode 100644 index 0000000000000..b1d569e58bf1d --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchoptions.sessionid.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchOptions](./kibana-plugin-plugins-data-public.isearchoptions.md) > [sessionId](./kibana-plugin-plugins-data-public.isearchoptions.sessionid.md) + +## ISearchOptions.sessionId property + +A session ID, grouping multiple search requests into a single session. + +Signature: + +```typescript +sessionId?: string; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.md index b68c4d61e4e03..bbf856480aedd 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.md @@ -17,5 +17,6 @@ export interface ISearchSetup | Property | Type | Description | | --- | --- | --- | | [aggs](./kibana-plugin-plugins-data-public.isearchsetup.aggs.md) | AggsSetup | | +| [session](./kibana-plugin-plugins-data-public.isearchsetup.session.md) | ISessionService | session management | | [usageCollector](./kibana-plugin-plugins-data-public.isearchsetup.usagecollector.md) | SearchUsageCollector | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.session.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.session.md new file mode 100644 index 0000000000000..7f39d9714a3a3 --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchsetup.session.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchSetup](./kibana-plugin-plugins-data-public.isearchsetup.md) > [session](./kibana-plugin-plugins-data-public.isearchsetup.session.md) + +## ISearchSetup.session property + +session management + +Signature: + +```typescript +session: ISessionService; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md index 5defe4a647614..4a69e94dd6f58 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.md @@ -19,5 +19,6 @@ export interface ISearchStart | [aggs](./kibana-plugin-plugins-data-public.isearchstart.aggs.md) | AggsStart | agg config sub service [AggsStart](./kibana-plugin-plugins-data-public.aggsstart.md) | | [search](./kibana-plugin-plugins-data-public.isearchstart.search.md) | ISearchGeneric | low level search [ISearchGeneric](./kibana-plugin-plugins-data-public.isearchgeneric.md) | | [searchSource](./kibana-plugin-plugins-data-public.isearchstart.searchsource.md) | ISearchStartSearchSource | high level search [ISearchStartSearchSource](./kibana-plugin-plugins-data-public.isearchstartsearchsource.md) | +| [session](./kibana-plugin-plugins-data-public.isearchstart.session.md) | ISessionService | session management | | [showError](./kibana-plugin-plugins-data-public.isearchstart.showerror.md) | (e: Error) => void | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.session.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.session.md new file mode 100644 index 0000000000000..de25cccd6d27a --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.isearchstart.session.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [ISearchStart](./kibana-plugin-plugins-data-public.isearchstart.md) > [session](./kibana-plugin-plugins-data-public.isearchstart.session.md) + +## ISearchStart.session property + +session management + +Signature: + +```typescript +session: ISessionService; +``` diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md index 02db74b1a9e91..1c8b6eb41a72e 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md @@ -7,7 +7,7 @@ Signature: ```typescript -protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; +protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, options?: ISearchOptions): Error; ``` ## Parameters @@ -17,7 +17,7 @@ protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal | e | any | | | request | IKibanaSearchRequest | | | timeoutSignal | AbortSignal | | -| appAbortSignal | AbortSignal | | +| options | ISearchOptions | | Returns: diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md index a02a6116d7ae0..40c7055e4c059 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptor.md @@ -27,7 +27,7 @@ export declare class SearchInterceptor | Method | Modifiers | Description | | --- | --- | --- | | [getTimeoutMode()](./kibana-plugin-plugins-data-public.searchinterceptor.gettimeoutmode.md) | | | -| [handleSearchError(e, request, timeoutSignal, appAbortSignal)](./kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md) | | | +| [handleSearchError(e, request, timeoutSignal, options)](./kibana-plugin-plugins-data-public.searchinterceptor.handlesearcherror.md) | | | | [search(request, options)](./kibana-plugin-plugins-data-public.searchinterceptor.search.md) | | Searches using the given search method. Overrides the AbortSignal with one that will abort either when cancelPending is called, when the request times out, or when the original AbortSignal is aborted. Updates pendingCount$ when the request is started/finalized. | | [showError(e)](./kibana-plugin-plugins-data-public.searchinterceptor.showerror.md) | | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.md index 63eb67ce48246..3653394d28b92 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.md @@ -15,6 +15,7 @@ export interface SearchInterceptorDeps | Property | Type | Description | | --- | --- | --- | | [http](./kibana-plugin-plugins-data-public.searchinterceptordeps.http.md) | CoreSetup['http'] | | +| [session](./kibana-plugin-plugins-data-public.searchinterceptordeps.session.md) | ISessionService | | | [startServices](./kibana-plugin-plugins-data-public.searchinterceptordeps.startservices.md) | Promise<[CoreStart, any, unknown]> | | | [toasts](./kibana-plugin-plugins-data-public.searchinterceptordeps.toasts.md) | ToastsSetup | | | [uiSettings](./kibana-plugin-plugins-data-public.searchinterceptordeps.uisettings.md) | CoreSetup['uiSettings'] | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.session.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.session.md new file mode 100644 index 0000000000000..40d00483317ba --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchinterceptordeps.session.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchInterceptorDeps](./kibana-plugin-plugins-data-public.searchinterceptordeps.md) > [session](./kibana-plugin-plugins-data-public.searchinterceptordeps.session.md) + +## SearchInterceptorDeps.session property + +Signature: + +```typescript +session: ISessionService; +``` diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md index 21ddaef3a0b94..af96e1413ba0c 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.md @@ -15,5 +15,6 @@ export interface ISearchOptions | Property | Type | Description | | --- | --- | --- | | [abortSignal](./kibana-plugin-plugins-data-server.isearchoptions.abortsignal.md) | AbortSignal | An AbortSignal that allows the caller of search to abort a search request. | +| [sessionId](./kibana-plugin-plugins-data-server.isearchoptions.sessionid.md) | string | A session ID, grouping multiple search requests into a single session. | | [strategy](./kibana-plugin-plugins-data-server.isearchoptions.strategy.md) | string | Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.sessionid.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.sessionid.md new file mode 100644 index 0000000000000..03043de5193d2 --- /dev/null +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.isearchoptions.sessionid.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-server](./kibana-plugin-plugins-data-server.md) > [ISearchOptions](./kibana-plugin-plugins-data-server.isearchoptions.md) > [sessionId](./kibana-plugin-plugins-data-server.isearchoptions.sessionid.md) + +## ISearchOptions.sessionId property + +A session ID, grouping multiple search requests into a single session. + +Signature: + +```typescript +sessionId?: string; +``` diff --git a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx index 2ae7c6550b0cc..fa45e433050ab 100644 --- a/src/plugins/dashboard/public/application/dashboard_app_controller.tsx +++ b/src/plugins/dashboard/public/application/dashboard_app_controller.tsx @@ -144,7 +144,6 @@ export class DashboardAppController { notifications, overlays, chrome, - injectedMetadata, fatalErrors, uiSettings, savedObjects, @@ -527,9 +526,6 @@ export class DashboardAppController { filterManager.getFilters() ); - timefilter.disableTimeRangeSelector(); - timefilter.disableAutoRefreshSelector(); - const landingPageUrl = () => `#${DashboardConstants.LANDING_PAGE_PATH}`; const getDashTitle = () => diff --git a/src/plugins/data/common/mocks.ts b/src/plugins/data/common/mocks.ts new file mode 100644 index 0000000000000..dde70b1d07443 --- /dev/null +++ b/src/plugins/data/common/mocks.ts @@ -0,0 +1,20 @@ +/* + * 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 { getSessionServiceMock } from './search/session/mocks'; diff --git a/src/plugins/data/common/search/es_search/types.ts b/src/plugins/data/common/search/es_search/types.ts index b1c3e5cdd3960..4d3bc088749a9 100644 --- a/src/plugins/data/common/search/es_search/types.ts +++ b/src/plugins/data/common/search/es_search/types.ts @@ -31,6 +31,11 @@ export interface ISearchOptions { * Use this option to force using a specific server side search strategy. Leave empty to use the default strategy. */ strategy?: string; + + /** + * A session ID, grouping multiple search requests into a single session. + */ + sessionId?: string; } export type ISearchRequestParams> = { diff --git a/src/plugins/data/common/search/index.ts b/src/plugins/data/common/search/index.ts index 2ee0db384cf06..e650cf10db87c 100644 --- a/src/plugins/data/common/search/index.ts +++ b/src/plugins/data/common/search/index.ts @@ -23,3 +23,4 @@ export * from './expressions'; export * from './search_source'; export * from './tabify'; export * from './types'; +export * from './session'; diff --git a/src/plugins/data/common/search/session/index.ts b/src/plugins/data/common/search/session/index.ts new file mode 100644 index 0000000000000..d8f7b5091eb8f --- /dev/null +++ b/src/plugins/data/common/search/session/index.ts @@ -0,0 +1,20 @@ +/* + * 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 * from './types'; diff --git a/src/plugins/data/common/search/session/mocks.ts b/src/plugins/data/common/search/session/mocks.ts new file mode 100644 index 0000000000000..7d5cd75b57534 --- /dev/null +++ b/src/plugins/data/common/search/session/mocks.ts @@ -0,0 +1,29 @@ +/* + * 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 { ISessionService } from './types'; + +export function getSessionServiceMock(): jest.Mocked { + return { + clear: jest.fn(), + start: jest.fn(), + getSessionId: jest.fn(), + getSession$: jest.fn(), + }; +} diff --git a/src/plugins/data/common/search/session/types.ts b/src/plugins/data/common/search/session/types.ts new file mode 100644 index 0000000000000..80ab74f1aa14d --- /dev/null +++ b/src/plugins/data/common/search/session/types.ts @@ -0,0 +1,41 @@ +/* + * 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 { Observable } from 'rxjs'; + +export interface ISessionService { + /** + * Returns the active session ID + * @returns The active session ID + */ + getSessionId: () => string | undefined; + /** + * Returns the observable that emits an update every time the session ID changes + * @returns `Observable` + */ + getSession$: () => Observable; + /** + * Starts a new session + */ + start: () => string; + /** + * Clears the active session. + */ + clear: () => void; +} diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md index 2ed3e440040de..d280b6f1faf7d 100644 --- a/src/plugins/data/public/public.api.md +++ b/src/plugins/data/public/public.api.md @@ -1389,6 +1389,7 @@ export type ISearchGeneric = void; } @@ -1987,7 +1993,7 @@ export class SearchInterceptor { // (undocumented) protected getTimeoutMode(): TimeoutErrorMode; // (undocumented) - protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, appAbortSignal?: AbortSignal): Error; + protected handleSearchError(e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, options?: ISearchOptions): Error; // @internal protected pendingCount$: BehaviorSubject; // @internal (undocumented) @@ -1998,8 +2004,8 @@ export class SearchInterceptor { abortSignal?: AbortSignal; timeout?: number; }): { - combinedSignal: AbortSignal; timeoutSignal: AbortSignal; + combinedSignal: AbortSignal; cleanup: () => void; }; // (undocumented) @@ -2013,6 +2019,8 @@ export interface SearchInterceptorDeps { // (undocumented) http: CoreSetup_2['http']; // (undocumented) + session: ISessionService; + // (undocumented) startServices: Promise<[CoreStart, any, unknown]>; // (undocumented) toasts: ToastsSetup; diff --git a/src/plugins/data/public/search/expressions/esaggs.ts b/src/plugins/data/public/search/expressions/esaggs.ts index 1021ef0f91d52..1f72bda44e4ed 100644 --- a/src/plugins/data/public/search/expressions/esaggs.ts +++ b/src/plugins/data/public/search/expressions/esaggs.ts @@ -149,7 +149,9 @@ const handleCourierRequest = async ({ request.stats(getRequestInspectorStats(requestSearchSource)); try { - const response = await requestSearchSource.fetch({ abortSignal }); + const response = await requestSearchSource.fetch({ + abortSignal, + }); request.stats(getResponseInspectorStats(response, searchSource)).ok({ json: response }); diff --git a/src/plugins/data/public/search/mocks.ts b/src/plugins/data/public/search/mocks.ts index 8bad4cd269b3f..836ddb618e746 100644 --- a/src/plugins/data/public/search/mocks.ts +++ b/src/plugins/data/public/search/mocks.ts @@ -20,11 +20,13 @@ import { searchAggsSetupMock, searchAggsStartMock } from './aggs/mocks'; import { searchSourceMock } from './search_source/mocks'; import { ISearchSetup, ISearchStart } from './types'; +import { getSessionServiceMock } from '../../common/mocks'; function createSetupContract(): jest.Mocked { return { aggs: searchAggsSetupMock(), __enhance: jest.fn(), + session: getSessionServiceMock(), }; } @@ -33,6 +35,7 @@ function createStartContract(): jest.Mocked { aggs: searchAggsStartMock(), search: jest.fn(), showError: jest.fn(), + session: getSessionServiceMock(), searchSource: searchSourceMock.createStartContract(), }; } diff --git a/src/plugins/data/public/search/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor.test.ts index ade15adc1c3a3..e8a728bb9cec3 100644 --- a/src/plugins/data/public/search/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor.test.ts @@ -17,12 +17,14 @@ * under the License. */ -import { CoreSetup } from '../../../../core/public'; +import { CoreSetup, CoreStart } from '../../../../core/public'; import { coreMock } from '../../../../core/public/mocks'; import { IEsSearchRequest } from '../../common/search'; import { SearchInterceptor } from './search_interceptor'; import { AbortError } from '../../common'; -import { SearchTimeoutError, PainlessError } from './errors'; +import { SearchTimeoutError, PainlessError, TimeoutErrorMode } from './errors'; +import { searchServiceMock } from './mocks'; +import { ISearchStart } from '.'; let searchInterceptor: SearchInterceptor; let mockCoreSetup: MockedKeys; @@ -31,13 +33,61 @@ const flushPromises = () => new Promise((resolve) => setImmediate(resolve)); jest.useFakeTimers(); describe('SearchInterceptor', () => { + let searchMock: jest.Mocked; + let mockCoreStart: MockedKeys; beforeEach(() => { mockCoreSetup = coreMock.createSetup(); + mockCoreStart = coreMock.createStart(); + searchMock = searchServiceMock.createStartContract(); searchInterceptor = new SearchInterceptor({ toasts: mockCoreSetup.notifications.toasts, - startServices: mockCoreSetup.getStartServices(), + startServices: new Promise((resolve) => { + resolve([mockCoreStart, {}, {}]); + }), uiSettings: mockCoreSetup.uiSettings, http: mockCoreSetup.http, + session: searchMock.session, + }); + }); + + describe('showError', () => { + test('Ignores an AbortError', async () => { + searchInterceptor.showError(new AbortError()); + expect(mockCoreSetup.notifications.toasts.addDanger).not.toBeCalled(); + expect(mockCoreSetup.notifications.toasts.addError).not.toBeCalled(); + }); + + test('Ignores a SearchTimeoutError', async () => { + searchInterceptor.showError(new SearchTimeoutError(new Error(), TimeoutErrorMode.UPGRADE)); + expect(mockCoreSetup.notifications.toasts.addDanger).not.toBeCalled(); + expect(mockCoreSetup.notifications.toasts.addError).not.toBeCalled(); + }); + + test('Renders a PainlessError', async () => { + searchInterceptor.showError( + new PainlessError( + { + body: { + attributes: { + error: { + failed_shards: { + reason: 'bananas', + }, + }, + }, + } as any, + }, + {} as any + ) + ); + expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); + expect(mockCoreSetup.notifications.toasts.addError).not.toBeCalled(); + }); + + test('Renders a general error', async () => { + searchInterceptor.showError(new Error('Oopsy')); + expect(mockCoreSetup.notifications.toasts.addDanger).not.toBeCalled(); + expect(mockCoreSetup.notifications.toasts.addError).toBeCalledTimes(1); }); }); @@ -49,149 +99,172 @@ describe('SearchInterceptor', () => { params: {}, }; const response = searchInterceptor.search(mockRequest); - - const result = await response.toPromise(); - expect(result).toBe(mockResponse); + expect(response.toPromise()).resolves.toBe(mockResponse); }); - test('Observable should fail if fetch has an internal error', async () => { - const mockResponse: any = { result: 500, message: 'Internal Error' }; - mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - const response = searchInterceptor.search(mockRequest); + describe('Should throw typed errors', () => { + test('Observable should fail if fetch has an internal error', async () => { + const mockResponse: any = new Error('Internal Error'); + mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest); + await expect(response.toPromise()).rejects.toThrow('Internal Error'); + }); - try { - await response.toPromise(); - } catch (e) { - expect(e).toBe(mockResponse); - } - }); + describe('Should handle Timeout errors', () => { + test('Should throw SearchTimeoutError on server timeout AND show toast', async () => { + const mockResponse: any = { + result: 500, + body: { + message: 'Request timed out', + }, + }; + mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest); + await expect(response.toPromise()).rejects.toThrow(SearchTimeoutError); + expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); + }); - test('Should throw SearchTimeoutError on server timeout AND show toast', async (done) => { - const mockResponse: any = { - result: 500, - body: { - message: 'Request timed out', - }, - }; - mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - const response = searchInterceptor.search(mockRequest); + test('Timeout error should show multiple times if not in a session', async () => { + const mockResponse: any = { + result: 500, + body: { + message: 'Request timed out', + }, + }; + mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; - try { - await response.toPromise(); - } catch (e) { - expect(e).toBeInstanceOf(SearchTimeoutError); - expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); - done(); - } - }); + await expect(searchInterceptor.search(mockRequest).toPromise()).rejects.toThrow( + SearchTimeoutError + ); + await expect(searchInterceptor.search(mockRequest).toPromise()).rejects.toThrow( + SearchTimeoutError + ); + expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(2); + }); - test('Search error should be debounced', async (done) => { - const mockResponse: any = { - result: 500, - body: { - message: 'Request timed out', - }, - }; - mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - try { - await searchInterceptor.search(mockRequest).toPromise(); - } catch (e) { - expect(e).toBeInstanceOf(SearchTimeoutError); - try { - await searchInterceptor.search(mockRequest).toPromise(); - } catch (e2) { + test('Timeout error should show once per each session', async () => { + const mockResponse: any = { + result: 500, + body: { + message: 'Request timed out', + }, + }; + mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + + await expect( + searchInterceptor.search(mockRequest, { sessionId: 'abc' }).toPromise() + ).rejects.toThrow(SearchTimeoutError); + await expect( + searchInterceptor.search(mockRequest, { sessionId: 'def' }).toPromise() + ).rejects.toThrow(SearchTimeoutError); + expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(2); + }); + + test('Timeout error should show once in a single session', async () => { + const mockResponse: any = { + result: 500, + body: { + message: 'Request timed out', + }, + }; + mockCoreSetup.http.fetch.mockRejectedValue(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + await expect( + searchInterceptor.search(mockRequest, { sessionId: 'abc' }).toPromise() + ).rejects.toThrow(SearchTimeoutError); + await expect( + searchInterceptor.search(mockRequest, { sessionId: 'abc' }).toPromise() + ).rejects.toThrow(SearchTimeoutError); expect(mockCoreSetup.notifications.toasts.addDanger).toBeCalledTimes(1); - done(); - } - } - }); + }); + }); - test('Should throw Painless error on server error with OSS format', async (done) => { - const mockResponse: any = { - result: 500, - body: { - attributes: { - error: { - failed_shards: [ - { - reason: { - lang: 'painless', - script_stack: ['a', 'b'], - reason: 'banana', + test('Should throw Painless error on server error with OSS format', async () => { + const mockResponse: any = { + result: 500, + body: { + attributes: { + error: { + failed_shards: [ + { + reason: { + lang: 'painless', + script_stack: ['a', 'b'], + reason: 'banana', + }, }, - }, - ], + ], + }, }, }, - }, - }; - mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - const response = searchInterceptor.search(mockRequest); + }; + mockCoreSetup.http.fetch.mockRejectedValueOnce(mockResponse); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest); + await expect(response.toPromise()).rejects.toThrow(PainlessError); + }); - try { - await response.toPromise(); - } catch (e) { - expect(e).toBeInstanceOf(PainlessError); - done(); - } - }); + test('Observable should fail if user aborts (test merged signal)', async () => { + const abortController = new AbortController(); + mockCoreSetup.http.fetch.mockImplementationOnce((options: any) => { + return new Promise((resolve, reject) => { + options.signal.addEventListener('abort', () => { + reject(new AbortError()); + }); - test('Observable should fail if user aborts (test merged signal)', async () => { - const abortController = new AbortController(); - mockCoreSetup.http.fetch.mockImplementationOnce((options: any) => { - return new Promise((resolve, reject) => { - options.signal.addEventListener('abort', () => { - reject(new AbortError()); + setTimeout(resolve, 500); }); - - setTimeout(resolve, 500); }); - }); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - const response = searchInterceptor.search(mockRequest, { - abortSignal: abortController.signal, - }); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest, { + abortSignal: abortController.signal, + }); - const next = jest.fn(); - const error = (e: any) => { - expect(next).not.toBeCalled(); - expect(e).toBeInstanceOf(AbortError); - }; - response.subscribe({ next, error }); - setTimeout(() => abortController.abort(), 200); - jest.advanceTimersByTime(5000); + const next = jest.fn(); + const error = (e: any) => { + expect(next).not.toBeCalled(); + expect(e).toBeInstanceOf(AbortError); + }; + response.subscribe({ next, error }); + setTimeout(() => abortController.abort(), 200); + jest.advanceTimersByTime(5000); - await flushPromises(); - }); + await flushPromises(); + }); - test('Immediately aborts if passed an aborted abort signal', async (done) => { - const abort = new AbortController(); - const mockRequest: IEsSearchRequest = { - params: {}, - }; - const response = searchInterceptor.search(mockRequest, { abortSignal: abort.signal }); - abort.abort(); + test('Immediately aborts if passed an aborted abort signal', async (done) => { + const abort = new AbortController(); + const mockRequest: IEsSearchRequest = { + params: {}, + }; + const response = searchInterceptor.search(mockRequest, { abortSignal: abort.signal }); + abort.abort(); - const error = (e: any) => { - expect(e).toBeInstanceOf(AbortError); - expect(mockCoreSetup.http.fetch).not.toBeCalled(); - done(); - }; - response.subscribe({ error }); + const error = (e: any) => { + expect(e).toBeInstanceOf(AbortError); + expect(mockCoreSetup.http.fetch).not.toBeCalled(); + done(); + }; + response.subscribe({ error }); + }); }); }); }); diff --git a/src/plugins/data/public/search/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor.ts index 2e42635a7f811..e3c6dd3e287d4 100644 --- a/src/plugins/data/public/search/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor.ts @@ -17,7 +17,7 @@ * under the License. */ -import { get, trimEnd, debounce } from 'lodash'; +import { get, memoize, trimEnd } from 'lodash'; import { BehaviorSubject, throwError, timer, defer, from, Observable, NEVER } from 'rxjs'; import { catchError, finalize } from 'rxjs/operators'; import { CoreStart, CoreSetup, ToastsSetup } from 'kibana/public'; @@ -28,6 +28,7 @@ import { IKibanaSearchResponse, ISearchOptions, ES_SEARCH_STRATEGY, + ISessionService, } from '../../common'; import { SearchUsageCollector } from './collectors'; import { SearchTimeoutError, PainlessError, isPainlessError, TimeoutErrorMode } from './errors'; @@ -39,6 +40,7 @@ export interface SearchInterceptorDeps { startServices: Promise<[CoreStart, any, unknown]>; toasts: ToastsSetup; usageCollector?: SearchUsageCollector; + session: ISessionService; } export class SearchInterceptor { @@ -86,16 +88,17 @@ export class SearchInterceptor { e: any, request: IKibanaSearchRequest, timeoutSignal: AbortSignal, - appAbortSignal?: AbortSignal + options?: ISearchOptions ): Error { if (timeoutSignal.aborted || get(e, 'body.message') === 'Request timed out') { // Handle a client or a server side timeout const err = new SearchTimeoutError(e, this.getTimeoutMode()); // Show the timeout error here, so that it's shown regardless of how an application chooses to handle errors. - this.showTimeoutError(err); + // The timeout error is shown any time a request times out, or once per session, if the request is part of a session. + this.showTimeoutError(err, options?.sessionId); return err; - } else if (appAbortSignal?.aborted) { + } else if (options?.abortSignal?.aborted) { // In the case an application initiated abort, throw the existing AbortError. return e; } else if (isPainlessError(e)) { @@ -162,27 +165,37 @@ export class SearchInterceptor { combinedSignal.addEventListener('abort', cleanup); return { - combinedSignal, timeoutSignal, + combinedSignal, cleanup, }; } + private showTimeoutErrorToast = (e: SearchTimeoutError, sessionId?: string) => { + this.deps.toasts.addDanger({ + title: 'Timed out', + text: toMountPoint(e.getErrorMessage(this.application)), + }); + }; + + private showTimeoutErrorMemoized = memoize( + this.showTimeoutErrorToast, + (_: SearchTimeoutError, sessionId: string) => { + return sessionId; + } + ); + /** - * Right now we are throttling but we will hook this up with background sessions to show only one - * error notification per session. + * Show one error notification per session. * @internal */ - private showTimeoutError = debounce( - (e: SearchTimeoutError) => { - this.deps.toasts.addDanger({ - title: 'Timed out', - text: toMountPoint(e.getErrorMessage(this.application)), - }); - }, - 30000, - { leading: true, trailing: false } - ); + private showTimeoutError = (e: SearchTimeoutError, sessionId?: string) => { + if (sessionId) { + this.showTimeoutErrorMemoized(e, sessionId); + } else { + this.showTimeoutErrorToast(e, sessionId); + } + }; /** * Searches using the given `search` method. Overrides the `AbortSignal` with one that will abort @@ -207,12 +220,9 @@ export class SearchInterceptor { abortSignal: options?.abortSignal, }); this.pendingCount$.next(this.pendingCount$.getValue() + 1); - return this.runSearch(request, combinedSignal, options?.strategy).pipe( - catchError((e: any) => { - return throwError( - this.handleSearchError(e, request, timeoutSignal, options?.abortSignal) - ); + catchError((e: Error) => { + return throwError(this.handleSearchError(e, request, timeoutSignal, options)); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 734e88e085661..f955dc5b6ebd5 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -31,6 +31,7 @@ import { ISearchOptions, SearchSourceService, SearchSourceDependencies, + ISessionService, } from '../../common/search'; import { getCallMsearch } from './legacy'; import { AggsService, AggsStartDependencies } from './aggs'; @@ -40,6 +41,7 @@ import { SearchUsageCollector, createUsageCollector } from './collectors'; import { UsageCollectionSetup } from '../../../usage_collection/public'; import { esdsl, esRawResponse } from './expressions'; import { ExpressionsSetup } from '../../../expressions/public'; +import { SessionService } from './session_service'; import { ConfigSchema } from '../../config'; import { SHARD_DELAY_AGG_NAME, @@ -64,6 +66,7 @@ export class SearchService implements Plugin { private readonly searchSourceService = new SearchSourceService(); private searchInterceptor!: ISearchInterceptor; private usageCollector?: SearchUsageCollector; + private sessionService!: ISessionService; constructor(private initializerContext: PluginInitializerContext) {} @@ -73,6 +76,7 @@ export class SearchService implements Plugin { ): ISearchSetup { this.usageCollector = createUsageCollector(getStartServices, usageCollection); + this.sessionService = new SessionService(this.initializerContext, getStartServices); /** * A global object that intercepts all searches and provides convenience methods for cancelling * all pending search requests, as well as getting the number of pending search requests. @@ -83,6 +87,7 @@ export class SearchService implements Plugin { uiSettings, startServices: getStartServices(), usageCollector: this.usageCollector!, + session: this.sessionService, }); expressions.registerFunction(esdsl); @@ -104,6 +109,7 @@ export class SearchService implements Plugin { __enhance: (enhancements: SearchEnhancements) => { this.searchInterceptor = enhancements.searchInterceptor; }, + session: this.sessionService, }; } @@ -142,6 +148,7 @@ export class SearchService implements Plugin { showError: (e: Error) => { this.searchInterceptor.showError(e); }, + session: this.sessionService, searchSource: this.searchSourceService.start(indexPatterns, searchSourceDependencies), }; } diff --git a/src/plugins/data/public/search/session_service.test.ts b/src/plugins/data/public/search/session_service.test.ts new file mode 100644 index 0000000000000..dd64d187f47d6 --- /dev/null +++ b/src/plugins/data/public/search/session_service.test.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 { SessionService } from './session_service'; +import { ISessionService } from '../../common'; +import { coreMock } from '../../../../core/public/mocks'; + +describe('Session service', () => { + let sessionService: ISessionService; + + beforeEach(() => { + const initializerContext = coreMock.createPluginInitializerContext(); + sessionService = new SessionService( + initializerContext, + coreMock.createSetup().getStartServices + ); + }); + + describe('Session management', () => { + it('Creates and clears a session', async () => { + sessionService.start(); + expect(sessionService.getSessionId()).not.toBeUndefined(); + sessionService.clear(); + expect(sessionService.getSessionId()).toBeUndefined(); + }); + }); +}); diff --git a/src/plugins/data/public/search/session_service.ts b/src/plugins/data/public/search/session_service.ts new file mode 100644 index 0000000000000..31524434af302 --- /dev/null +++ b/src/plugins/data/public/search/session_service.ts @@ -0,0 +1,80 @@ +/* + * 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 uuid from 'uuid'; +import { Subject, Subscription } from 'rxjs'; +import { PluginInitializerContext, StartServicesAccessor } from 'kibana/public'; +import { ISessionService } from '../../common/search'; +import { ConfigSchema } from '../../config'; + +export class SessionService implements ISessionService { + private sessionId?: string; + private session$: Subject = new Subject(); + private appChangeSubscription$?: Subscription; + private curApp?: string; + + constructor( + initializerContext: PluginInitializerContext, + getStartServices: StartServicesAccessor + ) { + /* + Make sure that apps don't leave sessions open. + */ + getStartServices().then(([coreStart]) => { + this.appChangeSubscription$ = coreStart.application.currentAppId$.subscribe((appName) => { + if (this.sessionId) { + const message = `Application '${this.curApp}' had an open session while navigating`; + if (initializerContext.env.mode.dev) { + // TODO: This setTimeout is necessary due to a race condition while navigating. + setTimeout(() => { + coreStart.fatalErrors.add(message); + }, 100); + } else { + // eslint-disable-next-line no-console + console.warn(message); + } + } + this.curApp = appName; + }); + }); + } + + public destroy() { + this.appChangeSubscription$?.unsubscribe(); + } + + public getSessionId() { + return this.sessionId; + } + + public getSession$() { + return this.session$.asObservable(); + } + + public start() { + this.sessionId = uuid.v4(); + this.session$.next(this.sessionId); + return this.sessionId; + } + + public clear() { + this.sessionId = undefined; + this.session$.next(this.sessionId); + } +} diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts index 85ef7aa4d97cb..c08d9f4c7be3f 100644 --- a/src/plugins/data/public/search/types.ts +++ b/src/plugins/data/public/search/types.ts @@ -21,7 +21,7 @@ import { PackageInfo } from 'kibana/server'; import { ISearchInterceptor } from './search_interceptor'; import { SearchUsageCollector } from './collectors'; import { AggsSetup, AggsSetupDependencies, AggsStartDependencies, AggsStart } from './aggs'; -import { ISearchGeneric, ISearchStartSearchSource } from '../../common/search'; +import { ISearchGeneric, ISessionService, ISearchStartSearchSource } from '../../common/search'; import { IndexPatternsContract } from '../../common/index_patterns/index_patterns'; import { UsageCollectionSetup } from '../../../usage_collection/public'; @@ -38,6 +38,11 @@ export interface SearchEnhancements { export interface ISearchSetup { aggs: AggsSetup; usageCollector?: SearchUsageCollector; + /** + * session management + * {@link ISessionService} + */ + session: ISessionService; /** * @internal */ @@ -67,6 +72,11 @@ export interface ISearchStart { * {@link ISearchStartSearchSource} */ searchSource: ISearchStartSearchSource; + /** + * session management + * {@link ISessionService} + */ + session: ISessionService; } export { SEARCH_EVENT_TYPE } from './collectors'; diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 0828460830f2c..0ed296a1d0662 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -685,6 +685,7 @@ export class IndexPatternsService implements Plugin_3 { await PageObjects.header.waitUntilLoadingHasFinished(); await this.clickKibanaIndexPatterns(); + const exists = await this.hasIndexPattern(indexPatternName); + + if (exists) { + await this.clickIndexPatternByName(indexPatternName); + return; + } + await PageObjects.header.waitUntilLoadingHasFinished(); await this.clickAddNewIndexPatternButton(); if (!isStandardIndexPattern) { diff --git a/test/functional/services/toasts.ts b/test/functional/services/toasts.ts index f5416a44e3b5a..1148da14556e2 100644 --- a/test/functional/services/toasts.ts +++ b/test/functional/services/toasts.ts @@ -71,6 +71,12 @@ export function ToastsProvider({ getService }: FtrProviderContext) { private async getGlobalToastList() { return await testSubjects.find('globalToastList'); } + + public async getToastCount() { + const list = await this.getGlobalToastList(); + const toasts = await list.findAllByCssSelector(`.euiToast`); + return toasts.length; + } } return new Toasts(); diff --git a/test/plugin_functional/plugins/session_notifications/kibana.json b/test/plugin_functional/plugins/session_notifications/kibana.json new file mode 100644 index 0000000000000..0b80b531d2f84 --- /dev/null +++ b/test/plugin_functional/plugins/session_notifications/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "session_notifications", + "version": "0.0.1", + "kibanaVersion": "kibana", + "configPath": ["session_notifications"], + "server": false, + "ui": true, + "requiredPlugins": ["data", "navigation"] +} \ No newline at end of file diff --git a/test/plugin_functional/plugins/session_notifications/package.json b/test/plugin_functional/plugins/session_notifications/package.json new file mode 100644 index 0000000000000..7a61867db2b58 --- /dev/null +++ b/test/plugin_functional/plugins/session_notifications/package.json @@ -0,0 +1,18 @@ +{ + "name": "session_notifications", + "version": "1.0.0", + "main": "target/test/plugin_functional/plugins/session_notifications", + "kibana": { + "version": "kibana", + "templateVersion": "1.0.0" + }, + "license": "Apache-2.0", + "scripts": { + "kbn": "node ../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "4.0.2" + } +} + diff --git a/test/plugin_functional/plugins/session_notifications/public/index.ts b/test/plugin_functional/plugins/session_notifications/public/index.ts new file mode 100644 index 0000000000000..fbc573e8dc6fe --- /dev/null +++ b/test/plugin_functional/plugins/session_notifications/public/index.ts @@ -0,0 +1,23 @@ +/* + * 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 { PluginInitializer } from 'kibana/public'; +import { SessionNotificationsPlugin } from './plugin'; + +export const plugin: PluginInitializer = () => new SessionNotificationsPlugin(); diff --git a/test/plugin_functional/plugins/session_notifications/public/plugin.tsx b/test/plugin_functional/plugins/session_notifications/public/plugin.tsx new file mode 100644 index 0000000000000..a41ecb3edebd2 --- /dev/null +++ b/test/plugin_functional/plugins/session_notifications/public/plugin.tsx @@ -0,0 +1,66 @@ +/* + * 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 { CoreSetup, CoreStart, Plugin } from 'kibana/public'; +import { AppPluginDependenciesStart, AppPluginDependenciesSetup } from './types'; + +export class SessionNotificationsPlugin implements Plugin { + private sessionIds: Array = []; + public setup(core: CoreSetup, { navigation }: AppPluginDependenciesSetup) { + const showSessions = { + id: 'showSessionsButton', + label: 'Show Sessions', + description: 'Sessions', + run: () => { + core.notifications.toasts.addInfo(this.sessionIds.join(','), { + toastLifeTimeMs: 50000, + }); + }, + tooltip: () => { + return this.sessionIds.join(','); + }, + testId: 'showSessionsButton', + }; + + navigation.registerMenuItem(showSessions); + + const clearSessions = { + id: 'clearSessionsButton', + label: 'Clear Sessions', + description: 'Sessions', + run: () => { + this.sessionIds.length = 0; + }, + testId: 'clearSessionsButton', + }; + + navigation.registerMenuItem(clearSessions); + } + + public start(core: CoreStart, { data }: AppPluginDependenciesStart) { + core.application.currentAppId$.subscribe(() => { + this.sessionIds.length = 0; + }); + + data.search.session.getSession$().subscribe((sessionId?: string) => { + this.sessionIds.push(sessionId); + }); + } + public stop() {} +} diff --git a/test/plugin_functional/plugins/session_notifications/public/types.ts b/test/plugin_functional/plugins/session_notifications/public/types.ts new file mode 100644 index 0000000000000..de9055d03d21e --- /dev/null +++ b/test/plugin_functional/plugins/session_notifications/public/types.ts @@ -0,0 +1,28 @@ +/* + * 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 { NavigationPublicPluginSetup } from '../../../../../src/plugins/navigation/public'; +import { DataPublicPluginStart } from '../../../../../src/plugins/data/public'; + +export interface AppPluginDependenciesSetup { + navigation: NavigationPublicPluginSetup; +} +export interface AppPluginDependenciesStart { + data: DataPublicPluginStart; +} diff --git a/test/plugin_functional/plugins/session_notifications/tsconfig.json b/test/plugin_functional/plugins/session_notifications/tsconfig.json new file mode 100644 index 0000000000000..3d9d8ca9451d4 --- /dev/null +++ b/test/plugin_functional/plugins/session_notifications/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./target", + "skipLibCheck": true + }, + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../../../typings/**/*", + ], + "exclude": [], + "references": [ + { "path": "../../../../src/core/tsconfig.json" } + ] +} diff --git a/test/plugin_functional/test_suites/data_plugin/index.ts b/test/plugin_functional/test_suites/data_plugin/index.ts index bbf9d823e357e..212a75b9cf441 100644 --- a/test/plugin_functional/test_suites/data_plugin/index.ts +++ b/test/plugin_functional/test_suites/data_plugin/index.ts @@ -35,7 +35,9 @@ export default function ({ await PageObjects.common.navigateToApp('settings'); await PageObjects.settings.createIndexPattern('shakespeare', ''); }); - loadTestFile(require.resolve('./index_patterns')); + loadTestFile(require.resolve('./search')); + loadTestFile(require.resolve('./session')); + loadTestFile(require.resolve('./index_patterns')); }); } diff --git a/test/plugin_functional/test_suites/data_plugin/index_patterns.ts b/test/plugin_functional/test_suites/data_plugin/index_patterns.ts index 4359816efb958..2c846dc780311 100644 --- a/test/plugin_functional/test_suites/data_plugin/index_patterns.ts +++ b/test/plugin_functional/test_suites/data_plugin/index_patterns.ts @@ -23,7 +23,9 @@ import '../../plugins/core_provider_plugin/types'; export default function ({ getService }: PluginFunctionalProviderContext) { const supertest = getService('supertest'); - describe('index patterns', function () { + // skipping the tests as it deletes index patterns created by other test causing unexpected failures + // https://github.com/elastic/kibana/issues/79886 + describe.skip('index patterns', function () { let indexPatternId = ''; it('can get all ids', async () => { diff --git a/test/plugin_functional/test_suites/data_plugin/session.ts b/test/plugin_functional/test_suites/data_plugin/session.ts new file mode 100644 index 0000000000000..88241fffae904 --- /dev/null +++ b/test/plugin_functional/test_suites/data_plugin/session.ts @@ -0,0 +1,83 @@ +/* + * 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 expect from '@kbn/expect'; +import { PluginFunctionalProviderContext } from '../../services'; + +export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) { + const PageObjects = getPageObjects(['common', 'header', 'dashboard', 'discover', 'timePicker']); + const filterBar = getService('filterBar'); + const testSubjects = getService('testSubjects'); + const toasts = getService('toasts'); + + const getSessionIds = async () => { + const sessionsBtn = await testSubjects.find('showSessionsButton'); + await sessionsBtn.click(); + const toast = await toasts.getToastElement(1); + const sessionIds = await toast.getVisibleText(); + return sessionIds.split(','); + }; + + describe('Session management', function describeIndexTests() { + describe('Discover', () => { + before(async () => { + await PageObjects.common.navigateToApp('discover'); + await testSubjects.click('clearSessionsButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); + }); + + afterEach(async () => { + await testSubjects.click('clearSessionsButton'); + await toasts.dismissAllToasts(); + }); + + it('Starts on index pattern select', async () => { + await PageObjects.discover.selectIndexPattern('shakespeare'); + await PageObjects.header.waitUntilLoadingHasFinished(); + const sessionIds = await getSessionIds(); + + // Discover calls destroy on index pattern change, which explicitly closes a session + expect(sessionIds.length).to.be(2); + expect(sessionIds[0].length).to.be(0); + expect(sessionIds[1].length).not.to.be(0); + }); + + it('Starts on a refresh', async () => { + await testSubjects.click('querySubmitButton'); + await PageObjects.header.waitUntilLoadingHasFinished(); + const sessionIds = await getSessionIds(); + expect(sessionIds.length).to.be(1); + }); + + it('Starts a new session on sort', async () => { + await PageObjects.discover.clickFieldListItemAdd('speaker'); + await PageObjects.discover.clickFieldSort('speaker'); + await PageObjects.header.waitUntilLoadingHasFinished(); + const sessionIds = await getSessionIds(); + expect(sessionIds.length).to.be(1); + }); + + it('Starts a new session on filter change', async () => { + await filterBar.addFilter('line_number', 'is', '4.3.108'); + await PageObjects.header.waitUntilLoadingHasFinished(); + const sessionIds = await getSessionIds(); + expect(sessionIds.length).to.be(1); + }); + }); + }); +} diff --git a/x-pack/plugins/data_enhanced/public/plugin.ts b/x-pack/plugins/data_enhanced/public/plugin.ts index ccc93316482c2..43ad4a9ed9b8b 100644 --- a/x-pack/plugins/data_enhanced/public/plugin.ts +++ b/x-pack/plugins/data_enhanced/public/plugin.ts @@ -40,6 +40,7 @@ export class DataEnhancedPlugin uiSettings: core.uiSettings, startServices: core.getStartServices(), usageCollector: data.search.usageCollector, + session: data.search.session, }); data.__enhance({ diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts index 6e34e4c1964c5..3187b41a2c55f 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.test.ts @@ -9,6 +9,7 @@ import { EnhancedSearchInterceptor } from './search_interceptor'; import { CoreSetup, CoreStart } from 'kibana/public'; import { AbortError, UI_SETTINGS } from '../../../../../src/plugins/data/common'; import { SearchTimeoutError } from 'src/plugins/data/public'; +import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks'; const timeTravel = (msToRun = 0) => { jest.advanceTimersByTime(msToRun); @@ -43,6 +44,7 @@ describe('EnhancedSearchInterceptor', () => { beforeEach(() => { mockCoreSetup = coreMock.createSetup(); mockCoreStart = coreMock.createStart(); + const dataPluginMockStart = dataPluginMock.createStartContract(); mockCoreSetup.uiSettings.get.mockImplementation((name: string) => { switch (name) { @@ -77,6 +79,7 @@ describe('EnhancedSearchInterceptor', () => { http: mockCoreSetup.http, uiSettings: mockCoreSetup.uiSettings, usageCollector: mockUsageCollector, + session: dataPluginMockStart.search.session, }); }); diff --git a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts index cca87c85e326c..aee32a7c62759 100644 --- a/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts +++ b/x-pack/plugins/data_enhanced/public/search/search_interceptor.ts @@ -98,7 +98,7 @@ export class EnhancedSearchInterceptor extends SearchInterceptor { if (id !== undefined) { this.deps.http.delete(`/internal/search/${strategy}/${id}`); } - return throwError(this.handleSearchError(e, request, timeoutSignal, options?.abortSignal)); + return throwError(this.handleSearchError(e, request, timeoutSignal, options)); }), finalize(() => { this.pendingCount$.next(this.pendingCount$.getValue() - 1); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx index 47da1e93cf004..bfc104b105236 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx @@ -21,6 +21,7 @@ import { CreateTimeline, UpdateTimelineLoading } from './types'; import { Ecs } from '../../../../common/ecs'; import { TimelineId, TimelineType, TimelineStatus } from '../../../../common/types/timeline'; import { ISearchStart } from '../../../../../../../src/plugins/data/public'; +import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; jest.mock('apollo-client'); @@ -29,7 +30,7 @@ describe('alert actions', () => { const unix = moment(anchor).valueOf(); let createTimeline: CreateTimeline; let updateTimelineIsLoading: UpdateTimelineLoading; - let searchStrategyClient: ISearchStart; + let searchStrategyClient: jest.Mocked; let clock: sinon.SinonFakeTimers; beforeEach(() => { @@ -42,11 +43,13 @@ describe('alert actions', () => { createTimeline = jest.fn() as jest.Mocked; updateTimelineIsLoading = jest.fn() as jest.Mocked; + searchStrategyClient = { aggs: {} as ISearchStart['aggs'], showError: jest.fn(), search: jest.fn().mockResolvedValue({ data: mockTimelineDetails }), searchSource: {} as ISearchStart['searchSource'], + session: dataPluginMock.createStartContract().search.session, }; jest.spyOn(apolloClient, 'query').mockImplementation((obj) => { From 2e37bd070358fa955bc669f9e33d42db4be7f995 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Fri, 16 Oct 2020 12:55:46 +0300 Subject: [PATCH 124/128] Add script to identify plugin dependencies for TS project references migration (#80463) * move kbn-dev-utils plugin helpers under a dedicated folder * use getPluginSearchPaths in kbn-config & kbn-optimizer * add a script to find plugin dependencies not migrated to TS project refs * update docs * add a script reporting all circular deps between plugins based on kibana.json declaration, so it doesn't provide all the cases * fix optimizer scan logic. removed by mistake * revert changes. fails on CI * remove prod depenedency on kbn/dev-utils * remove last export * only run plugin discovery once to speed up circular dep detection * address comments * address comments * update fixtures Co-authored-by: spalger --- .../best-practices/typescript.asciidoc | 2 +- packages/kbn-config/src/env.ts | 21 ++-- packages/kbn-config/src/index.ts | 1 + packages/kbn-config/src/plugins/index.ts | 19 ++++ .../src/plugins/plugin_search_paths.ts | 36 +++++++ packages/kbn-dev-utils/src/index.ts | 3 +- .../src/plugin_list/discover_plugins.ts | 2 +- packages/kbn-dev-utils/src/plugins/index.ts | 21 ++++ .../parse_kibana_platform_plugin.ts | 39 ++++++-- ...simple_kibana_platform_plugin_discovery.ts | 0 packages/kbn-optimizer/package.json | 1 + .../mock_repo/plugins/bar/kibana.json | 3 +- .../mock_repo/plugins/foo/kibana.json | 3 +- .../mock_repo/plugins/nested/baz/kibana.json | 3 +- .../test_plugins/test_baz/kibana.json | 3 +- .../mock_repo/x-pack/baz/kibana.json | 3 +- .../src/optimizer/optimizer_config.ts | 22 ++--- scripts/find_plugin_circular_deps.js | 21 ++++ scripts/find_plugins_without_ts_refs.js | 21 ++++ src/dev/plugin_discovery/find_plugins.ts | 50 ++++++++++ src/dev/plugin_discovery/get_plugin_deps.ts | 89 +++++++++++++++++ src/dev/plugin_discovery/index.ts | 21 ++++ src/dev/run_find_plugin_circular_deps.ts | 73 ++++++++++++++ src/dev/run_find_plugins_without_ts_refs.ts | 95 +++++++++++++++++++ 24 files changed, 508 insertions(+), 44 deletions(-) create mode 100644 packages/kbn-config/src/plugins/index.ts create mode 100644 packages/kbn-config/src/plugins/plugin_search_paths.ts create mode 100644 packages/kbn-dev-utils/src/plugins/index.ts rename packages/kbn-dev-utils/src/{ => plugins}/parse_kibana_platform_plugin.ts (56%) rename packages/kbn-dev-utils/src/{ => plugins}/simple_kibana_platform_plugin_discovery.ts (100%) create mode 100644 scripts/find_plugin_circular_deps.js create mode 100644 scripts/find_plugins_without_ts_refs.js create mode 100644 src/dev/plugin_discovery/find_plugins.ts create mode 100644 src/dev/plugin_discovery/get_plugin_deps.ts create mode 100644 src/dev/plugin_discovery/index.ts create mode 100644 src/dev/run_find_plugin_circular_deps.ts create mode 100644 src/dev/run_find_plugins_without_ts_refs.ts diff --git a/docs/developer/best-practices/typescript.asciidoc b/docs/developer/best-practices/typescript.asciidoc index a2cda1e0b1e87..583a98f296de5 100644 --- a/docs/developer/best-practices/typescript.asciidoc +++ b/docs/developer/best-practices/typescript.asciidoc @@ -28,7 +28,7 @@ This architecture imposes several limitations to which we must comply: [discrete] ==== Prerequisites Since project refs rely on generated `d.ts` files, the migration order does matter. You can migrate your plugin only when all the plugin dependencies already have migrated. It creates a situation where commonly used plugins (such as `data` or `kibana_react`) have to migrate first. -https://github.com/elastic/kibana/issues/79343 is going to provide a tool for identifying a plugin dependency tree. +Run `node scripts/find_plugins_without_ts_refs.js --id your_plugin_id` to get a list of plugins that should be switched to TS project refs to unblock your plugin migration. [discrete] ==== Implementation diff --git a/packages/kbn-config/src/env.ts b/packages/kbn-config/src/env.ts index e4585056696f9..e7b4658262235 100644 --- a/packages/kbn-config/src/env.ts +++ b/packages/kbn-config/src/env.ts @@ -19,6 +19,7 @@ import { resolve, join } from 'path'; import loadJsonFile from 'load-json-file'; +import { getPluginSearchPaths } from './plugins'; import { PackageInfo, EnvironmentMode } from './types'; /** @internal */ @@ -114,21 +115,11 @@ export class Env { this.binDir = resolve(this.homeDir, 'bin'); this.logDir = resolve(this.homeDir, 'log'); - /** - * BEWARE: this needs to stay roughly synchronized with the @kbn/optimizer - * `packages/kbn-optimizer/src/optimizer_config.ts` determines the paths - * that should be searched for plugins to build - */ - this.pluginSearchPaths = [ - resolve(this.homeDir, 'src', 'plugins'), - ...(options.cliArgs.oss ? [] : [resolve(this.homeDir, 'x-pack', 'plugins')]), - resolve(this.homeDir, 'plugins'), - ...(options.cliArgs.runExamples ? [resolve(this.homeDir, 'examples')] : []), - ...(options.cliArgs.runExamples && !options.cliArgs.oss - ? [resolve(this.homeDir, 'x-pack', 'examples')] - : []), - resolve(this.homeDir, '..', 'kibana-extra'), - ]; + this.pluginSearchPaths = getPluginSearchPaths({ + rootDir: this.homeDir, + oss: options.cliArgs.oss, + examples: options.cliArgs.runExamples, + }); this.cliArgs = Object.freeze(options.cliArgs); this.configs = Object.freeze(options.configs); diff --git a/packages/kbn-config/src/index.ts b/packages/kbn-config/src/index.ts index f02514a92e606..68609c6d5c7c3 100644 --- a/packages/kbn-config/src/index.ts +++ b/packages/kbn-config/src/index.ts @@ -35,3 +35,4 @@ export { ObjectToConfigAdapter } from './object_to_config_adapter'; export { CliArgs, Env, RawPackageInfo } from './env'; export { EnvironmentMode, PackageInfo } from './types'; export { LegacyObjectToConfigAdapter, LegacyLoggingConfig } from './legacy'; +export { getPluginSearchPaths } from './plugins'; diff --git a/packages/kbn-config/src/plugins/index.ts b/packages/kbn-config/src/plugins/index.ts new file mode 100644 index 0000000000000..7d02f9fb984c2 --- /dev/null +++ b/packages/kbn-config/src/plugins/index.ts @@ -0,0 +1,19 @@ +/* + * 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 { getPluginSearchPaths } from './plugin_search_paths'; diff --git a/packages/kbn-config/src/plugins/plugin_search_paths.ts b/packages/kbn-config/src/plugins/plugin_search_paths.ts new file mode 100644 index 0000000000000..a7d151c3275c8 --- /dev/null +++ b/packages/kbn-config/src/plugins/plugin_search_paths.ts @@ -0,0 +1,36 @@ +/* + * 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 { resolve } from 'path'; + +interface SearchOptions { + rootDir: string; + oss: boolean; + examples: boolean; +} + +export function getPluginSearchPaths({ rootDir, oss, examples }: SearchOptions) { + return [ + resolve(rootDir, 'src', 'plugins'), + ...(oss ? [] : [resolve(rootDir, 'x-pack', 'plugins')]), + resolve(rootDir, 'plugins'), + ...(examples ? [resolve(rootDir, 'examples')] : []), + ...(examples && !oss ? [resolve(rootDir, 'x-pack', 'examples')] : []), + resolve(rootDir, '..', 'kibana-extra'), + ]; +} diff --git a/packages/kbn-dev-utils/src/index.ts b/packages/kbn-dev-utils/src/index.ts index 6a845825f0fd4..98385b49dafa9 100644 --- a/packages/kbn-dev-utils/src/index.ts +++ b/packages/kbn-dev-utils/src/index.ts @@ -40,7 +40,6 @@ export * from './axios'; export * from './stdio'; export * from './ci_stats_reporter'; export * from './plugin_list'; -export * from './simple_kibana_platform_plugin_discovery'; +export * from './plugins'; export * from './streams'; export * from './babel'; -export * from './parse_kibana_platform_plugin'; diff --git a/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts b/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts index e8f6735205b19..9782067e61343 100644 --- a/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts +++ b/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts @@ -24,7 +24,7 @@ import MarkdownIt from 'markdown-it'; import cheerio from 'cheerio'; import { REPO_ROOT } from '@kbn/utils'; -import { simpleKibanaPlatformPluginDiscovery } from '../simple_kibana_platform_plugin_discovery'; +import { simpleKibanaPlatformPluginDiscovery } from '../plugins'; import { extractAsciidocInfo } from './extract_asciidoc_info'; export interface Plugin { diff --git a/packages/kbn-dev-utils/src/plugins/index.ts b/packages/kbn-dev-utils/src/plugins/index.ts new file mode 100644 index 0000000000000..8705682f355c7 --- /dev/null +++ b/packages/kbn-dev-utils/src/plugins/index.ts @@ -0,0 +1,21 @@ +/* + * 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 * from './parse_kibana_platform_plugin'; +export * from './simple_kibana_platform_plugin_discovery'; diff --git a/packages/kbn-dev-utils/src/parse_kibana_platform_plugin.ts b/packages/kbn-dev-utils/src/plugins/parse_kibana_platform_plugin.ts similarity index 56% rename from packages/kbn-dev-utils/src/parse_kibana_platform_plugin.ts rename to packages/kbn-dev-utils/src/plugins/parse_kibana_platform_plugin.ts index 83d8c2684d7ca..16aaecb3e478d 100644 --- a/packages/kbn-dev-utils/src/parse_kibana_platform_plugin.ts +++ b/packages/kbn-dev-utils/src/plugins/parse_kibana_platform_plugin.ts @@ -23,12 +23,27 @@ import loadJsonFile from 'load-json-file'; export interface KibanaPlatformPlugin { readonly directory: string; readonly manifestPath: string; - readonly manifest: { - id: string; - ui: boolean; - server: boolean; - [key: string]: unknown; - }; + readonly manifest: Manifest; +} + +function isValidDepsDeclaration(input: unknown, type: string): string[] { + if (typeof input === 'undefined') return []; + if (Array.isArray(input) && input.every((i) => typeof i === 'string')) { + return input; + } + throw new TypeError(`The "${type}" in plugin manifest should be an array of strings.`); +} + +interface Manifest { + id: string; + ui: boolean; + server: boolean; + kibanaVersion: string; + version: string; + requiredPlugins: readonly string[]; + optionalPlugins: readonly string[]; + requiredBundles: readonly string[]; + extraPublicDirs: readonly string[]; } export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformPlugin { @@ -36,7 +51,7 @@ export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformP throw new TypeError('expected new platform manifest path to be absolute'); } - const manifest = loadJsonFile.sync(manifestPath); + const manifest: Partial = loadJsonFile.sync(manifestPath); if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) { throw new TypeError('expected new platform plugin manifest to be a JSON encoded object'); } @@ -45,6 +60,10 @@ export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformP throw new TypeError('expected new platform plugin manifest to have a string id'); } + if (typeof manifest.version !== 'string') { + throw new TypeError('expected new platform plugin manifest to have a string version'); + } + return { directory: Path.dirname(manifestPath), manifestPath, @@ -54,6 +73,12 @@ export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformP ui: !!manifest.ui, server: !!manifest.server, id: manifest.id, + version: manifest.version, + kibanaVersion: manifest.kibanaVersion || manifest.version, + requiredPlugins: isValidDepsDeclaration(manifest.requiredPlugins, 'requiredPlugins'), + optionalPlugins: isValidDepsDeclaration(manifest.optionalPlugins, 'optionalPlugins'), + requiredBundles: isValidDepsDeclaration(manifest.requiredBundles, 'requiredBundles'), + extraPublicDirs: isValidDepsDeclaration(manifest.extraPublicDirs, 'extraPublicDirs'), }, }; } diff --git a/packages/kbn-dev-utils/src/simple_kibana_platform_plugin_discovery.ts b/packages/kbn-dev-utils/src/plugins/simple_kibana_platform_plugin_discovery.ts similarity index 100% rename from packages/kbn-dev-utils/src/simple_kibana_platform_plugin_discovery.ts rename to packages/kbn-dev-utils/src/plugins/simple_kibana_platform_plugin_discovery.ts diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index c9e414dbc5177..63146fc7a1834 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -14,6 +14,7 @@ "@babel/core": "^7.11.6", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", + "@kbn/config": "1.0.0", "@kbn/std": "1.0.0", "@kbn/ui-shared-deps": "1.0.0", "autoprefixer": "^9.7.4", diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json index 33f53e336598d..a5e9f34a22aa6 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json @@ -1,5 +1,6 @@ { "id": "bar", "ui": true, - "requiredBundles": ["foo"] + "requiredBundles": ["foo"], + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json index 256856181ccd8..27730df199887 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json @@ -1,4 +1,5 @@ { "id": "foo", - "ui": true + "ui": true, + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json index 6e4e9c70a115c..a8f991ee11465 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json @@ -1,3 +1,4 @@ { - "id": "baz" + "id": "baz", + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json index b9e044523a6a5..d8a8b2e548e4a 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json @@ -1,3 +1,4 @@ { - "id": "test_baz" + "id": "test_baz", + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack/baz/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack/baz/kibana.json index 10602d2e7981a..64ec7ff5ccf3e 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack/baz/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack/baz/kibana.json @@ -1,4 +1,5 @@ { "id": "baz", - "ui": true + "ui": true, + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index 8091f6aa90508..1443fccda04d8 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -19,6 +19,7 @@ import Path from 'path'; import Os from 'os'; +import { getPluginSearchPaths } from '@kbn/config'; import { Bundle, @@ -167,19 +168,14 @@ export class OptimizerConfig { throw new TypeError('outputRoot must be an absolute path'); } - /** - * BEWARE: this needs to stay roughly synchronized with - * `src/core/server/config/env.ts` which determines which paths - * should be searched for plugins to load - */ - const pluginScanDirs = options.pluginScanDirs || [ - Path.resolve(repoRoot, 'src/plugins'), - ...(oss ? [] : [Path.resolve(repoRoot, 'x-pack/plugins')]), - Path.resolve(repoRoot, 'plugins'), - ...(examples ? [Path.resolve('examples')] : []), - ...(examples && !oss ? [Path.resolve('x-pack/examples')] : []), - Path.resolve(repoRoot, '../kibana-extra'), - ]; + const pluginScanDirs = + options.pluginScanDirs || + getPluginSearchPaths({ + rootDir: repoRoot, + oss, + examples, + }); + if (!pluginScanDirs.every((p) => Path.isAbsolute(p))) { throw new TypeError('pluginScanDirs must all be absolute paths'); } diff --git a/scripts/find_plugin_circular_deps.js b/scripts/find_plugin_circular_deps.js new file mode 100644 index 0000000000000..6b0661cb841b4 --- /dev/null +++ b/scripts/find_plugin_circular_deps.js @@ -0,0 +1,21 @@ +/* + * 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. + */ + +require('../src/setup_node_env'); +require('../src/dev/run_find_plugin_circular_deps'); diff --git a/scripts/find_plugins_without_ts_refs.js b/scripts/find_plugins_without_ts_refs.js new file mode 100644 index 0000000000000..5f543a045f739 --- /dev/null +++ b/scripts/find_plugins_without_ts_refs.js @@ -0,0 +1,21 @@ +/* + * 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. + */ + +require('../src/setup_node_env'); +require('../src/dev/run_find_plugins_without_ts_refs'); diff --git a/src/dev/plugin_discovery/find_plugins.ts b/src/dev/plugin_discovery/find_plugins.ts new file mode 100644 index 0000000000000..4e7c34698c964 --- /dev/null +++ b/src/dev/plugin_discovery/find_plugins.ts @@ -0,0 +1,50 @@ +/* + * 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 Path from 'path'; +import { REPO_ROOT } from '@kbn/dev-utils'; +import { getPluginSearchPaths } from '@kbn/config'; +import { KibanaPlatformPlugin, simpleKibanaPlatformPluginDiscovery } from '@kbn/dev-utils'; + +export interface SearchOptions { + oss: boolean; + examples: boolean; + extraPluginScanDirs: string[]; +} + +export function findPlugins({ + oss, + examples, + extraPluginScanDirs, +}: SearchOptions): Map { + const pluginSearchPaths = getPluginSearchPaths({ + rootDir: REPO_ROOT, + oss, + examples, + }); + + for (const extraScanDir of extraPluginScanDirs) { + if (!Path.isAbsolute(extraScanDir)) { + throw new TypeError('extraPluginScanDirs must all be absolute paths'); + } + pluginSearchPaths.push(extraScanDir); + } + + const plugins = simpleKibanaPlatformPluginDiscovery(pluginSearchPaths, []); + return new Map(plugins.map((p) => [p.manifest.id, p])); +} diff --git a/src/dev/plugin_discovery/get_plugin_deps.ts b/src/dev/plugin_discovery/get_plugin_deps.ts new file mode 100644 index 0000000000000..498feefd97094 --- /dev/null +++ b/src/dev/plugin_discovery/get_plugin_deps.ts @@ -0,0 +1,89 @@ +/* + * 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 { KibanaPlatformPlugin } from '@kbn/dev-utils'; + +interface AllOptions { + id: string; + pluginMap: Map; +} + +interface CircularRefsError { + from: string; + to: string; + stack: string[]; +} + +export type SearchErrors = CircularRefsError; + +interface State { + deps: Set; + stack: string[]; + errors: Map; +} + +function traverse(pluginMap: Map, state: State, id: string) { + const plugin = pluginMap.get(id); + if (plugin === undefined) { + throw new Error(`Unknown plugin id: ${id}`); + } + + const prevIndex = state.stack.indexOf(id); + const isVisited = prevIndex > -1; + if (isVisited) { + const from = state.stack[state.stack.length - 1]; + const to = id; + const key = `circular-${[from, to].sort().join('-')}`; + + if (!state.errors.has(key)) { + const error: CircularRefsError = { + from, + to, + // provide sub-stack with circular refs only + stack: state.stack.slice(prevIndex), + }; + state.errors.set(key, error); + } + + return; + } + + state.stack.push(id); + new Set([ + ...plugin.manifest.requiredPlugins, + ...plugin.manifest.optionalPlugins, + ...plugin.manifest.requiredBundles, + ]).forEach((depId) => { + state.deps.add(pluginMap.get(depId)!); + traverse(pluginMap, state, depId); + }); + + state.stack.pop(); +} + +export function getPluginDeps({ pluginMap, id }: AllOptions): State { + const state: State = { + deps: new Set(), + errors: new Map(), + stack: [], + }; + + traverse(pluginMap, state, id); + + return state; +} diff --git a/src/dev/plugin_discovery/index.ts b/src/dev/plugin_discovery/index.ts new file mode 100644 index 0000000000000..4a4be65dfaef0 --- /dev/null +++ b/src/dev/plugin_discovery/index.ts @@ -0,0 +1,21 @@ +/* + * 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 * from './find_plugins'; +export * from './get_plugin_deps'; diff --git a/src/dev/run_find_plugin_circular_deps.ts b/src/dev/run_find_plugin_circular_deps.ts new file mode 100644 index 0000000000000..501e2c4fed048 --- /dev/null +++ b/src/dev/run_find_plugin_circular_deps.ts @@ -0,0 +1,73 @@ +/* + * 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 { run } from '@kbn/dev-utils'; +import { findPlugins, getPluginDeps, SearchErrors } from './plugin_discovery'; + +interface AllOptions { + examples?: boolean; + extraPluginScanDirs?: string[]; +} + +run( + async ({ flags, log }) => { + const { examples = false, extraPluginScanDirs = [] } = flags as AllOptions; + + const pluginMap = findPlugins({ + oss: false, + examples, + extraPluginScanDirs, + }); + + const allErrors = new Map(); + for (const pluginId of pluginMap.keys()) { + const { errors } = getPluginDeps({ + pluginMap, + id: pluginId, + }); + + for (const [errorId, error] of errors) { + if (!allErrors.has(errorId)) { + allErrors.set(errorId, error); + } + } + } + + if (allErrors.size > 0) { + allErrors.forEach((error) => { + log.warning( + `Circular refs detected: ${[...error.stack, error.to].map((p) => `[${p}]`).join(' --> ')}` + ); + }); + } + }, + { + flags: { + boolean: ['examples'], + default: { + examples: false, + }, + allowUnexpected: false, + help: ` + --examples Include examples folder + --extraPluginScanDirs Include extra scan folder + `, + }, + } +); diff --git a/src/dev/run_find_plugins_without_ts_refs.ts b/src/dev/run_find_plugins_without_ts_refs.ts new file mode 100644 index 0000000000000..ad63884671e24 --- /dev/null +++ b/src/dev/run_find_plugins_without_ts_refs.ts @@ -0,0 +1,95 @@ +/* + * 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 Path from 'path'; +import Fs from 'fs'; +import { get } from 'lodash'; +import { run } from '@kbn/dev-utils'; +import { getPluginDeps, findPlugins } from './plugin_discovery'; + +interface AllOptions { + id?: string; + examples?: boolean; + extraPluginScanDirs?: string[]; +} + +run( + async ({ flags, log }) => { + const { examples = false, extraPluginScanDirs = [], id } = flags as AllOptions; + + if (!id) { + throw new Error('Plugin id required'); + } + + const pluginMap = findPlugins({ + oss: false, + examples, + extraPluginScanDirs, + }); + + const result = getPluginDeps({ + pluginMap, + id, + }); + + if (result.errors.size > 0) { + result.errors.forEach((error) => { + log.warning( + `Circular refs detected: ${[...error.stack, error.to].map((p) => `[${p}]`).join(' --> ')}` + ); + }); + } + + const notMigratedPlugins = [...result.deps].filter( + (plugin) => !isMigratedToTsProjectRefs(plugin.directory) + ); + if (notMigratedPlugins.length > 0) { + log.info( + `Dependencies haven't been migrated to TS project refs yet:\n${notMigratedPlugins + .map((p) => p.manifest.id) + .join('\n')}` + ); + } + }, + { + flags: { + boolean: ['examples'], + string: ['id'], + default: { + examples: false, + }, + allowUnexpected: false, + help: ` + --id Plugin id to perform deps search for + --examples Include examples folder + --extraPluginScanDirs Include extra scan folder + `, + }, + } +); + +function isMigratedToTsProjectRefs(dir: string): boolean { + try { + const path = Path.join(dir, 'tsconfig.json'); + const content = Fs.readFileSync(path, { encoding: 'utf8' }); + return get(JSON.parse(content), 'compilerOptions.composite', false); + } catch (e) { + return false; + } +} From 51ac14ba57097ede9e323ed8595a79c211baba84 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Fri, 16 Oct 2020 07:21:57 -0400 Subject: [PATCH 125/128] Allow the default space to be accessed via `/s/default` (#77109) * Allow the default space to be accessed via /s/default * apply suggestions from code review --- .../common/lib/spaces_url_parser.test.ts | 53 ++++++++++-- .../spaces/common/lib/spaces_url_parser.ts | 28 ++++-- .../on_request_interceptor.ts | 5 +- .../spaces_service/spaces_service.test.ts | 2 +- .../server/spaces_service/spaces_service.ts | 2 +- .../apis/spaces/get_active_space.ts | 14 +++ .../common/lib/space_test_utils.ts | 21 +++++ .../common/suites/create.ts | 86 ++++++++++--------- .../common/suites/delete.ts | 50 +++++------ .../common/suites/get.ts | 16 ++-- .../common/suites/get_all.ts | 50 +++++------ 11 files changed, 208 insertions(+), 119 deletions(-) diff --git a/x-pack/plugins/spaces/common/lib/spaces_url_parser.test.ts b/x-pack/plugins/spaces/common/lib/spaces_url_parser.test.ts index b25d79c0a6907..2b34bc77ec686 100644 --- a/x-pack/plugins/spaces/common/lib/spaces_url_parser.test.ts +++ b/x-pack/plugins/spaces/common/lib/spaces_url_parser.test.ts @@ -10,41 +10,76 @@ describe('getSpaceIdFromPath', () => { describe('without a serverBasePath defined', () => { test('it identifies the space url context', () => { const basePath = `/s/my-awesome-space-lives-here`; - expect(getSpaceIdFromPath(basePath)).toEqual('my-awesome-space-lives-here'); + expect(getSpaceIdFromPath(basePath)).toEqual({ + spaceId: 'my-awesome-space-lives-here', + pathHasExplicitSpaceIdentifier: true, + }); }); test('ignores space identifiers in the middle of the path', () => { const basePath = `/this/is/a/crazy/path/s/my-awesome-space-lives-here`; - expect(getSpaceIdFromPath(basePath)).toEqual(DEFAULT_SPACE_ID); + expect(getSpaceIdFromPath(basePath)).toEqual({ + spaceId: DEFAULT_SPACE_ID, + pathHasExplicitSpaceIdentifier: false, + }); }); test('it handles base url without a space url context', () => { const basePath = `/this/is/a/crazy/path/s`; - expect(getSpaceIdFromPath(basePath)).toEqual(DEFAULT_SPACE_ID); + expect(getSpaceIdFromPath(basePath)).toEqual({ + spaceId: DEFAULT_SPACE_ID, + pathHasExplicitSpaceIdentifier: false, + }); + }); + + test('it identifies the space url context with the default space', () => { + const basePath = `/s/${DEFAULT_SPACE_ID}`; + expect(getSpaceIdFromPath(basePath)).toEqual({ + spaceId: DEFAULT_SPACE_ID, + pathHasExplicitSpaceIdentifier: true, + }); }); }); describe('with a serverBasePath defined', () => { test('it identifies the space url context', () => { const basePath = `/s/my-awesome-space-lives-here`; - expect(getSpaceIdFromPath(basePath, '/')).toEqual('my-awesome-space-lives-here'); + expect(getSpaceIdFromPath(basePath, '/')).toEqual({ + spaceId: 'my-awesome-space-lives-here', + pathHasExplicitSpaceIdentifier: true, + }); }); test('it identifies the space url context following the server base path', () => { const basePath = `/server-base-path-here/s/my-awesome-space-lives-here`; - expect(getSpaceIdFromPath(basePath, '/server-base-path-here')).toEqual( - 'my-awesome-space-lives-here' - ); + expect(getSpaceIdFromPath(basePath, '/server-base-path-here')).toEqual({ + spaceId: 'my-awesome-space-lives-here', + pathHasExplicitSpaceIdentifier: true, + }); }); test('ignores space identifiers in the middle of the path', () => { const basePath = `/this/is/a/crazy/path/s/my-awesome-space-lives-here`; - expect(getSpaceIdFromPath(basePath, '/this/is/a')).toEqual(DEFAULT_SPACE_ID); + expect(getSpaceIdFromPath(basePath, '/this/is/a')).toEqual({ + spaceId: DEFAULT_SPACE_ID, + pathHasExplicitSpaceIdentifier: false, + }); + }); + + test('it identifies the space url context with the default space following the server base path', () => { + const basePath = `/server-base-path-here/s/${DEFAULT_SPACE_ID}`; + expect(getSpaceIdFromPath(basePath, '/server-base-path-here')).toEqual({ + spaceId: DEFAULT_SPACE_ID, + pathHasExplicitSpaceIdentifier: true, + }); }); test('it handles base url without a space url context', () => { const basePath = `/this/is/a/crazy/path/s`; - expect(getSpaceIdFromPath(basePath, basePath)).toEqual(DEFAULT_SPACE_ID); + expect(getSpaceIdFromPath(basePath, basePath)).toEqual({ + spaceId: DEFAULT_SPACE_ID, + pathHasExplicitSpaceIdentifier: false, + }); }); }); }); diff --git a/x-pack/plugins/spaces/common/lib/spaces_url_parser.ts b/x-pack/plugins/spaces/common/lib/spaces_url_parser.ts index 994ec7c59cb6e..be950e6a651e6 100644 --- a/x-pack/plugins/spaces/common/lib/spaces_url_parser.ts +++ b/x-pack/plugins/spaces/common/lib/spaces_url_parser.ts @@ -5,20 +5,22 @@ */ import { DEFAULT_SPACE_ID } from '../constants'; +const spaceContextRegex = /^\/s\/([a-z0-9_\-]+)/; + export function getSpaceIdFromPath( requestBasePath: string = '/', serverBasePath: string = '/' -): string { - let pathToCheck: string = requestBasePath; +): { spaceId: string; pathHasExplicitSpaceIdentifier: boolean } { + const pathToCheck: string = stripServerBasePath(requestBasePath, serverBasePath); - if (serverBasePath && serverBasePath !== '/' && requestBasePath.startsWith(serverBasePath)) { - pathToCheck = requestBasePath.substr(serverBasePath.length); - } // Look for `/s/space-url-context` in the base path - const matchResult = pathToCheck.match(/^\/s\/([a-z0-9_\-]+)/); + const matchResult = pathToCheck.match(spaceContextRegex); if (!matchResult || matchResult.length === 0) { - return DEFAULT_SPACE_ID; + return { + spaceId: DEFAULT_SPACE_ID, + pathHasExplicitSpaceIdentifier: false, + }; } // Ignoring first result, we only want the capture group result at index 1 @@ -28,7 +30,10 @@ export function getSpaceIdFromPath( throw new Error(`Unable to determine Space ID from request path: ${requestBasePath}`); } - return spaceId; + return { + spaceId, + pathHasExplicitSpaceIdentifier: true, + }; } export function addSpaceIdToPath( @@ -45,3 +50,10 @@ export function addSpaceIdToPath( } return `${basePath}${requestedPath}`; } + +function stripServerBasePath(requestBasePath: string, serverBasePath: string) { + if (serverBasePath && serverBasePath !== '/' && requestBasePath.startsWith(serverBasePath)) { + return requestBasePath.substr(serverBasePath.length); + } + return requestBasePath; +} diff --git a/x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.ts b/x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.ts index 4b3a5d662f12d..6408803c2114b 100644 --- a/x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.ts +++ b/x-pack/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.ts @@ -10,7 +10,6 @@ import { CoreSetup, } from 'src/core/server'; import { format } from 'url'; -import { DEFAULT_SPACE_ID } from '../../../common/constants'; import { modifyUrl } from '../utils/url'; import { getSpaceIdFromPath } from '../../../common'; @@ -28,9 +27,9 @@ export function initSpacesOnRequestInterceptor({ http }: OnRequestInterceptorDep // If navigating within the context of a space, then we store the Space's URL Context on the request, // and rewrite the request to not include the space identifier in the URL. - const spaceId = getSpaceIdFromPath(path, serverBasePath); + const { spaceId, pathHasExplicitSpaceIdentifier } = getSpaceIdFromPath(path, serverBasePath); - if (spaceId !== DEFAULT_SPACE_ID) { + if (pathHasExplicitSpaceIdentifier) { const reqBasePath = `/s/${spaceId}`; http.basePath.set(request, reqBasePath); diff --git a/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts b/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts index b341d76c86649..b48bf971d0c1b 100644 --- a/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts +++ b/x-pack/plugins/spaces/server/spaces_service/spaces_service.test.ts @@ -58,7 +58,7 @@ const createService = async (serverBasePath: string = '') => { serverBasePath, } as HttpServiceSetup['basePath']; httpSetup.basePath.get = jest.fn().mockImplementation((request: KibanaRequest) => { - const spaceId = getSpaceIdFromPath(request.url.path); + const { spaceId } = getSpaceIdFromPath(request.url.path); if (spaceId !== DEFAULT_SPACE_ID) { return `/s/${spaceId}`; diff --git a/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts b/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts index cf181a78efcb8..3630675a7ed3f 100644 --- a/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts +++ b/x-pack/plugins/spaces/server/spaces_service/spaces_service.ts @@ -63,7 +63,7 @@ export class SpacesService { ? (request as Record).getBasePath() : http.basePath.get(request); - const spaceId = getSpaceIdFromPath(basePath, http.basePath.serverBasePath); + const { spaceId } = getSpaceIdFromPath(basePath, http.basePath.serverBasePath); return spaceId; }; diff --git a/x-pack/test/api_integration/apis/spaces/get_active_space.ts b/x-pack/test/api_integration/apis/spaces/get_active_space.ts index b925df3918825..16cb03fe8a316 100644 --- a/x-pack/test/api_integration/apis/spaces/get_active_space.ts +++ b/x-pack/test/api_integration/apis/spaces/get_active_space.ts @@ -35,6 +35,20 @@ export default function ({ getService }: FtrProviderContext) { }); }); + it('returns the default space when explicitly referenced', async () => { + await supertest + .get('/s/default/internal/spaces/_active_space') + .set('kbn-xsrf', 'xxx') + .expect(200, { + id: 'default', + name: 'Default', + description: 'This is your default space!', + color: '#00bfb3', + disabledFeatures: [], + _reserved: true, + }); + }); + it('returns the foo space', async () => { await supertest .get('/s/foo-space/internal/spaces/_active_space') diff --git a/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts b/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts index f233bc1d11d7c..c8e13f6bada7a 100644 --- a/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts +++ b/x-pack/test/spaces_api_integration/common/lib/space_test_utils.ts @@ -13,3 +13,24 @@ export function getUrlPrefix(spaceId?: string) { export function getIdPrefix(spaceId?: string) { return spaceId === DEFAULT_SPACE_ID ? '' : `${spaceId}-`; } + +export function getTestScenariosForSpace(spaceId: string) { + const explicitScenario = { + spaceId, + urlPrefix: `/s/${spaceId}`, + scenario: `when referencing the ${spaceId} space explicitly in the URL`, + }; + + if (spaceId === DEFAULT_SPACE_ID) { + return [ + { + spaceId, + urlPrefix: ``, + scenario: 'when referencing the default space implicitly', + }, + explicitScenario, + ]; + } + + return [explicitScenario]; +} diff --git a/x-pack/test/spaces_api_integration/common/suites/create.ts b/x-pack/test/spaces_api_integration/common/suites/create.ts index 4de638c784147..7c2120ce6eeaf 100644 --- a/x-pack/test/spaces_api_integration/common/suites/create.ts +++ b/x-pack/test/spaces_api_integration/common/suites/create.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { getUrlPrefix } from '../lib/space_test_utils'; +import { getTestScenariosForSpace } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; interface CreateTest { @@ -67,56 +67,58 @@ export function createTestSuiteFactory(esArchiver: any, supertest: SuperTest { describeFn(description, () => { - before(() => esArchiver.load('saved_objects/spaces')); - after(() => esArchiver.unload('saved_objects/spaces')); + beforeEach(() => esArchiver.load('saved_objects/spaces')); + afterEach(() => esArchiver.unload('saved_objects/spaces')); - it(`should return ${tests.newSpace.statusCode}`, async () => { - return supertest - .post(`${getUrlPrefix(spaceId)}/api/spaces/space`) - .auth(user.username, user.password) - .send({ - name: 'marketing', - id: 'marketing', - description: 'a description', - color: '#5c5959', - disabledFeatures: [], - }) - .expect(tests.newSpace.statusCode) - .then(tests.newSpace.response); - }); - - describe('when it already exists', () => { - it(`should return ${tests.alreadyExists.statusCode}`, async () => { + getTestScenariosForSpace(spaceId).forEach(({ urlPrefix, scenario }) => { + it(`should return ${tests.newSpace.statusCode} ${scenario}`, async () => { return supertest - .post(`${getUrlPrefix(spaceId)}/api/spaces/space`) + .post(`${urlPrefix}/api/spaces/space`) .auth(user.username, user.password) .send({ - name: 'space_1', - id: 'space_1', - color: '#ffffff', + name: 'marketing', + id: 'marketing', description: 'a description', + color: '#5c5959', disabledFeatures: [], }) - .expect(tests.alreadyExists.statusCode) - .then(tests.alreadyExists.response); + .expect(tests.newSpace.statusCode) + .then(tests.newSpace.response); }); - }); - describe('when _reserved is specified', () => { - it(`should return ${tests.reservedSpecified.statusCode} and ignore _reserved`, async () => { - return supertest - .post(`${getUrlPrefix(spaceId)}/api/spaces/space`) - .auth(user.username, user.password) - .send({ - name: 'reserved space', - id: 'reserved', - description: 'a description', - color: '#5c5959', - _reserved: true, - disabledFeatures: [], - }) - .expect(tests.reservedSpecified.statusCode) - .then(tests.reservedSpecified.response); + describe('when it already exists', () => { + it(`should return ${tests.alreadyExists.statusCode} ${scenario}`, async () => { + return supertest + .post(`${urlPrefix}/api/spaces/space`) + .auth(user.username, user.password) + .send({ + name: 'space_1', + id: 'space_1', + color: '#ffffff', + description: 'a description', + disabledFeatures: [], + }) + .expect(tests.alreadyExists.statusCode) + .then(tests.alreadyExists.response); + }); + }); + + describe('when _reserved is specified', () => { + it(`should return ${tests.reservedSpecified.statusCode} and ignore _reserved ${scenario}`, async () => { + return supertest + .post(`${urlPrefix}/api/spaces/space`) + .auth(user.username, user.password) + .send({ + name: 'reserved space', + id: 'reserved', + description: 'a description', + color: '#5c5959', + _reserved: true, + disabledFeatures: [], + }) + .expect(tests.reservedSpecified.statusCode) + .then(tests.reservedSpecified.response); + }); }); }); }); diff --git a/x-pack/test/spaces_api_integration/common/suites/delete.ts b/x-pack/test/spaces_api_integration/common/suites/delete.ts index 69b5697d8a9a8..2a6b2c0e69d1d 100644 --- a/x-pack/test/spaces_api_integration/common/suites/delete.ts +++ b/x-pack/test/spaces_api_integration/common/suites/delete.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { getUrlPrefix } from '../lib/space_test_utils'; +import { getTestScenariosForSpace } from '../lib/space_test_utils'; import { MULTI_NAMESPACE_SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; @@ -176,7 +176,7 @@ export function deleteTestSuiteFactory(es: any, esArchiver: any, supertest: Supe { user = {}, spaceId, tests }: DeleteTestDefinition ) => { describeFn(description, () => { - before(async () => { + beforeEach(async () => { await esArchiver.load('saved_objects/spaces'); // since we want to verify that we only delete the right things @@ -189,33 +189,35 @@ export function deleteTestSuiteFactory(es: any, esArchiver: any, supertest: Supe .auth(user.username, user.password) .expect(200); }); - after(() => esArchiver.unload('saved_objects/spaces')); + afterEach(() => esArchiver.unload('saved_objects/spaces')); - it(`should return ${tests.exists.statusCode}`, async () => { - return supertest - .delete(`${getUrlPrefix(spaceId)}/api/spaces/space/space_2`) - .auth(user.username, user.password) - .expect(tests.exists.statusCode) - .then(tests.exists.response); - }); - - describe(`when the space is reserved`, () => { - it(`should return ${tests.reservedSpace.statusCode}`, async () => { + getTestScenariosForSpace(spaceId).forEach(({ urlPrefix, scenario }) => { + it(`should return ${tests.exists.statusCode} ${scenario}`, async () => { return supertest - .delete(`${getUrlPrefix(spaceId)}/api/spaces/space/default`) + .delete(`${urlPrefix}/api/spaces/space/space_2`) .auth(user.username, user.password) - .expect(tests.reservedSpace.statusCode) - .then(tests.reservedSpace.response); + .expect(tests.exists.statusCode) + .then(tests.exists.response); }); - }); - describe(`when the space doesn't exist`, () => { - it(`should return ${tests.doesntExist.statusCode}`, async () => { - return supertest - .delete(`${getUrlPrefix(spaceId)}/api/spaces/space/space_3`) - .auth(user.username, user.password) - .expect(tests.doesntExist.statusCode) - .then(tests.doesntExist.response); + describe(`when the space is reserved`, () => { + it(`should return ${tests.reservedSpace.statusCode} ${scenario}`, async () => { + return supertest + .delete(`${urlPrefix}/api/spaces/space/default`) + .auth(user.username, user.password) + .expect(tests.reservedSpace.statusCode) + .then(tests.reservedSpace.response); + }); + }); + + describe(`when the space doesn't exist`, () => { + it(`should return ${tests.doesntExist.statusCode} ${scenario}`, async () => { + return supertest + .delete(`${urlPrefix}/api/spaces/space/space_3`) + .auth(user.username, user.password) + .expect(tests.doesntExist.statusCode) + .then(tests.doesntExist.response); + }); }); }); }); diff --git a/x-pack/test/spaces_api_integration/common/suites/get.ts b/x-pack/test/spaces_api_integration/common/suites/get.ts index bd0e2a18d5c50..6bf5b0f180237 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; import { SuperAgent } from 'superagent'; -import { getUrlPrefix } from '../lib/space_test_utils'; +import { getTestScenariosForSpace } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; interface GetTest { @@ -80,12 +80,14 @@ export function getTestSuiteFactory(esArchiver: any, supertest: SuperAgent) before(() => esArchiver.load('saved_objects/spaces')); after(() => esArchiver.unload('saved_objects/spaces')); - it(`should return ${tests.default.statusCode}`, async () => { - return supertest - .get(`${getUrlPrefix(currentSpaceId)}/api/spaces/space/${spaceId}`) - .auth(user.username, user.password) - .expect(tests.default.statusCode) - .then(tests.default.response); + getTestScenariosForSpace(currentSpaceId).forEach(({ urlPrefix, scenario }) => { + it(`should return ${tests.default.statusCode} ${scenario}`, async () => { + return supertest + .get(`${urlPrefix}/api/spaces/space/${spaceId}`) + .auth(user.username, user.password) + .expect(tests.default.statusCode) + .then(tests.default.response); + }); }); }); }; diff --git a/x-pack/test/spaces_api_integration/common/suites/get_all.ts b/x-pack/test/spaces_api_integration/common/suites/get_all.ts index d41d73bba90bc..fce48e4938baa 100644 --- a/x-pack/test/spaces_api_integration/common/suites/get_all.ts +++ b/x-pack/test/spaces_api_integration/common/suites/get_all.ts @@ -5,7 +5,7 @@ */ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; -import { getUrlPrefix } from '../lib/space_test_utils'; +import { getTestScenariosForSpace } from '../lib/space_test_utils'; import { DescribeFn, TestDefinitionAuthentication } from '../lib/types'; interface GetAllTest { @@ -71,33 +71,35 @@ export function getAllTestSuiteFactory(esArchiver: any, supertest: SuperTest esArchiver.load('saved_objects/spaces')); after(() => esArchiver.unload('saved_objects/spaces')); - it(`should return ${tests.exists.statusCode}`, async () => { - return supertest - .get(`${getUrlPrefix(spaceId)}/api/spaces/space`) - .auth(user.username, user.password) - .expect(tests.exists.statusCode) - .then(tests.exists.response); - }); - - describe('copySavedObjects purpose', () => { - it(`should return ${tests.copySavedObjectsPurpose.statusCode}`, async () => { + getTestScenariosForSpace(spaceId).forEach(({ scenario, urlPrefix }) => { + it(`should return ${tests.exists.statusCode} ${scenario}`, async () => { return supertest - .get(`${getUrlPrefix(spaceId)}/api/spaces/space`) - .query({ purpose: 'copySavedObjectsIntoSpace' }) + .get(`${urlPrefix}/api/spaces/space`) .auth(user.username, user.password) - .expect(tests.copySavedObjectsPurpose.statusCode) - .then(tests.copySavedObjectsPurpose.response); + .expect(tests.exists.statusCode) + .then(tests.exists.response); }); - }); - describe('copySavedObjects purpose', () => { - it(`should return ${tests.shareSavedObjectsPurpose.statusCode}`, async () => { - return supertest - .get(`${getUrlPrefix(spaceId)}/api/spaces/space`) - .query({ purpose: 'shareSavedObjectsIntoSpace' }) - .auth(user.username, user.password) - .expect(tests.copySavedObjectsPurpose.statusCode) - .then(tests.copySavedObjectsPurpose.response); + describe('copySavedObjects purpose', () => { + it(`should return ${tests.copySavedObjectsPurpose.statusCode} ${scenario}`, async () => { + return supertest + .get(`${urlPrefix}/api/spaces/space`) + .query({ purpose: 'copySavedObjectsIntoSpace' }) + .auth(user.username, user.password) + .expect(tests.copySavedObjectsPurpose.statusCode) + .then(tests.copySavedObjectsPurpose.response); + }); + }); + + describe('copySavedObjects purpose', () => { + it(`should return ${tests.shareSavedObjectsPurpose.statusCode} ${scenario}`, async () => { + return supertest + .get(`${urlPrefix}/api/spaces/space`) + .query({ purpose: 'shareSavedObjectsIntoSpace' }) + .auth(user.username, user.password) + .expect(tests.copySavedObjectsPurpose.statusCode) + .then(tests.copySavedObjectsPurpose.response); + }); }); }); }); From a377204f85dc3bbbb048dc315966c54707adff9d Mon Sep 17 00:00:00 2001 From: ymao1 Date: Fri, 16 Oct 2020 07:35:46 -0400 Subject: [PATCH 126/128] [Alerting UI] Disable "Save" button for Alerts with broken Connectors (#80579) * Adding check for broken connectors in action form * Adding check for broken connectors in action form * Adding unit test * PR fixes --- .../action_form.test.tsx | 49 ++++++++++++++----- .../action_connector_form/action_form.tsx | 12 +++++ .../sections/alert_form/alert_edit.tsx | 6 ++- .../sections/alert_form/alert_form.tsx | 3 ++ 4 files changed, 58 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx index 7ee1e0d3f3fa6..34569dcc75240 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx @@ -114,7 +114,7 @@ describe('action_form', () => { describe('action_form in alert', () => { let wrapper: ReactWrapper; - async function setup() { + async function setup(customActions?: AlertAction[]) { const { loadAllActions } = jest.requireMock('../../lib/action_connector_api'); loadAllActions.mockResolvedValueOnce([ { @@ -177,6 +177,7 @@ describe('action_form', () => { show: true, }, }, + setHasActionsWithBrokenConnector: jest.fn(), actionTypeRegistry: actionTypeRegistry as any, docLinks: { ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' }, }; @@ -198,16 +199,18 @@ describe('action_form', () => { schedule: { interval: '1m', }, - actions: [ - { - group: 'default', - id: 'test', - actionTypeId: actionType.id, - params: { - message: '', - }, - }, - ], + actions: customActions + ? customActions + : [ + { + group: 'default', + id: 'test', + actionTypeId: actionType.id, + params: { + message: '', + }, + }, + ], tags: [], muteAll: false, enabled: false, @@ -229,6 +232,7 @@ describe('action_form', () => { setActionParamsProperty={(key: string, value: any, index: number) => (initialAlert.actions[index] = { ...initialAlert.actions[index], [key]: value }) } + setHasActionsWithBrokenConnector={deps!.setHasActionsWithBrokenConnector} http={deps!.http} actionTypeRegistry={deps!.actionTypeRegistry} defaultActionMessage={'Alert [{{ctx.metadata.name}}] has exceeded the threshold'} @@ -306,6 +310,7 @@ describe('action_form', () => { .find(`EuiToolTip [data-test-subj="${actionType.id}-ActionTypeSelectOption"]`) .exists() ).toBeFalsy(); + expect(deps.setHasActionsWithBrokenConnector).toHaveBeenLastCalledWith(false); }); it('does not render action types disabled by config', async () => { @@ -392,5 +397,27 @@ describe('action_form', () => { ); expect(actionOption.exists()).toBeFalsy(); }); + + it('recognizes actions with broken connectors', async () => { + await setup([ + { + group: 'default', + id: 'test', + actionTypeId: actionType.id, + params: { + message: '', + }, + }, + { + group: 'default', + id: 'connector-doesnt-exist', + actionTypeId: actionType.id, + params: { + message: 'broken', + }, + }, + ]); + expect(deps.setHasActionsWithBrokenConnector).toHaveBeenLastCalledWith(true); + }); }); }); 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 1b176e0f63dbd..94571c4eb1e5b 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 @@ -62,6 +62,7 @@ interface ActionAccordionFormProps { messageVariables?: ActionVariable[]; defaultActionMessage?: string; setHasActionsDisabled?: (value: boolean) => void; + setHasActionsWithBrokenConnector?: (value: boolean) => void; capabilities: ApplicationStart['capabilities']; } @@ -83,6 +84,7 @@ export const ActionForm = ({ defaultActionMessage, toastNotifications, setHasActionsDisabled, + setHasActionsWithBrokenConnector, capabilities, docLinks, }: ActionAccordionFormProps) => { @@ -171,6 +173,16 @@ export const ActionForm = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [connectors, actionTypesIndex]); + useEffect(() => { + const hasActionWithBrokenConnector = actions.some( + (action) => !connectors.find((connector) => connector.id === action.id) + ); + if (setHasActionsWithBrokenConnector) { + setHasActionsWithBrokenConnector(hasActionWithBrokenConnector); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [actions, connectors]); + const preconfiguredMessage = i18n.translate( 'xpack.triggersActionsUI.sections.actionForm.preconfiguredTitleMessage', { 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 999873a650f07..b60aa04ee9f27 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 @@ -38,6 +38,9 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { const [{ alert }, dispatch] = useReducer(alertReducer, { alert: initialAlert }); const [isSaving, setIsSaving] = useState(false); const [hasActionsDisabled, setHasActionsDisabled] = useState(false); + const [hasActionsWithBrokenConnector, setHasActionsWithBrokenConnector] = useState( + false + ); const setAlert = (key: string, value: any) => { dispatch({ command: { type: 'setAlert' }, payload: { key, value } }); }; @@ -155,6 +158,7 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { errors={errors} canChangeTrigger={false} setHasActionsDisabled={setHasActionsDisabled} + setHasActionsWithBrokenConnector={setHasActionsWithBrokenConnector} operation="i18n.translate('xpack.triggersActionsUI.sections.alertEdit.operationName', { defaultMessage: 'edit', })" @@ -176,7 +180,7 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { data-test-subj="saveEditedAlertButton" type="submit" iconType="check" - isDisabled={hasErrors || hasActionErrors} + isDisabled={hasErrors || hasActionErrors || hasActionsWithBrokenConnector} isLoading={isSaving} onClick={async () => { setIsSaving(true); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx index c69c33c0fe22e..8800f149c033b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_form.tsx @@ -81,6 +81,7 @@ interface AlertFormProps { errors: IErrorObject; canChangeTrigger?: boolean; // to hide Change trigger button setHasActionsDisabled?: (value: boolean) => void; + setHasActionsWithBrokenConnector?: (value: boolean) => void; operation: string; } @@ -90,6 +91,7 @@ export const AlertForm = ({ dispatch, errors, setHasActionsDisabled, + setHasActionsWithBrokenConnector, operation, }: AlertFormProps) => { const alertsContext = useAlertsContext(); @@ -260,6 +262,7 @@ export const AlertForm = ({ From d859ad3683d4f0c6c454d7cc2f2e99f3ba852262 Mon Sep 17 00:00:00 2001 From: Sandra Gonzales Date: Fri, 16 Oct 2020 07:43:03 -0400 Subject: [PATCH 127/128] [Ingest Manager] add skipIfNoDockerRegistry to package_install_complete test (#80779) * fix missing skipIfNoDockerRegistry * skip afterEach if server doesn't exist --- .../apis/epm/data_stream.ts | 3 ++ .../apis/epm/package_install_complete.ts | 29 ++++++++++++------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/data_stream.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/data_stream.ts index b9558240ca007..d1d909f773a2b 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/epm/data_stream.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/data_stream.ts @@ -12,6 +12,8 @@ export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; const supertest = getService('supertest'); const es = getService('es'); + const dockerServers = getService('dockerServers'); + const server = dockerServers.get('registry'); const pkgName = 'datastreams'; const pkgVersion = '0.1.0'; const pkgUpdateVersion = '0.2.0'; @@ -63,6 +65,7 @@ export default function (providerContext: FtrProviderContext) { }); }); afterEach(async () => { + if (!server) return; await es.transport.request({ method: 'DELETE', path: `/_data_stream/${logsTemplateName}-default`, diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/package_install_complete.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/package_install_complete.ts index 6b43c9d74c6bf..6fd4b64f0ee5e 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/epm/package_install_complete.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/package_install_complete.ts @@ -9,6 +9,7 @@ import { PACKAGES_SAVED_OBJECT_TYPE, MAX_TIME_COMPLETE_INSTALL, } from '../../../../plugins/ingest_manager/common'; +import { skipIfNoDockerRegistry } from '../../helpers'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; export default function (providerContext: FtrProviderContext) { @@ -19,12 +20,14 @@ export default function (providerContext: FtrProviderContext) { const pkgVersion = '0.1.0'; const pkgUpdateVersion = '0.2.0'; describe('setup checks packages completed install', async () => { + skipIfNoDockerRegistry(providerContext); describe('package install', async () => { before(async () => { await supertest .post(`/api/fleet/epm/packages/${pkgName}-0.1.0`) .set('kbn-xsrf', 'xxxx') - .send({ force: true }); + .send({ force: true }) + .expect(200); }); it('should have not reinstalled if package install completed', async function () { const packageBeforeSetup = await kibanaServer.savedObjects.get({ @@ -32,7 +35,7 @@ export default function (providerContext: FtrProviderContext) { id: pkgName, }); const installStartedAtBeforeSetup = packageBeforeSetup.attributes.install_started_at; - await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').send(); + await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').expect(200); const packageAfterSetup = await kibanaServer.savedObjects.get({ type: PACKAGES_SAVED_OBJECT_TYPE, id: pkgName, @@ -51,7 +54,7 @@ export default function (providerContext: FtrProviderContext) { install_started_at: previousInstallDate, }, }); - await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').send(); + await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').expect(200); const packageAfterSetup = await kibanaServer.savedObjects.get({ type: PACKAGES_SAVED_OBJECT_TYPE, id: pkgName, @@ -71,7 +74,7 @@ export default function (providerContext: FtrProviderContext) { install_started_at: previousInstallDate, }, }); - await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').send(); + await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').expect(200); const packageAfterSetup = await kibanaServer.savedObjects.get({ type: PACKAGES_SAVED_OBJECT_TYPE, id: pkgName, @@ -83,7 +86,8 @@ export default function (providerContext: FtrProviderContext) { after(async () => { await supertest .delete(`/api/fleet/epm/packages/multiple_versions-0.1.0`) - .set('kbn-xsrf', 'xxxx'); + .set('kbn-xsrf', 'xxxx') + .expect(200); }); }); describe('package update', async () => { @@ -91,11 +95,13 @@ export default function (providerContext: FtrProviderContext) { await supertest .post(`/api/fleet/epm/packages/${pkgName}-0.1.0`) .set('kbn-xsrf', 'xxxx') - .send({ force: true }); + .send({ force: true }) + .expect(200); await supertest .post(`/api/fleet/epm/packages/${pkgName}-0.2.0`) .set('kbn-xsrf', 'xxxx') - .send({ force: true }); + .send({ force: true }) + .expect(200); }); it('should have not reinstalled if package update completed', async function () { const packageBeforeSetup = await kibanaServer.savedObjects.get({ @@ -103,7 +109,7 @@ export default function (providerContext: FtrProviderContext) { id: pkgName, }); const installStartedAtBeforeSetup = packageBeforeSetup.attributes.install_started_at; - await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').send(); + await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').expect(200); const packageAfterSetup = await kibanaServer.savedObjects.get({ type: PACKAGES_SAVED_OBJECT_TYPE, id: pkgName, @@ -124,7 +130,7 @@ export default function (providerContext: FtrProviderContext) { install_version: pkgUpdateVersion, // set version back }, }); - await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').send(); + await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').expect(200); const packageAfterSetup = await kibanaServer.savedObjects.get({ type: PACKAGES_SAVED_OBJECT_TYPE, id: pkgName, @@ -147,7 +153,7 @@ export default function (providerContext: FtrProviderContext) { version: pkgVersion, // set version back }, }); - await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').send(); + await supertest.post(`/api/fleet/setup`).set('kbn-xsrf', 'xxx').expect(200); const packageAfterSetup = await kibanaServer.savedObjects.get({ type: PACKAGES_SAVED_OBJECT_TYPE, id: pkgName, @@ -160,7 +166,8 @@ export default function (providerContext: FtrProviderContext) { after(async () => { await supertest .delete(`/api/fleet/epm/packages/multiple_versions-0.1.0`) - .set('kbn-xsrf', 'xxxx'); + .set('kbn-xsrf', 'xxxx') + .expect(200); }); }); }); From 149b63c9bd425878c9d66fd171d34e3b67b1dba3 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Fri, 16 Oct 2020 14:58:07 +0300 Subject: [PATCH 128/128] [Timelion] Remove kui usage (#80287) * [Timelion] Remove kui usage * Fix custom checkbox * Add tim prefix to the new classes * Fix functional test * PR fixes * Fix type * PR comments * Remove the last fontawesome usages * fix timelion links Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- src/plugins/timelion/public/_app.scss | 62 +++++++++++++++++++ .../timelion/public/directives/_form.scss | 48 ++++++++++++++ .../directives/_saved_object_finder.scss | 37 +++++++++++ .../public/directives/cells/_cells.scss | 1 - .../public/directives/cells/cells.html | 6 +- .../directives/fullscreen/fullscreen.html | 2 +- .../directives/saved_object_finder.html | 32 ++++------ .../public/directives/saved_object_finder.js | 1 - .../saved_object_save_as_checkbox.html | 9 +-- .../directives/timelion_expression_input.html | 2 +- .../timelion_help/_timelion_help.scss | 8 +++ .../timelion_help/timelion_help.html | 30 ++++----- .../timelion_interval/_timelion_interval.scss | 6 ++ .../timelion_interval/timelion_interval.html | 2 +- src/plugins/timelion/public/index.html | 15 +++-- .../timelion/public/partials/load_sheet.html | 2 +- .../timelion/public/partials/save_sheet.html | 11 ++-- .../public/partials/sheet_options.html | 2 +- src/plugins/timelion/public/plugin.ts | 8 +-- 19 files changed, 217 insertions(+), 67 deletions(-) diff --git a/src/plugins/timelion/public/_app.scss b/src/plugins/timelion/public/_app.scss index 8b9078caba5a8..d8a6eb423a670 100644 --- a/src/plugins/timelion/public/_app.scss +++ b/src/plugins/timelion/public/_app.scss @@ -15,7 +15,69 @@ margin: $euiSizeM; } +.timApp__title { + display: flex; + align-items: center; + padding: $euiSizeM $euiSizeS; + font-size: $euiSize; + font-weight: $euiFontWeightBold; + border-bottom: 1px solid $euiColorLightShade; + flex-grow: 1; + background-color: $euiColorEmptyShade; +} + .timApp__stats { font-weight: $euiFontWeightRegular; color: $euiColorMediumShade; } + +.timApp__form { + display: flex; + align-items: flex-start; + margin-top: $euiSize; + margin-bottom: $euiSize; +} + +.timApp__expression { + display: flex; + flex: 1; + margin-right: $euiSizeS; +} + +.timApp__button { + margin-top: $euiSizeS; + padding: $euiSizeXS $euiSizeM; + font-size: $euiSize; + border: none; + border-radius: $euiSizeXS; + color: $euiColorEmptyShade; + background-color: $euiColorPrimary; +} + +.timApp__button--secondary { + margin-top: $euiSizeS; + padding: $euiSizeXS $euiSizeM; + font-size: $euiSize; + border: 1px solid $euiColorPrimary; + border-radius: $euiSizeXS; + color: $euiColorPrimary; + width: 100%; +} + +.timApp__sectionTitle { + margin-bottom: $euiSizeM; + font-size: 18px; + color: $euiColorDarkestShade; +} + +.timApp__helpText { + margin-bottom: $euiSize; + font-size: 14px; + color: $euiColorDarkShade; +} + +.timApp__label { + font-size: $euiSize; + line-height: 1.5; + font-weight: $euiFontWeightBold; +} diff --git a/src/plugins/timelion/public/directives/_form.scss b/src/plugins/timelion/public/directives/_form.scss index 3fcf70700a864..370dd25f8263f 100644 --- a/src/plugins/timelion/public/directives/_form.scss +++ b/src/plugins/timelion/public/directives/_form.scss @@ -34,3 +34,51 @@ select.form-control { .fullWidth { width: 100%; } + +.timDropdownWarning { + margin-bottom: $euiSize; + padding: $euiSizeXS $euiSizeS; + color: $euiColorDarkestShade; + border-left: solid 2px $euiColorDanger; + font-size: $euiSizeM; +} + +.timFormCheckbox { + display: flex; + align-items: center; + line-height: 1.5; + position: relative; +} + +.timFormCheckbox__input { + appearance: none; + background-color: $euiColorLightestShade; + border: 1px solid $euiColorLightShade; + border-radius: $euiSizeXS; + width: $euiSize; + height: $euiSize; + font-size: $euiSizeM; + transition: background-color .1s linear; +} + +.timFormCheckbox__input:checked { + border-color: $euiColorPrimary; + background-color: $euiColorPrimary; +} + +.timFormCheckbox__icon { + position: absolute; + top: 0; + left: 2px; +} + +.timFormTextarea { + padding: $euiSizeXS $euiSizeM; + font-size: $euiSize; + line-height: 1.5; + color: $euiColorDarkestShade; + background-color: $euiFormBackgroundColor; + border: 1px solid $euiColorLightShade; + border-radius: $euiSizeXS; + transition: border-color .1s linear; +} diff --git a/src/plugins/timelion/public/directives/_saved_object_finder.scss b/src/plugins/timelion/public/directives/_saved_object_finder.scss index e1a055a5f49e9..3a2489afb5721 100644 --- a/src/plugins/timelion/public/directives/_saved_object_finder.scss +++ b/src/plugins/timelion/public/directives/_saved_object_finder.scss @@ -27,6 +27,42 @@ saved-object-finder { + .timSearchBar { + display: flex; + align-items: center; + } + + .timSearchBar__section { + position: relative; + margin-right: $euiSize; + flex: 1; + } + + .timSearchBar__icon { + position: absolute; + top: $euiSizeS; + left: $euiSizeS; + font-size: $euiSize; + color: $euiColorDarkShade; + } + + .timSearchBar__input { + padding: $euiSizeS $euiSizeM; + color: $euiColorDarkestShade; + background-color: $euiColorEmptyShade; + border: 1px solid $euiColorLightShade; + border-radius: $euiSizeXS; + transition: border-color .1s linear; + padding-left: $euiSizeXL; + width: 100%; + font-size: $euiSize; + } + + .timSearchBar__pagecount { + font-size: $euiSize; + color: $euiColorDarkShade; + } + .list-sort-button { border-top-left-radius: 0; border-top-right-radius: 0; @@ -34,6 +70,7 @@ saved-object-finder { padding: $euiSizeS $euiSize; font-weight: $euiFontWeightRegular; background-color: $euiColorLightestShade; + margin-top: $euiSize; } .li-striped { diff --git a/src/plugins/timelion/public/directives/cells/_cells.scss b/src/plugins/timelion/public/directives/cells/_cells.scss index 899bf984e72c8..6cd71378a81de 100644 --- a/src/plugins/timelion/public/directives/cells/_cells.scss +++ b/src/plugins/timelion/public/directives/cells/_cells.scss @@ -33,7 +33,6 @@ text-align: center; width: $euiSizeL; height: $euiSizeL; - line-height: $euiSizeL; border-radius: $euiSizeL / 2; border: $euiBorderThin; background-color: $euiColorLightestShade; diff --git a/src/plugins/timelion/public/directives/cells/cells.html b/src/plugins/timelion/public/directives/cells/cells.html index 6be1b089d2deb..f90b85abaf920 100644 --- a/src/plugins/timelion/public/directives/cells/cells.html +++ b/src/plugins/timelion/public/directives/cells/cells.html @@ -25,7 +25,7 @@ tooltip-append-to-body="1" aria-label="{{ ::'timelion.cells.actions.removeAriaLabel' | i18n: { defaultMessage: 'Remove chart' } }}" > - +
diff --git a/src/plugins/timelion/public/directives/fullscreen/fullscreen.html b/src/plugins/timelion/public/directives/fullscreen/fullscreen.html index 194596ba79d0e..1ed6aa82ea3b9 100644 --- a/src/plugins/timelion/public/directives/fullscreen/fullscreen.html +++ b/src/plugins/timelion/public/directives/fullscreen/fullscreen.html @@ -8,7 +8,7 @@ tooltip-append-to-body="1" aria-label="{{ ::'timelion.fullscreen.exitAriaLabel' | i18n: { defaultMessage: 'Exit full screen' } }}" > - +
diff --git a/src/plugins/timelion/public/directives/saved_object_finder.html b/src/plugins/timelion/public/directives/saved_object_finder.html index ad148801c03a4..1ce10efe4e0a8 100644 --- a/src/plugins/timelion/public/directives/saved_object_finder.html +++ b/src/plugins/timelion/public/directives/saved_object_finder.html @@ -1,13 +1,11 @@
-
-
-
- +
+
+ -
-
-

+

-
+
@@ -45,7 +45,7 @@ @@ -82,7 +82,7 @@ @@ -100,7 +100,7 @@ @@ -222,7 +222,7 @@ @@ -230,7 +230,7 @@ @@ -371,7 +371,7 @@ @@ -379,7 +379,7 @@ @@ -484,7 +484,7 @@ @@ -492,7 +492,7 @@ @@ -587,7 +587,7 @@ @@ -596,7 +596,7 @@ @@ -606,7 +606,7 @@

@@ -618,7 +618,7 @@
-
+
. diff --git a/src/plugins/timelion/public/directives/timelion_interval/_timelion_interval.scss b/src/plugins/timelion/public/directives/timelion_interval/_timelion_interval.scss index b371c4400a303..7ce09155cafd8 100644 --- a/src/plugins/timelion/public/directives/timelion_interval/_timelion_interval.scss +++ b/src/plugins/timelion/public/directives/timelion_interval/_timelion_interval.scss @@ -4,6 +4,12 @@ timelion-interval { .timInterval__input { width: $euiSizeXL * 2; + padding: $euiSizeXS $euiSizeM; + color: $euiColorDarkestShade; + border: 1px solid $euiColorLightShade; + border-radius: $euiSizeXS; + transition: border-color .1s linear; + font-size: 14px; } .timInterval__input--compact { diff --git a/src/plugins/timelion/public/directives/timelion_interval/timelion_interval.html b/src/plugins/timelion/public/directives/timelion_interval/timelion_interval.html index 11c79e6a16820..49009355e49f4 100644 --- a/src/plugins/timelion/public/directives/timelion_interval/timelion_interval.html +++ b/src/plugins/timelion/public/directives/timelion_interval/timelion_interval.html @@ -1,7 +1,7 @@ - + -
+
-
+
@@ -62,14 +61,14 @@
-
+

diff --git a/src/plugins/timelion/public/partials/save_sheet.html b/src/plugins/timelion/public/partials/save_sheet.html index a0e0727f3ec82..7773a9d25df71 100644 --- a/src/plugins/timelion/public/partials/save_sheet.html +++ b/src/plugins/timelion/public/partials/save_sheet.html @@ -19,7 +19,7 @@
@@ -28,20 +28,21 @@ id="savedSheet" ng-model="opts.savedSheet.title" input-focus="select" - class="form-control kuiVerticalRhythmSmall" + class="form-control" + style="margin-bottom: 4px;" placeholder="{{ ::'timelion.topNavMenu.save.saveEntireSheet.inputPlaceholder' | i18n: { defaultMessage: 'Name this sheet...' } }}" aria-label="{{ ::'timelion.topNavMenu.save.saveEntireSheet.inputAriaLabel' | i18n: { defaultMessage: 'Name' } }}" > diff --git a/src/plugins/timelion/public/partials/sheet_options.html b/src/plugins/timelion/public/partials/sheet_options.html index e882cfe52958e..eae5709331659 100644 --- a/src/plugins/timelion/public/partials/sheet_options.html +++ b/src/plugins/timelion/public/partials/sheet_options.html @@ -1,6 +1,6 @@

diff --git a/src/plugins/timelion/public/plugin.ts b/src/plugins/timelion/public/plugin.ts index b435cc6fd399b..e5bfe7a27ad10 100644 --- a/src/plugins/timelion/public/plugin.ts +++ b/src/plugins/timelion/public/plugin.ts @@ -21,7 +21,6 @@ import { BehaviorSubject } from 'rxjs'; import { filter, map } from 'rxjs/operators'; import { CoreSetup, - CoreStart, Plugin, PluginInitializerContext, DEFAULT_APP_CATEGORIES, @@ -31,7 +30,7 @@ import { AppNavLinkStatus, } from '../../../core/public'; import { Panel } from './panels/panel'; -import { initAngularBootstrap, KibanaLegacyStart } from '../../kibana_legacy/public'; +import { initAngularBootstrap } from '../../kibana_legacy/public'; import { createKbnUrlTracker } from '../../kibana_utils/public'; import { DataPublicPluginStart, esFilters, DataPublicPluginSetup } from '../../data/public'; import { NavigationPublicPluginStart } from '../../navigation/public'; @@ -53,7 +52,6 @@ export interface TimelionPluginStartDependencies { visualizations: VisualizationsStart; visTypeTimelion: VisTypeTimelionPluginStart; savedObjects: SavedObjectsStart; - kibanaLegacy: KibanaLegacyStart; } /** @internal */ @@ -142,9 +140,7 @@ export class TimelionPlugin }); } - public start(core: CoreStart, { kibanaLegacy }: { kibanaLegacy: KibanaLegacyStart }) { - kibanaLegacy.loadFontAwesome(); - } + public start() {} public stop(): void { if (this.stopUrlTracking) {