From cf261dc73175f9f222a50d58ef38f48b7fe0cfc7 Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Mon, 30 Oct 2023 09:43:49 +0100 Subject: [PATCH 01/45] [fleet]: update bundled Universal Profiling Collector (#168838) ## Summary Update bundled Universal Profiling Collector from 8.10.0 to 8.11.0. This change depends on https://github.com/elastic/integrations/pull/8189. ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) Signed-off-by: Florian Lehner --- fleet_packages.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fleet_packages.json b/fleet_packages.json index 3cdd6bb3a5ea8..4640e150b1a36 100644 --- a/fleet_packages.json +++ b/fleet_packages.json @@ -47,7 +47,7 @@ }, { "name": "profiler_collector", - "version": "8.10.0", + "version": "8.11.0", "forceAlignStackVersion": true }, { @@ -58,4 +58,4 @@ "name": "security_detection_engine", "version": "8.10.3" } -] \ No newline at end of file +] From b9f347b39df367da988b8b21f2204a4e020e2355 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 30 Oct 2023 05:38:45 -0500 Subject: [PATCH 02/45] Upgrade caniuse-lite db (#169878) After 6 months caniuse-lite starts logging out of date messages. Similar to https://github.com/elastic/kibana/pull/153318 --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index af448bc28a07f..7d0bb20bda72c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12504,9 +12504,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001335, caniuse-lite@^1.0.30001400: - version "1.0.30001492" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001492.tgz#4a06861788a52b4c81fd3344573b68cc87fe062b" - integrity sha512-2efF8SAZwgAX1FJr87KWhvuJxnGJKOnctQa8xLOskAXNXq8oiuqgl6u1kk3fFpsp3GgvzlRjiK1sl63hNtFADw== + version "1.0.30001554" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001554.tgz#ba80d88dff9acbc0cd4b7535fc30e0191c5e2e2a" + integrity sha512-A2E3U//MBwbJVzebddm1YfNp7Nud5Ip+IPn4BozBmn4KqVX7AvluoIDFWjsv5OkGnKUXQVmMSoMKLa3ScCblcQ== canvg@^3.0.9: version "3.0.9" From e804ef2dc84f9518807d3ee48d878d3ba6173807 Mon Sep 17 00:00:00 2001 From: Mykola Harmash Date: Mon, 30 Oct 2023 11:49:47 +0100 Subject: [PATCH 03/45] [ObsUX] Enable Hosts advanced setting on serverless (#170035) Closes https://github.com/elastic/kibana/issues/169198 ## Summary * Adds Hosts advanced setting to the observability settings allowlist * Adds tests for Hosts sidenav item visibility https://github.com/elastic/kibana/assets/793851/3c9252c8-a4cb-4da8-aa60-23a8aeb60d4c # How to test * Run locally in serverless mode * Make sure "Observability Hosts view" toggle is visible on the Advanced Settings screen * Toggle the setting on and off, make sure sidenav properly reacts to the changes * Go to Infra settings screen * Toggle the same setting on and off, and also make sure sidenav properly reacts to the changes --- .../settings/observability_project/index.ts | 1 + .../page_objects/svl_common_navigation.ts | 11 ++++ .../test_suites/observability/infra/index.ts | 1 + .../observability/infra/navigation.ts | 63 +++++++++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 x-pack/test_serverless/functional/test_suites/observability/infra/navigation.ts diff --git a/packages/serverless/settings/observability_project/index.ts b/packages/serverless/settings/observability_project/index.ts index a1ae020b8abe3..cae05789e3116 100644 --- a/packages/serverless/settings/observability_project/index.ts +++ b/packages/serverless/settings/observability_project/index.ts @@ -27,4 +27,5 @@ export const OBSERVABILITY_PROJECT_SETTINGS = [ settings.OBSERVABILITY_APM_TRACE_EXPLORER_TAB_ID, settings.OBSERVABILITY_ENABLE_AWS_LAMBDA_METRICS_ID, settings.OBSERVABILITY_APM_ENABLE_CRITICAL_PATH_ID, + settings.OBSERVABILITY_ENABLE_INFRASTRUCTURE_HOSTS_VIEW_ID, ]; diff --git a/x-pack/test_serverless/functional/page_objects/svl_common_navigation.ts b/x-pack/test_serverless/functional/page_objects/svl_common_navigation.ts index 483496315bd04..41101d5a653d4 100644 --- a/x-pack/test_serverless/functional/page_objects/svl_common_navigation.ts +++ b/x-pack/test_serverless/functional/page_objects/svl_common_navigation.ts @@ -65,6 +65,17 @@ export function SvlCommonNavigationProvider(ctx: FtrProviderContext) { expect(await getByVisibleText('~nav-item', by.text)).not.be(null); } }, + async expectLinkMissing( + by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string } + ) { + if ('deepLinkId' in by) { + await testSubjects.missingOrFail(`~nav-item-deepLinkId-${by.deepLinkId}`); + } else if ('navId' in by) { + await testSubjects.missingOrFail(`~nav-item-id-${by.navId}`); + } else { + expect(await getByVisibleText('~nav-item', by.text)).be(null); + } + }, async expectLinkActive( by: { deepLinkId: AppDeepLinkId } | { navId: string } | { text: string } ) { diff --git a/x-pack/test_serverless/functional/test_suites/observability/infra/index.ts b/x-pack/test_serverless/functional/test_suites/observability/infra/index.ts index 814b0c97e4b1a..b646ba71f960e 100644 --- a/x-pack/test_serverless/functional/test_suites/observability/infra/index.ts +++ b/x-pack/test_serverless/functional/test_suites/observability/infra/index.ts @@ -10,6 +10,7 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Observability Infra', function () { loadTestFile(require.resolve('./header_menu')); + loadTestFile(require.resolve('./navigation')); loadTestFile(require.resolve('./node_details')); loadTestFile(require.resolve('./hosts_page')); loadTestFile(require.resolve('./infra')); diff --git a/x-pack/test_serverless/functional/test_suites/observability/infra/navigation.ts b/x-pack/test_serverless/functional/test_suites/observability/infra/navigation.ts new file mode 100644 index 0000000000000..6f03c8831ce85 --- /dev/null +++ b/x-pack/test_serverless/functional/test_suites/observability/infra/navigation.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { enableInfrastructureHostsView } from '@kbn/observability-plugin/common'; +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default ({ getPageObjects, getService }: FtrProviderContext) => { + const kibanaServer = getService('kibanaServer'); + const svlObltNavigation = getService('svlObltNavigation'); + const browser = getService('browser'); + const pageObjects = getPageObjects(['svlCommonPage', 'svlCommonNavigation', 'header']); + + const setHostsSetting = async (value: boolean) => { + await kibanaServer.uiSettings.update({ [enableInfrastructureHostsView]: value }); + await browser.refresh(); + await pageObjects.svlCommonNavigation.expectExists(); + }; + + const openInfraSection = async () => { + await pageObjects.svlCommonNavigation.sidenav.openSection('observability_project_nav.metrics'); + }; + + describe('Infra Side Navigation', () => { + before(async () => { + await pageObjects.svlCommonPage.login(); + await svlObltNavigation.navigateToLandingPage(); + }); + + after(async () => { + await pageObjects.svlCommonPage.forceLogout(); + }); + + describe('when Hosts settings is on', () => { + before(async () => { + await setHostsSetting(true); + await openInfraSection(); + }); + + it("shows the 'Hosts' nav item", async () => { + await pageObjects.svlCommonNavigation.sidenav.expectLinkExists({ + deepLinkId: 'metrics:hosts', + }); + }); + }); + + describe('when Hosts settings is off', () => { + before(async () => { + await setHostsSetting(false); + await openInfraSection(); + }); + + it("hides the 'Hosts' nav item", async () => { + await pageObjects.svlCommonNavigation.sidenav.expectLinkMissing({ + deepLinkId: 'metrics:hosts', + }); + }); + }); + }); +}; From af88e0932fc6499c112d46efd751155f09bf809c Mon Sep 17 00:00:00 2001 From: "Eyo O. Eyo" <7893459+eokoneyo@users.noreply.github.com> Date: Mon, 30 Oct 2023 12:28:11 +0100 Subject: [PATCH 04/45] Limit download concurrency for files within Kibana (#168601) ## Summary Closes https://github.com/elastic/kibana/issues/151986 This PR adds a limit to the number of concurrent downloads a user can initiate from kibana. P.S. This PR renames the previous static method `configureConcurrentUpload` exposed by the `ElasticsearchBlobStorageClient` to `configureConcurrentTransfers` so that one might use a single static method to configure the limits for concurrent transfers possible for upload and download transfers in one go. The new static method `configureConcurrentTransfers` accepts either a number or a tuple, when a number is passed it's value is set as the concurrent limit for both uploads and transfers, when a tuple is passed the value of the first index sets the concurrent limit for uploads, whilst the value of the second index sets the concurrent limit for downloads. --- packages/kbn-logging-mocks/src/logger.mock.ts | 3 +- .../adapters/es/es.test.ts | 109 +++++++++++++++++- .../blob_storage_service/adapters/es/es.ts | 39 +++++-- .../adapters/es/integration_tests/es.test.ts | 2 +- .../blob_storage_service.ts | 10 +- .../file_client/create_es_file_client.test.ts | 2 +- .../file_client/create_es_file_client.ts | 1 + 7 files changed, 148 insertions(+), 18 deletions(-) diff --git a/packages/kbn-logging-mocks/src/logger.mock.ts b/packages/kbn-logging-mocks/src/logger.mock.ts index dd3303dda9410..90436fb4516d2 100644 --- a/packages/kbn-logging-mocks/src/logger.mock.ts +++ b/packages/kbn-logging-mocks/src/logger.mock.ts @@ -24,9 +24,10 @@ const createLoggerMock = (context: string[] = []) => { isLevelEnabled: jest.fn(), }; mockLog.get.mockImplementation((...ctx) => ({ - ctx, ...mockLog, + context: Array.isArray(context) ? context.concat(ctx) : [context, ...ctx].filter(Boolean), })); + mockLog.isLevelEnabled.mockReturnValue(true); return mockLog; diff --git a/src/plugins/files/server/blob_storage_service/adapters/es/es.test.ts b/src/plugins/files/server/blob_storage_service/adapters/es/es.test.ts index d94b2c78c5885..947d9eecd8fd0 100644 --- a/src/plugins/files/server/blob_storage_service/adapters/es/es.test.ts +++ b/src/plugins/files/server/blob_storage_service/adapters/es/es.test.ts @@ -7,19 +7,22 @@ */ import { Readable } from 'stream'; +import { encode } from 'cbor-x'; import { promisify } from 'util'; import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; import { Semaphore } from '@kbn/std'; +import { errors } from '@elastic/elasticsearch'; +import type { GetResponse } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchBlobStorageClient } from './es'; -import { errors } from '@elastic/elasticsearch'; const setImmediate = promisify(global.setImmediate); describe('ElasticsearchBlobStorageClient', () => { let esClient: ReturnType; - let semaphore: Semaphore; + let uploadSemaphore: Semaphore; + let downloadSemaphore: Semaphore; let logger: ReturnType; // Exposed `clearCache()` which resets the cache for the memoized `createIndexIfNotExists()` method @@ -38,20 +41,24 @@ describe('ElasticsearchBlobStorageClient', () => { index, undefined, logger, - semaphore, + uploadSemaphore, + downloadSemaphore, indexIsAlias ); }; beforeEach(() => { - semaphore = new Semaphore(1); + uploadSemaphore = new Semaphore(1); + downloadSemaphore = new Semaphore(1); logger = loggingSystemMock.createLogger(); esClient = elasticsearchServiceMock.createElasticsearchClient(); + + jest.clearAllMocks(); }); test('limits max concurrent uploads', async () => { const blobStoreClient = createBlobStoreClient(); - const acquireSpy = jest.spyOn(semaphore, 'acquire'); + const uploadAcquireSpy = jest.spyOn(uploadSemaphore, 'acquire'); esClient.index.mockImplementation(() => { return new Promise((res, rej) => setTimeout(() => rej('failed'), 100)); }); @@ -62,7 +69,7 @@ describe('ElasticsearchBlobStorageClient', () => { blobStoreClient.upload(Readable.from(['test'])).catch(() => {}), ]; await setImmediate(); - expect(acquireSpy).toHaveBeenCalledTimes(4); + expect(uploadAcquireSpy).toHaveBeenCalledTimes(4); await p1; expect(esClient.index).toHaveBeenCalledTimes(1); await p2; @@ -71,6 +78,96 @@ describe('ElasticsearchBlobStorageClient', () => { expect(esClient.index).toHaveBeenCalledTimes(4); }); + test('limits max concurrent downloads', async () => { + const index = 'someplace'; + + const blobStoreClient = createBlobStoreClient(index); + const downloadAcquireSpy = jest.spyOn(downloadSemaphore, 'acquire'); + + const downloadsToQueueCount = 4; + const documentsChunkCount = 2; + + const createDownloadContent = (documentId: number, chunkId: number) => { + return Buffer.concat([ + Buffer.from(`download content ${documentId}.${chunkId}`, 'utf8'), + Buffer.alloc(10 * 1028, `chunk ${chunkId}`), + ]); + }; + + const downloadContentMap = Array.from(new Array(downloadsToQueueCount)).map( + (_, documentIdx) => ({ + fileContent: Array.from(new Array(documentsChunkCount)).map((__, chunkIdx) => + createDownloadContent(documentIdx, chunkIdx) + ), + }) + ); + + esClient.get.mockImplementation(({ id: headChunkId }) => { + const [documentId, chunkId] = headChunkId.split(/\./); + + return new Promise(function (resolve) { + setTimeout( + () => + resolve( + Readable.from([ + encode({ + found: true, + _source: { + data: downloadContentMap[Number(documentId)].fileContent[Number(chunkId)], + }, + }), + ]) as unknown as GetResponse + ), + 100 + ); + }); + }); + + const getDownloadStreamContent = async (stream: Readable) => { + const chunks: Buffer[] = []; + + for await (const chunk of stream) { + chunks.push(chunk); + } + + /** + * we are guaranteed that the chunks for the complete document + * will equal the document chunk count specified within this test suite. + * See {@link ContentStream#isRead} + */ + expect(chunks.length).toBe(documentsChunkCount); + + return Buffer.concat(chunks).toString(); + }; + + const [p1, p2, ...rest] = downloadContentMap.map(({ fileContent }, idx) => { + // expected document size will be our returned mock file content + // will be the sum of the lengths of chunks the entire document is split into + const documentSize = fileContent.reduce((total, chunk) => total + chunk.length, 0); + + return blobStoreClient.download({ + id: String(idx), + size: documentSize, + }); + }); + + await setImmediate(); + expect(downloadAcquireSpy).toHaveBeenCalledTimes(downloadsToQueueCount); + + const p1DownloadStream = await p1; + const p1DownloadContent = await getDownloadStreamContent(p1DownloadStream); + expect(esClient.get).toHaveBeenCalledTimes(1 * documentsChunkCount); + expect(p1DownloadContent).toEqual(expect.stringMatching(/^download\scontent\s0.*/)); + + const p2DownloadStream = await p2; + const p2DownloadContent = await getDownloadStreamContent(p2DownloadStream); + expect(esClient.get).toHaveBeenCalledTimes(2 * documentsChunkCount); + expect(p2DownloadContent).toEqual(expect.stringMatching(/^download\scontent\s1.*/)); + + await Promise.all(rest.map((dp) => dp.then((ds) => getDownloadStreamContent(ds)))); + expect(esClient.get).toHaveBeenCalledTimes(downloadsToQueueCount * documentsChunkCount); + }); + describe('.createIndexIfNotExists()', () => { let data: Readable; diff --git a/src/plugins/files/server/blob_storage_service/adapters/es/es.ts b/src/plugins/files/server/blob_storage_service/adapters/es/es.ts index a08d220b1c8e2..5650d9ae29684 100644 --- a/src/plugins/files/server/blob_storage_service/adapters/es/es.ts +++ b/src/plugins/files/server/blob_storage_service/adapters/es/es.ts @@ -13,7 +13,7 @@ import { Semaphore } from '@kbn/std'; import { Readable, Transform } from 'stream'; import { pipeline } from 'stream/promises'; import { promisify } from 'util'; -import { lastValueFrom, defer } from 'rxjs'; +import { lastValueFrom, defer, firstValueFrom } from 'rxjs'; import { PerformanceMetricEvent, reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { memoize } from 'lodash'; import { FilesPlugin } from '../../../plugin'; @@ -38,14 +38,21 @@ interface UploadOptions { } export class ElasticsearchBlobStorageClient implements BlobStorageClient { - private static defaultSemaphore: Semaphore; + private static defaultUploadSemaphore: Semaphore; + private static defaultDownloadSemaphore: Semaphore; /** - * Call this function once to globally set a concurrent upload limit for + * Call this function once to globally set the concurrent transfer (upload/download) limit for * all {@link ElasticsearchBlobStorageClient} instances. */ - public static configureConcurrentUpload(capacity: number) { - this.defaultSemaphore = new Semaphore(capacity); + public static configureConcurrentTransfers(capacity: number | [number, number]) { + if (Array.isArray(capacity)) { + this.defaultUploadSemaphore = new Semaphore(capacity[0]); + this.defaultDownloadSemaphore = new Semaphore(capacity[1]); + } else { + this.defaultUploadSemaphore = new Semaphore(capacity); + this.defaultDownloadSemaphore = new Semaphore(capacity); + } } constructor( @@ -57,11 +64,23 @@ export class ElasticsearchBlobStorageClient implements BlobStorageClient { * Override the default concurrent upload limit by passing in a different * semaphore */ - private readonly uploadSemaphore = ElasticsearchBlobStorageClient.defaultSemaphore, + private readonly uploadSemaphore = ElasticsearchBlobStorageClient.defaultUploadSemaphore, + /** + * Override the default concurrent download limit by passing in a different + * semaphore + */ + private readonly downloadSemaphore = ElasticsearchBlobStorageClient.defaultDownloadSemaphore, /** Indicates that the index provided is an alias (changes how content is retrieved internally) */ private readonly indexIsAlias: boolean = false ) { - assert(this.uploadSemaphore, `No default semaphore provided and no semaphore was passed in.`); + assert( + this.uploadSemaphore, + `No default semaphore provided and no semaphore was passed in for uploads.` + ); + assert( + this.downloadSemaphore, + `No default semaphore provided and no semaphore was passed in for downloads.` + ); } /** @@ -187,7 +206,11 @@ export class ElasticsearchBlobStorageClient implements BlobStorageClient { // right after uploading it, we refresh the index before downloading the file. await this.esClient.indices.refresh({ index: this.index }); - return this.getReadableContentStream(id, size); + return firstValueFrom( + defer(() => Promise.resolve(this.getReadableContentStream(id, size))).pipe( + this.downloadSemaphore.acquire() + ) + ); } public async delete(id: string): Promise { diff --git a/src/plugins/files/server/blob_storage_service/adapters/es/integration_tests/es.test.ts b/src/plugins/files/server/blob_storage_service/adapters/es/integration_tests/es.test.ts index 1e6b357cbf874..981810e968f65 100644 --- a/src/plugins/files/server/blob_storage_service/adapters/es/integration_tests/es.test.ts +++ b/src/plugins/files/server/blob_storage_service/adapters/es/integration_tests/es.test.ts @@ -25,7 +25,7 @@ describe('Elasticsearch blob storage', () => { let esRefreshIndexSpy: jest.SpyInstance; beforeAll(async () => { - ElasticsearchBlobStorageClient.configureConcurrentUpload(Infinity); + ElasticsearchBlobStorageClient.configureConcurrentTransfers(Infinity); const { startES, startKibana } = createTestServers({ adjustTimeout: jest.setTimeout }); manageES = await startES(); manageKbn = await startKibana(); diff --git a/src/plugins/files/server/blob_storage_service/blob_storage_service.ts b/src/plugins/files/server/blob_storage_service/blob_storage_service.ts index 0cc4ba81997b8..3667957ba25d8 100644 --- a/src/plugins/files/server/blob_storage_service/blob_storage_service.ts +++ b/src/plugins/files/server/blob_storage_service/blob_storage_service.ts @@ -22,8 +22,16 @@ export class BlobStorageService { */ private readonly concurrentUploadsToES = 20; + /** + * The number of downloads per Kibana instance that can be running simultaneously + */ + private readonly concurrentDownloadsFromES = 5; + constructor(private readonly esClient: ElasticsearchClient, private readonly logger: Logger) { - ElasticsearchBlobStorageClient.configureConcurrentUpload(this.concurrentUploadsToES); + ElasticsearchBlobStorageClient.configureConcurrentTransfers([ + this.concurrentUploadsToES, + this.concurrentDownloadsFromES, + ]); } private createESBlobStorage({ diff --git a/src/plugins/files/server/file_client/create_es_file_client.test.ts b/src/plugins/files/server/file_client/create_es_file_client.test.ts index 68589b334c8ea..1cf7343824ff3 100644 --- a/src/plugins/files/server/file_client/create_es_file_client.test.ts +++ b/src/plugins/files/server/file_client/create_es_file_client.test.ts @@ -23,7 +23,7 @@ describe('When initializing file client via createESFileClient()', () => { let logger: MockedLogger; beforeEach(() => { - ElasticsearchBlobStorageClient.configureConcurrentUpload(Infinity); + ElasticsearchBlobStorageClient.configureConcurrentTransfers(Infinity); esClient = elasticsearchServiceMock.createElasticsearchClient(); logger = loggingSystemMock.createLogger(); }); diff --git a/src/plugins/files/server/file_client/create_es_file_client.ts b/src/plugins/files/server/file_client/create_es_file_client.ts index 47b044618efc2..755071d66328c 100644 --- a/src/plugins/files/server/file_client/create_es_file_client.ts +++ b/src/plugins/files/server/file_client/create_es_file_client.ts @@ -78,6 +78,7 @@ export function createEsFileClient(arg: CreateEsFileClientArgs): FileClient { undefined, logger, undefined, + undefined, indexIsAlias ), undefined, From 3ea90b3baaf9d1329ca02ffd9be6bd9fb3077def Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Mon, 30 Oct 2023 13:22:45 +0100 Subject: [PATCH 05/45] [ML] Fixes multi line truncation for id/description columns in job lists (#169830) Makes use of EUI's new multi-line truncation option for table columns (https://github.com/elastic/eui/issues/7226). For columns that use this feature, this PR wraps the text in custom render functions which add a `span` element with a `title` attribute and the cells text. --- .../log_rate_analysis_results_table.tsx | 21 +++++-------- .../components/analytics_list/use_columns.tsx | 12 +++++-- .../components/transform_list/use_columns.tsx | 31 ++++++++++--------- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx index de66040c8e382..95f4460c7f918 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table.tsx @@ -6,7 +6,6 @@ */ import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; -import { css } from '@emotion/react'; import { orderBy, isEqual } from 'lodash'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; @@ -54,14 +53,7 @@ const PAGINATION_SIZE_OPTIONS = [5, 10, 20, 50]; const DEFAULT_SORT_FIELD = 'pValue'; const DEFAULT_SORT_DIRECTION = 'asc'; -const TRUNCATE_MAX_LINES = 3; -const cssMultiLineTruncation = css` - display: -webkit-box; - line-clamp: ${TRUNCATE_MAX_LINES}; - -webkit-line-clamp: ${TRUNCATE_MAX_LINES}; - -webkit-box-orient: vertical; - overflow: hidden; -`; +const TRUNCATE_TEXT_LINES = 3; interface LogRateAnalysisResultsTableProps { significantTerms: SignificantTerm[]; @@ -169,11 +161,12 @@ export const LogRateAnalysisResultsTable: FC = )} - {fieldName} + {fieldName} ); }, sortable: true, + truncateText: true, valign: 'middle', }, { @@ -183,21 +176,21 @@ export const LogRateAnalysisResultsTable: FC = defaultMessage: 'Field value', }), render: (_, { fieldValue, type }) => ( -
+ {type === 'keyword' ? ( String(fieldValue) ) : ( - {fieldValue} + {String(fieldValue)} )} -
+ ), sortable: true, textOnly: true, - truncateText: false, + truncateText: { lines: TRUNCATE_TEXT_LINES }, valign: 'middle', }, { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx index 6f4a380cc2c8b..38516df7611bc 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns.tsx @@ -37,6 +37,8 @@ import { useActions } from './use_actions'; import { useMlLink } from '../../../../../contexts/kibana'; import { ML_PAGES } from '../../../../../../../common/constants/locator'; +const TRUNCATE_TEXT_LINES = 3; + enum TASK_STATE_COLOR { analyzing = 'primary', failed = 'danger', @@ -212,9 +214,12 @@ export const useColumns = ( defaultMessage: 'ID', }), sortable: (item: DataFrameAnalyticsListRow) => item.id, - truncateText: true, + truncateText: { lines: TRUNCATE_TEXT_LINES }, 'data-test-subj': 'mlAnalyticsTableColumnId', scope: 'row', + render: (id: string) => { + return {id}; + }, }, { field: DataFrameAnalyticsListColumn.description, @@ -222,8 +227,11 @@ export const useColumns = ( defaultMessage: 'Description', }), sortable: true, - truncateText: true, + truncateText: { lines: TRUNCATE_TEXT_LINES }, 'data-test-subj': 'mlAnalyticsTableColumnJobDescription', + render: (description: string) => { + return {description}; + }, }, { field: DataFrameAnalyticsListColumn.memoryStatus, diff --git a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx index 732261bde89d2..6edc99c3c45f7 100644 --- a/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx +++ b/x-pack/plugins/transform/public/app/sections/transform_management/components/transform_list/use_columns.tsx @@ -41,6 +41,8 @@ import { isManagedTransform } from '../../../../common/managed_transforms_utils' import { TransformHealthColoredDot } from './transform_health_colored_dot'; import { TransformTaskStateBadge } from './transform_task_state_badge'; +const TRUNCATE_TEXT_LINES = 3; + const TRANSFORM_INSUFFICIENT_PERMISSIONS_MSG = i18n.translate( 'xpack.transform.transformList.needsReauthorizationBadge.insufficientPermissions', { @@ -131,13 +133,22 @@ export const useColumns = ( 'data-test-subj': 'transformListColumnId', name: 'ID', sortable: true, - truncateText: true, + truncateText: { lines: TRUNCATE_TEXT_LINES }, scope: 'row', render: (transformId, item) => { - if (!isManagedTransform(item)) return transformId; + if (!isManagedTransform(item)) return {transformId}; return ( <> - {transformId} + + {transformId} +   - {text} - - ); + return {text}; }, }, { From d1b7b7fd411e30f087cc6a5764d37206aec0ad16 Mon Sep 17 00:00:00 2001 From: Juan Pablo Djeredjian Date: Mon, 30 Oct 2023 13:42:23 +0100 Subject: [PATCH 06/45] [Security Solution ] Fix flake in `bundled_prebuilt_rules_package/prerelease_packages.ts` API Integration test (#169780) Fixes: https://github.com/elastic/kibana/issues/162192 ## Summary Removes flake by explicitly calling the Fleet endpoint to install the latest package, and doing assertions before actually installing the rules. Previously we were calling directly `installPrebuiltRules` without having explicitly installed the package before. The old installation endpoint would check that the package was installed before proceeding, but the new install method doesn't. So the explicit installation is required. ## Flaky test runs - ~https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3721~ - ~https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3730 [CONTROL - NO CHANGES]~ - https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3773 [After refactor] --- .../prerelease_packages.ts | 16 ++++++--- .../install_fleet_package_by_url.ts | 35 ++++++++++++++++--- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/bundled_prebuilt_rules_package/prerelease_packages.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/bundled_prebuilt_rules_package/prerelease_packages.ts index fd69e3128c3e7..7348b596d1404 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/bundled_prebuilt_rules_package/prerelease_packages.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/bundled_prebuilt_rules_package/prerelease_packages.ts @@ -9,6 +9,7 @@ import { FtrProviderContext } from '../../common/ftr_provider_context'; import { deleteAllPrebuiltRuleAssets, deleteAllRules } from '../../utils'; import { getInstalledRules } from '../../utils/prebuilt_rules/get_installed_rules'; import { getPrebuiltRulesStatus } from '../../utils/prebuilt_rules/get_prebuilt_rules_status'; +import { installPrebuiltRulesPackageViaFleetAPI } from '../../utils/prebuilt_rules/install_fleet_package_by_url'; import { installPrebuiltRules } from '../../utils/prebuilt_rules/install_prebuilt_rules'; // eslint-disable-next-line import/no-default-export @@ -37,14 +38,21 @@ export default ({ getService }: FtrProviderContext): void => { expect(statusBeforePackageInstallation.stats.num_prebuilt_rules_to_install).toBe(0); expect(statusBeforePackageInstallation.stats.num_prebuilt_rules_to_upgrade).toBe(0); - await installPrebuiltRules(es, supertest); + await installPrebuiltRulesPackageViaFleetAPI(es, supertest); - // Verify that status is updated after package installation const statusAfterPackageInstallation = await getPrebuiltRulesStatus(supertest); - expect(statusAfterPackageInstallation.stats.num_prebuilt_rules_installed).toBe(1); // 1 rule in package 99.0.0 - expect(statusAfterPackageInstallation.stats.num_prebuilt_rules_to_install).toBe(0); + expect(statusAfterPackageInstallation.stats.num_prebuilt_rules_installed).toBe(0); + expect(statusAfterPackageInstallation.stats.num_prebuilt_rules_to_install).toBe(1); // 1 rule in package 99.0.0 expect(statusAfterPackageInstallation.stats.num_prebuilt_rules_to_upgrade).toBe(0); + await installPrebuiltRules(es, supertest); + + // Verify that status is updated after package installation + const statusAfterRulesInstallation = await getPrebuiltRulesStatus(supertest); + expect(statusAfterRulesInstallation.stats.num_prebuilt_rules_installed).toBe(1); // 1 rule in package 99.0.0 + expect(statusAfterRulesInstallation.stats.num_prebuilt_rules_to_install).toBe(0); + expect(statusAfterRulesInstallation.stats.num_prebuilt_rules_to_upgrade).toBe(0); + // Get installed rules const rulesResponse = await getInstalledRules(supertest); diff --git a/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/install_fleet_package_by_url.ts b/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/install_fleet_package_by_url.ts index 802626881b8e6..bccdf28906e23 100644 --- a/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/install_fleet_package_by_url.ts +++ b/x-pack/test/detection_engine_api_integration/utils/prebuilt_rules/install_fleet_package_by_url.ts @@ -10,7 +10,7 @@ import { ALL_SAVED_OBJECT_INDICES } from '@kbn/core-saved-objects-server'; import { InstallPackageResponse } from '@kbn/fleet-plugin/common/types'; /** - * Installs prebuilt rules package `security_detection_engine` by version. + * Installs latest available non-prerelease prebuilt rules package `security_detection_engine`. * * @param es Elasticsearch client * @param supertest SuperTest instance @@ -18,13 +18,12 @@ import { InstallPackageResponse } from '@kbn/fleet-plugin/common/types'; * @returns Fleet install package response */ -export const installPrebuiltRulesPackageByVersion = async ( +export const installPrebuiltRulesPackageViaFleetAPI = async ( es: Client, - supertest: SuperTest.SuperTest, - version: string + supertest: SuperTest.SuperTest ): Promise => { const fleetResponse = await supertest - .post(`/api/fleet/epm/packages/security_detection_engine/${version}`) + .post(`/api/fleet/epm/packages/security_detection_engine`) .set('kbn-xsrf', 'xxxx') .type('application/json') .send({ force: true }) @@ -48,3 +47,29 @@ export const installPrebuiltRulesPackageByVersion = async ( return fleetResponse.body as InstallPackageResponse; }; +/** + * Installs prebuilt rules package `security_detection_engine`, passing in the version + * of the package as a parameter to the utl. + * + * @param es Elasticsearch client + * @param supertest SuperTest instance + * @param version Semver version of the `security_detection_engine` package to install + * @returns Fleet install package response + */ + +export const installPrebuiltRulesPackageByVersion = async ( + es: Client, + supertest: SuperTest.SuperTest, + version: string +): Promise => { + const fleetResponse = await supertest + .post(`/api/fleet/epm/packages/security_detection_engine/${version}`) + .set('kbn-xsrf', 'xxxx') + .type('application/json') + .send({ force: true }) + .expect(200); + + await es.indices.refresh({ index: ALL_SAVED_OBJECT_INDICES }); + + return fleetResponse.body as InstallPackageResponse; +}; From fe62cd54b563078a7c0d41c087371c2da57dca7a Mon Sep 17 00:00:00 2001 From: Maxim Kholod Date: Mon, 30 Oct 2023 14:24:17 +0100 Subject: [PATCH 07/45] [Cloud Security] unskip cloud_security_posture serverless functional ftr test (#169827) ## Summary The test was skipped in https://github.com/elastic/kibana/issues/168904 due to flakiness Unskipping as I run it two times on flaky test runner with 50 and 100 runs (https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3731 and https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3745) and all of them were green --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../ftr/cloud_security_posture/compliance_dashboard.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/compliance_dashboard.ts b/x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/compliance_dashboard.ts index 6a6734b8f3fe3..54fa5a725e29a 100644 --- a/x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/compliance_dashboard.ts +++ b/x-pack/test_serverless/functional/test_suites/security/ftr/cloud_security_posture/compliance_dashboard.ts @@ -56,8 +56,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await pageObjects.svlCommonPage.forceLogout(); }); - // FLAKY: https://github.com/elastic/kibana/issues/168904 - describe.skip('Kubernetes Dashboard', () => { + describe('Kubernetes Dashboard', () => { it('displays accurate summary compliance score', async () => { const scoreElement = await dashboard.getKubernetesComplianceScore(); From 9a68094c677ea6a08b50a722ac1494d84036d443 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Mon, 30 Oct 2023 15:48:55 +0100 Subject: [PATCH 08/45] [ML] AIOps: Refactor routes to use handlers. (#170096) This refactors the code for server side routes in the AIOps plugin. It splits up the routes into an outer `define_route.ts` and an inner `route_handler_factory.ts` file. - `define_route.ts` creates a versioned route and uses `route_handler_factory.ts` to be passed in as the route handler. - `route_handler_factory.ts` includes all logic for the route, previously this was an inline callback in the route definition. The aim of this PR is to be better prepared for future route versioning. Splitting out the handler simplifies the route definition and will allow the reuse of handlers for different versions or the usage of different handlers. --- .../common/api/log_categorization/schema.ts | 3 + x-pack/plugins/aiops/server/plugin.ts | 7 +- .../define_route.ts | 37 + .../route_handler_factory.ts | 83 ++ x-pack/plugins/aiops/server/routes/index.ts | 8 - .../aiops/server/routes/log_categorization.ts | 91 -- .../aiops/server/routes/log_rate_analysis.ts | 867 ------------------ .../routes/log_rate_analysis/define_route.ts | 44 + .../queries/duplicate_identifier.ts | 0 .../queries/fetch_categories.ts | 10 +- .../queries/fetch_category_counts.ts | 8 +- .../queries/fetch_frequent_item_sets.test.ts | 2 +- .../queries/fetch_frequent_item_sets.ts | 4 +- .../queries/fetch_index_info.test.ts | 2 +- .../queries/fetch_index_info.ts | 2 +- .../queries/fetch_significant_categories.ts | 6 +- .../fetch_significant_term_p_values.ts | 6 +- .../fetch_terms_2_categories_counts.ts | 12 +- .../get_field_value_pair_counts.test.ts | 8 +- .../queries/get_field_value_pair_counts.ts | 2 +- .../queries/get_filters.test.ts | 0 .../queries/get_filters.ts | 2 +- .../queries/get_group_filter.test.ts | 2 +- .../queries/get_group_filter.ts | 2 +- ...get_groups_with_readded_duplicates.test.ts | 4 +- .../get_groups_with_readded_duplicates.ts | 2 +- .../queries/get_histogram_query.test.ts | 0 .../queries/get_histogram_query.ts | 2 +- .../queries/get_marked_duplicates.test.ts | 8 +- .../queries/get_marked_duplicates.ts | 2 +- .../get_missing_significant_terms.test.ts | 4 +- .../queries/get_missing_significant_terms.ts | 0 .../queries/get_normalized_score.ts | 0 .../queries/get_query_with_params.test.ts | 0 .../queries/get_query_with_params.ts | 2 +- .../queries/get_request_base.test.ts | 0 .../queries/get_request_base.ts | 2 +- .../get_significant_term_groups.test.ts | 8 +- .../queries/get_significant_term_groups.ts | 2 +- .../get_simple_hierarchical_tree.test.ts | 6 +- .../queries/get_simple_hierarchical_tree.ts | 2 +- ...et_simple_hierarchical_tree_leaves.test.ts | 6 +- .../get_simple_hierarchical_tree_leaves.ts | 2 +- .../queries/get_value_counts.test.ts | 2 +- .../queries/get_value_counts.ts | 2 +- .../queries/get_values_descending.test.ts | 2 +- .../queries/get_values_descending.ts | 2 +- ...ransform_significant_term_to_group.test.ts | 4 +- .../transform_significant_term_to_group.ts | 2 +- .../route_handler_factory.ts | 851 +++++++++++++++++ 50 files changed, 1088 insertions(+), 1037 deletions(-) create mode 100644 x-pack/plugins/aiops/server/routes/categorization_field_validation/define_route.ts create mode 100644 x-pack/plugins/aiops/server/routes/categorization_field_validation/route_handler_factory.ts delete mode 100755 x-pack/plugins/aiops/server/routes/index.ts delete mode 100644 x-pack/plugins/aiops/server/routes/log_categorization.ts delete mode 100644 x-pack/plugins/aiops/server/routes/log_rate_analysis.ts create mode 100644 x-pack/plugins/aiops/server/routes/log_rate_analysis/define_route.ts rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/duplicate_identifier.ts (100%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/fetch_categories.ts (90%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/fetch_category_counts.ts (90%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/fetch_frequent_item_sets.test.ts (93%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/fetch_frequent_item_sets.ts (98%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/fetch_index_info.test.ts (97%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/fetch_index_info.ts (97%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/fetch_significant_categories.ts (95%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/fetch_significant_term_p_values.ts (96%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/fetch_terms_2_categories_counts.ts (92%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_field_value_pair_counts.test.ts (78%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_field_value_pair_counts.ts (91%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_filters.test.ts (100%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_filters.ts (90%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_group_filter.test.ts (84%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_group_filter.ts (95%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_groups_with_readded_duplicates.test.ts (89%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_groups_with_readded_duplicates.ts (94%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_histogram_query.test.ts (100%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_histogram_query.ts (92%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_marked_duplicates.test.ts (90%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_marked_duplicates.ts (91%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_missing_significant_terms.test.ts (91%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_missing_significant_terms.ts (100%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_normalized_score.ts (100%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_query_with_params.test.ts (100%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_query_with_params.ts (92%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_request_base.test.ts (100%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_request_base.ts (82%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_significant_term_groups.test.ts (64%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_significant_term_groups.ts (98%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_simple_hierarchical_tree.test.ts (93%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_simple_hierarchical_tree.ts (99%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_simple_hierarchical_tree_leaves.test.ts (87%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_simple_hierarchical_tree_leaves.ts (95%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_value_counts.test.ts (88%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_value_counts.ts (90%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_values_descending.test.ts (88%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/get_values_descending.ts (90%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/transform_significant_term_to_group.test.ts (90%) rename x-pack/plugins/aiops/server/routes/{ => log_rate_analysis}/queries/transform_significant_term_to_group.ts (95%) create mode 100644 x-pack/plugins/aiops/server/routes/log_rate_analysis/route_handler_factory.ts diff --git a/x-pack/plugins/aiops/common/api/log_categorization/schema.ts b/x-pack/plugins/aiops/common/api/log_categorization/schema.ts index 09b8c75bf0b48..6445e32252695 100644 --- a/x-pack/plugins/aiops/common/api/log_categorization/schema.ts +++ b/x-pack/plugins/aiops/common/api/log_categorization/schema.ts @@ -65,3 +65,6 @@ export const categorizationFieldValidationSchema = schema.object({ indicesOptions: indicesOptionsSchema, includeExamples: schema.boolean(), }); +export type CategorizationFieldValidationSchema = TypeOf< + typeof categorizationFieldValidationSchema +>; diff --git a/x-pack/plugins/aiops/server/plugin.ts b/x-pack/plugins/aiops/server/plugin.ts index 5cb23cb6ce57d..22ab62ecf52b3 100755 --- a/x-pack/plugins/aiops/server/plugin.ts +++ b/x-pack/plugins/aiops/server/plugin.ts @@ -19,9 +19,8 @@ import { AiopsPluginSetupDeps, AiopsPluginStartDeps, } from './types'; - -import { defineLogRateAnalysisRoute } from './routes'; -import { defineLogCategorizationRoutes } from './routes/log_categorization'; +import { defineRoute as defineLogRateAnalysisRoute } from './routes/log_rate_analysis/define_route'; +import { defineRoute as defineCategorizationFieldValidationRoute } from './routes/categorization_field_validation/define_route'; import { registerCasesPersistableState } from './register_cases'; export class AiopsPlugin @@ -59,7 +58,7 @@ export class AiopsPlugin // Register server side APIs core.getStartServices().then(([coreStart, depsStart]) => { defineLogRateAnalysisRoute(router, aiopsLicense, this.logger, coreStart, this.usageCounter); - defineLogCategorizationRoutes(router, aiopsLicense, this.usageCounter); + defineCategorizationFieldValidationRoute(router, aiopsLicense, this.usageCounter); }); return {}; diff --git a/x-pack/plugins/aiops/server/routes/categorization_field_validation/define_route.ts b/x-pack/plugins/aiops/server/routes/categorization_field_validation/define_route.ts new file mode 100644 index 0000000000000..e2daf77392cfc --- /dev/null +++ b/x-pack/plugins/aiops/server/routes/categorization_field_validation/define_route.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { IRouter } from '@kbn/core/server'; +import type { DataRequestHandlerContext } from '@kbn/data-plugin/server'; +import type { UsageCounter } from '@kbn/usage-collection-plugin/server'; +import { categorizationFieldValidationSchema } from '../../../common/api/log_categorization/schema'; +import { AIOPS_API_ENDPOINT } from '../../../common/api'; +import type { AiopsLicense } from '../../types'; +import { routeHandlerFactory } from './route_handler_factory'; + +export const defineRoute = ( + router: IRouter, + license: AiopsLicense, + usageCounter?: UsageCounter +) => { + router.versioned + .post({ + path: AIOPS_API_ENDPOINT.CATEGORIZATION_FIELD_VALIDATION, + access: 'internal', + }) + .addVersion( + { + version: '1', + validate: { + request: { + body: categorizationFieldValidationSchema, + }, + }, + }, + routeHandlerFactory(license, usageCounter) + ); +}; diff --git a/x-pack/plugins/aiops/server/routes/categorization_field_validation/route_handler_factory.ts b/x-pack/plugins/aiops/server/routes/categorization_field_validation/route_handler_factory.ts new file mode 100644 index 0000000000000..3d203595813d4 --- /dev/null +++ b/x-pack/plugins/aiops/server/routes/categorization_field_validation/route_handler_factory.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + KibanaRequest, + RequestHandlerContext, + RequestHandler, + KibanaResponseFactory, +} from '@kbn/core/server'; +import { categorizationExamplesProvider } from '@kbn/ml-category-validator'; +import type { UsageCounter } from '@kbn/usage-collection-plugin/server'; +import { wrapError } from '../error_wrapper'; +import { trackAIOpsRouteUsage } from '../../lib/track_route_usage'; +import { AIOPS_TELEMETRY_ID } from '../../../common/constants'; +import { AIOPS_API_ENDPOINT } from '../../../common/api'; +import type { AiopsLicense } from '../../types'; +import type { CategorizationFieldValidationSchema } from '../../../common/api/log_categorization/schema'; + +export const routeHandlerFactory: ( + license: AiopsLicense, + usageCounter?: UsageCounter +) => RequestHandler = + (license, usageCounter) => + async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ) => { + const { headers } = request; + trackAIOpsRouteUsage( + `POST ${AIOPS_API_ENDPOINT.CATEGORIZATION_FIELD_VALIDATION}`, + headers[AIOPS_TELEMETRY_ID.AIOPS_ANALYSIS_RUN_ORIGIN], + usageCounter + ); + + if (!license.isActivePlatinumLicense) { + return response.forbidden(); + } + try { + const { + elasticsearch: { client }, + } = await context.core; + + const { + indexPatternTitle, + timeField, + query, + size, + field, + start, + end, + analyzer, + runtimeMappings, + indicesOptions, + includeExamples, + } = request.body; + + const { validateCategoryExamples } = categorizationExamplesProvider(client); + const resp = await validateCategoryExamples( + indexPatternTitle, + query, + size, + field, + timeField, + start, + end, + analyzer ?? {}, + runtimeMappings, + indicesOptions, + includeExamples + ); + + return response.ok({ + body: resp, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }; diff --git a/x-pack/plugins/aiops/server/routes/index.ts b/x-pack/plugins/aiops/server/routes/index.ts deleted file mode 100755 index ff9e54f1fcc2d..0000000000000 --- a/x-pack/plugins/aiops/server/routes/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export { defineLogRateAnalysisRoute } from './log_rate_analysis'; diff --git a/x-pack/plugins/aiops/server/routes/log_categorization.ts b/x-pack/plugins/aiops/server/routes/log_categorization.ts deleted file mode 100644 index dd437f68617ad..0000000000000 --- a/x-pack/plugins/aiops/server/routes/log_categorization.ts +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { IRouter } from '@kbn/core/server'; -import type { DataRequestHandlerContext } from '@kbn/data-plugin/server'; -import { categorizationExamplesProvider } from '@kbn/ml-category-validator'; -import type { UsageCounter } from '@kbn/usage-collection-plugin/server'; -import { categorizationFieldValidationSchema } from '../../common/api/log_categorization/schema'; -import { AIOPS_API_ENDPOINT } from '../../common/api'; -import type { AiopsLicense } from '../types'; -import { wrapError } from './error_wrapper'; -import { trackAIOpsRouteUsage } from '../lib/track_route_usage'; -import { AIOPS_TELEMETRY_ID } from '../../common/constants'; - -export const defineLogCategorizationRoutes = ( - router: IRouter, - license: AiopsLicense, - usageCounter?: UsageCounter -) => { - router.versioned - .post({ - path: AIOPS_API_ENDPOINT.CATEGORIZATION_FIELD_VALIDATION, - access: 'internal', - }) - .addVersion( - { - version: '1', - validate: { - request: { - body: categorizationFieldValidationSchema, - }, - }, - }, - async (context, request, response) => { - const { headers } = request; - trackAIOpsRouteUsage( - `POST ${AIOPS_API_ENDPOINT.CATEGORIZATION_FIELD_VALIDATION}`, - headers[AIOPS_TELEMETRY_ID.AIOPS_ANALYSIS_RUN_ORIGIN], - usageCounter - ); - - if (!license.isActivePlatinumLicense) { - return response.forbidden(); - } - try { - const { - elasticsearch: { client }, - } = await context.core; - - const { - indexPatternTitle, - timeField, - query, - size, - field, - start, - end, - analyzer, - runtimeMappings, - indicesOptions, - includeExamples, - } = request.body; - - const { validateCategoryExamples } = categorizationExamplesProvider(client); - const resp = await validateCategoryExamples( - indexPatternTitle, - query, - size, - field, - timeField, - start, - end, - analyzer ?? {}, - runtimeMappings, - indicesOptions, - includeExamples - ); - - return response.ok({ - body: resp, - }); - } catch (e) { - return response.customError(wrapError(e)); - } - } - ); -}; diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis.ts deleted file mode 100644 index 7576faa22ec27..0000000000000 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis.ts +++ /dev/null @@ -1,867 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { queue } from 'async'; - -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; - -import { i18n } from '@kbn/i18n'; -import type { CoreStart, IRouter } from '@kbn/core/server'; -import { KBN_FIELD_TYPES } from '@kbn/field-types'; -import type { Logger } from '@kbn/logging'; -import type { DataRequestHandlerContext } from '@kbn/data-plugin/server'; -import { streamFactory } from '@kbn/ml-response-stream/server'; -import type { - SignificantTerm, - SignificantTermGroup, - NumericChartData, - NumericHistogramField, -} from '@kbn/ml-agg-utils'; -import { SIGNIFICANT_TERM_TYPE } from '@kbn/ml-agg-utils'; -import { fetchHistogramsForFields } from '@kbn/ml-agg-utils'; -import { createExecutionContext } from '@kbn/ml-route-utils'; -import type { UsageCounter } from '@kbn/usage-collection-plugin/server'; - -import { RANDOM_SAMPLER_SEED, AIOPS_TELEMETRY_ID } from '../../common/constants'; -import { - addSignificantTermsAction, - addSignificantTermsGroupAction, - addSignificantTermsGroupHistogramAction, - addSignificantTermsHistogramAction, - aiopsLogRateAnalysisSchema, - addErrorAction, - pingAction, - resetAllAction, - resetErrorsAction, - resetGroupsAction, - updateLoadingStateAction, - AiopsLogRateAnalysisApiAction, -} from '../../common/api/log_rate_analysis'; -import { getCategoryQuery } from '../../common/api/log_categorization/get_category_query'; -import { AIOPS_API_ENDPOINT } from '../../common/api'; - -import { PLUGIN_ID } from '../../common'; - -import { isRequestAbortedError } from '../lib/is_request_aborted_error'; -import type { AiopsLicense } from '../types'; - -import { fetchSignificantCategories } from './queries/fetch_significant_categories'; -import { fetchSignificantTermPValues } from './queries/fetch_significant_term_p_values'; -import { fetchIndexInfo } from './queries/fetch_index_info'; -import { fetchFrequentItemSets } from './queries/fetch_frequent_item_sets'; -import { fetchTerms2CategoriesCounts } from './queries/fetch_terms_2_categories_counts'; -import { getHistogramQuery } from './queries/get_histogram_query'; -import { getGroupFilter } from './queries/get_group_filter'; -import { getSignificantTermGroups } from './queries/get_significant_term_groups'; -import { trackAIOpsRouteUsage } from '../lib/track_route_usage'; - -// 10s ping frequency to keep the stream alive. -const PING_FREQUENCY = 10000; - -// Overall progress is a float from 0 to 1. -const LOADED_FIELD_CANDIDATES = 0.2; -const PROGRESS_STEP_P_VALUES = 0.5; -const PROGRESS_STEP_GROUPING = 0.1; -const PROGRESS_STEP_HISTOGRAMS = 0.1; -const PROGRESS_STEP_HISTOGRAMS_GROUPS = 0.1; - -export const defineLogRateAnalysisRoute = ( - router: IRouter, - license: AiopsLicense, - logger: Logger, - coreStart: CoreStart, - usageCounter?: UsageCounter -) => { - router.versioned - .post({ - path: AIOPS_API_ENDPOINT.LOG_RATE_ANALYSIS, - - access: 'internal', - }) - .addVersion( - { - version: '1', - validate: { - request: { - body: aiopsLogRateAnalysisSchema, - }, - }, - }, - async (context, request, response) => { - const { headers } = request; - - trackAIOpsRouteUsage( - `POST ${AIOPS_API_ENDPOINT.LOG_RATE_ANALYSIS}`, - headers[AIOPS_TELEMETRY_ID.AIOPS_ANALYSIS_RUN_ORIGIN], - usageCounter - ); - - if (!license.isActivePlatinumLicense) { - return response.forbidden(); - } - - const client = (await context.core).elasticsearch.client.asCurrentUser; - const executionContext = createExecutionContext(coreStart, PLUGIN_ID, request.route.path); - - return await coreStart.executionContext.withContext(executionContext, () => { - let logMessageCounter = 1; - - function logDebugMessage(msg: string) { - logger.debug(`Log Rate Analysis #${logMessageCounter}: ${msg}`); - logMessageCounter++; - } - - logDebugMessage('Starting analysis.'); - - const groupingEnabled = !!request.body.grouping; - const sampleProbability = request.body.sampleProbability ?? 1; - - const controller = new AbortController(); - const abortSignal = controller.signal; - - let isRunning = false; - let loaded = 0; - let shouldStop = false; - request.events.aborted$.subscribe(() => { - logDebugMessage('aborted$ subscription trigger.'); - shouldStop = true; - controller.abort(); - }); - request.events.completed$.subscribe(() => { - logDebugMessage('completed$ subscription trigger.'); - shouldStop = true; - controller.abort(); - }); - - const { - end: streamEnd, - push, - responseWithHeaders, - } = streamFactory( - request.headers, - logger, - request.body.compressResponse, - request.body.flushFix - ); - - function pushPingWithTimeout() { - setTimeout(() => { - if (isRunning) { - logDebugMessage('Ping message.'); - push(pingAction()); - pushPingWithTimeout(); - } - }, PING_FREQUENCY); - } - - function end() { - if (isRunning) { - isRunning = false; - logDebugMessage('Ending analysis.'); - streamEnd(); - } else { - logDebugMessage('end() was called again with isRunning already being false.'); - } - } - - function endWithUpdatedLoadingState() { - push( - updateLoadingStateAction({ - ccsWarning: false, - loaded: 1, - loadingState: i18n.translate( - 'xpack.aiops.logRateAnalysis.loadingState.doneMessage', - { - defaultMessage: 'Done.', - } - ), - }) - ); - - end(); - } - - function pushError(m: string) { - logDebugMessage('Push error.'); - push(addErrorAction(m)); - } - - async function runAnalysis() { - try { - isRunning = true; - - if (!request.body.overrides) { - logDebugMessage('Full Reset.'); - push(resetAllAction()); - } else { - logDebugMessage('Reset Errors.'); - push(resetErrorsAction()); - } - - if (request.body.overrides?.regroupOnly) { - logDebugMessage('Reset Groups.'); - push(resetGroupsAction()); - } - - if (request.body.overrides?.loaded) { - logDebugMessage(`Set 'loaded' override to '${request.body.overrides?.loaded}'.`); - loaded = request.body.overrides?.loaded; - } - - pushPingWithTimeout(); - - // Step 1: Index Info: Field candidates, total doc count, sample probability - - const fieldCandidates: string[] = []; - let fieldCandidatesCount = fieldCandidates.length; - - const textFieldCandidates: string[] = []; - - let totalDocCount = 0; - - if (!request.body.overrides?.remainingFieldCandidates) { - logDebugMessage('Fetch index information.'); - push( - updateLoadingStateAction({ - ccsWarning: false, - loaded, - loadingState: i18n.translate( - 'xpack.aiops.logRateAnalysis.loadingState.loadingIndexInformation', - { - defaultMessage: 'Loading index information.', - } - ), - }) - ); - - try { - const indexInfo = await fetchIndexInfo( - client, - request.body, - ['message', 'error.message'], - abortSignal - ); - - fieldCandidates.push(...indexInfo.fieldCandidates); - fieldCandidatesCount = fieldCandidates.length; - textFieldCandidates.push(...indexInfo.textFieldCandidates); - totalDocCount = indexInfo.totalDocCount; - } catch (e) { - if (!isRequestAbortedError(e)) { - logger.error(`Failed to fetch index information, got: \n${e.toString()}`); - pushError(`Failed to fetch index information.`); - } - end(); - return; - } - - logDebugMessage(`Total document count: ${totalDocCount}`); - logDebugMessage(`Sample probability: ${sampleProbability}`); - - loaded += LOADED_FIELD_CANDIDATES; - - pushPingWithTimeout(); - - push( - updateLoadingStateAction({ - ccsWarning: false, - loaded, - loadingState: i18n.translate( - 'xpack.aiops.logRateAnalysis.loadingState.identifiedFieldCandidates', - { - defaultMessage: - 'Identified {fieldCandidatesCount, plural, one {# field candidate} other {# field candidates}}.', - values: { - fieldCandidatesCount, - }, - } - ), - }) - ); - - if (fieldCandidatesCount === 0) { - endWithUpdatedLoadingState(); - } else if (shouldStop) { - logDebugMessage('shouldStop after fetching field candidates.'); - end(); - return; - } - } - - // Step 2: Significant Categories and Terms - - // This will store the combined count of detected significant log patterns and keywords - let fieldValuePairsCount = 0; - - const significantCategories: SignificantTerm[] = request.body.overrides - ?.significantTerms - ? request.body.overrides?.significantTerms.filter( - (d) => d.type === SIGNIFICANT_TERM_TYPE.LOG_PATTERN - ) - : []; - - // Get significant categories of text fields - if (textFieldCandidates.length > 0) { - significantCategories.push( - ...(await fetchSignificantCategories( - client, - request.body, - textFieldCandidates, - logger, - sampleProbability, - pushError, - abortSignal - )) - ); - - if (significantCategories.length > 0) { - push(addSignificantTermsAction(significantCategories)); - } - } - - const significantTerms: SignificantTerm[] = request.body.overrides?.significantTerms - ? request.body.overrides?.significantTerms.filter( - (d) => d.type === SIGNIFICANT_TERM_TYPE.KEYWORD - ) - : []; - - const fieldsToSample = new Set(); - - // Don't use more than 10 here otherwise Kibana will emit an error - // regarding a limit of abort signal listeners of more than 10. - const MAX_CONCURRENT_QUERIES = 10; - - let remainingFieldCandidates: string[]; - let loadingStepSizePValues = PROGRESS_STEP_P_VALUES; - - if (request.body.overrides?.remainingFieldCandidates) { - fieldCandidates.push(...request.body.overrides?.remainingFieldCandidates); - remainingFieldCandidates = request.body.overrides?.remainingFieldCandidates; - fieldCandidatesCount = fieldCandidates.length; - loadingStepSizePValues = - LOADED_FIELD_CANDIDATES + - PROGRESS_STEP_P_VALUES - - (request.body.overrides?.loaded ?? PROGRESS_STEP_P_VALUES); - } else { - remainingFieldCandidates = fieldCandidates; - } - - logDebugMessage('Fetch p-values.'); - - const pValuesQueue = queue(async function (fieldCandidate: string) { - loaded += (1 / fieldCandidatesCount) * loadingStepSizePValues; - - let pValues: Awaited>; - - try { - pValues = await fetchSignificantTermPValues( - client, - request.body, - [fieldCandidate], - logger, - sampleProbability, - pushError, - abortSignal - ); - } catch (e) { - if (!isRequestAbortedError(e)) { - logger.error( - `Failed to fetch p-values for '${fieldCandidate}', got: \n${e.toString()}` - ); - pushError(`Failed to fetch p-values for '${fieldCandidate}'.`); - } - return; - } - - remainingFieldCandidates = remainingFieldCandidates.filter( - (d) => d !== fieldCandidate - ); - - if (pValues.length > 0) { - pValues.forEach((d) => { - fieldsToSample.add(d.fieldName); - }); - significantTerms.push(...pValues); - - push(addSignificantTermsAction(pValues)); - } - - push( - updateLoadingStateAction({ - ccsWarning: false, - loaded, - loadingState: i18n.translate( - 'xpack.aiops.logRateAnalysis.loadingState.identifiedFieldValuePairs', - { - defaultMessage: - 'Identified {fieldValuePairsCount, plural, one {# significant field/value pair} other {# significant field/value pairs}}.', - values: { - fieldValuePairsCount, - }, - } - ), - remainingFieldCandidates, - }) - ); - }, MAX_CONCURRENT_QUERIES); - - pValuesQueue.push(fieldCandidates, (err) => { - if (err) { - logger.error(`Failed to fetch p-values.', got: \n${err.toString()}`); - pushError(`Failed to fetch p-values.`); - pValuesQueue.kill(); - end(); - } else if (shouldStop) { - logDebugMessage('shouldStop fetching p-values.'); - pValuesQueue.kill(); - end(); - } - }); - await pValuesQueue.drain(); - - fieldValuePairsCount = significantCategories.length + significantTerms.length; - - if (fieldValuePairsCount === 0) { - logDebugMessage('Stopping analysis, did not find significant terms.'); - endWithUpdatedLoadingState(); - return; - } - - const histogramFields: [NumericHistogramField] = [ - { fieldName: request.body.timeFieldName, type: KBN_FIELD_TYPES.DATE }, - ]; - - logDebugMessage('Fetch overall histogram.'); - - let overallTimeSeries: NumericChartData | undefined; - - const overallHistogramQuery = getHistogramQuery(request.body); - - try { - overallTimeSeries = ( - (await fetchHistogramsForFields( - client, - request.body.index, - overallHistogramQuery, - // fields - histogramFields, - // samplerShardSize - -1, - undefined, - abortSignal, - sampleProbability, - RANDOM_SAMPLER_SEED - )) as [NumericChartData] - )[0]; - } catch (e) { - if (!isRequestAbortedError(e)) { - logger.error( - `Failed to fetch the overall histogram data, got: \n${e.toString()}` - ); - pushError(`Failed to fetch overall histogram data.`); - } - // Still continue the analysis even if loading the overall histogram fails. - } - - function pushHistogramDataLoadingState() { - push( - updateLoadingStateAction({ - ccsWarning: false, - loaded, - loadingState: i18n.translate( - 'xpack.aiops.logRateAnalysis.loadingState.loadingHistogramData', - { - defaultMessage: 'Loading histogram data.', - } - ), - }) - ); - } - - if (shouldStop) { - logDebugMessage('shouldStop after fetching overall histogram.'); - end(); - return; - } - - if (groupingEnabled) { - logDebugMessage('Group results.'); - - push( - updateLoadingStateAction({ - ccsWarning: false, - loaded, - loadingState: i18n.translate( - 'xpack.aiops.logRateAnalysis.loadingState.groupingResults', - { - defaultMessage: 'Transforming significant field/value pairs into groups.', - } - ), - groupsMissing: true, - }) - ); - - try { - const { fields, itemSets } = await fetchFrequentItemSets( - client, - request.body.index, - JSON.parse(request.body.searchQuery) as estypes.QueryDslQueryContainer, - significantTerms, - request.body.timeFieldName, - request.body.deviationMin, - request.body.deviationMax, - logger, - sampleProbability, - pushError, - abortSignal - ); - - if (significantCategories.length > 0 && significantTerms.length > 0) { - const { - fields: significantCategoriesFields, - itemSets: significantCategoriesItemSets, - } = await fetchTerms2CategoriesCounts( - client, - request.body, - JSON.parse(request.body.searchQuery) as estypes.QueryDslQueryContainer, - significantTerms, - itemSets, - significantCategories, - request.body.deviationMin, - request.body.deviationMax, - logger, - pushError, - abortSignal - ); - - fields.push(...significantCategoriesFields); - itemSets.push(...significantCategoriesItemSets); - } - - if (shouldStop) { - logDebugMessage('shouldStop after fetching frequent_item_sets.'); - end(); - return; - } - - if (fields.length > 0 && itemSets.length > 0) { - const significantTermGroups = getSignificantTermGroups( - itemSets, - [...significantTerms, ...significantCategories], - fields - ); - - // We'll find out if there's at least one group with at least two items, - // only then will we return the groups to the clients and make the grouping option available. - const maxItems = Math.max(...significantTermGroups.map((g) => g.group.length)); - - if (maxItems > 1) { - push(addSignificantTermsGroupAction(significantTermGroups)); - } - - loaded += PROGRESS_STEP_GROUPING; - - pushHistogramDataLoadingState(); - - if (shouldStop) { - logDebugMessage('shouldStop after grouping.'); - end(); - return; - } - - logDebugMessage(`Fetch ${significantTermGroups.length} group histograms.`); - - const groupHistogramQueue = queue(async function (cpg: SignificantTermGroup) { - if (shouldStop) { - logDebugMessage('shouldStop abort fetching group histograms.'); - groupHistogramQueue.kill(); - end(); - return; - } - - if (overallTimeSeries !== undefined) { - const histogramQuery = getHistogramQuery(request.body, getGroupFilter(cpg)); - - let cpgTimeSeries: NumericChartData; - try { - cpgTimeSeries = ( - (await fetchHistogramsForFields( - client, - request.body.index, - histogramQuery, - // fields - [ - { - fieldName: request.body.timeFieldName, - type: KBN_FIELD_TYPES.DATE, - interval: overallTimeSeries.interval, - min: overallTimeSeries.stats[0], - max: overallTimeSeries.stats[1], - }, - ], - // samplerShardSize - -1, - undefined, - abortSignal, - sampleProbability, - RANDOM_SAMPLER_SEED - )) as [NumericChartData] - )[0]; - } catch (e) { - if (!isRequestAbortedError(e)) { - logger.error( - `Failed to fetch the histogram data for group #${ - cpg.id - }, got: \n${e.toString()}` - ); - pushError(`Failed to fetch the histogram data for group #${cpg.id}.`); - } - return; - } - const histogram = - overallTimeSeries.data.map((o) => { - const current = cpgTimeSeries.data.find( - (d1) => d1.key_as_string === o.key_as_string - ) ?? { - doc_count: 0, - }; - return { - key: o.key, - key_as_string: o.key_as_string ?? '', - doc_count_significant_term: current.doc_count, - doc_count_overall: Math.max(0, o.doc_count - current.doc_count), - }; - }) ?? []; - - push( - addSignificantTermsGroupHistogramAction([ - { - id: cpg.id, - histogram, - }, - ]) - ); - } - }, MAX_CONCURRENT_QUERIES); - - groupHistogramQueue.push(significantTermGroups); - await groupHistogramQueue.drain(); - } - } catch (e) { - if (!isRequestAbortedError(e)) { - logger.error( - `Failed to transform field/value pairs into groups, got: \n${e.toString()}` - ); - pushError(`Failed to transform field/value pairs into groups.`); - } - } - } - - loaded += PROGRESS_STEP_HISTOGRAMS_GROUPS; - - logDebugMessage(`Fetch ${significantTerms.length} field/value histograms.`); - - // time series filtered by fields - if ( - significantTerms.length > 0 && - overallTimeSeries !== undefined && - !request.body.overrides?.regroupOnly - ) { - const fieldValueHistogramQueue = queue(async function (cp: SignificantTerm) { - if (shouldStop) { - logDebugMessage('shouldStop abort fetching field/value histograms.'); - fieldValueHistogramQueue.kill(); - end(); - return; - } - - if (overallTimeSeries !== undefined) { - const histogramQuery = getHistogramQuery(request.body, [ - { - term: { [cp.fieldName]: cp.fieldValue }, - }, - ]); - - let cpTimeSeries: NumericChartData; - - try { - cpTimeSeries = ( - (await fetchHistogramsForFields( - client, - request.body.index, - histogramQuery, - // fields - [ - { - fieldName: request.body.timeFieldName, - type: KBN_FIELD_TYPES.DATE, - interval: overallTimeSeries.interval, - min: overallTimeSeries.stats[0], - max: overallTimeSeries.stats[1], - }, - ], - // samplerShardSize - -1, - undefined, - abortSignal, - sampleProbability, - RANDOM_SAMPLER_SEED - )) as [NumericChartData] - )[0]; - } catch (e) { - logger.error( - `Failed to fetch the histogram data for field/value pair "${cp.fieldName}:${ - cp.fieldValue - }", got: \n${e.toString()}` - ); - pushError( - `Failed to fetch the histogram data for field/value pair "${cp.fieldName}:${cp.fieldValue}".` - ); - return; - } - - const histogram = - overallTimeSeries.data.map((o) => { - const current = cpTimeSeries.data.find( - (d1) => d1.key_as_string === o.key_as_string - ) ?? { - doc_count: 0, - }; - return { - key: o.key, - key_as_string: o.key_as_string ?? '', - doc_count_significant_term: current.doc_count, - doc_count_overall: Math.max(0, o.doc_count - current.doc_count), - }; - }) ?? []; - - const { fieldName, fieldValue } = cp; - - loaded += (1 / fieldValuePairsCount) * PROGRESS_STEP_HISTOGRAMS; - pushHistogramDataLoadingState(); - push( - addSignificantTermsHistogramAction([ - { - fieldName, - fieldValue, - histogram, - }, - ]) - ); - } - }, MAX_CONCURRENT_QUERIES); - - fieldValueHistogramQueue.push(significantTerms); - await fieldValueHistogramQueue.drain(); - } - - // histograms for text field patterns - if ( - overallTimeSeries !== undefined && - significantCategories.length > 0 && - !request.body.overrides?.regroupOnly - ) { - const significantCategoriesHistogramQueries = significantCategories.map((d) => { - const histogramQuery = getHistogramQuery(request.body); - const categoryQuery = getCategoryQuery(d.fieldName, [ - { key: `${d.key}`, count: d.doc_count, examples: [] }, - ]); - if (Array.isArray(histogramQuery.bool?.filter)) { - histogramQuery.bool?.filter?.push(categoryQuery); - } - return histogramQuery; - }); - - for (const [i, histogramQuery] of significantCategoriesHistogramQueries.entries()) { - const cp = significantCategories[i]; - let catTimeSeries: NumericChartData; - - try { - catTimeSeries = ( - (await fetchHistogramsForFields( - client, - request.body.index, - histogramQuery, - // fields - [ - { - fieldName: request.body.timeFieldName, - type: KBN_FIELD_TYPES.DATE, - interval: overallTimeSeries.interval, - min: overallTimeSeries.stats[0], - max: overallTimeSeries.stats[1], - }, - ], - // samplerShardSize - -1, - undefined, - abortSignal, - sampleProbability, - RANDOM_SAMPLER_SEED - )) as [NumericChartData] - )[0]; - } catch (e) { - logger.error( - `Failed to fetch the histogram data for field/value pair "${cp.fieldName}:${ - cp.fieldValue - }", got: \n${e.toString()}` - ); - pushError( - `Failed to fetch the histogram data for field/value pair "${cp.fieldName}:${cp.fieldValue}".` - ); - return; - } - - const histogram = - overallTimeSeries.data.map((o) => { - const current = catTimeSeries.data.find( - (d1) => d1.key_as_string === o.key_as_string - ) ?? { - doc_count: 0, - }; - return { - key: o.key, - key_as_string: o.key_as_string ?? '', - doc_count_significant_term: current.doc_count, - doc_count_overall: Math.max(0, o.doc_count - current.doc_count), - }; - }) ?? []; - - const { fieldName, fieldValue } = cp; - - loaded += (1 / fieldValuePairsCount) * PROGRESS_STEP_HISTOGRAMS; - pushHistogramDataLoadingState(); - push( - addSignificantTermsHistogramAction([ - { - fieldName, - fieldValue, - histogram, - }, - ]) - ); - } - } - - endWithUpdatedLoadingState(); - } catch (e) { - if (!isRequestAbortedError(e)) { - logger.error(`Log Rate Analysis failed to finish, got: \n${e.toString()}`); - pushError(`Log Rate Analysis failed to finish.`); - } - end(); - } - } - - // Do not call this using `await` so it will run asynchronously while we return the stream already. - runAnalysis(); - - return response.ok(responseWithHeaders); - }); - } - ); -}; diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/define_route.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/define_route.ts new file mode 100644 index 0000000000000..3be8ef8dcdd2b --- /dev/null +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/define_route.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CoreStart, IRouter } from '@kbn/core/server'; +import type { Logger } from '@kbn/logging'; +import type { DataRequestHandlerContext } from '@kbn/data-plugin/server'; +import type { UsageCounter } from '@kbn/usage-collection-plugin/server'; + +import { aiopsLogRateAnalysisSchema } from '../../../common/api/log_rate_analysis'; +import { AIOPS_API_ENDPOINT } from '../../../common/api'; + +import type { AiopsLicense } from '../../types'; + +import { routeHandlerFactory } from './route_handler_factory'; + +export const defineRoute = ( + router: IRouter, + license: AiopsLicense, + logger: Logger, + coreStart: CoreStart, + usageCounter?: UsageCounter +) => { + router.versioned + .post({ + path: AIOPS_API_ENDPOINT.LOG_RATE_ANALYSIS, + + access: 'internal', + }) + .addVersion( + { + version: '1', + validate: { + request: { + body: aiopsLogRateAnalysisSchema, + }, + }, + }, + routeHandlerFactory(license, logger, coreStart, usageCounter) + ); +}; diff --git a/x-pack/plugins/aiops/server/routes/queries/duplicate_identifier.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/duplicate_identifier.ts similarity index 100% rename from x-pack/plugins/aiops/server/routes/queries/duplicate_identifier.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/duplicate_identifier.ts diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_categories.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_categories.ts similarity index 90% rename from x-pack/plugins/aiops/server/routes/queries/fetch_categories.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_categories.ts index b58e438e3882a..bb49622ad999c 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_categories.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_categories.ts @@ -15,16 +15,16 @@ import { type RandomSamplerWrapper, } from '@kbn/ml-random-sampler-utils'; -import { RANDOM_SAMPLER_SEED } from '../../../common/constants'; -import type { AiopsLogRateAnalysisSchema } from '../../../common/api/log_rate_analysis'; -import { createCategoryRequest } from '../../../common/api/log_categorization/create_category_request'; +import { RANDOM_SAMPLER_SEED } from '../../../../common/constants'; +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis'; +import { createCategoryRequest } from '../../../../common/api/log_categorization/create_category_request'; import type { Category, CategoriesAgg, SparkLinesPerCategory, -} from '../../../common/api/log_categorization/types'; +} from '../../../../common/api/log_categorization/types'; -import { isRequestAbortedError } from '../../lib/is_request_aborted_error'; +import { isRequestAbortedError } from '../../../lib/is_request_aborted_error'; import { getQueryWithParams } from './get_query_with_params'; diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_category_counts.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_category_counts.ts similarity index 90% rename from x-pack/plugins/aiops/server/routes/queries/fetch_category_counts.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_category_counts.ts index f27d2190a8ca6..fc7c14fb8f2ee 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_category_counts.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_category_counts.ts @@ -12,11 +12,11 @@ import { ElasticsearchClient } from '@kbn/core/server'; import type { Logger } from '@kbn/logging'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import type { AiopsLogRateAnalysisSchema } from '../../../common/api/log_rate_analysis'; -import { getCategoryQuery } from '../../../common/api/log_categorization/get_category_query'; -import type { Category } from '../../../common/api/log_categorization/types'; +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis'; +import { getCategoryQuery } from '../../../../common/api/log_categorization/get_category_query'; +import type { Category } from '../../../../common/api/log_categorization/types'; -import { isRequestAbortedError } from '../../lib/is_request_aborted_error'; +import { isRequestAbortedError } from '../../../lib/is_request_aborted_error'; import { getQueryWithParams } from './get_query_with_params'; import type { FetchCategoriesResponse } from './fetch_categories'; diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_frequent_item_sets.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_frequent_item_sets.test.ts similarity index 93% rename from x-pack/plugins/aiops/server/routes/queries/fetch_frequent_item_sets.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_frequent_item_sets.test.ts index e2dbeb781b999..d262ecc026481 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_frequent_item_sets.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_frequent_item_sets.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; +import { significantTerms } from '../../../../common/__mocks__/artificial_logs/significant_terms'; import { getShouldClauses, getFrequentItemSetsAggFields } from './fetch_frequent_item_sets'; diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_frequent_item_sets.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_frequent_item_sets.ts similarity index 98% rename from x-pack/plugins/aiops/server/routes/queries/fetch_frequent_item_sets.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_frequent_item_sets.ts index d73c3742e8e66..c74030efaea7f 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_frequent_item_sets.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_frequent_item_sets.ts @@ -15,12 +15,12 @@ import type { Logger } from '@kbn/logging'; import { type SignificantTerm } from '@kbn/ml-agg-utils'; import { createRandomSamplerWrapper } from '@kbn/ml-random-sampler-utils'; -import { RANDOM_SAMPLER_SEED, LOG_RATE_ANALYSIS_SETTINGS } from '../../../common/constants'; +import { RANDOM_SAMPLER_SEED, LOG_RATE_ANALYSIS_SETTINGS } from '../../../../common/constants'; import type { SignificantTermDuplicateGroup, ItemSet, FetchFrequentItemSetsResponse, -} from '../../../common/types'; +} from '../../../../common/types'; interface FrequentItemSetsAggregation extends estypes.AggregationsSamplerAggregation { fi: { diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_index_info.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.test.ts similarity index 97% rename from x-pack/plugins/aiops/server/routes/queries/fetch_index_info.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.test.ts index 3cbdf3d25420b..790989df9833c 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_index_info.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.test.ts @@ -9,7 +9,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ElasticsearchClient } from '@kbn/core/server'; -import type { AiopsLogRateAnalysisSchema } from '../../../common/api/log_rate_analysis'; +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis'; import { fetchIndexInfo, getRandomDocsRequest } from './fetch_index_info'; diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_index_info.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.ts similarity index 97% rename from x-pack/plugins/aiops/server/routes/queries/fetch_index_info.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.ts index 08c510405e32c..cfb9426a739ad 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_index_info.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_index_info.ts @@ -10,7 +10,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { ES_FIELD_TYPES } from '@kbn/field-types'; import type { ElasticsearchClient } from '@kbn/core/server'; -import type { AiopsLogRateAnalysisSchema } from '../../../common/api/log_rate_analysis'; +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis'; import { getQueryWithParams } from './get_query_with_params'; import { getRequestBase } from './get_request_base'; diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_significant_categories.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_significant_categories.ts similarity index 95% rename from x-pack/plugins/aiops/server/routes/queries/fetch_significant_categories.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_significant_categories.ts index d8bd92f04e6a6..c9e54be509426 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_significant_categories.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_significant_categories.ts @@ -12,9 +12,9 @@ import type { Logger } from '@kbn/logging'; import { criticalTableLookup, type Histogram } from '@kbn/ml-chi2test'; import { type SignificantTerm, SIGNIFICANT_TERM_TYPE } from '@kbn/ml-agg-utils'; -import type { Category } from '../../../common/api/log_categorization/types'; -import type { AiopsLogRateAnalysisSchema } from '../../../common/api/log_rate_analysis'; -import { LOG_RATE_ANALYSIS_SETTINGS } from '../../../common/constants'; +import type { Category } from '../../../../common/api/log_categorization/types'; +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis'; +import { LOG_RATE_ANALYSIS_SETTINGS } from '../../../../common/constants'; import { fetchCategories } from './fetch_categories'; import { fetchCategoryCounts } from './fetch_category_counts'; diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_significant_term_p_values.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_significant_term_p_values.ts similarity index 96% rename from x-pack/plugins/aiops/server/routes/queries/fetch_significant_term_p_values.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_significant_term_p_values.ts index ec1500092168f..00bcd918f48d9 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_significant_term_p_values.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_significant_term_p_values.ts @@ -15,10 +15,10 @@ import { type RandomSamplerWrapper, } from '@kbn/ml-random-sampler-utils'; -import { LOG_RATE_ANALYSIS_SETTINGS, RANDOM_SAMPLER_SEED } from '../../../common/constants'; -import type { AiopsLogRateAnalysisSchema } from '../../../common/api/log_rate_analysis'; +import { LOG_RATE_ANALYSIS_SETTINGS, RANDOM_SAMPLER_SEED } from '../../../../common/constants'; +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis'; -import { isRequestAbortedError } from '../../lib/is_request_aborted_error'; +import { isRequestAbortedError } from '../../../lib/is_request_aborted_error'; import { getNormalizedScore } from './get_normalized_score'; import { getQueryWithParams } from './get_query_with_params'; diff --git a/x-pack/plugins/aiops/server/routes/queries/fetch_terms_2_categories_counts.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_terms_2_categories_counts.ts similarity index 92% rename from x-pack/plugins/aiops/server/routes/queries/fetch_terms_2_categories_counts.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_terms_2_categories_counts.ts index a88090d3ab059..f1629f212ae99 100644 --- a/x-pack/plugins/aiops/server/routes/queries/fetch_terms_2_categories_counts.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_terms_2_categories_counts.ts @@ -14,13 +14,13 @@ import type { Logger } from '@kbn/logging'; import type { FieldValuePair, SignificantTerm } from '@kbn/ml-agg-utils'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; -import type { AiopsLogRateAnalysisSchema } from '../../../common/api/log_rate_analysis'; -import type { FetchFrequentItemSetsResponse, ItemSet } from '../../../common/types'; -import { getCategoryQuery } from '../../../common/api/log_categorization/get_category_query'; -import type { Category } from '../../../common/api/log_categorization/types'; -import { LOG_RATE_ANALYSIS_SETTINGS } from '../../../common/constants'; +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis'; +import type { FetchFrequentItemSetsResponse, ItemSet } from '../../../../common/types'; +import { getCategoryQuery } from '../../../../common/api/log_categorization/get_category_query'; +import type { Category } from '../../../../common/api/log_categorization/types'; +import { LOG_RATE_ANALYSIS_SETTINGS } from '../../../../common/constants'; -import { isRequestAbortedError } from '../../lib/is_request_aborted_error'; +import { isRequestAbortedError } from '../../../lib/is_request_aborted_error'; import { getQueryWithParams } from './get_query_with_params'; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_field_value_pair_counts.test.ts similarity index 78% rename from x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_field_value_pair_counts.test.ts index a762c04f14810..fb04844c5bd3d 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_field_value_pair_counts.test.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { significantTermGroups } from '../../../common/__mocks__/farequote/significant_term_groups'; -import { fields } from '../../../common/__mocks__/artificial_logs/fields'; -import { filteredFrequentItemSets } from '../../../common/__mocks__/artificial_logs/filtered_frequent_item_sets'; -import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; +import { significantTermGroups } from '../../../../common/__mocks__/farequote/significant_term_groups'; +import { fields } from '../../../../common/__mocks__/artificial_logs/fields'; +import { filteredFrequentItemSets } from '../../../../common/__mocks__/artificial_logs/filtered_frequent_item_sets'; +import { significantTerms } from '../../../../common/__mocks__/artificial_logs/significant_terms'; import { getFieldValuePairCounts } from './get_field_value_pair_counts'; import { getSimpleHierarchicalTree } from './get_simple_hierarchical_tree'; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_field_value_pair_counts.ts similarity index 91% rename from x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_field_value_pair_counts.ts index 7637bf27919ab..429306139d402 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_field_value_pair_counts.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_field_value_pair_counts.ts @@ -7,7 +7,7 @@ import type { SignificantTermGroup } from '@kbn/ml-agg-utils'; -import type { FieldValuePairCounts } from '../../../common/types'; +import type { FieldValuePairCounts } from '../../../../common/types'; /** * Get a nested record of field/value pairs with counts diff --git a/x-pack/plugins/aiops/server/routes/queries/get_filters.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_filters.test.ts similarity index 100% rename from x-pack/plugins/aiops/server/routes/queries/get_filters.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_filters.test.ts diff --git a/x-pack/plugins/aiops/server/routes/queries/get_filters.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_filters.ts similarity index 90% rename from x-pack/plugins/aiops/server/routes/queries/get_filters.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_filters.ts index a9d61755ec43b..5e58e138dac6b 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_filters.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_filters.ts @@ -9,7 +9,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { ESFilter } from '@kbn/es-types'; -import type { AiopsLogRateAnalysisSchema } from '../../../common/api/log_rate_analysis'; +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis'; export function rangeQuery( start?: number, diff --git a/x-pack/plugins/aiops/server/routes/queries/get_group_filter.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_group_filter.test.ts similarity index 84% rename from x-pack/plugins/aiops/server/routes/queries/get_group_filter.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_group_filter.test.ts index c27916a67eb63..e4d30a4438c6e 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_group_filter.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_group_filter.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { finalSignificantTermGroups } from '../../../common/__mocks__/artificial_logs/final_significant_term_groups'; +import { finalSignificantTermGroups } from '../../../../common/__mocks__/artificial_logs/final_significant_term_groups'; import { getGroupFilter } from './get_group_filter'; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_group_filter.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_group_filter.ts similarity index 95% rename from x-pack/plugins/aiops/server/routes/queries/get_group_filter.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_group_filter.ts index 86fd60b9fe8b0..d968ce90ec91b 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_group_filter.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_group_filter.ts @@ -9,7 +9,7 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { type SignificantTermGroup, SIGNIFICANT_TERM_TYPE } from '@kbn/ml-agg-utils'; -import { getCategoryQuery } from '../../../common/api/log_categorization/get_category_query'; +import { getCategoryQuery } from '../../../../common/api/log_categorization/get_category_query'; // Transforms a list of significant terms from a group in a query filter. // Uses a `term` filter for single field value combinations. diff --git a/x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_groups_with_readded_duplicates.test.ts similarity index 89% rename from x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_groups_with_readded_duplicates.test.ts index c0a2da80a080b..3c7325cdb49eb 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_groups_with_readded_duplicates.test.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { significantTermGroups } from '../../../common/__mocks__/artificial_logs/significant_term_groups'; -import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; +import { significantTermGroups } from '../../../../common/__mocks__/artificial_logs/significant_term_groups'; +import { significantTerms } from '../../../../common/__mocks__/artificial_logs/significant_terms'; import { duplicateIdentifier } from './duplicate_identifier'; import { getGroupsWithReaddedDuplicates } from './get_groups_with_readded_duplicates'; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_groups_with_readded_duplicates.ts similarity index 94% rename from x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_groups_with_readded_duplicates.ts index e6afb5e52ab53..6defb9d886662 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_groups_with_readded_duplicates.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_groups_with_readded_duplicates.ts @@ -9,7 +9,7 @@ import { uniqWith, isEqual } from 'lodash'; import type { SignificantTermGroup } from '@kbn/ml-agg-utils'; -import type { SignificantTermDuplicateGroup } from '../../../common/types'; +import type { SignificantTermDuplicateGroup } from '../../../../common/types'; export function getGroupsWithReaddedDuplicates( groups: SignificantTermGroup[], diff --git a/x-pack/plugins/aiops/server/routes/queries/get_histogram_query.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_histogram_query.test.ts similarity index 100% rename from x-pack/plugins/aiops/server/routes/queries/get_histogram_query.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_histogram_query.test.ts diff --git a/x-pack/plugins/aiops/server/routes/queries/get_histogram_query.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_histogram_query.ts similarity index 92% rename from x-pack/plugins/aiops/server/routes/queries/get_histogram_query.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_histogram_query.ts index ad99a967894f6..b45d7d026638e 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_histogram_query.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_histogram_query.ts @@ -7,7 +7,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import type { AiopsLogRateAnalysisSchema } from '../../../common/api/log_rate_analysis'; +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis'; import { getQueryWithParams } from './get_query_with_params'; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_marked_duplicates.test.ts similarity index 90% rename from x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_marked_duplicates.test.ts index 694767a17b55d..9c0d86a392e4d 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_marked_duplicates.test.ts @@ -5,10 +5,10 @@ * 2.0. */ -import { significantTermGroups } from '../../../common/__mocks__/farequote/significant_term_groups'; -import { fields } from '../../../common/__mocks__/artificial_logs/fields'; -import { filteredFrequentItemSets } from '../../../common/__mocks__/artificial_logs/filtered_frequent_item_sets'; -import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; +import { significantTermGroups } from '../../../../common/__mocks__/farequote/significant_term_groups'; +import { fields } from '../../../../common/__mocks__/artificial_logs/fields'; +import { filteredFrequentItemSets } from '../../../../common/__mocks__/artificial_logs/filtered_frequent_item_sets'; +import { significantTerms } from '../../../../common/__mocks__/artificial_logs/significant_terms'; import { getFieldValuePairCounts } from './get_field_value_pair_counts'; import { getMarkedDuplicates } from './get_marked_duplicates'; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_marked_duplicates.ts similarity index 91% rename from x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_marked_duplicates.ts index 202aa4f016326..7708d0634bda3 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_marked_duplicates.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_marked_duplicates.ts @@ -7,7 +7,7 @@ import type { SignificantTermGroup } from '@kbn/ml-agg-utils'; -import type { FieldValuePairCounts } from '../../../common/types'; +import type { FieldValuePairCounts } from '../../../../common/types'; /** * Analyse duplicate field/value pairs in groups. diff --git a/x-pack/plugins/aiops/server/routes/queries/get_missing_significant_terms.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_missing_significant_terms.test.ts similarity index 91% rename from x-pack/plugins/aiops/server/routes/queries/get_missing_significant_terms.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_missing_significant_terms.test.ts index e721143ad150c..412b7013b64d6 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_missing_significant_terms.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_missing_significant_terms.test.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { significantTermGroups } from '../../../common/__mocks__/artificial_logs/significant_term_groups'; -import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; +import { significantTermGroups } from '../../../../common/__mocks__/artificial_logs/significant_term_groups'; +import { significantTerms } from '../../../../common/__mocks__/artificial_logs/significant_terms'; import { duplicateIdentifier } from './duplicate_identifier'; import { getGroupsWithReaddedDuplicates } from './get_groups_with_readded_duplicates'; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_missing_significant_terms.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_missing_significant_terms.ts similarity index 100% rename from x-pack/plugins/aiops/server/routes/queries/get_missing_significant_terms.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_missing_significant_terms.ts diff --git a/x-pack/plugins/aiops/server/routes/queries/get_normalized_score.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_normalized_score.ts similarity index 100% rename from x-pack/plugins/aiops/server/routes/queries/get_normalized_score.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_normalized_score.ts diff --git a/x-pack/plugins/aiops/server/routes/queries/get_query_with_params.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_query_with_params.test.ts similarity index 100% rename from x-pack/plugins/aiops/server/routes/queries/get_query_with_params.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_query_with_params.test.ts diff --git a/x-pack/plugins/aiops/server/routes/queries/get_query_with_params.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_query_with_params.ts similarity index 92% rename from x-pack/plugins/aiops/server/routes/queries/get_query_with_params.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_query_with_params.ts index c9d15d6b89232..2d68c666b78ea 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_query_with_params.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_query_with_params.ts @@ -9,7 +9,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { FieldValuePair } from '@kbn/ml-agg-utils'; -import type { AiopsLogRateAnalysisSchema } from '../../../common/api/log_rate_analysis'; +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis'; import { getFilters } from './get_filters'; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_request_base.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_request_base.test.ts similarity index 100% rename from x-pack/plugins/aiops/server/routes/queries/get_request_base.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_request_base.test.ts diff --git a/x-pack/plugins/aiops/server/routes/queries/get_request_base.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_request_base.ts similarity index 82% rename from x-pack/plugins/aiops/server/routes/queries/get_request_base.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_request_base.ts index a6449249573f6..2410be74ea6b0 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_request_base.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_request_base.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { AiopsLogRateAnalysisSchema } from '../../../common/api/log_rate_analysis'; +import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis'; export const getRequestBase = ({ index, includeFrozen }: AiopsLogRateAnalysisSchema) => ({ index, diff --git a/x-pack/plugins/aiops/server/routes/queries/get_significant_term_groups.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_significant_term_groups.test.ts similarity index 64% rename from x-pack/plugins/aiops/server/routes/queries/get_significant_term_groups.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_significant_term_groups.test.ts index f15be7b8ed61b..05a682bc4ea34 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_significant_term_groups.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_significant_term_groups.test.ts @@ -7,10 +7,10 @@ import { orderBy } from 'lodash'; -import { fields } from '../../../common/__mocks__/artificial_logs/fields'; -import { frequentItemSets } from '../../../common/__mocks__/artificial_logs/frequent_item_sets'; -import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; -import { finalSignificantTermGroups } from '../../../common/__mocks__/artificial_logs/final_significant_term_groups'; +import { fields } from '../../../../common/__mocks__/artificial_logs/fields'; +import { frequentItemSets } from '../../../../common/__mocks__/artificial_logs/frequent_item_sets'; +import { significantTerms } from '../../../../common/__mocks__/artificial_logs/significant_terms'; +import { finalSignificantTermGroups } from '../../../../common/__mocks__/artificial_logs/final_significant_term_groups'; import { getSignificantTermGroups } from './get_significant_term_groups'; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_significant_term_groups.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_significant_term_groups.ts similarity index 98% rename from x-pack/plugins/aiops/server/routes/queries/get_significant_term_groups.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_significant_term_groups.ts index 33337603bd04e..1b498b0d76595 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_significant_term_groups.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_significant_term_groups.ts @@ -15,7 +15,7 @@ import { getSimpleHierarchicalTree } from './get_simple_hierarchical_tree'; import { getSimpleHierarchicalTreeLeaves } from './get_simple_hierarchical_tree_leaves'; import { getMissingSignificantTerms } from './get_missing_significant_terms'; import { transformSignificantTermToGroup } from './transform_significant_term_to_group'; -import type { ItemSet } from '../../../common/types'; +import type { ItemSet } from '../../../../common/types'; export function getSignificantTermGroups( itemsets: ItemSet[], diff --git a/x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree.test.ts similarity index 93% rename from x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree.test.ts index 1713e677c2b14..32f73d6ca2387 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree.test.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { fields } from '../../../common/__mocks__/artificial_logs/fields'; -import { filteredFrequentItemSets } from '../../../common/__mocks__/artificial_logs/filtered_frequent_item_sets'; -import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; +import { fields } from '../../../../common/__mocks__/artificial_logs/fields'; +import { filteredFrequentItemSets } from '../../../../common/__mocks__/artificial_logs/filtered_frequent_item_sets'; +import { significantTerms } from '../../../../common/__mocks__/artificial_logs/significant_terms'; import { getSimpleHierarchicalTree } from './get_simple_hierarchical_tree'; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree.ts similarity index 99% rename from x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree.ts index fc445fd88f1a6..2ffcc184fa71e 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree.ts @@ -7,7 +7,7 @@ import type { SignificantTerm } from '@kbn/ml-agg-utils'; -import type { ItemSet, SimpleHierarchicalTreeNode } from '../../../common/types'; +import type { ItemSet, SimpleHierarchicalTreeNode } from '../../../../common/types'; import { getValueCounts } from './get_value_counts'; import { getValuesDescending } from './get_values_descending'; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree_leaves.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree_leaves.test.ts similarity index 87% rename from x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree_leaves.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree_leaves.test.ts index 5ca23395c9815..6cb4f4b3c6532 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree_leaves.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree_leaves.test.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { fields } from '../../../common/__mocks__/artificial_logs/fields'; -import { filteredFrequentItemSets } from '../../../common/__mocks__/artificial_logs/filtered_frequent_item_sets'; -import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; +import { fields } from '../../../../common/__mocks__/artificial_logs/fields'; +import { filteredFrequentItemSets } from '../../../../common/__mocks__/artificial_logs/filtered_frequent_item_sets'; +import { significantTerms } from '../../../../common/__mocks__/artificial_logs/significant_terms'; import { getSimpleHierarchicalTree } from './get_simple_hierarchical_tree'; import { getSimpleHierarchicalTreeLeaves } from './get_simple_hierarchical_tree_leaves'; diff --git a/x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree_leaves.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree_leaves.ts similarity index 95% rename from x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree_leaves.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree_leaves.ts index f51d88c6ac3a5..bd183239eeadf 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_simple_hierarchical_tree_leaves.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree_leaves.ts @@ -9,7 +9,7 @@ import { orderBy } from 'lodash'; import type { SignificantTermGroup } from '@kbn/ml-agg-utils'; import { stringHash } from '@kbn/ml-string-hash'; -import type { SimpleHierarchicalTreeNode } from '../../../common/types'; +import type { SimpleHierarchicalTreeNode } from '../../../../common/types'; /** * Get leaves from hierarchical tree. diff --git a/x-pack/plugins/aiops/server/routes/queries/get_value_counts.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_value_counts.test.ts similarity index 88% rename from x-pack/plugins/aiops/server/routes/queries/get_value_counts.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_value_counts.test.ts index 515031c0e2af0..9187ed7c0eaab 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_value_counts.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_value_counts.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { frequentItemSets } from '../../../common/__mocks__/artificial_logs/frequent_item_sets'; +import { frequentItemSets } from '../../../../common/__mocks__/artificial_logs/frequent_item_sets'; import { getValueCounts } from './get_value_counts'; describe('getValueCounts', () => { diff --git a/x-pack/plugins/aiops/server/routes/queries/get_value_counts.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_value_counts.ts similarity index 90% rename from x-pack/plugins/aiops/server/routes/queries/get_value_counts.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_value_counts.ts index 42f022db5dccf..5c54412e46a5b 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_value_counts.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_value_counts.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ItemSet } from '../../../common/types'; +import type { ItemSet } from '../../../../common/types'; export function getValueCounts(df: ItemSet[], field: string) { return df.reduce>((p, c) => { diff --git a/x-pack/plugins/aiops/server/routes/queries/get_values_descending.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_values_descending.test.ts similarity index 88% rename from x-pack/plugins/aiops/server/routes/queries/get_values_descending.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_values_descending.test.ts index d8beffed5c8ce..cc5fc2d39bee3 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_values_descending.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_values_descending.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { frequentItemSets } from '../../../common/__mocks__/artificial_logs/frequent_item_sets'; +import { frequentItemSets } from '../../../../common/__mocks__/artificial_logs/frequent_item_sets'; import { getValuesDescending } from './get_values_descending'; describe('getValuesDescending', () => { diff --git a/x-pack/plugins/aiops/server/routes/queries/get_values_descending.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_values_descending.ts similarity index 90% rename from x-pack/plugins/aiops/server/routes/queries/get_values_descending.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_values_descending.ts index bad62b3056ace..9e6fd92178e82 100644 --- a/x-pack/plugins/aiops/server/routes/queries/get_values_descending.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_values_descending.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { ItemSet } from '../../../common/types'; +import type { ItemSet } from '../../../../common/types'; import { getValueCounts } from './get_value_counts'; diff --git a/x-pack/plugins/aiops/server/routes/queries/transform_significant_term_to_group.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/transform_significant_term_to_group.test.ts similarity index 90% rename from x-pack/plugins/aiops/server/routes/queries/transform_significant_term_to_group.test.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/transform_significant_term_to_group.test.ts index e22d6fcec784a..860baa3c800a2 100644 --- a/x-pack/plugins/aiops/server/routes/queries/transform_significant_term_to_group.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/transform_significant_term_to_group.test.ts @@ -5,8 +5,8 @@ * 2.0. */ -import { significantTermGroups } from '../../../common/__mocks__/artificial_logs/significant_term_groups'; -import { significantTerms } from '../../../common/__mocks__/artificial_logs/significant_terms'; +import { significantTermGroups } from '../../../../common/__mocks__/artificial_logs/significant_term_groups'; +import { significantTerms } from '../../../../common/__mocks__/artificial_logs/significant_terms'; import { duplicateIdentifier } from './duplicate_identifier'; import { getGroupsWithReaddedDuplicates } from './get_groups_with_readded_duplicates'; diff --git a/x-pack/plugins/aiops/server/routes/queries/transform_significant_term_to_group.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/transform_significant_term_to_group.ts similarity index 95% rename from x-pack/plugins/aiops/server/routes/queries/transform_significant_term_to_group.ts rename to x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/transform_significant_term_to_group.ts index 54ae0839c5c19..9f95a5c0fa2db 100644 --- a/x-pack/plugins/aiops/server/routes/queries/transform_significant_term_to_group.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/transform_significant_term_to_group.ts @@ -8,7 +8,7 @@ import { stringHash } from '@kbn/ml-string-hash'; import type { SignificantTerm, SignificantTermGroup } from '@kbn/ml-agg-utils'; -import type { SignificantTermDuplicateGroup } from '../../../common/types'; +import type { SignificantTermDuplicateGroup } from '../../../../common/types'; export function transformSignificantTermToGroup( significantTerm: SignificantTerm, diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/route_handler_factory.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/route_handler_factory.ts new file mode 100644 index 0000000000000..b522d6d6ee818 --- /dev/null +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/route_handler_factory.ts @@ -0,0 +1,851 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { queue } from 'async'; + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { + CoreStart, + KibanaRequest, + RequestHandlerContext, + RequestHandler, + KibanaResponseFactory, +} from '@kbn/core/server'; +import type { Logger } from '@kbn/logging'; +import { i18n } from '@kbn/i18n'; +import { KBN_FIELD_TYPES } from '@kbn/field-types'; +import { streamFactory } from '@kbn/ml-response-stream/server'; +import type { + SignificantTerm, + SignificantTermGroup, + NumericChartData, + NumericHistogramField, +} from '@kbn/ml-agg-utils'; +import { SIGNIFICANT_TERM_TYPE } from '@kbn/ml-agg-utils'; +import { fetchHistogramsForFields } from '@kbn/ml-agg-utils'; +import { createExecutionContext } from '@kbn/ml-route-utils'; +import type { UsageCounter } from '@kbn/usage-collection-plugin/server'; + +import { RANDOM_SAMPLER_SEED, AIOPS_TELEMETRY_ID } from '../../../common/constants'; +import { + addSignificantTermsAction, + addSignificantTermsGroupAction, + addSignificantTermsGroupHistogramAction, + addSignificantTermsHistogramAction, + addErrorAction, + pingAction, + resetAllAction, + resetErrorsAction, + resetGroupsAction, + updateLoadingStateAction, + AiopsLogRateAnalysisApiAction, + type AiopsLogRateAnalysisSchema, +} from '../../../common/api/log_rate_analysis'; +import { getCategoryQuery } from '../../../common/api/log_categorization/get_category_query'; +import { AIOPS_API_ENDPOINT } from '../../../common/api'; + +import { PLUGIN_ID } from '../../../common'; + +import { isRequestAbortedError } from '../../lib/is_request_aborted_error'; +import type { AiopsLicense } from '../../types'; + +import { fetchSignificantCategories } from './queries/fetch_significant_categories'; +import { fetchSignificantTermPValues } from './queries/fetch_significant_term_p_values'; +import { fetchIndexInfo } from './queries/fetch_index_info'; +import { fetchFrequentItemSets } from './queries/fetch_frequent_item_sets'; +import { fetchTerms2CategoriesCounts } from './queries/fetch_terms_2_categories_counts'; +import { getHistogramQuery } from './queries/get_histogram_query'; +import { getGroupFilter } from './queries/get_group_filter'; +import { getSignificantTermGroups } from './queries/get_significant_term_groups'; +import { trackAIOpsRouteUsage } from '../../lib/track_route_usage'; + +// 10s ping frequency to keep the stream alive. +const PING_FREQUENCY = 10000; + +// Overall progress is a float from 0 to 1. +const LOADED_FIELD_CANDIDATES = 0.2; +const PROGRESS_STEP_P_VALUES = 0.5; +const PROGRESS_STEP_GROUPING = 0.1; +const PROGRESS_STEP_HISTOGRAMS = 0.1; +const PROGRESS_STEP_HISTOGRAMS_GROUPS = 0.1; + +export const routeHandlerFactory: ( + license: AiopsLicense, + logger: Logger, + coreStart: CoreStart, + usageCounter?: UsageCounter +) => RequestHandler = + (license, logger, coreStart, usageCounter) => + async ( + context: RequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ) => { + const { headers } = request; + + trackAIOpsRouteUsage( + `POST ${AIOPS_API_ENDPOINT.LOG_RATE_ANALYSIS}`, + headers[AIOPS_TELEMETRY_ID.AIOPS_ANALYSIS_RUN_ORIGIN], + usageCounter + ); + + if (!license.isActivePlatinumLicense) { + return response.forbidden(); + } + + const client = (await context.core).elasticsearch.client.asCurrentUser; + const executionContext = createExecutionContext(coreStart, PLUGIN_ID, request.route.path); + + return await coreStart.executionContext.withContext(executionContext, () => { + let logMessageCounter = 1; + + function logDebugMessage(msg: string) { + logger.debug(`Log Rate Analysis #${logMessageCounter}: ${msg}`); + logMessageCounter++; + } + + logDebugMessage('Starting analysis.'); + + const groupingEnabled = !!request.body.grouping; + const sampleProbability = request.body.sampleProbability ?? 1; + + const controller = new AbortController(); + const abortSignal = controller.signal; + + let isRunning = false; + let loaded = 0; + let shouldStop = false; + request.events.aborted$.subscribe(() => { + logDebugMessage('aborted$ subscription trigger.'); + shouldStop = true; + controller.abort(); + }); + request.events.completed$.subscribe(() => { + logDebugMessage('completed$ subscription trigger.'); + shouldStop = true; + controller.abort(); + }); + + const { + end: streamEnd, + push, + responseWithHeaders, + } = streamFactory( + request.headers, + logger, + request.body.compressResponse, + request.body.flushFix + ); + + function pushPingWithTimeout() { + setTimeout(() => { + if (isRunning) { + logDebugMessage('Ping message.'); + push(pingAction()); + pushPingWithTimeout(); + } + }, PING_FREQUENCY); + } + + function end() { + if (isRunning) { + isRunning = false; + logDebugMessage('Ending analysis.'); + streamEnd(); + } else { + logDebugMessage('end() was called again with isRunning already being false.'); + } + } + + function endWithUpdatedLoadingState() { + push( + updateLoadingStateAction({ + ccsWarning: false, + loaded: 1, + loadingState: i18n.translate('xpack.aiops.logRateAnalysis.loadingState.doneMessage', { + defaultMessage: 'Done.', + }), + }) + ); + + end(); + } + + function pushError(m: string) { + logDebugMessage('Push error.'); + push(addErrorAction(m)); + } + + async function runAnalysis() { + try { + isRunning = true; + + if (!request.body.overrides) { + logDebugMessage('Full Reset.'); + push(resetAllAction()); + } else { + logDebugMessage('Reset Errors.'); + push(resetErrorsAction()); + } + + if (request.body.overrides?.regroupOnly) { + logDebugMessage('Reset Groups.'); + push(resetGroupsAction()); + } + + if (request.body.overrides?.loaded) { + logDebugMessage(`Set 'loaded' override to '${request.body.overrides?.loaded}'.`); + loaded = request.body.overrides?.loaded; + } + + pushPingWithTimeout(); + + // Step 1: Index Info: Field candidates, total doc count, sample probability + + const fieldCandidates: string[] = []; + let fieldCandidatesCount = fieldCandidates.length; + + const textFieldCandidates: string[] = []; + + let totalDocCount = 0; + + if (!request.body.overrides?.remainingFieldCandidates) { + logDebugMessage('Fetch index information.'); + push( + updateLoadingStateAction({ + ccsWarning: false, + loaded, + loadingState: i18n.translate( + 'xpack.aiops.logRateAnalysis.loadingState.loadingIndexInformation', + { + defaultMessage: 'Loading index information.', + } + ), + }) + ); + + try { + const indexInfo = await fetchIndexInfo( + client, + request.body, + ['message', 'error.message'], + abortSignal + ); + + fieldCandidates.push(...indexInfo.fieldCandidates); + fieldCandidatesCount = fieldCandidates.length; + textFieldCandidates.push(...indexInfo.textFieldCandidates); + totalDocCount = indexInfo.totalDocCount; + } catch (e) { + if (!isRequestAbortedError(e)) { + logger.error(`Failed to fetch index information, got: \n${e.toString()}`); + pushError(`Failed to fetch index information.`); + } + end(); + return; + } + + logDebugMessage(`Total document count: ${totalDocCount}`); + logDebugMessage(`Sample probability: ${sampleProbability}`); + + loaded += LOADED_FIELD_CANDIDATES; + + pushPingWithTimeout(); + + push( + updateLoadingStateAction({ + ccsWarning: false, + loaded, + loadingState: i18n.translate( + 'xpack.aiops.logRateAnalysis.loadingState.identifiedFieldCandidates', + { + defaultMessage: + 'Identified {fieldCandidatesCount, plural, one {# field candidate} other {# field candidates}}.', + values: { + fieldCandidatesCount, + }, + } + ), + }) + ); + + if (fieldCandidatesCount === 0) { + endWithUpdatedLoadingState(); + } else if (shouldStop) { + logDebugMessage('shouldStop after fetching field candidates.'); + end(); + return; + } + } + + // Step 2: Significant Categories and Terms + + // This will store the combined count of detected significant log patterns and keywords + let fieldValuePairsCount = 0; + + const significantCategories: SignificantTerm[] = request.body.overrides?.significantTerms + ? request.body.overrides?.significantTerms.filter( + (d) => d.type === SIGNIFICANT_TERM_TYPE.LOG_PATTERN + ) + : []; + + // Get significant categories of text fields + if (textFieldCandidates.length > 0) { + significantCategories.push( + ...(await fetchSignificantCategories( + client, + request.body, + textFieldCandidates, + logger, + sampleProbability, + pushError, + abortSignal + )) + ); + + if (significantCategories.length > 0) { + push(addSignificantTermsAction(significantCategories)); + } + } + + const significantTerms: SignificantTerm[] = request.body.overrides?.significantTerms + ? request.body.overrides?.significantTerms.filter( + (d) => d.type === SIGNIFICANT_TERM_TYPE.KEYWORD + ) + : []; + + const fieldsToSample = new Set(); + + // Don't use more than 10 here otherwise Kibana will emit an error + // regarding a limit of abort signal listeners of more than 10. + const MAX_CONCURRENT_QUERIES = 10; + + let remainingFieldCandidates: string[]; + let loadingStepSizePValues = PROGRESS_STEP_P_VALUES; + + if (request.body.overrides?.remainingFieldCandidates) { + fieldCandidates.push(...request.body.overrides?.remainingFieldCandidates); + remainingFieldCandidates = request.body.overrides?.remainingFieldCandidates; + fieldCandidatesCount = fieldCandidates.length; + loadingStepSizePValues = + LOADED_FIELD_CANDIDATES + + PROGRESS_STEP_P_VALUES - + (request.body.overrides?.loaded ?? PROGRESS_STEP_P_VALUES); + } else { + remainingFieldCandidates = fieldCandidates; + } + + logDebugMessage('Fetch p-values.'); + + const pValuesQueue = queue(async function (fieldCandidate: string) { + loaded += (1 / fieldCandidatesCount) * loadingStepSizePValues; + + let pValues: Awaited>; + + try { + pValues = await fetchSignificantTermPValues( + client, + request.body, + [fieldCandidate], + logger, + sampleProbability, + pushError, + abortSignal + ); + } catch (e) { + if (!isRequestAbortedError(e)) { + logger.error( + `Failed to fetch p-values for '${fieldCandidate}', got: \n${e.toString()}` + ); + pushError(`Failed to fetch p-values for '${fieldCandidate}'.`); + } + return; + } + + remainingFieldCandidates = remainingFieldCandidates.filter((d) => d !== fieldCandidate); + + if (pValues.length > 0) { + pValues.forEach((d) => { + fieldsToSample.add(d.fieldName); + }); + significantTerms.push(...pValues); + + push(addSignificantTermsAction(pValues)); + } + + push( + updateLoadingStateAction({ + ccsWarning: false, + loaded, + loadingState: i18n.translate( + 'xpack.aiops.logRateAnalysis.loadingState.identifiedFieldValuePairs', + { + defaultMessage: + 'Identified {fieldValuePairsCount, plural, one {# significant field/value pair} other {# significant field/value pairs}}.', + values: { + fieldValuePairsCount, + }, + } + ), + remainingFieldCandidates, + }) + ); + }, MAX_CONCURRENT_QUERIES); + + pValuesQueue.push(fieldCandidates, (err) => { + if (err) { + logger.error(`Failed to fetch p-values.', got: \n${err.toString()}`); + pushError(`Failed to fetch p-values.`); + pValuesQueue.kill(); + end(); + } else if (shouldStop) { + logDebugMessage('shouldStop fetching p-values.'); + pValuesQueue.kill(); + end(); + } + }); + await pValuesQueue.drain(); + + fieldValuePairsCount = significantCategories.length + significantTerms.length; + + if (fieldValuePairsCount === 0) { + logDebugMessage('Stopping analysis, did not find significant terms.'); + endWithUpdatedLoadingState(); + return; + } + + const histogramFields: [NumericHistogramField] = [ + { fieldName: request.body.timeFieldName, type: KBN_FIELD_TYPES.DATE }, + ]; + + logDebugMessage('Fetch overall histogram.'); + + let overallTimeSeries: NumericChartData | undefined; + + const overallHistogramQuery = getHistogramQuery(request.body); + + try { + overallTimeSeries = ( + (await fetchHistogramsForFields( + client, + request.body.index, + overallHistogramQuery, + // fields + histogramFields, + // samplerShardSize + -1, + undefined, + abortSignal, + sampleProbability, + RANDOM_SAMPLER_SEED + )) as [NumericChartData] + )[0]; + } catch (e) { + if (!isRequestAbortedError(e)) { + logger.error(`Failed to fetch the overall histogram data, got: \n${e.toString()}`); + pushError(`Failed to fetch overall histogram data.`); + } + // Still continue the analysis even if loading the overall histogram fails. + } + + function pushHistogramDataLoadingState() { + push( + updateLoadingStateAction({ + ccsWarning: false, + loaded, + loadingState: i18n.translate( + 'xpack.aiops.logRateAnalysis.loadingState.loadingHistogramData', + { + defaultMessage: 'Loading histogram data.', + } + ), + }) + ); + } + + if (shouldStop) { + logDebugMessage('shouldStop after fetching overall histogram.'); + end(); + return; + } + + if (groupingEnabled) { + logDebugMessage('Group results.'); + + push( + updateLoadingStateAction({ + ccsWarning: false, + loaded, + loadingState: i18n.translate( + 'xpack.aiops.logRateAnalysis.loadingState.groupingResults', + { + defaultMessage: 'Transforming significant field/value pairs into groups.', + } + ), + groupsMissing: true, + }) + ); + + try { + const { fields, itemSets } = await fetchFrequentItemSets( + client, + request.body.index, + JSON.parse(request.body.searchQuery) as estypes.QueryDslQueryContainer, + significantTerms, + request.body.timeFieldName, + request.body.deviationMin, + request.body.deviationMax, + logger, + sampleProbability, + pushError, + abortSignal + ); + + if (significantCategories.length > 0 && significantTerms.length > 0) { + const { + fields: significantCategoriesFields, + itemSets: significantCategoriesItemSets, + } = await fetchTerms2CategoriesCounts( + client, + request.body, + JSON.parse(request.body.searchQuery) as estypes.QueryDslQueryContainer, + significantTerms, + itemSets, + significantCategories, + request.body.deviationMin, + request.body.deviationMax, + logger, + pushError, + abortSignal + ); + + fields.push(...significantCategoriesFields); + itemSets.push(...significantCategoriesItemSets); + } + + if (shouldStop) { + logDebugMessage('shouldStop after fetching frequent_item_sets.'); + end(); + return; + } + + if (fields.length > 0 && itemSets.length > 0) { + const significantTermGroups = getSignificantTermGroups( + itemSets, + [...significantTerms, ...significantCategories], + fields + ); + + // We'll find out if there's at least one group with at least two items, + // only then will we return the groups to the clients and make the grouping option available. + const maxItems = Math.max(...significantTermGroups.map((g) => g.group.length)); + + if (maxItems > 1) { + push(addSignificantTermsGroupAction(significantTermGroups)); + } + + loaded += PROGRESS_STEP_GROUPING; + + pushHistogramDataLoadingState(); + + if (shouldStop) { + logDebugMessage('shouldStop after grouping.'); + end(); + return; + } + + logDebugMessage(`Fetch ${significantTermGroups.length} group histograms.`); + + const groupHistogramQueue = queue(async function (cpg: SignificantTermGroup) { + if (shouldStop) { + logDebugMessage('shouldStop abort fetching group histograms.'); + groupHistogramQueue.kill(); + end(); + return; + } + + if (overallTimeSeries !== undefined) { + const histogramQuery = getHistogramQuery(request.body, getGroupFilter(cpg)); + + let cpgTimeSeries: NumericChartData; + try { + cpgTimeSeries = ( + (await fetchHistogramsForFields( + client, + request.body.index, + histogramQuery, + // fields + [ + { + fieldName: request.body.timeFieldName, + type: KBN_FIELD_TYPES.DATE, + interval: overallTimeSeries.interval, + min: overallTimeSeries.stats[0], + max: overallTimeSeries.stats[1], + }, + ], + // samplerShardSize + -1, + undefined, + abortSignal, + sampleProbability, + RANDOM_SAMPLER_SEED + )) as [NumericChartData] + )[0]; + } catch (e) { + if (!isRequestAbortedError(e)) { + logger.error( + `Failed to fetch the histogram data for group #${ + cpg.id + }, got: \n${e.toString()}` + ); + pushError(`Failed to fetch the histogram data for group #${cpg.id}.`); + } + return; + } + const histogram = + overallTimeSeries.data.map((o) => { + const current = cpgTimeSeries.data.find( + (d1) => d1.key_as_string === o.key_as_string + ) ?? { + doc_count: 0, + }; + return { + key: o.key, + key_as_string: o.key_as_string ?? '', + doc_count_significant_term: current.doc_count, + doc_count_overall: Math.max(0, o.doc_count - current.doc_count), + }; + }) ?? []; + + push( + addSignificantTermsGroupHistogramAction([ + { + id: cpg.id, + histogram, + }, + ]) + ); + } + }, MAX_CONCURRENT_QUERIES); + + groupHistogramQueue.push(significantTermGroups); + await groupHistogramQueue.drain(); + } + } catch (e) { + if (!isRequestAbortedError(e)) { + logger.error( + `Failed to transform field/value pairs into groups, got: \n${e.toString()}` + ); + pushError(`Failed to transform field/value pairs into groups.`); + } + } + } + + loaded += PROGRESS_STEP_HISTOGRAMS_GROUPS; + + logDebugMessage(`Fetch ${significantTerms.length} field/value histograms.`); + + // time series filtered by fields + if ( + significantTerms.length > 0 && + overallTimeSeries !== undefined && + !request.body.overrides?.regroupOnly + ) { + const fieldValueHistogramQueue = queue(async function (cp: SignificantTerm) { + if (shouldStop) { + logDebugMessage('shouldStop abort fetching field/value histograms.'); + fieldValueHistogramQueue.kill(); + end(); + return; + } + + if (overallTimeSeries !== undefined) { + const histogramQuery = getHistogramQuery(request.body, [ + { + term: { [cp.fieldName]: cp.fieldValue }, + }, + ]); + + let cpTimeSeries: NumericChartData; + + try { + cpTimeSeries = ( + (await fetchHistogramsForFields( + client, + request.body.index, + histogramQuery, + // fields + [ + { + fieldName: request.body.timeFieldName, + type: KBN_FIELD_TYPES.DATE, + interval: overallTimeSeries.interval, + min: overallTimeSeries.stats[0], + max: overallTimeSeries.stats[1], + }, + ], + // samplerShardSize + -1, + undefined, + abortSignal, + sampleProbability, + RANDOM_SAMPLER_SEED + )) as [NumericChartData] + )[0]; + } catch (e) { + logger.error( + `Failed to fetch the histogram data for field/value pair "${cp.fieldName}:${ + cp.fieldValue + }", got: \n${e.toString()}` + ); + pushError( + `Failed to fetch the histogram data for field/value pair "${cp.fieldName}:${cp.fieldValue}".` + ); + return; + } + + const histogram = + overallTimeSeries.data.map((o) => { + const current = cpTimeSeries.data.find( + (d1) => d1.key_as_string === o.key_as_string + ) ?? { + doc_count: 0, + }; + return { + key: o.key, + key_as_string: o.key_as_string ?? '', + doc_count_significant_term: current.doc_count, + doc_count_overall: Math.max(0, o.doc_count - current.doc_count), + }; + }) ?? []; + + const { fieldName, fieldValue } = cp; + + loaded += (1 / fieldValuePairsCount) * PROGRESS_STEP_HISTOGRAMS; + pushHistogramDataLoadingState(); + push( + addSignificantTermsHistogramAction([ + { + fieldName, + fieldValue, + histogram, + }, + ]) + ); + } + }, MAX_CONCURRENT_QUERIES); + + fieldValueHistogramQueue.push(significantTerms); + await fieldValueHistogramQueue.drain(); + } + + // histograms for text field patterns + if ( + overallTimeSeries !== undefined && + significantCategories.length > 0 && + !request.body.overrides?.regroupOnly + ) { + const significantCategoriesHistogramQueries = significantCategories.map((d) => { + const histogramQuery = getHistogramQuery(request.body); + const categoryQuery = getCategoryQuery(d.fieldName, [ + { key: `${d.key}`, count: d.doc_count, examples: [] }, + ]); + if (Array.isArray(histogramQuery.bool?.filter)) { + histogramQuery.bool?.filter?.push(categoryQuery); + } + return histogramQuery; + }); + + for (const [i, histogramQuery] of significantCategoriesHistogramQueries.entries()) { + const cp = significantCategories[i]; + let catTimeSeries: NumericChartData; + + try { + catTimeSeries = ( + (await fetchHistogramsForFields( + client, + request.body.index, + histogramQuery, + // fields + [ + { + fieldName: request.body.timeFieldName, + type: KBN_FIELD_TYPES.DATE, + interval: overallTimeSeries.interval, + min: overallTimeSeries.stats[0], + max: overallTimeSeries.stats[1], + }, + ], + // samplerShardSize + -1, + undefined, + abortSignal, + sampleProbability, + RANDOM_SAMPLER_SEED + )) as [NumericChartData] + )[0]; + } catch (e) { + logger.error( + `Failed to fetch the histogram data for field/value pair "${cp.fieldName}:${ + cp.fieldValue + }", got: \n${e.toString()}` + ); + pushError( + `Failed to fetch the histogram data for field/value pair "${cp.fieldName}:${cp.fieldValue}".` + ); + return; + } + + const histogram = + overallTimeSeries.data.map((o) => { + const current = catTimeSeries.data.find( + (d1) => d1.key_as_string === o.key_as_string + ) ?? { + doc_count: 0, + }; + return { + key: o.key, + key_as_string: o.key_as_string ?? '', + doc_count_significant_term: current.doc_count, + doc_count_overall: Math.max(0, o.doc_count - current.doc_count), + }; + }) ?? []; + + const { fieldName, fieldValue } = cp; + + loaded += (1 / fieldValuePairsCount) * PROGRESS_STEP_HISTOGRAMS; + pushHistogramDataLoadingState(); + push( + addSignificantTermsHistogramAction([ + { + fieldName, + fieldValue, + histogram, + }, + ]) + ); + } + } + + endWithUpdatedLoadingState(); + } catch (e) { + if (!isRequestAbortedError(e)) { + logger.error(`Log Rate Analysis failed to finish, got: \n${e.toString()}`); + pushError(`Log Rate Analysis failed to finish.`); + } + end(); + } + } + + // Do not call this using `await` so it will run asynchronously while we return the stream already. + runAnalysis(); + + return response.ok(responseWithHeaders); + }); + }; From d1c587ff1d072735e109cff5c16ca6a243eacea3 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 30 Oct 2023 15:59:44 +0100 Subject: [PATCH 09/45] [Synthetics] Update alert condition text (#170013) ## Summary Fixes https://github.com/elastic/kibana/issues/168727 --- .../translations/translations/fr-FR.json | 2 +- .../translations/translations/ja-JP.json | 2 +- .../translations/translations/zh-CN.json | 2 +- .../lib/alerts/status_check.test.ts | 30 +++++++++---------- .../legacy_uptime/lib/alerts/translations.ts | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index ba03ddd298d5f..d1e0682fac22e 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -39373,4 +39373,4 @@ "xpack.painlessLab.walkthroughButtonLabel": "Présentation", "xpack.serverlessObservability.nav.getStarted": "Démarrer" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b2a00bc71bd43..582fe51373f9a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -39364,4 +39364,4 @@ "xpack.painlessLab.walkthroughButtonLabel": "実地検証", "xpack.serverlessObservability.nav.getStarted": "使ってみる" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5c97128b8c8f1..264c37f71ad83 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -39358,4 +39358,4 @@ "xpack.painlessLab.walkthroughButtonLabel": "指导", "xpack.serverlessObservability.nav.getStarted": "开始使用" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/status_check.test.ts b/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/status_check.test.ts index 8755427253369..9f35a9c6f6c73 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/status_check.test.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/status_check.test.ts @@ -129,7 +129,7 @@ const mockStatusAlertDocument = ( return { fields: { ...mockCommonAlertDocumentFields(monitor.monitorInfo), - [ALERT_REASON]: `Monitor "First" from ${monitor.monitorInfo.observer?.geo?.name} failed ${count} times in the last ${interval}. Alert when > ${numTimes}. Checked at ${checkedAt}.`, + [ALERT_REASON]: `Monitor "First" from ${monitor.monitorInfo.observer?.geo?.name} failed ${count} times in the last ${interval}. Alert when >= ${numTimes}. Checked at ${checkedAt}.`, }, id: getInstanceId( monitorInfo, @@ -293,8 +293,8 @@ describe('status check alert', () => { "monitorUrl": "localhost:8080", "observerHostname": undefined, "observerLocation": "harrisburg", - "reason": "Monitor \\"First\\" from harrisburg failed 234 times in the last 15 mins. Alert when > 5. Checked at July 6, 2020 9:14 PM.", - "statusMessage": "failed 234 times in the last 15 mins. Alert when > 5.", + "reason": "Monitor \\"First\\" from harrisburg failed 234 times in the last 15 mins. Alert when >= 5. Checked at July 6, 2020 9:14 PM.", + "statusMessage": "failed 234 times in the last 15 mins. Alert when >= 5.", }, ] `); @@ -312,8 +312,8 @@ describe('status check alert', () => { "monitorUrl": "localhost:8080", "observerHostname": undefined, "observerLocation": "harrisburg", - "reason": "Monitor \\"First\\" from harrisburg failed 234 times in the last 15 mins. Alert when > 5. Checked at July 6, 2020 9:14 PM.", - "statusMessage": "failed 234 times in the last 15 mins. Alert when > 5.", + "reason": "Monitor \\"First\\" from harrisburg failed 234 times in the last 15 mins. Alert when >= 5. Checked at July 6, 2020 9:14 PM.", + "statusMessage": "failed 234 times in the last 15 mins. Alert when >= 5.", "viewInAppUrl": "http://localhost:5601/hfe/app/uptime/monitor/Zmlyc3Q=?dateRangeEnd=now&dateRangeStart=2022-03-17T13%3A13%3A33.755Z&filters=%5B%5B%22observer.geo.name%22%2C%5B%22harrisburg%22%5D%5D%5D", }, ] @@ -375,8 +375,8 @@ describe('status check alert', () => { "monitorUrl": "localhost:8080", "observerHostname": undefined, "observerLocation": "harrisburg", - "reason": "Monitor \\"First\\" from harrisburg failed 234 times in the last 15m. Alert when > 5. Checked at July 6, 2020 9:14 PM.", - "statusMessage": "failed 234 times in the last 15m. Alert when > 5.", + "reason": "Monitor \\"First\\" from harrisburg failed 234 times in the last 15m. Alert when >= 5. Checked at July 6, 2020 9:14 PM.", + "statusMessage": "failed 234 times in the last 15m. Alert when >= 5.", }, ] `); @@ -394,8 +394,8 @@ describe('status check alert', () => { "monitorUrl": "localhost:8080", "observerHostname": undefined, "observerLocation": "harrisburg", - "reason": "Monitor \\"First\\" from harrisburg failed 234 times in the last 15m. Alert when > 5. Checked at July 6, 2020 9:14 PM.", - "statusMessage": "failed 234 times in the last 15m. Alert when > 5.", + "reason": "Monitor \\"First\\" from harrisburg failed 234 times in the last 15m. Alert when >= 5. Checked at July 6, 2020 9:14 PM.", + "statusMessage": "failed 234 times in the last 15m. Alert when >= 5.", "viewInAppUrl": "http://localhost:5601/hfe/app/uptime/monitor/Zmlyc3Q=?dateRangeEnd=now&dateRangeStart=2022-03-17T13%3A13%3A33.755Z&filters=%5B%5B%22observer.geo.name%22%2C%5B%22harrisburg%22%5D%5D%5D", }, ] @@ -448,8 +448,8 @@ describe('status check alert', () => { "monitorUrl": "localhost:8080", "observerHostname": undefined, "observerLocation": "harrisburg", - "reason": "Monitor \\"First\\" from harrisburg failed 234 times in the last 14h. Alert when > 4. Checked at July 6, 2020 9:14 PM.", - "statusMessage": "failed 234 times in the last 14h. Alert when > 4.", + "reason": "Monitor \\"First\\" from harrisburg failed 234 times in the last 14h. Alert when >= 4. Checked at July 6, 2020 9:14 PM.", + "statusMessage": "failed 234 times in the last 14h. Alert when >= 4.", }, ] `); @@ -665,8 +665,8 @@ describe('status check alert', () => { "monitorUrl": "localhost:8080", "observerHostname": undefined, "observerLocation": "harrisburg", - "reason": "Monitor \\"First\\" from harrisburg failed 234 times in the last 15 mins. Alert when > 3. Checked at July 6, 2020 9:14 PM.", - "statusMessage": "failed 234 times in the last 15 mins. Alert when > 3.", + "reason": "Monitor \\"First\\" from harrisburg failed 234 times in the last 15 mins. Alert when >= 3. Checked at July 6, 2020 9:14 PM.", + "statusMessage": "failed 234 times in the last 15 mins. Alert when >= 3.", }, ] `); @@ -1476,7 +1476,7 @@ describe('status check alert', () => { numTimes: 10, interval: '30 days', }) - ).toMatchInlineSnapshot(`"failed 235 times in the last 30 days. Alert when > 10."`); + ).toMatchInlineSnapshot(`"failed 235 times in the last 30 days. Alert when >= 10."`); }); it('creates message for availability item', () => { @@ -1539,7 +1539,7 @@ describe('status check alert', () => { } ) ).toMatchInlineSnapshot( - `"failed 235 times in the last 30 days. Alert when > 10. The 5 mins availability is 58.04%. Alert when < 90%."` + `"failed 235 times in the last 30 days. Alert when >= 10. The 5 mins availability is 58.04%. Alert when < 90%."` ); }); }); diff --git a/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/translations.ts b/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/translations.ts index 4a0990384426e..e48b59e627688 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/translations.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/translations.ts @@ -340,7 +340,7 @@ export const durationAnomalyTranslations = { export const statusCheckTranslations = { downMonitorsLabel: (count: number, interval: string, numTimes: number) => i18n.translate('xpack.uptime.alerts.monitorStatus.actionVariables.down', { - defaultMessage: `failed {count} times in the last {interval}. Alert when > {numTimes}.`, + defaultMessage: `failed {count} times in the last {interval}. Alert when >= {numTimes}.`, values: { count, interval, From efc2248d32946b36a8d49fb5509282b6ebc162cb Mon Sep 17 00:00:00 2001 From: Sergi Massaneda Date: Mon, 30 Oct 2023 16:10:47 +0100 Subject: [PATCH 10/45] [Security Solution] Fix icon color regression (#170123) ## Summary The changes in https://github.com/elastic/kibana/pull/169625 introduced a regression on ESS when displaying the `Entity Analytics` icon, removing the `fill` property caused the icon to be displayed using the wrong color. ### Before In ESS it loses the green + black color and it is shown in blue (it is inside a link) ![before_ess](https://github.com/elastic/kibana/assets/17747913/23f8657e-5cf1-4ff2-9635-ea13dbbf2edd) In serverless it was displaying the correct black color: ![before_serverless](https://github.com/elastic/kibana/assets/17747913/3f3b207d-9e41-4e62-aaf1-c12f76d46269) ### After: In ESS it is displayed with default green + black color ![after_ess](https://github.com/elastic/kibana/assets/17747913/91b8930f-5ca6-47d3-8499-b38a9e053f89) In serverless it keeps showing in black color as expected: ![after_serverless](https://github.com/elastic/kibana/assets/17747913/3f3b207d-9e41-4e62-aaf1-c12f76d46269) --- .../kbn-management/cards_navigation/src/cards_navigation.tsx | 2 +- .../security_solution/public/common/icons/entity_analytics.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/kbn-management/cards_navigation/src/cards_navigation.tsx b/packages/kbn-management/cards_navigation/src/cards_navigation.tsx index 303742dd25fdf..b7ab58a4777db 100644 --- a/packages/kbn-management/cards_navigation/src/cards_navigation.tsx +++ b/packages/kbn-management/cards_navigation/src/cards_navigation.tsx @@ -184,7 +184,7 @@ export const CardsNavigation = ({ } + icon={} titleSize="xs" title={app.title} description={app.description} diff --git a/x-pack/plugins/security_solution/public/common/icons/entity_analytics.tsx b/x-pack/plugins/security_solution/public/common/icons/entity_analytics.tsx index a9e4123fe4d41..e282f07c4dcdb 100644 --- a/x-pack/plugins/security_solution/public/common/icons/entity_analytics.tsx +++ b/x-pack/plugins/security_solution/public/common/icons/entity_analytics.tsx @@ -20,12 +20,13 @@ export const IconEntityAnalytics: React.FC> = ({ ...prop fillRule="evenodd" clipRule="evenodd" d="M25.332 7C25.332 8.10457 26.2275 9 27.332 9C28.4366 9 29.332 8.10457 29.332 7C29.332 5.89543 28.4366 5 27.332 5C26.2275 5 25.332 5.89543 25.332 7ZM23.332 7C23.332 7.37644 23.384 7.74073 23.4812 8.08609L17.6976 11.1707C15.9888 8.65367 13.1035 7 9.83203 7C4.58533 7 0.332031 11.2533 0.332031 16.5C0.332031 21.7467 4.58533 26 9.83203 26C12.6903 26 15.2537 24.7377 16.9952 22.7403L23.387 26.3356C23.3508 26.5517 23.332 26.7737 23.332 27C23.332 29.2091 25.1229 31 27.332 31C29.5412 31 31.332 29.2091 31.332 27C31.332 24.7909 29.5412 23 27.332 23C26.0677 23 24.9404 23.5866 24.2074 24.5024L18.1491 21.0946C18.672 20.15 19.0387 19.1068 19.2143 18H24.4581C24.9021 19.7252 26.4682 21 28.332 21C30.5412 21 32.332 19.2091 32.332 17C32.332 14.7909 30.5412 13 28.332 13C26.4682 13 24.9021 14.2748 24.458 16H19.3191C19.2631 14.9207 19.027 13.8891 18.6403 12.9346L24.49 9.81475C25.2149 10.5466 26.2205 11 27.332 11C29.5412 11 31.332 9.20914 31.332 7C31.332 4.79086 29.5412 3 27.332 3C25.1229 3 23.332 4.79086 23.332 7ZM28.332 19C27.2275 19 26.332 18.1046 26.332 17C26.332 15.8954 27.2275 15 28.332 15C29.4366 15 30.332 15.8954 30.332 17C30.332 18.1046 29.4366 19 28.332 19ZM25.332 27C25.332 28.1046 26.2275 29 27.332 29C28.4366 29 29.332 28.1046 29.332 27C29.332 25.8954 28.4366 25 27.332 25C26.2275 25 25.332 25.8954 25.332 27ZM9.83203 24C5.68989 24 2.33203 20.6421 2.33203 16.5C2.33203 12.3579 5.6899 9 9.83203 9C13.9742 9 17.332 12.3579 17.332 16.5C17.332 20.6421 13.9742 24 9.83203 24Z" - className="euiIcon__fillSecondary" + fill="#343741" /> From 540e6c0acb1b357e4f309091b27a6bdc24bf04c0 Mon Sep 17 00:00:00 2001 From: Brad White Date: Mon, 30 Oct 2023 09:34:43 -0600 Subject: [PATCH 11/45] [Watcher] Fix sorting on JSX headers (#170085) ## Summary Closes #164126 EUI in memory tables need to have stable headers between renders to sort properly when the header is a JSX element ([Docs](https://eui.elastic.co/#/tabular-content/in-memory-tables)). This PR stabilizes the headers for `WatchListPage` only. I noticed there were some other columns which likely have the same issue in Watcher, but I'm unsure how far reaching these changes need to be. Reading through the issue @ElenaStoeva mentions changing the sorting anyways, so a quick fix seems more appropriate. ![Untitled](https://github.com/elastic/kibana/assets/14021797/7d79c576-58b8-4ef5-a713-b87a3c52ea19) ## Release Note Fixed issue where certain columns in the Watcher table were not sorting properly. --- .../watch_list_page/watch_list_page.tsx | 147 ++++++++++-------- 1 file changed, 78 insertions(+), 69 deletions(-) diff --git a/x-pack/plugins/watcher/public/application/sections/watch_list_page/watch_list_page.tsx b/x-pack/plugins/watcher/public/application/sections/watch_list_page/watch_list_page.tsx index 76a8328fb532a..37d73d0c8a25a 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_list_page/watch_list_page.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_list_page/watch_list_page.tsx @@ -47,6 +47,80 @@ import { goToCreateThresholdAlert, goToCreateAdvancedWatch } from '../../lib/nav import { useAppContext } from '../../app_context'; import { PageError as GenericPageError } from '../../shared_imports'; +/* + * EuiMemoryTable relies on referential equality of a column's name field when sorting by that column. + * Therefore, we want the JSX elements preserved through renders. + */ +const stateColumnHeader = ( + + + {i18n.translate('xpack.watcher.sections.watchList.watchTable.stateHeader', { + defaultMessage: 'State', + })}{' '} + + + +); + +const conditionLastMetHeader = ( + + + {i18n.translate('xpack.watcher.sections.watchList.watchTable.lastFiredHeader', { + defaultMessage: 'Condition last met', + })}{' '} + + + +); + +const lastCheckedHeader = ( + + + {i18n.translate('xpack.watcher.sections.watchList.watchTable.lastTriggeredHeader', { + defaultMessage: 'Last checked', + })}{' '} + + + +); + +const commentHeader = ( + + + {i18n.translate('xpack.watcher.sections.watchList.watchTable.commentHeader', { + defaultMessage: 'Comment', + })}{' '} + + + +); + export const WatchListPage = () => { // hooks const { @@ -273,46 +347,14 @@ export const WatchListPage = () => { }, { field: 'watchStatus.state', - name: ( - - - {i18n.translate('xpack.watcher.sections.watchList.watchTable.stateHeader', { - defaultMessage: 'State', - })}{' '} - - - - ), + name: stateColumnHeader, sortable: true, width: '130px', render: (state: string) => , }, { field: 'watchStatus.lastMetCondition', - name: ( - - - {i18n.translate('xpack.watcher.sections.watchList.watchTable.lastFiredHeader', { - defaultMessage: 'Condition last met', - })}{' '} - - - - ), + name: conditionLastMetHeader, sortable: true, truncateText: true, width: '160px', @@ -322,23 +364,7 @@ export const WatchListPage = () => { }, { field: 'watchStatus.lastChecked', - name: ( - - - {i18n.translate('xpack.watcher.sections.watchList.watchTable.lastTriggeredHeader', { - defaultMessage: 'Last checked', - })}{' '} - - - - ), + name: lastCheckedHeader, sortable: true, truncateText: true, width: '160px', @@ -348,24 +374,7 @@ export const WatchListPage = () => { }, { field: 'watchStatus.comment', - name: ( - - - {i18n.translate('xpack.watcher.sections.watchList.watchTable.commentHeader', { - defaultMessage: 'Comment', - })}{' '} - - - - ), + name: commentHeader, sortable: true, truncateText: true, }, From 21c0b0b4205ba2d442656776b23b18e2dc062940 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Mon, 30 Oct 2023 16:37:05 +0100 Subject: [PATCH 12/45] [licensing] add license fetcher cache (#170006) ## Summary Related to https://github.com/elastic/kibana/issues/169788 Fix https://github.com/elastic/kibana/issues/117394 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../licensing/common/license_update.ts | 15 +- .../licensing/server/license_fetcher.test.ts | 172 ++++++++++++++++++ .../licensing/server/license_fetcher.ts | 133 ++++++++++++++ .../licensing/server/licensing_config.ts | 12 +- .../plugins/licensing/server/plugin.test.ts | 139 +++++--------- x-pack/plugins/licensing/server/plugin.ts | 120 ++---------- x-pack/plugins/licensing/server/types.ts | 2 + x-pack/plugins/licensing/tsconfig.json | 3 +- 8 files changed, 386 insertions(+), 210 deletions(-) create mode 100644 x-pack/plugins/licensing/server/license_fetcher.test.ts create mode 100644 x-pack/plugins/licensing/server/license_fetcher.ts diff --git a/x-pack/plugins/licensing/common/license_update.ts b/x-pack/plugins/licensing/common/license_update.ts index b344d8ce2d16a..a35b7aa6e6785 100644 --- a/x-pack/plugins/licensing/common/license_update.ts +++ b/x-pack/plugins/licensing/common/license_update.ts @@ -17,6 +17,7 @@ import { takeUntil, finalize, startWith, + throttleTime, } from 'rxjs/operators'; import { hasLicenseInfoChanged } from './has_license_info_changed'; import type { ILicense } from './types'; @@ -29,11 +30,15 @@ export function createLicenseUpdate( ) { const manuallyRefresh$ = new Subject(); - const fetched$ = merge(triggerRefresh$, manuallyRefresh$).pipe( - takeUntil(stop$), - exhaustMap(fetcher), - share() - ); + const fetched$ = merge( + triggerRefresh$, + manuallyRefresh$.pipe( + throttleTime(1000, undefined, { + leading: true, + trailing: true, + }) + ) + ).pipe(takeUntil(stop$), exhaustMap(fetcher), share()); // provide a first, empty license, so that we can compare in the filter below const startWithArgs = initialValues ? [undefined, initialValues] : [undefined]; diff --git a/x-pack/plugins/licensing/server/license_fetcher.test.ts b/x-pack/plugins/licensing/server/license_fetcher.test.ts new file mode 100644 index 0000000000000..efd9b001fa0ff --- /dev/null +++ b/x-pack/plugins/licensing/server/license_fetcher.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { getLicenseFetcher } from './license_fetcher'; +import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; +import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; + +type EsLicense = estypes.XpackInfoMinimalLicenseInformation; + +const delay = (ms: number) => new Promise((res) => setTimeout(res, ms)); + +function buildRawLicense(options: Partial = {}): EsLicense { + return { + uid: 'uid-000000001234', + status: 'active', + type: 'basic', + mode: 'basic', + expiry_date_in_millis: 1000, + ...options, + }; +} + +describe('LicenseFetcher', () => { + let logger: MockedLogger; + let clusterClient: ReturnType; + + beforeEach(() => { + logger = loggerMock.create(); + clusterClient = elasticsearchServiceMock.createClusterClient(); + }); + + it('returns the license for successful calls', async () => { + clusterClient.asInternalUser.xpack.info.mockResponse({ + license: buildRawLicense({ + uid: 'license-1', + }), + features: {}, + } as any); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 50_000, + }); + + const license = await fetcher(); + expect(license.uid).toEqual('license-1'); + }); + + it('returns the latest license for successful calls', async () => { + clusterClient.asInternalUser.xpack.info + .mockResponseOnce({ + license: buildRawLicense({ + uid: 'license-1', + }), + features: {}, + } as any) + .mockResponseOnce({ + license: buildRawLicense({ + uid: 'license-2', + }), + features: {}, + } as any); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 50_000, + }); + + let license = await fetcher(); + expect(license.uid).toEqual('license-1'); + + license = await fetcher(); + expect(license.uid).toEqual('license-2'); + }); + + it('returns an error license in case of error', async () => { + clusterClient.asInternalUser.xpack.info.mockResponseImplementation(() => { + throw new Error('woups'); + }); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 50_000, + }); + + const license = await fetcher(); + expect(license.error).toEqual('woups'); + }); + + it('returns a license successfully fetched after an error', async () => { + clusterClient.asInternalUser.xpack.info + .mockResponseImplementationOnce(() => { + throw new Error('woups'); + }) + .mockResponseOnce({ + license: buildRawLicense({ + uid: 'license-1', + }), + features: {}, + } as any); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 50_000, + }); + + let license = await fetcher(); + expect(license.error).toEqual('woups'); + license = await fetcher(); + expect(license.uid).toEqual('license-1'); + }); + + it('returns the latest fetched license after an error within the cache duration period', async () => { + clusterClient.asInternalUser.xpack.info + .mockResponseOnce({ + license: buildRawLicense({ + uid: 'license-1', + }), + features: {}, + } as any) + .mockResponseImplementationOnce(() => { + throw new Error('woups'); + }); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 50_000, + }); + + let license = await fetcher(); + expect(license.uid).toEqual('license-1'); + license = await fetcher(); + expect(license.uid).toEqual('license-1'); + }); + + it('returns an error license after an error exceeding the cache duration period', async () => { + clusterClient.asInternalUser.xpack.info + .mockResponseOnce({ + license: buildRawLicense({ + uid: 'license-1', + }), + features: {}, + } as any) + .mockResponseImplementationOnce(() => { + throw new Error('woups'); + }); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 1, + }); + + let license = await fetcher(); + expect(license.uid).toEqual('license-1'); + + await delay(50); + + license = await fetcher(); + expect(license.error).toEqual('woups'); + }); +}); diff --git a/x-pack/plugins/licensing/server/license_fetcher.ts b/x-pack/plugins/licensing/server/license_fetcher.ts new file mode 100644 index 0000000000000..43d9c204bbf66 --- /dev/null +++ b/x-pack/plugins/licensing/server/license_fetcher.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { createHash } from 'crypto'; +import stringify from 'json-stable-stringify'; +import type { MaybePromise } from '@kbn/utility-types'; +import { isPromise } from '@kbn/std'; +import type { IClusterClient, Logger } from '@kbn/core/server'; +import type { + ILicense, + PublicLicense, + PublicFeatures, + LicenseType, + LicenseStatus, +} from '../common/types'; +import { License } from '../common/license'; +import type { ElasticsearchError, LicenseFetcher } from './types'; + +export const getLicenseFetcher = ({ + clusterClient, + logger, + cacheDurationMs, +}: { + clusterClient: MaybePromise; + logger: Logger; + cacheDurationMs: number; +}): LicenseFetcher => { + let currentLicense: ILicense | undefined; + let lastSuccessfulFetchTime: number | undefined; + + return async () => { + const client = isPromise(clusterClient) ? await clusterClient : clusterClient; + try { + const response = await client.asInternalUser.xpack.info(); + const normalizedLicense = + response.license && response.license.type !== 'missing' + ? normalizeServerLicense(response.license) + : undefined; + const normalizedFeatures = response.features + ? normalizeFeatures(response.features) + : undefined; + + const signature = sign({ + license: normalizedLicense, + features: normalizedFeatures, + error: '', + }); + + currentLicense = new License({ + license: normalizedLicense, + features: normalizedFeatures, + signature, + }); + lastSuccessfulFetchTime = Date.now(); + + return currentLicense; + } catch (error) { + logger.warn( + `License information could not be obtained from Elasticsearch due to ${error} error` + ); + + if (lastSuccessfulFetchTime && lastSuccessfulFetchTime + cacheDurationMs > Date.now()) { + return currentLicense!; + } else { + const errorMessage = getErrorMessage(error); + const signature = sign({ error: errorMessage }); + + return new License({ + error: getErrorMessage(error), + signature, + }); + } + } + }; +}; + +function normalizeServerLicense( + license: estypes.XpackInfoMinimalLicenseInformation +): PublicLicense { + return { + uid: license.uid, + type: license.type as LicenseType, + mode: license.mode as LicenseType, + expiryDateInMillis: + typeof license.expiry_date_in_millis === 'string' + ? parseInt(license.expiry_date_in_millis, 10) + : license.expiry_date_in_millis, + status: license.status as LicenseStatus, + }; +} + +function normalizeFeatures(rawFeatures: estypes.XpackInfoFeatures) { + const features: PublicFeatures = {}; + for (const [name, feature] of Object.entries(rawFeatures)) { + features[name] = { + isAvailable: feature.available, + isEnabled: feature.enabled, + }; + } + return features; +} + +function sign({ + license, + features, + error, +}: { + license?: PublicLicense; + features?: PublicFeatures; + error?: string; +}) { + return createHash('sha256') + .update( + stringify({ + license, + features, + error, + }) + ) + .digest('hex'); +} + +function getErrorMessage(error: ElasticsearchError): string { + if (error.status === 400) { + return 'X-Pack plugin is not installed on the Elasticsearch cluster.'; + } + return error.message; +} diff --git a/x-pack/plugins/licensing/server/licensing_config.ts b/x-pack/plugins/licensing/server/licensing_config.ts index 459c69b650dbb..66899602e04cb 100644 --- a/x-pack/plugins/licensing/server/licensing_config.ts +++ b/x-pack/plugins/licensing/server/licensing_config.ts @@ -10,12 +10,18 @@ import { PluginConfigDescriptor } from '@kbn/core/server'; const configSchema = schema.object({ api_polling_frequency: schema.duration({ defaultValue: '30s' }), + license_cache_duration: schema.duration({ + defaultValue: '300s', + validate: (value) => { + if (value.asMinutes() > 15) { + return 'license cache duration must be shorter than 15 minutes'; + } + }, + }), }); export type LicenseConfigType = TypeOf; export const config: PluginConfigDescriptor = { - schema: schema.object({ - api_polling_frequency: schema.duration({ defaultValue: '30s' }), - }), + schema: configSchema, }; diff --git a/x-pack/plugins/licensing/server/plugin.test.ts b/x-pack/plugins/licensing/server/plugin.test.ts index b087b6f3f03fa..129dc6aee66da 100644 --- a/x-pack/plugins/licensing/server/plugin.test.ts +++ b/x-pack/plugins/licensing/server/plugin.test.ts @@ -56,22 +56,23 @@ describe('licensing plugin', () => { return client; }; - describe('#start', () => { - describe('#license$', () => { - let plugin: LicensingPlugin; - let pluginInitContextMock: ReturnType; + let plugin: LicensingPlugin; + let pluginInitContextMock: ReturnType; - beforeEach(() => { - pluginInitContextMock = coreMock.createPluginInitializerContext({ - api_polling_frequency: moment.duration(100), - }); - plugin = new LicensingPlugin(pluginInitContextMock); - }); + beforeEach(() => { + pluginInitContextMock = coreMock.createPluginInitializerContext({ + api_polling_frequency: moment.duration(100), + license_cache_duration: moment.duration(1000), + }); + plugin = new LicensingPlugin(pluginInitContextMock); + }); - afterEach(async () => { - await plugin.stop(); - }); + afterEach(async () => { + await plugin?.stop(); + }); + describe('#start', () => { + describe('#license$', () => { it('returns license', async () => { const esClient = createEsClient({ license: buildRawLicense(), @@ -79,8 +80,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); const license = await firstValueFrom(license$); expect(license.isAvailable).toBe(true); }); @@ -92,8 +93,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); await firstValueFrom(license$); expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); @@ -111,8 +112,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); const [first, second, third] = await firstValueFrom(license$.pipe(take(3), toArray())); expect(first.type).toBe('basic'); @@ -125,8 +126,8 @@ describe('licensing plugin', () => { esClient.asInternalUser.xpack.info.mockRejectedValue(new Error('test')); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); const license = await firstValueFrom(license$); expect(license.isAvailable).toBe(false); @@ -140,8 +141,8 @@ describe('licensing plugin', () => { esClient.asInternalUser.xpack.info.mockRejectedValue(error); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); const license = await firstValueFrom(license$); expect(license.isAvailable).toBe(false); @@ -169,8 +170,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); const [first, second, third] = await firstValueFrom(license$.pipe(take(3), toArray())); @@ -186,8 +187,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - await plugin.start(); + plugin.setup(coreSetup); + plugin.start(); await flushPromises(); @@ -201,8 +202,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - await plugin.start(); + plugin.setup(coreSetup); + plugin.start(); await flushPromises(); @@ -229,8 +230,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); const [first, second, third] = await firstValueFrom(license$.pipe(take(3), toArray())); expect(first.signature === third.signature).toBe(true); @@ -239,16 +240,12 @@ describe('licensing plugin', () => { }); describe('#refresh', () => { - let plugin: LicensingPlugin; - afterEach(async () => { - await plugin.stop(); - }); - it('forces refresh immediately', async () => { plugin = new LicensingPlugin( coreMock.createPluginInitializerContext({ // disable polling mechanism api_polling_frequency: moment.duration(50000), + license_cache_duration: moment.duration(1000), }) ); const esClient = createEsClient({ @@ -257,31 +254,26 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { refresh, license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { refresh, license$ } = plugin.start(); expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(0); - await license$.pipe(take(1)).toPromise(); + await firstValueFrom(license$); expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); - refresh(); + await refresh(); await flushPromises(); expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(2); }); }); describe('#createLicensePoller', () => { - let plugin: LicensingPlugin; - - afterEach(async () => { - await plugin.stop(); - }); - it(`creates a poller fetching license from passed 'clusterClient' every 'api_polling_frequency' ms`, async () => { plugin = new LicensingPlugin( coreMock.createPluginInitializerContext({ api_polling_frequency: moment.duration(50000), + license_cache_duration: moment.duration(1000), }) ); @@ -290,8 +282,8 @@ describe('licensing plugin', () => { features: {}, }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { createLicensePoller, license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { createLicensePoller, license$ } = plugin.start(); const customClient = createEsClient({ license: buildRawLicense({ type: 'gold' }), @@ -313,19 +305,13 @@ describe('licensing plugin', () => { expect(customLicense.isAvailable).toBe(true); expect(customLicense.type).toBe('gold'); - expect(await license$.pipe(take(1)).toPromise()).not.toBe(customLicense); + expect(await firstValueFrom(license$)).not.toBe(customLicense); }); it('creates a poller with a manual refresh control', async () => { - plugin = new LicensingPlugin( - coreMock.createPluginInitializerContext({ - api_polling_frequency: moment.duration(100), - }) - ); - const coreSetup = coreMock.createSetup(); - await plugin.setup(coreSetup); - const { createLicensePoller } = await plugin.start(); + plugin.setup(coreSetup); + const { createLicensePoller } = plugin.start(); const customClient = createEsClient({ license: buildRawLicense({ type: 'gold' }), @@ -344,24 +330,10 @@ describe('licensing plugin', () => { }); describe('extends core contexts', () => { - let plugin: LicensingPlugin; - - beforeEach(() => { - plugin = new LicensingPlugin( - coreMock.createPluginInitializerContext({ - api_polling_frequency: moment.duration(100), - }) - ); - }); - - afterEach(async () => { - await plugin.stop(); - }); - it('provides a licensing context to http routes', async () => { const coreSetup = coreMock.createSetup(); - await plugin.setup(coreSetup); + plugin.setup(coreSetup); expect(coreSetup.http.registerRouteHandlerContext.mock.calls).toMatchInlineSnapshot(` Array [ @@ -375,22 +347,10 @@ describe('licensing plugin', () => { }); describe('registers on pre-response interceptor', () => { - let plugin: LicensingPlugin; - - beforeEach(() => { - plugin = new LicensingPlugin( - coreMock.createPluginInitializerContext({ api_polling_frequency: moment.duration(100) }) - ); - }); - - afterEach(async () => { - await plugin.stop(); - }); - it('once', async () => { const coreSetup = coreMock.createSetup(); - await plugin.setup(coreSetup); + plugin.setup(coreSetup); expect(coreSetup.http.registerOnPreResponse).toHaveBeenCalledTimes(1); }); @@ -399,14 +359,9 @@ describe('licensing plugin', () => { describe('#stop', () => { it('stops polling', async () => { - const plugin = new LicensingPlugin( - coreMock.createPluginInitializerContext({ - api_polling_frequency: moment.duration(100), - }) - ); const coreSetup = coreMock.createSetup(); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); let completed = false; license$.subscribe({ complete: () => (completed = true) }); diff --git a/x-pack/plugins/licensing/server/plugin.ts b/x-pack/plugins/licensing/server/plugin.ts index 0d21cd689bf46..b3ac583e7c81e 100644 --- a/x-pack/plugins/licensing/server/plugin.ts +++ b/x-pack/plugins/licensing/server/plugin.ts @@ -8,12 +8,7 @@ import type { Observable, Subject, Subscription } from 'rxjs'; import { ReplaySubject, timer } from 'rxjs'; import moment from 'moment'; -import { createHash } from 'crypto'; -import stringify from 'json-stable-stringify'; - -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { MaybePromise } from '@kbn/utility-types'; -import { isPromise } from '@kbn/std'; import type { CoreSetup, Logger, @@ -21,73 +16,17 @@ import type { PluginInitializerContext, IClusterClient, } from '@kbn/core/server'; - import { registerAnalyticsContextProvider } from '../common/register_analytics_context_provider'; -import type { - ILicense, - PublicLicense, - PublicFeatures, - LicenseType, - LicenseStatus, -} from '../common/types'; +import type { ILicense } from '../common/types'; import type { LicensingPluginSetup, LicensingPluginStart } from './types'; -import { License } from '../common/license'; import { createLicenseUpdate } from '../common/license_update'; - -import type { ElasticsearchError } from './types'; import { registerRoutes } from './routes'; import { FeatureUsageService } from './services'; - import type { LicenseConfigType } from './licensing_config'; import { createRouteHandlerContext } from './licensing_route_handler_context'; import { createOnPreResponseHandler } from './on_pre_response_handler'; import { getPluginStatus$ } from './plugin_status'; - -function normalizeServerLicense( - license: estypes.XpackInfoMinimalLicenseInformation -): PublicLicense { - return { - uid: license.uid, - type: license.type as LicenseType, - mode: license.mode as LicenseType, - expiryDateInMillis: - typeof license.expiry_date_in_millis === 'string' - ? parseInt(license.expiry_date_in_millis, 10) - : license.expiry_date_in_millis, - status: license.status as LicenseStatus, - }; -} - -function normalizeFeatures(rawFeatures: estypes.XpackInfoFeatures) { - const features: PublicFeatures = {}; - for (const [name, feature] of Object.entries(rawFeatures)) { - features[name] = { - isAvailable: feature.available, - isEnabled: feature.enabled, - }; - } - return features; -} - -function sign({ - license, - features, - error, -}: { - license?: PublicLicense; - features?: PublicFeatures; - error?: string; -}) { - return createHash('sha256') - .update( - stringify({ - license, - features, - error, - }) - ) - .digest('hex'); -} +import { getLicenseFetcher } from './license_fetcher'; /** * @public @@ -153,9 +92,16 @@ export class LicensingPlugin implements Plugin - this.fetchLicense(clusterClient) + const { license$, refreshManually } = createLicenseUpdate( + intervalRefresh$, + this.stop$, + licenseFetcher ); this.loggingSubscription = license$.subscribe((license) => @@ -178,50 +124,6 @@ export class LicensingPlugin implements Plugin): Promise => { - const client = isPromise(clusterClient) ? await clusterClient : clusterClient; - try { - const response = await client.asInternalUser.xpack.info(); - const normalizedLicense = - response.license && response.license.type !== 'missing' - ? normalizeServerLicense(response.license) - : undefined; - const normalizedFeatures = response.features - ? normalizeFeatures(response.features) - : undefined; - - const signature = sign({ - license: normalizedLicense, - features: normalizedFeatures, - error: '', - }); - - return new License({ - license: normalizedLicense, - features: normalizedFeatures, - signature, - }); - } catch (error) { - this.logger.warn( - `License information could not be obtained from Elasticsearch due to ${error} error` - ); - const errorMessage = this.getErrorMessage(error); - const signature = sign({ error: errorMessage }); - - return new License({ - error: this.getErrorMessage(error), - signature, - }); - } - }; - - private getErrorMessage(error: ElasticsearchError): string { - if (error.status === 400) { - return 'X-Pack plugin is not installed on the Elasticsearch cluster.'; - } - return error.message; - } - public start() { if (!this.refresh || !this.license$) { throw new Error('Setup has not been completed'); diff --git a/x-pack/plugins/licensing/server/types.ts b/x-pack/plugins/licensing/server/types.ts index 83b39cb663715..fcccdecb66c00 100644 --- a/x-pack/plugins/licensing/server/types.ts +++ b/x-pack/plugins/licensing/server/types.ts @@ -14,6 +14,8 @@ export interface ElasticsearchError extends Error { status?: number; } +export type LicenseFetcher = () => Promise; + /** * Result from remote request fetching raw feature set. * @internal diff --git a/x-pack/plugins/licensing/tsconfig.json b/x-pack/plugins/licensing/tsconfig.json index 323f77b3b0ebc..1deb735f99466 100644 --- a/x-pack/plugins/licensing/tsconfig.json +++ b/x-pack/plugins/licensing/tsconfig.json @@ -15,7 +15,8 @@ "@kbn/i18n", "@kbn/analytics-client", "@kbn/subscription-tracking", - "@kbn/core-analytics-browser" + "@kbn/core-analytics-browser", + "@kbn/logging-mocks" ], "exclude": ["target/**/*"] } From 27db507202f7e2053e117ddb23ad7bae560fa387 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Mon, 30 Oct 2023 08:40:58 -0700 Subject: [PATCH 13/45] [DOCS] Rule visibility details for Elasticsearch query rules (#170072) --- docs/user/alerting/alerting-setup.asciidoc | 36 +++++++++-------- .../images/rule-types-es-query-conditions.png | Bin 162003 -> 192647 bytes .../images/rule-types-es-query-invalid.png | Bin 145914 -> 0 bytes .../images/rule-types-es-query-valid.png | Bin 138353 -> 20416 bytes .../alerting/rule-types/es-query.asciidoc | 38 +++++++++--------- .../stack_alerting/es_query_rule.ts | 35 ++-------------- 6 files changed, 42 insertions(+), 67 deletions(-) delete mode 100644 docs/user/alerting/images/rule-types-es-query-invalid.png diff --git a/docs/user/alerting/alerting-setup.asciidoc b/docs/user/alerting/alerting-setup.asciidoc index 40ecdedad2cf4..7f184a295ac51 100644 --- a/docs/user/alerting/alerting-setup.asciidoc +++ b/docs/user/alerting/alerting-setup.asciidoc @@ -58,26 +58,14 @@ feature. To change rule settings, you must have `all` privileges for the such as flapping detection. For more information on configuring roles that provide access to features, go to <>. -For details about the prerequisites for each API, refer to <>. +Each rule also has a rule visibility value (or `consumer` in the APIs), which affects the {kib} feature privileges that are required to access it. +To view or edit a rule that has a `Stack Rules` rule visibility, for example, you must have the appropriate *Management > {stack-rules-feature}* feature privileges. -[float] -[[alerting-restricting-actions]] -==== Restrict actions - -For security reasons you may wish to limit the extent to which {kib} can connect -to external services. <> allows you to disable certain -<> and allowlist the hostnames that {kib} can connect with. - -[float] -[[alerting-spaces]] -=== Space isolation - -Rules and connectors are isolated to the {kib} space in which they were created. -A rule or connector created in one space will not be visible in another. +For details about the prerequisites required to run each API, refer to <>. [float] [[alerting-authorization]] -=== Authorization +==== API keys Rules are authorized using an API key. Its credentials are used to run all background tasks associated with the rule, including condition checks like {es} queries and triggered actions. @@ -100,11 +88,25 @@ In both cases, the API key is subsequently associated with the rule and used whe [IMPORTANT] ============================================== -If a rule requires certain privileges, such as index privileges, to run and a user without those privileges updates the rule, the rule will no longer function. +If a rule requires certain privileges, such as index privileges, to run and a user without those privileges updates the rule, the rule will no longer function. Conversely, if a user with greater or administrator privileges modifies the rule, it will begin running with increased privileges. The same behavior occurs when you change the API key in the header of your API calls. ============================================== +[float] +[[alerting-restricting-actions]] +==== Restrict actions + +For security reasons you may wish to limit the extent to which {kib} can connect to external services. +You can use <> to disable certain <> and allowlist the hostnames that {kib} can connect with. + +[float] +[[alerting-spaces]] +=== Space isolation + +Rules and connectors are isolated to the {kib} space in which they were created. +A rule or connector created in one space will not be visible in another. + [float] [[alerting-ccs-setup]] === {ccs-cap} diff --git a/docs/user/alerting/images/rule-types-es-query-conditions.png b/docs/user/alerting/images/rule-types-es-query-conditions.png index cbe18e7bf0bdb9ab54886546be9e2db07a4e3560..47eed98caf5e65ae81df589ced7013a825fcc596 100644 GIT binary patch literal 192647 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 O}QKvf2oi*OpD}_{QwK*eB-iCdC7s zXMZ<)euVClW9uYSYh?rjry5k1))JnTiN(V&LG!04y;WJV;(CgJNE$4Mmpue0ar-a~ z9NN256m2GN#*8s9VkmZjs?d>e&KS|A*ix?EMNj7wS&d1LGM!*6;2RkAE_!26OV)QA z%Xxmnz%jUag>(wBpbr3g|M0wgm|3it3fhx)`0w%wLcNMn&g`YLlCt3wPoh`?Lp)|x zF=vR!`Sefl4%E^)^;N!CV|YPlsapK7mRA~QF{aO8<2p`O>wkVujvR>81eptc1c1u4 zQd%X*L$Cdwz4euX+Vx#XzVaY{B0OMT_C~G}B*_DIh9=H=uABe!9IE|#xJz$xx$#52 zvW<|s&nS6}t_HX_<@E83Zi>+~WppUqf&C7;GGz9uQ0>FPwYBjypXd_y+@~OOyhwHc z#DpWrLUX!u%QgxJoioB3CV1 zz6sx3$j&dO)2sGMntwN9TQyu(k5i81-^{%L*kF&0>i03--~7oFG{X1e^vB$uOZR}O zv1y`bZp0pYiLzv$|AA zMbB-mPVNZ_zioGnkIf&VpvbzFUhS^_o&Oz1s_ULf1d~(;^>VJ7J+w-#W0Tp20av@aN@;NCEXTWy%6--Nm!Ty<&PqPYbwJPD z)IyHu+Dgj(xTt%RSgN;iYCM~8gmZS(Q$@bw_=m%ZO^^d~n0qVebYyQmuhA%K_}Plo z_@2uMa!fW^yS%`M4M$Y;k6;1rcD+17cVd@|9RI zh#2R^mqNSF-&Q-*d;}}Y=Enxi4-bd~Zvr=Fym-PpYRFc6^hZae1}n1LV_1gvM-fSu zNBLgA?X$P$<1SmlE(6FZj=38Z{xm6Zr6+98o7MXH2iD~T=W55kKn638vAkN_s^}`0BJX#g=6$bHiNa6tG%<411;hoNFR27C>7I}p=g)$d$9Y+?K zw44>R>w5LrI$sT6^`w>*>3(s4zo^Inl4N!0R^RdY170!S#tQS)D#Fsy-mdq99h&F6 z!ClThw7Sw@H?}SxB@9g@>_up*eTf^9C8D^d0D`T1dmsCdqt%uzz3{@(e(Qm%B=XZq=!kcHdr=i&-xhxFX?<@mP>5SLgSSLX(~)#`)NMa%2sJE&EaZ zh`*J^e9&9G_KX_*0S&vC_=^ntPm#GBnIvHb!W?#oe0U^)eXvnC8!9upwyfKdSm$iE zJeV_pu=zZH?Qm;w$`Iu^tG1fzyVG7CNJ?8H8a@DR)7E;&sR=P(y{jmVsB=oryGGwB z=C5JF);QMC)oJc?xQ!-d5Vv|i-%fl4eH7pgdo*_nM+6Ak#>yy@UD@?;!S%d-$MQlh zN}`2(Cw%?n(^MCA24qSuX5c}y%;%r(77I+F)w#B#j1ape$hI+RO{k4K|LD5|vAU$rdNajvTVg;Z9>-K?oy!9O~^Tr)3^c_1{_1#nnXy+)~ zNMkS)*;X)IbfKCg7f3F|cINs^77m=@Mbxs5y{)3uisG#mjf)bX%j-xP$$cCvawo0R z{O*mZb5b#u3Lc)d@3HX2l&WxA?ZXWL>an1!Ndq(G5~)d&mbnVDvaU&hR7dw*fjSec zLv-ZF`94eqA*I7?)y^e-7QsVRBJ{6@(v+mC{cC(yE16eHPt+HWqmO}h%_#7%w;bTS$lgqKHuR) z$A$mMv(ETCP)ButFmKFLc+3eM-0481StsXqhKHMsF!J4&Y;GWnb^ftx&o~VvO2XzYVG(u2XU_!)i){Nq*7_V#1qlkiEILrt z90zR9z0NY|3?Ckv2{#CBjxoxJoR)Dx-^hc%dPq}Vl>jHfWTtDH*fC02zjFPsT5ZfK z>K}OO9Kn~Qay;A1h%kml2o|keA>1s_Ks(q~v{8yO^HohYg|@Aoxi-D*x~pG{@9c{2 z%wzSbJ0Ow+~cdxCO0R9?|$;|4C^l7JCnC~cp2brmI2H9)`&n(jex5?l#e{UZ8 zxa3hFWBp*`L*I*j%nN!ktIfxjg9lHCt6cLfGRFNW2#)!90G~>}{CIQ>1mh1HDCsX$ zE1kX5CEhF2(Ra6c1{0S`talF&nVYv2ayxLJ^(cD0k(jg%*t(w0twe94R~h&D)KCzv zo%im~RPpIImVU3YQ?>w1=sQ~Rd?m#I{mRX*bLgAa#gCG+SZQ1w^r6?jIT6 zGOVYna;$S)Fgst9bbIsITW_E!y;?74=STjy1I%B&=9!J& zMa~>h4W4Q*3%kzSM_;Y$u}OO)Q#;;eCTR&0_pCp{Ki&8y4dZsAU3Y+)U&y<8MAO!E zVNarK+i|*LK~Yp;bNLGxxE{M_vuYHjM+n=yG>W#YC>x!A6$hNC4CFBh&-f?fJ_A^Y znDwvhEs2uRjwiiE$+zG*rmz=c_h;V%Y2;?+GTaTu2M%1!-%@N5@o9!tVh#g(9Hwt6 zg;2IH4SMcN)O=;iCwzJ7ja_%T+q{a5w-X)7(V8b+bgkD9LL*dDgge84Q=1BUJ%D+* z6g<>|H#qS$jtusmY4t>IvBE&1sZT3_!>8Jj%o5Era-W9#rfyb3A)8j@h#8=^{{sc9 zH3#F9F>CVOZ6G%9$7Fjm*Ds2y=Slj)Rif{m_w4Vvon3Ko{j7b>Zy|%p8@W2NVhthQ z>VrT>lq>d>$CW;{;2(U6EbhgcHTSxaGI`aNm0KTE1>$gV(OjrB0VOi(T~O2aHrQ~R z(4uFFJ5al4#M1l7aHYB^MIbapdARk2VE`=?OM%hxXP`bMtE{sHy`pFUl$C304rBvadZ*2{J?v;i#&lL4iD~Sn1JdUgE&;3N^$j#a zD$WJ-K5ws0di1M)Vstc*Pg0+#-*@1Mq-C_im?F40lgY&8o568PtZx)cv`pYwB}Gy= zf2cwwx^D?6hmN|?z800SNAz5$A84xTl$15G_xa95A{IA@H-Yk3H%iF1ZBk^w?xW*5 zpUi`vEd!T9^AkXJ7qs4!=N%9>9dEZ0FOGV-PA2=TB6hA4iICw`1`<92mZJnPK+2eK zUX_;kpk$^iqUBryMh7cJvs!LwyY)WDe75J{?H#hBO@W%_5m^iK;P};c3FGCHk_NrB zNO=zC9_9S!%9>o~+AGr*zLLbxU)RI1YO~MhoxWXqzf@HvDM{K$t!?HuaC&lx^)-{e z&2XTfOel6~Of+cCfnM)>0iVWFX{Y~?km>{07no3(ah%m|GvndBIZ%C#;18AB55t); zl;&8+3iW2Vn)qc~$bj9bw!Q;w><22xcv> z4S`#7%B0kjG(UEPOWt)SVLAX!@~P7+Zg>*$^JNd>&8!X|de4VQ)(v8NZA31uVc$Gm z$Lr_6C9k=|;gymkVfwNEOR3oLbrlSKPgw2o97Zn2gw1q(x_WXM&SR_$xV^8s2O)#? z8rkSqU9gdti3u+Wmm5Nx1;ix<`?Vu{cX6y>(YFEAa;*4Vkq7cODyw%CaA761qHAtIZReYii40LaAPk!lPyME6 zSJ1l}w?n*mBHHQM+cGIhW9f5Y-8h})F4e=6$t2pujz065qVKFqFkL7{?-M?k7-~sV zLMH3Y1=dH8y)B1w?eZ>`IB?m3N3)#udwMqZHGtWP@)44z_fA?d-Iau+_%L+O0Kqob z!F&o{hJQHGu@&d)xqn8ldXC;b6+L++zhXswRh#H~m_3Zn^a};T9dg#Vn>2IJodg3}tdFYkAQiw>e;_#Rq{)>1D!~Pl$xwLs9;>8~xC*!!LZ2Se zQb|aA^>8QDoyl}r8oC0U;YI=U#(L8ic)(i9j|zQVesTc-81QS9Pdw=4Bn6F%ToMy( z^XwjJhLBf37rA;Y@Cg`+8obxih>sb z9+hXoSry(ya^Z(hCxw4D7o%m%i=aHd8 z@TsLW=^1s-09rv&lH5xY`=2jU5p=XJ)n62{*?k~vH72Bo@CeQ^sAy%R)Y>b{(&Q@l z$<7mSp<_IwqbQholNeO6_hvwu##5?0D|hbX$Cb3i`oIPCwnTwP>F)ezrd1h8=L!TYc$&!&3olgM{UDTOTD4Hm` zpjRezgIfF83oK{9Az^sK)%8=0`*< zzw)2#5yw<%&n-AO2>qI-pHFfVKah+2kamW@fVU_)fS0E5b8-CzX~~WRa9s_k#)K;s0WhnuIoKN_i}~NwJ7Ey+4}xBWPd4dpW%Icl|DI?@%c-ep!l_ z|8Bw-_5simA&q#iUum~rI-8~kAS&Z1xsPJ3Uoa9mwrcJWB3MB(9{$gUW2`2!bhi5Ez|eZ5iO5E+!JDG2|q<=PZQl{3mmU->zIL? z9HR7UF7QNza$C@n9|_~ve6dN8QeNoBRJp=FeFUv?cZ!_*ceeoM_FC9Ol3XD4m(=Cd zFMTB;I$Is{Ych|X)Li^tz1s!KHTx=(^a|PgGR0@V{^dLD3#r+)`ph?G_&T?wW)mF# z$4)8F$&}?i_)zyc4agh5b&R}q?k_V>qR3Vyd?Di;myIwcBKiKWFV<4G{1+@?{ueS?o|ia#0<=nDF4>F;q03|&Ol6c0f0M7$X?%N zT5Sf7N)y~Ue@Xq<(^(a^HWcUA5pHH_7pb|hGMf>8W=Uy2V9SZ1=D(#LaLSwOI^BP8 zQvd8q?W9T9MbuWp1ff~l)i~vj`TY?J)`BR0qw2drvT?Gd_OAftt0&nX$f*b@#0tR^ zA%$+=S^r0oO1ZD!(wzY=ulk=_@exel*5}oZ26i3Q3jC&yawq*>QCTE9v~qV^u3!;^ zv1UICYmOA*zfWv0PMp1w4A6k7aXafMEiUZu46Zj+C?`<_lSy$MBn{L$K^L*9&+b)mzP*nfTKF~9J8Y|zr}+}Vh3lHBlV@+qlb0oBjefLGPvD97V1|AHH#Jaq(N{P}M{ ze)mrT#*Oj?gZN)G@Y@vN6FILNKsx_IvHkzm58t%;4irYl-<4&E6?zV1aQFV3X?;WQ z_k&fKn!oAo?>6%@hrtdhKmRL=`K4q3%YN9y>#^J^FXT9{ zruSvbS9>XssF=HRZxOHH2;wr;b3DXw6un3 zTL>>qH74Qdo~hz*dioztZ}u2<#>zM4_^X@6RSWf*c999cHdBA3MBf2_vHw z!seag8yyJU*)HxACr`7<`5q%7{m(-(@#I`ezrxCvWOie{^QuM>L5&dnm3^b~wIL{L z$9WD5-?E|SeSB%9dNHY&!*l&rD5cr7vv8#R(Hnj|ESAW1aBXkxYK-8&<}Qew_i)Oi zvdA^K@NJ>zD_{F$r|mUroCqy!q=$vlpF{DjSs(+Ws9P6C*c1V# zamqHR%W#3hN;Vp@^^F8uzua*U}c(6~x(mYN0G$>9qsD$%HOw7Z?3;4a>a+aI0=se-6vE2ksZlnq*LLZD4* z%ymAUV?|E88T<^aoYf-vTKHhx5Z~sqkx%kUgRB&5BWhcR8>fmJ%~g}LLm7GmVx`4y zfaVJfcLSo$_^8How*q)rEuK8bFNu%yn0pm@7OTUIo}!3&Zj_z%WTLCiSVT-_=Iw&U zR>${$imaG|gd|tYA)0QuRQJA*9%G4K^jce}O(G(f;JmA^Qs3k)E>?~BY_)}q;3kWT z;x#(s?Y*or?Wn<)Da2<4-tyjAUwEQ2{hcElZ+<(y^|>QGA9d5=!mvhij)PCD zrsJJ>)6SARCep=PG{-#gi4Ml+;EO&y+F^I%ihtXP>hR-Bx|}%=9A}Z*JEc9#({0hw zq0|iZs;mTcg+CL!F;)ufvQ_+94`L z2-H%0mCbx1l;IvdC^2MWei`GT1sAb+ zGPstv?D2Y;!kOCfA}??Q_IrI#9IaRV&RF`Zd(_c^Ob5ckp7UXR&kRMIM2)_$yKzJkXkJv$E=lN0pT+`cC;h@jPUKdADr#>^I%jXFRq-5mEy089-%PGm3O9 zm(+Y*w*(vOOBb1SE?1duuS5z&ky^Tj_*_f#3WjT9Wkozaa3nNlN?&r-S){HTc%mbE zyBp&hkt`u^>&c2DW@xH*QuN?u@p#PjCE;h`OAqt%GtK+UkXt3axVZDxCM_TBiCdsZ zHSslH;=q)aJLMlUAXjzqjhA`a)}wF3L)1Jfu%KEvCe!C@!lRN&AB(WpgFo;6L1Q)%D`05r1I0p0j}Ok6x{@5zDo6P4u$-TK@?@#ye_{doTFB zF?DSzo4StHzcPB=@Vi{UeLW2b!{+HW=sk4!q@e?e zn$|WY>WDwh=MzKvZh43yO1{Gy*S^!g@!SSe5>q~1JZu-OFKOQS@gdGQYCN}m_n8rQ z2Rah2vf`CnEY4%(117l%r&}u1o9p{BGYW&J- z*28m_@moU$J(CcJ0y~2pdt4$kkOJm?O=`V;G)xD)zfrFpo}1yIhOyfa<<9HhU~!)s zze0qk1akSz6#tM*H0P_sC3Bgv1?TKP$8j=;jugT&{g^eclG&iXK!zzg-@!UT9dh_V z6?Phd%X)%tjl7bEx;5(_-uwBsVSVvq-MBKV!w>NRo!K*OR=q8J?M@4|{wgH7TJ4cH zW1P`hz5`gvEhF6Py-f*X?!uL~k$Y%-*t;h~j%bJ)7I_tqshl0oiaZ5@ zl?}eha!=Mcy<~pq3CEB3?qwLovaT>1AMR`=bvGpHZ>^r~y}Qm$-09S<9|_9h^p~9; z^N)Xgr>(FfcEh9m@PMKopn)6woD6^yqSw`91aG#(jL{y!R3BsiVVSQ1mU(N_EaR!$ z-YO=;$=Q-Wb9Hph;c>Z%sT)_wRZla_%eC)^(DYg!oX!3Iv<2>Z@xz$Q*!LTT@C^;Y zg+5utX`^`LXRIR*rHK=?-^hbM2aU;IOK_Z=67=c6;cv%laFO4~Ug6d>sOxpna|Rz) z;VP^UxNu`r-YUQ-&5=y=2atO4P&pE607IK}M>Wl8|s?<|x(q%RF zCaX#Yx-JiJjqWup!mqqqm3Dm)ykyy5W2&-vvG+=6)d6}^F(90uQJ2xhZBJfUes}Rw zAd6WxQ~FDf%~8S2g*IrNn>mlZTVa2grtT{B*|o++ZNHQ*Rs-Rhk~nm{ms{ie*rz?I z3zXlvs@I;~ucsUoL4>CUoc*s)^;0(sz$DVeIx2lG!l&jc9+l(UB%=c-;5;oe4ZEX{ zI>b zyjW{2a-l>_oLDc>oR)aSq=n2({PKZtjY4Oud4Xk?1llErU~GxSr2v#*0k@B5b0y(S zXR|Jxir0`Qtn|yv(}-KYi;?=%Zm_+g{i1t6e(|D^?X&)bho2NqU0-5RbF);)%)7eS zzA+J%@5UuXz|?h1(%!o~n(df5hd_P5KfhH@I7MYFm3B~(-s^tO9K|fQFmxsDNq=0r z-bQo)gB?F#zodP5r)ts@Z`DPm9b+#?rR}v)*o{Pw*1f<0^;P}R*=scP2@=lWIULGo zZn(&bKR?}|eredTjv_?7G-3;ila{>f&|DSo5ciJ}Cx0bPlqenZR25G=#Z0m~ zstjwyViY7@)QNC+xWqI|tup^*AL*T5eKknWPi=XxiPlCSq<^MbH0F<=46yo@N^yIREXNfcczPQsQJ)Z7=(}Q# zY*FC5UYq%+AbsWZ&j|+V=vO_*{9{)+dD>!@loY(YfBf6}pBq2WnT@KPTz2l*&EeIItf{-;O*%j0JS zywr+X|DUe}1|kOxgl3domHi(E>g4Iq7f*L*l@{FlIW0&BwSz2 z4OcuLdhXWce9*cYD+tQ*%&-;Sa~=^~D!x}w_@3#OZyZXfc3Buok-ufpmtNvJ=#Ddz zl?9M4v_3m+qT_j0E42DHqCyFdouI{`H0Id^N28cq-@n?=rL0mmF%!lMzBe)r#&@ax zA$#O}9(Yq-N-uTDJv#A*l9e^E>xfoMxhrpvaYP4j!8{}q4vRMKWT<(6gs{2)9y0rp zpExCMeXMcC%8y3U<+Jy>GKm2XJe&+wO^A8}n&?&l22Ua<8bCa-d%k-0vA$>!k! z#0u*1HOVb<)~37NIH+`e$1+Rh`HpSH7Hs;-fVS?zP0!_QV{1XEgiC*9bX15eekjHs zBBguvNdJB)bUNi6ZMR5sLAD77R*Q+aVd>2` zehK)biMbkkyW-CeSPz#SpY~4eL{=?~9Za^2^bH*@LXBv2_Z`8(Ucm@U2n^Pk2hVJ> zt(=e27w6SYb|tgTzYmIp^omGP7g;5q zEo{qi;mqtiWy|TTW4fXIz^FK>lhaCdY%>TN-$`wjH_#d>K!yCP?~_~+?-)w9bX56m z;~(iquZJ*!WO5#VDQuZ-`8~Yx!}Au{DsW;pnDqklytMakY%Uc(4b~Bb2M&C^wJD&Q zge;uc+0QMD4t!gLrR7Tw=S%c`vxDlQb>w*_PugGB(7I9ICM7M zh5o}pKv$fRu8NE-1gyKf&n@UQ<_rn|3X!g-jeFipBcr|;l|6rPw#mwBVIWsue6j9z zziEUk=1Ajv;?yaouj@Q$N)!_-C(=OMcKVyl&&VF%m9%dE?UG*9_pEZ7rAr7JpnQW2saX{Rej~RCGLJ931YtR75^mcuC z=K3-v@{?XyV9&lj^GhrREYv40a+^S9@MxFjQpBCzw%*Sjm9LDwbFAF7N(G>XrBK99LyITKsxE z^_jG2qlfHTEvH8($oe=6?UyQPqoZync~Kh!v0<&;=BTR)E@ec)3sF@EO5drPsATVWFOZ}{rW z@vM)j_*H!%mfI7N=jtOesq4;lbdLgEohbrl&!2&n7_h}ioiDyU^o`$ zKxMIm?+3)=`H}v5l6FOw+XH)nudg5^16%S1Xw8Y!Uj>)@Uyp|bn!hHj3{@#4PE$=b zPTXQ&M};z6q#euCjiEoT=i_CqQ$Byd0Oysh9G|w|&~?dd$1W|;?21&?iFr?uP%JS4 z!E2}Bi;19d;8F$}`G8ZlTRd^%MOnJfvMi>W`Qi<5QTjTwi5J@DMnJeEFIC$&?|+w& zg*>V1szP@Fzq00Fo*b2#b1))XrdsjPK|UmS#IpQb6CRsWZHKRhPcO&X@l#sH7*p|j zt<+>a(UBg4rkzWyL_=TzFxOWLRbs`(kfRcJ(5au0he!gNy_a>c(#%|TAZ6W zxf>tnne^u;c990VKDF!;%0ql5WzucGR$QC)RGYh7+t|wD`t#P2?8Sg99V(Fl>xkXP ziCs10Nv|$MX@QjbU<-u}%W)Rbpt%9@&XKJZzwz%%8}hJA7TE%D_X?M{8}Iwo0#|#v z_IEn7o(x0m_$d9wa;YNvJIgqMf$N2rmdsXX>JA*IlRCdrbtpUCGK*S|6v(;R5{-0H z*lq-MQI?H*D8`6F)ouX^n0jC?&+QUc%Nk9^{eJiNmTvkAqtI z_qW4O@k}ZcqgrjP=W=JgC@o9A@&>bjLDwTgh=ke~F_>DtymXYUbh5|ms+pXap@*vX zc18Ng_aId7S!J&z9Bvp#xgH#RG!9%Y9ZBCuW-_gM43dmb1o5BBNN_RZ7L3v{TN=zS z^VQKLnZEXG6GFM`xBoO=^p9yAt@4pGrlsep}9v{Jp zmsLwf4(G$LdZwqDepk}Z9DuVy-7r#eZLI`_dQ5AUXg~DLem{vQziQhrPsDDGHL1wc zZde%t$p`JI8D0Z$+;JW#iXVA#xrm=JC&&h4yJu7Xntq?X9SG#N*L9IBKs^8}awpfZ zjsvyV&nQaBkgZA<>9e0|J~KW$d)VBt21(-*{w>s8ew2Jb8I1Y#3SyK^s;=r*q;+`v z(_R1?C;eGLFk75P|4ZEPP#IYC1@y*VYiV&+E}BWkTShqX+!6sh*npKe zln<2qz^!#wo!`)#$_ilX-~39gffaGO3D;ELdH+O5sZRFyFSjrNCjSMiBK7IVh5L!? zCzhov2bM)*NL4KFZ1=+tq_d$;s9+4}0Gk)zr2%s(_+MQ4vL| zSm;gZC7_3cg(|%V0V$#P5=z8EP^2gXq@xm$8hVEa5s(_BgyUzS;|CXkk-1veQ1Z^CDolkd-E2OJvyhceJ7 zFK#zdr7HNGf;4}Y)E*R}B%UL2eUqpQTn6)KJ`Hh6xpFv@%$HzRcBQ*~;31n-hS_cp zlWWrX5WksEy>u@-4C|O%S`8Q~Ifq1?hl(BLFl8e%#k5Rxu&FZy=NrHJer?b}hNBPTj3~s0&-+ zE*bG&Jt5MpT+X7#TOa}L?=gFPz_X>Q7|4tgMhh+SI{4Sh?))vj@Bu=LrI?{&X&Nn% z^?g}NQi56@TZj!z*6fbLFVu82or4BT3%HN_J+9@3&S~VY73APmshs ztlK5u=+;tVH8pJ*Engi=mWIXILy(=;BI*{b-S2)%$8wBIplx=3Ic|P0GtG{hvO5GF zJ-Az@IDKiLRr>Xv1~R(#p4?haF?{6p<%sq*AsIoeIvJ(pv>=|Zy?pYu;Iid7>B~zo zL}_O#*{&|?venMI)m(5v{YJNASe@Y@+S3+uXQVx-;%^~ehAF^hBTa^9Xym;=E$zm= z`YNqpNbfNIXkeP5h%~fZ-zaD@LWSH^REB^du#25)jY$Z^c-+i4QFhm}g-Sl;^-=}P)0IlMgzD-Y zRONOM(AUA!pWaWuvsfxLOelvwTlwHvcIA1yQ+_AGdPP8LZYyhRY}oELPTZkyX+k$( zx8Kn*va%eNU4oaoHMp5YNLrRN-ziM>9ceIMh!Hsn2QJ9QPgV}z#Q_zVRk6vH8FlqF zk?nOr%k1r2=HmfT%zt`9w=+Ozyu(4Kb8Ap#JTE+$ErrT``@@gFD2FzwON^+dz6h1p z{nja4=CZ`D{@WxMyL2rMYvv}#B-VONSB={Gk;855k^uvIE_C&S?M6zom4AwOgAs0N zw~DPDoki{|YUyXr7g%J5Gt${}C{|762ki8ltwSrkfI1*ngZXk+5v)B+V*y*+%F!Ll zt5w=BjIy-Ov~p}7u9u@iiXW&R?(m^c9eMPuTnke*gm(30?rGj4h^NN&Ve{Ny+n2a5QrjlG43^2gJ-OxR zzXRq{GD#j>z@>o#!-z(Y<@eWB0Ck8K-rr33d%>)H@UjPGInEV=zym#Fw5m3{VF+K& zVB#AD-1)CGd~*9571TlMsb0W+rUEsFQJPczeBywQ=H7Ig4{mbvAtFpZWJxdm)IAOl zhhP(oNaff$pw&;VZggSXHWRFh-+S5at5v=6Uz0XHf>bWmaKmQddj-}Xfun(^%Gg7> zKs9hn%HAa_pjL|TiQLti<+PJ15T`nZ`QBQEQ`L_TXJ|^<;ynK@ttaT^@|~=i06m^0 zBJR-hc|DmmBk87%opH%y$g{bWfO&{jtR^%JTrce+9!>pM(q4Ip2F{Zmh#n;~GE}Lk zPdX3&*u{h~K!{b({`Ou^(Gwmx!>H_UfBW*$D@BZSfS>(LMm6^D`x!^ZoefA{I1aS{ z6)%7KE!5{B(3_dFP9W*`UjPE1dSZx?j{18({K!b-;?q5?6jrhfxFZg|ugEt?x1$I8E`!{_0zG3?=Ab)M}S7=aM;>^x)^&d^G~j z_gP{Mh44<62i8?f?ds}t|H>gO7f_tbYc6Q~Jis^s*a#h*tJTYM0r-!Bd*`zbvR-C9 z$f@At#IKn)I50EzTI}fSk(&M)kJ%PD5XV;(4?2ndOz(_2V5TB6!e!50$7>MFW>xN1 z0i!2>d8_^u;P4eIjOXZWi39?oH)6aj^-~7(?3EtR(4`;uJ34fKJ5B}M&*jIJI1iT) zDj0rVl-q&lfYq|`gLa5i3Rn6McACt&{LRrjQq7^Ht3V#HWtBM9NUcH%305ySY~7M6 z|G&+l?zvd-;%nX1BY#?UGs5x=4jgFnPRhx58vl^z=P5mM0j5;Iuyt{8x6qICtZ2iJ zCS8GOkTfsg6`#RuVznKtagpf1V>!zZi;SLCeza4z|Y_)+`ddVSn4smo8-GT?@sZ+!w=fg4$v zoecxb8Ew#<#8Z=H624ddCu(M}0KSOn@p7pM4#0Ku+yIzE$Zhm5#_fL?sqE4bhx%qG z2%G4-dH_bTeN_4vF~X|?0vkEL2%L|rD8BeBvs|$RfCPce>dOK)FUBqZn5Z}Vzc34E z`o&A+MJ8@(N$-`R;>dEa*WbL2BfLZ|0gO;>Wp$%74a7GVuz*(EIx;OU2J)`{G|eZU zYp(Y|EF51~ND8}mi1z-1jwb=8ek^Y z+xsC_MAzD|2+^7=fVDZym-bwij6G6Ee<8$I6JSKj9kaYD39O=sbIAj2PaZC-z~F`F zFUY(622e|LEbD9-1AFgX)7-?r!ub~q53&H;XFkCQt)GSYN=zvrpxqg-{t{IE@s5K9 zK;ywJ1Npz>1;D%A4T!4TGr_2zPzEfM51<=NaYld$EQ zZ~<73%v$(${fuU)v@ifJ%q=bJ7o#-t5g=4x-E!eid#ix92|EI}-qq**^W1I+UI5rC z+HYLyS8zwRlf5Az>6ayOhz6*O-zcoL`vn~Pk(F-gsy*CG#@uY>U=P`kwR~<;YA5rN zCi`#3@Lw~Q1T=fzWzSUUl?xYz%~@Tmrh(MegY4&t682IbFZ{Ow_l^cQ-s9YzaPN!6 zJ)g#eg;je^(xZ2~){=Ez>HUQ}{}Ah>YXG@8%KfzxMA_lmusvpEDFqOorVpwSNhWXm zejPCQJ75Gg%N*y%9rHvw0>MEW$p503kA)5%nbA;}O4sprd!QaEy3Xa<&#QPlW9vxa zJgRLxIS?dd)X?|zZHn42{N1A;x)iRVhmAoA_W-SC2qeN>$A8l}G^X0XZGRP5V?BW3rC9P1u(r3z=?@Vhm*%^JOT8awr}~}(HdIOsMcsZ; z9W;dVKjMt;;LB4wDBiX}rhZf{fdZejyihUjs~?DTAOA%{exo7wIPz?&FoARhr=K%N zUFvjvkL>#eu;!6veWgns0JI2a<<)p+EH7`pvwqTZSo4=9ipd5<;f&;wK~So7!ioMD z8+@Fq0PyYxs78+r!jyCNFMh@;$s^vCbIj{bREFKzT>(63k^f?R&N_eq8eJUHN+Rm+ zu7py{i2biG*^P)=HYl10jx!)jf##3tNBL~K`0tWG3+1s;sdGQdcoXLe&in5z+BYF# zbyr_=lAR%~5BP%5GyJMN?;J%Z)KH+``E+q>%X1zxdd7tPRU@lpmkQUr0$Myj>#?&A zQMZB3DaY=YPMax5F`lL^TFwj11o=AsWsQnrpNNYyQ;{+ zWD=ysO^ilRHwOejSp3-|z**x~Jht=WQSOZT-R^(nl*b@0xh> z7ik!&1QwdyKq*Z7S(ba|kG4q1MEV~6Oy1k%i$B)2G+ZJd?Y=^|6tw-iZET!PBk$u} zdyq2X+JVc7e$w*mJT4sN>*C6wzv?4TfKA(9=W%&_t(q>DOid3iv=mm+rp!XFC6^ z&OgldkL>)T3jUu})Y0??PySsC;2)*^k4gQG}Ga=b*|JPSSXd z+far|s8|e~(C~z&L!Jqk8$Xc$Hs*{KEeJ;H9QM zF{}ul(zu~8keuE9KxF9VJW{4f%q`hiaQYgV@$@}>O~4s7FGn+$ofS832AgzPegA$( zLyhH-i!rJKD!@=d=8o1p6Kf~>$t$RGx*pRwx{WHGWc-uu0#20;KN;L_?!d%iyIgA; z+XhUyxfCRXBkzQ}j!WlGn5+*nZ#3>Hi**VNKs#5THC+s@8FXljkjSMt9eVXmXu5yP&<_mm69JYMP=-5qGyc80=lxCGJBAxRl z8&sc2YWXk_Vks7JCHc#h&EZ!31;$B8DKuc>WjzZla68leo6oEhhb5~UKG>~}b2V_k z+`X+!?D-k3;cF|(xo(k)SgnA(v9K5F@i^H*Hn(|UW79%66ziwh!L-kvl^5Y?kYaiN z6VP2EW1!mrUu8@FYGqJw|I#F3!7{ZHe6mcm;hNOY-qiK|UZy?n^QO{F{H;*XL)UeL!lD%hE7$#Q*nZQFI6 zw=kz(ija=&q_?-&H%{%~x>aTGZ#z;)IINs7J+HL!k&J1!Uu6!wGPixjqA(-UCG)mt z^eTJ2DG1hB!q)it@z%hk(!~~TDZ!wP@PZ}t;bB-H@0hn`R~q=E4O2|@nfT`k)}IBD z7xIa&pVy zHIEmR#Qo=-N2ub7!H6e=A+;`GoBLL2x1I9x3udV?bJ=U<6}d)hjx@d0lQLq#A5xrp z=Es3!G7jV-+}DZs9d|h~z-i(p46`NEBxPA*dTYpwABoxz(WsiOrG=+i3`U?Vc1LjD zv;Es{oJs+oiwKaM%DFSBt}_c3+ve|f2+7EUgb78Sl?!ktLq?+{MSH8rM}tf=;xicE zF=6kCB1hQ;GY{4ctXt~YLl5kC-OD-mqMjHg4!4+|zn}h%h>P!)!@WMk^g5BtcO*-D z=*A&!!U&|Js|Gh=5CU6O&V6c>@bF_T1 z3T2+{K{tTp;hZz>#TIbak*UiLiu4?FD#ALd-)4@-yuGvr*rA`!MGFdV4U;^ zQk<}K#wE_S{(A@+%&b@!!??^UYX#?!1RJrNQD$+gNmfR8>2#d#k_VB@8pLs7A+%k$ z(qDO^RD~jLU0RR1*IkQ>lVMP=+bO(+!A+nw<3%z9;=!r4Rm4M(g7d}R00d-d+2a#9 z<(eRpc$woH+H7vR*S97uaCYI58i#JRVhH2CR@xp;n~4j?d8tV=ioQ!`R-XIx*>O;& zK^g9WlwI|vXJYzYXUZUT%CxSd`)LnBGaP;&XBQUTEGNWL{4ieAYkme>D?5X8+AnBB zyd8GQ621mZ`Td30m{`wGa8nN>1cJ31Hm5n8l(~Zo`j?j{TES=3XE_22KrjUo))LV^KTwencg7H2aB}`r3Gy3lZlia_8zqT!VXZ*H**vBUBR8)C(+b9Cp7D( z(F%ooWy!T=b(`or5^87aHR1&fLX@Ikdp$u;RujpH5cYd58Du51t%7qInrJsB?G$Fa zFKNElY(f^!@j!mX%jBqR=wvv0 zZo6Plgvo=kS_JARGHkoe<75Wa+`c)Ejq8=lo}4Atri3YHV|Df>UlGaXliL;Ey3B_G z*83?%_vo7rrsF_KF}3m?euV)Q7al5+dd^Mff2=sylbcoipYu#Tbil@3`9lwRc_gb8^F&cwxaC_@s@4X;O(WWpS1X0jmhhEwBCmt|1Mw)-9hq&wo%r=q9M1WnxJUQ0Dk+7{p(lOs(m z4OZJt5jJ)n9;9}p38s>f(XQOorc%^_lqi*_-8J7gJlzZ8JO-50K~VjjHH;JEsM9Mi zy0?d;I7>P%>EbriRRnO?A#nY6PH0e!k=iPH*KfFw>vq(;$dLm%_ z=ILnx5fd=WZH-a47TTRXUC;X6QQ8;H9l3-GUvVLmV1e%U@h4+jwx76#eS9{ zjjN%h(8?}Y@hc}?meig0IAzHteCjS5jW`U<(>V1ig^(^VO`>Rs;L&@@wDD+KL^@W- z9xW8K&&SkXJ!tu5cbaje&{FoG_vUmXUmlf+$%)17wHbfTkJ4*z@Pg=y@1@C0`*-() zgA@<7470q0l|ccOgnfvBxR0-@c6*#mYHX_C9FFOff^R|6sOX915YNrc>5(A}igci{ zz*$B9ydJ^q%fRmYio4`p(w-R@Vm#?rR~#770G5Je-Y{dTPRMK=gdDc{P~O1XshfmFQgpO6e%A*TZn9?2zy(VdS(O z-{F3vxB|)H0R$6ez@##~(T*dE55{@#2FJ3~+aWjmq|Ch+GTBs-+X&!Rodp8tiys$* zpK%R*-y#P&h06}veJEEH$7qtw?cnU4`b`4z<#1`K=})g` z@8=2@#o;@^ec$nmq4@#=TcodC{(aZyBe%xX=kUt8mct_DJX;^WUPDfeT?#q4UoDc; z*-nm~rcAn^R)Dl(sW0#Q;mw zjZPYwENCZXP9`x^)~3bR@N?!r1?K6tO`B{=xOz(DdU)Dv3Ym>;)GnW@f! zJDn3a@Xex#$3pE++p+Kz{>R4+z5eikGCx|>`azafn{Bo4;SvkAD#TdN2BJgXzW-y% zzQaU`laEnp`pNtEmK?6KN?QCu^K0BR;-}%F&B1t<34ZUkA4NE zhuO5bYZeNk`^#@2$XQqf$W%+&`SDJFC`JvYT)ntU$XrLud98Vh#d%V*4OJTH?7z z(qF0QHBh1#nwX--i}mk%ajNs7_V_??tFpP;JDw@5K@Ka^W!D*tALLZy8ghpS!SZ5Y zN?^qrH{7C|3DyAftlE4JK?>)<+f9nQ4?)XoEU}1K=yzKP`JB%}LR*wA>W$oMwg!m5 z-ERXJTeho7VW-QlBj2YW8mmzpwJJGAV73pEs{;Yk>Nmf}KnE;BmcJAYJmH%D@r`;} z9~a|Q)4EuysKgkc8IYEAtFootS#MrgP#c;K#m?13ZP=)ikdWd+VWd?YL@T`*Ya|y! zd6kXfUQ4<#QQ4Z|{<-1+6IE=aXLI~@eu^TvuLy-TqG9%n#;9V$y-wm4ANTe4%$AA< zUe!o5g-zWGuGHG!!pyB1%5R_9*yb&#EXoqsns&xDg{m6lgYWE^)3aJ0GzWKRx-R-< zm_L+|?sc2=Q?6Iv#}6qo;)1WGTnbFelFpiPr6W048mzhR1y<6HHd|pU6?aW{hI1$T zzQ;h-Z0pMiJ1bdOQez+kouvXaxd+|aa?@QRo5NfKzuWN)^}LU9yo3m1XAi8OkO}dX zwJ{J^IH-W_2Ako|-pBBlPKJ74Ik?-PaCp~ywd@XusY3P`hAs{iP3Zio{7ay+Ud*J< zWJ7Hun!UBI#x$8YMSzRp*?Wr$`;^LD7DM@fN*`)oBSHDrjM>>UD)#ZcW4b2tcBn?= zBSUEVe8OG`Jt33l+NG2V`<)+3W{-zWiO}(n28{{&_od6kyXq11u@6-A7$+-p(})if z3@6nDy0CA#?&_)4f1ar9m`Bo5Bo&23EGZqb6g}v>ni&`)KUOA=>oqvsO9NT9R!H3x zu)!mm9DAcnfV3QY7oN{&YU(4pj2tj;)m6A?F!ISG^}g|0yLcno zee3Ln_{;Xa$#!N+Zv^F&WZI2ravOm7Ck0ni!}rEZK#Yc&)LTf$~>W$P8`1 z3#1y=<{*z!!Kd)WP3ZK?kC;Laf3tYq+c&m;CqL%6TZ1!lWdf!*v~EuSque*??6dd7 zk10s2W{)3uSC7^F3lG)L$nWoV20Ibs^PIJloW#z^W^#pGYRMpqZfw5k*7S-$xD}7s z8VqusVdr7c?9}C&49(A%3I^>Sd~BlKW;Nei&1@rSD|_lkUhM{jWcOa*&w-mK5n*|( zU%GVXwgscHu551uLQ=S0rm`&5Hsg^bj_tH*6CbPI&fPhDma{WETH;c$)!KTZf24Bc zWU8d4aZ-f%c&xtf9z$nu6~rJ9Vz-t|-ivO{g?02%R)Zd+MARs?Zw@PGcY@*1^`eF= z%!v#h{6Q*q*j;fp2gP(GSVAOfThZAZhig=bRnEIHlgU; z6o1#Hr&o=Z!+`@rMwG6T!g4eg8@Ml`u9;n4D}QxN&9m-}TG*WVzUPY4RB37K%Q1WJJ~!3l#}Ha5|f!of}G=+uGhe@ zDf>L~5f@ch*j&4;!=<&OzanB{sa8MZh%K#fOYss#BsMTDZYv$Q8N1}|wVU<@4~t`n z7UWeJz9=iZL;F7mU4ha?%c{49KpMeHj6M*4e5p@*7~{Y&#gyLg;2NXQWeI^ociF8j zwoS-D8;4(zY&W;}MkF?Yi%T~;(u+d$UKBgQOp0U*y7y(`p+HaUy(B3&k803I>$MMi zT)MqyF)PzDc@m)Q)C=%l3#ivXkQ&6nVo$yPwr`|LRd2f)ym^2{h&#Veyj0j$#iwL@ zt3JzsO{gzP-Y_lM5DG-Gfq2AR=5m49L+M+LZ5_JM9W#q1?q@GepoK5%rNFuG^kKhq z+#T*E{@y$$OKwhkkf#p0(J&&!Ke&*Bh^4wkZZ@FlGdF-tpzLSb;fr1=VP-)MNW8yfgw6Z`IkoWK<3t59p>X+)th4miUNwl3Q2P=bZLZvbpN9&17I=4>c4H!ZAL07%A^P5iNeUM6|XU>x{q>@0-6oUTWUELhIC<>lo1OB z<=L@7rY34cl4UKq8c=azfyA5=6^IB`rr9xZ@Ebr-DOD0-0Zr`dbS z`ZLNNr#1c7j5i*2%>qkpla+8N8h$4!Vzvh(X)b~^vNOP1^v6NxTLU^v0Ox#VEDUVs ziDy70;$hhhiOr?Yw(3X`UZls1i3XTkrP~bkTKTLeD}9^qnU@@%eLnYf%@W9|)Dhs>7 zeLwe-`YN!#>pMSG-`W$NPzU5jVrKUBg#vS({Cbp%TOxct#UnEg2X5>VamZ31+q^| zQIr|mCUz1y6kzD(IoV3X1^2!8*Oo!(9EE!=R*y}RbiPI07{v`aEgg67tqp@~H&?s# z(tNeCX6Fu`jwGmAK3tpYoBnOEbQgNsEh<&mJ|(oKe#7J~%h3 z@=I|^-N>j&HEG1QoOrOx!rYjh#thNQm&6&VRemvOZP?7tMf5NZdp+FINipK@s*E!% zP#5T`)E{OpEb|&r?l9EX46&?UD}WkSt21pvuKti5_J#H|5Ie|*X}W8^={vh-eclgK z96*6FX#56l=K1dEbF*2e_}=eR0ona`R(oZ?1;&%2H!s-=Y&|q7smrA`ltx?l3fE_) z2?)58@27lGNPeQsfE#ss+!yFz7>^qwNk0pWLo3A2rWixQ?eUTlhg|&$73{hPfZoj| zl64j8O9aCaB)3cG2!Zl>zeiy6Z)M>?F?&Bv)Qy67CYL+qz0(_S-mfuXWeFTcgiLb3 z;}HGy?sKqa2Pzeo*Og1HC)Uan$$)HQX}wF#BVTs#ahf>_} zeJT_#Gm_{*Dj`iog^g`zW0PB$cCz>SHst(w3pk0+2qh>9dM@pAqc&c?Ft?;zoeKxf z?y+0FKOeYSW&%O9>VH{)k&UYawW!jlLLs*n`y&Jj!s6$>Ms9Yu`C6nb3#bPUB%$&> zjR&cj=lf5YW_oeuu$sO@yaD?~=AF%YV#sDKbA?oBHs1aku^{cEz<^b>o@|^BgJ}zF zog(|uyW5>P!2#qWum~_@sS9qlJ|^%aHB5O>Yg^f9+@_Q;B)U=SGndO5-8$u2VeM#}c2mrV!i7XI z>p8ahWvFxH#&_k!8M7|_3q^%xvJ}OIwvn}>yq9|Q z?mXq$-LxEX3#W={9FK@&4D=C_tFWAU9D*Dpnx*R|Jwi83_XbqtRwq@xarVma%^G9YPb+! zKaL*u5YNd_fJ0X;SY^%f`v{tRx%hFoJH>_KZ;#ftiT0bqN87fEN z>YNAcr8?5VdXKI$eAE=!5>?MTS#h@=s);P=E1J1*Jy*j9oV$8pT&?V~Rwj5GQ6UCI z=`m0#!z_Knq;!6D1*5U$`4HN$mZ=Bvhiq^|qo|UD0Z!%)uV~u<)6McvSl2lz=a`yL zg=*TZSFG`p$|s{s%50hs9*}h(Ev_1*G5^ zd2?LC5SB0F_w+&H3@;lI&gI3#3?TWOHBdJZWxFq=m6ggMqA{>i>4-o{4sXAqr3Xsp zP}COpqU6^m){#}c7E2pj*7WiFTgrOvm>+MQq%#YuqgWD#rtG{5>l>R){U0T;u9Jyx z%o3>1=X~nF=9IouX+vN0Itnz~dwqC#;cDK}KK3;uq`SOb|FZi7P$7vt!a4o*pn@Ot zd-g4MHTY^(z|QPNF~sUNdPeX$pQtgLR?zl(j}S=nm}K^jgncGf;B_ieK?Iv;3~7Af(4e2<*Ls5%OzJi+-k_ zzFW4rVQ(KsjSjFM3WOgPS6ZROMJm?PhR)pD@^9;H@c8P!7c!V|xU-g^lFl^%ss^0t zx$UbADtRkNzupr}FDOu4k8Y8pZVI*Va-xZeYeg!rB<1rR6?M>LZv(Sf5P#73&sIpU z^w$wL&HX5G!cMZQMIuhbCg)YZE5W>_1EPKH5!=cV5*+9G4q(!S@revrVWW|DcGX~C zA+e8lqicrx)>~yFnUY<4k^RgBWRHO zL|658jhbHW6Qb6S`sbvs(l%ETi6olvef8}bY=_ti@YlHl>ljzs?p8Rm|o<|yY`IkZJ+ z&v}0h+m7c-KZOrQKSEVZdFj@}Ffi7;Xl!<_x=a1rlA3rH2cEc#nkBaKqGY|CXD2<0&PI(4$-|qRUCVZU8 zsTMFd_pwbsL40I@cdb{Y5dK)D)-6TotX52&&1M;$b#V(`LJg- zrSVGv?^{(%g=j@F10d2&&sI`+Si_Zv6~jc6t6c$5=;t#x=ANGaxMgy^3l{^<%7WSv z%l(O}+gv{$etgoYgJan{jggYjJH0J#p1gkXR%=YJ^v(}um__aUs>*%Q0z2;k)p~9i zg93>U(JGW?;?uwN0c#ksP~~H1WQ2v4og258v;GhnxB!8Gygv>O_$Aq%x2UDNTd`}) zC3d59PygJP3ngqJCh!%H|LwtZjmSs9><`|FkE7!9UTHsefzKwiw3j;qY(+9J> zaoYn4{ty+IAd4@tz444B)oznlhvQj7iw}z5%~a9)^W&=Xd8Yl{sMAJ+2cI3aCha+A zoPf10cQso6Cg)qb=~Aft60(ym=OSv85cVnb%JQ++UcDX2iMxk0BK7_;J zL}!LgT!LEpPF#IId}zRBTr{R8-n1zTOACKL__Y-@Rt(Wud{}wKP^$FS%M}w>5ftv3 z?QY%WGJDjn*X>EL#>>~SQ2kqWX#<-dYhHJ2gbNs?e7>fWvN$*26=DH{*xjs%hOV!#>&H_Vr<^?P0(t5lqs=P!_dXvr5fp4K%(!S}2c@ zeFE?Q&Xzb3{QNym=cb!t$2V|M4GeU!AFf7G6p0v$Nk%MqFl>EPl}_fqGdA$dekw^M zdoJgeT3n?@;f$-Y>$T(Oul(l;{d7JfgT=sFSer`cy8G{4G14}(H(kU|iM_M96_Z~{ z=TBK~VKz-rP`1>N$I;~<(w=uBA!uf(LcP@1_q`|3B`#|LQW{&}IG_v2=}?N^hXZ)m zt5Lczo!r+i{wC_58IIGLx=y(>pH?iS@y&gCv86!qIxenqm{fmBl77A0lyFB1F6$I6 z#LXphI_B8lpW7?v!$)>1hVVjWe8MmT%_Ddg7ht6H2N%sqJFV|CeE3E*@u;do4n6PWX$W`j5r} z{<&gGld>QkZ74PN@_%{b3C+IBlc(`8*;1CjkorF~Kl(@P2)P?E`C94b4by*jwt*Z7?^1D;9Ye@+HDbHifb|3vZ(zc$Mk*D_UU22ow~7lugN@qwF_;$%3C?h1M()9{~*j4 zTB?niE|2Y15W$Layu@}{I@`;0b6)d?vqc^LY6e%Z|sc?ZHXh)$%Ezq3oxDqJw5+cU1W`&Y6 zGE}+@bnjM5)eCuid7@CPgjAJE^WnFtSZ&&lozu#)D}?S*3#1=v`(Mj`{j37b2hV7w zT{N9mDm^3JTj4|rg*l09^E7DzS#LWN6|nC=(6OXoLsgC!emPC0Udf63lK;4kSv_H) zqg_U#((zzKAqQ!+XO!QikLt&${h{=)D4k!gk9feEM=lz3+00#hChqI6`gXx_) zX<6p*rd7Tn6@>}~3VODGe=s{oTznCD5qk#|Pl6?!@o51zCG@UI96G4ORZ_ zK~4#TK4B?4TQn%zLK`xWqU7@Su{1-(?ctj?txlyU6B{4xOsB(K-m(qkC6-!bt&LR+ z?et!bWi=KRif(4`TMJX`&3s@TWWIhuz3}Nex!0ZXEuZ#$EN`JDS`?6PQus6FD5)&k zmoLw+PNGrWF1_yzS)9n%=CvJufbb0w5I!zN|0}X?;U6z0zF#>YGu8EquA;6#I3Flw zXL)73RNjer8H=rY+~A)!mG(^4&y(6buxj;VExK~o`aI(8l9v?kynwxSNA>cVrBmOr ziz~yWwF-6jD+6Y$*CIC`;rR<8iqt_{yDl!+^kqL+xaMHYEtAh7ND`lDh3X$90N*%U zl-|InomO^YW22Q@KHP-=xxYd1#F~K9U_Qda@!f~vC@zp4*ynyai2Z7*9Tox9nQISy z1}qw@u=c9CZt+YX;c3$N!eVxsH&jP;eRpFP_Rw--RlYwPB^mg>I#ZMV;?CZ)khObk znd75d@3}_@UdNo>J>2(-y(q~YK*?Kwu1I!f5jj=B2Xg38Iu0q=NLxFfPB5-fvw?+EgCCaA#MnpD+aZ<5LFmdN{Wnpzs}?*ol! zJNu)2VmXvOn)s8IEtLWYZFB@eM;Ee*dCuaMl`r0Jwf|0%w985lqoMfb=!%>dCt
  • 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 bitxkfjd5lpC_2z8Zi9*8nNx))ov-}|Ul>hb literal 162003 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 P7=Xaj)z4*}Q$iB}tu)2w diff --git a/docs/user/alerting/images/rule-types-es-query-valid.png b/docs/user/alerting/images/rule-types-es-query-valid.png index 938662f49a063b843e924f62a03c18a7e4bb2aa0..6c63f777dfdc3827753370e4eb76fdccb578c3e5 100644 GIT binary patch literal 20416 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 diff --git a/docs/user/alerting/rule-types/es-query.asciidoc b/docs/user/alerting/rule-types/es-query.asciidoc index 2f5e53b7b342d..6d7c24bcacf07 100644 --- a/docs/user/alerting/rule-types/es-query.asciidoc +++ b/docs/user/alerting/rule-types/es-query.asciidoc @@ -42,6 +42,26 @@ Exclude matches from previous run:: Turn on to avoid alert duplication by excluding documents that have already been detected by the previous rule run. This option is not available when a grouping field is specified. +You can optionally change the check interval, which defines how often to evaluate the rule conditions. + +You must select a scope value, which affects the <> that are required to access the rule. +For example when it's set to `Stack Rules`, you must have the appropriate *Management > {stack-rules-feature}* feature privileges to view or edit the rule. + +[float] +=== Test your query + +Use the *Test query* feature to verify that your query is valid. + +Valid queries are run against the selected indices using the configured time window. +The number of documents that match the query is displayed. +For example: + +[role="screenshot"] +image::user/alerting/images/rule-types-es-query-valid.png[Test {es} query returns number of matches when valid] +// NOTE: This is an autogenerated screenshot. Do not edit it directly. + +An error message is shown if the query is invalid. + [float] === Add actions @@ -155,24 +175,6 @@ Labels: // NOTCONSOLE -- -[float] -=== Test your query - -Use the *Test query* feature to verify that your query DSL is valid. - -* Valid queries are run against the configured *index* using the configured -*time window*. The number of documents that match the query is displayed. -+ -[role="screenshot"] -image::user/alerting/images/rule-types-es-query-valid.png[Test {es} query returns number of matches when valid] -// NOTE: This is an autogenerated screenshot. Do not edit it directly. - -* An error message is shown if the query is invalid. -+ -[role="screenshot"] -image::user/alerting/images/rule-types-es-query-invalid.png[Test {es} query shows error when invalid] -// NOTE: This is an autogenerated screenshot. Do not edit it directly. - [float] === Handling multiple matches of the same document diff --git a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/es_query_rule.ts b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/es_query_rule.ts index e1a0bf73dd975..ef41d15fcd6e3 100644 --- a/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/es_query_rule.ts +++ b/x-pack/test/screenshot_creation/apps/response_ops_docs/stack_alerting/es_query_rule.ts @@ -31,26 +31,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }, }, }); - const invalidQueryJson = JSON.stringify({ - query: { - bool: { - filter: [ - { - error_clause: { - 'host.keyword': 'www.elastic.co', - }, - }, - ], - }, - }, - }); describe('elasticsearch query rule', function () { it('create rule screenshot', async () => { await pageObjects.common.navigateToApp('triggersActions'); await pageObjects.header.waitUntilLoadingHasFinished(); await rules.common.clickCreateAlertButton(); - await testSubjects.scrollIntoView('ruleNameInput'); await testSubjects.setValue('ruleNameInput', ruleName); await testSubjects.click(`.es-query-SelectOption`); await testSubjects.click('queryFormType_esQuery'); @@ -60,11 +46,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await testSubjects.click('thresholdAlertTimeFieldSelect'); await testSubjects.setValue('thresholdAlertTimeFieldSelect', '@timestamp'); await testSubjects.click('closePopover'); + await comboBox.set('ruleFormConsumerSelect', 'Stack Rules'); + await testSubjects.scrollIntoView('ruleNameInput'); await commonScreenshots.takeScreenshot( 'rule-types-es-query-conditions', screenshotDirectories, 1400, - 1500 + 1900 ); // Test a valid query await testSubjects.setValue('queryJsonEditor', '', { @@ -87,23 +75,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 1400, 1500 ); - // Test an invalid query - await testSubjects.setValue('queryJsonEditor', '', { - clearWithKeyboard: true, - }); - await queryJsonEditor.clearValue(); - await testSubjects.setValue('queryJsonEditor', invalidQueryJson, { - clearWithKeyboard: true, - }); - await testSubjects.click('testQuery'); - await testSubjects.scrollIntoView('ruleNameInput'); - await pageObjects.header.waitUntilLoadingHasFinished(); - await commonScreenshots.takeScreenshot( - 'rule-types-es-query-invalid', - screenshotDirectories, - 1400, - 1500 - ); // Create an email connector action await testSubjects.click('.email-alerting-ActionTypeSelectOption'); await testSubjects.scrollIntoView('addAlertActionButton'); From bbd4800ac435e0a24b243984986bb92692429b5f Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Mon, 30 Oct 2023 08:41:54 -0700 Subject: [PATCH 14/45] [DOCS] Add maxScheduledPerMinute to alerting circuit breakers (#169991) --- .../alerting-production-considerations.asciidoc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/user/production-considerations/alerting-production-considerations.asciidoc b/docs/user/production-considerations/alerting-production-considerations.asciidoc index 59c8a4bfa6d15..90e687a8f39a4 100644 --- a/docs/user/production-considerations/alerting-production-considerations.asciidoc +++ b/docs/user/production-considerations/alerting-production-considerations.asciidoc @@ -72,6 +72,11 @@ There are several scenarios where running alerting rules and actions can start t Running large numbers of rules at very short intervals can quickly clog up Task Manager throughput, leading to higher schedule drift. Use `xpack.alerting.rules.minimumScheduleInterval.value` to set a minimum schedule interval for rules. The default (and recommended) value for this configuration is `1m`. Use `xpack.alerting.rules.minimumScheduleInterval.enforce` to specify whether to strictly enforce this minimum. While the default value for this setting is `false` to maintain backwards compatibility with existing rules, set this to `true` to prevent new and updated rules from running at an interval below the minimum. +Another related setting is `xpack.alerting.rules.maxScheduledPerMinute`, which limits the number of rules that can run per minute. +For example if it's set to `400`, you can have 400 rules with one minute check intervals or 2,000 rules with 5 minute check intervals. +You cannot create or edit a rule if its check interval would cause this setting to be exceeded. +To stay within this limit, delete or disable some rules or update the check intervals so that your rules run less frequently. + [float] ==== Rules that run for a long time @@ -106,4 +111,4 @@ xpack.alerting.rules.run: connectorTypeOverrides: - id: '.email' max: 200 --- \ No newline at end of file +-- From 9fe0eb9aced2c44c7c46ef58dbc136eeb76b2a8b Mon Sep 17 00:00:00 2001 From: "Quynh Nguyen (Quinn)" <43350163+qn895@users.noreply.github.com> Date: Mon, 30 Oct 2023 10:50:58 -0500 Subject: [PATCH 15/45] [ML] Unskip flaky transform creation test with runtime field mappings (#168546) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../runtime_mappings_saved_search/creation_runtime_mappings.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_runtime_mappings.ts b/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_runtime_mappings.ts index ae6ac9adab2f5..3f0adc5783893 100644 --- a/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_runtime_mappings.ts +++ b/x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/creation_runtime_mappings.ts @@ -34,8 +34,7 @@ export default function ({ getService }: FtrProviderContext) { }, }; - // Failing: See https://github.com/elastic/kibana/issues/166395 - describe.skip('creation with runtime mappings', function () { + describe('creation with runtime mappings', function () { before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); await transform.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp'); From 1c9d15bf0325c52d0926ab7b2f48d33736303ab1 Mon Sep 17 00:00:00 2001 From: jennypavlova Date: Mon, 30 Oct 2023 17:26:50 +0100 Subject: [PATCH 16/45] [ObsUX] Support all metadata fields in asset details flyout (#170051) Closes #163489 ## Summary This PR adds support for all metadata fields to be displayed in the assets details view (flyout and page). It removes the current predefined mapping. The `host`, `agent`, and `cloud` groups will stay the same and we will show all properties present in the groups. The other metadata table functionality stays the same. ### Before [Old Metadata Mapping](https://github.com/elastic/kibana/blob/e804ef2dc84f9518807d3ee48d878d3ba6173807/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/utils.ts) ![image](https://github.com/elastic/kibana/assets/14139027/83cefdf6-f7dd-48b1-8564-bb6a17eccc37) ### After Shows all metadata received from the API for `host`, `agent`, and `cloud` groups ![image](https://github.com/elastic/kibana/assets/14139027/2912845f-be87-4b22-8db2-8bb6aefa9e58) ## Testing - Go to Host view and open the flyout for a host (selecting the metadata tab) - Check the metadata table - All fields returned by the API should be visible: image - Check the pin and add/remove filter functionalities and make sure they still work (also for the "new" fields) - Open it as a page and do the same check - Open inventory and click on a host - Same checks should be done there in the asset details flyout and full page --- .../components/expandable_content.tsx | 3 +- .../metadata/add_metadata_filter_button.tsx | 6 +- .../asset_details/tabs/metadata/utils.test.ts | 372 ++++++++++++++++++ .../asset_details/tabs/metadata/utils.ts | 140 +++---- 4 files changed, 420 insertions(+), 101 deletions(-) create mode 100644 x-pack/plugins/infra/public/components/asset_details/tabs/metadata/utils.test.ts diff --git a/x-pack/plugins/infra/public/components/asset_details/components/expandable_content.tsx b/x-pack/plugins/infra/public/components/asset_details/components/expandable_content.tsx index 0cd5e1a53013a..b5ef833c13c66 100644 --- a/x-pack/plugins/infra/public/components/asset_details/components/expandable_content.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/components/expandable_content.tsx @@ -11,9 +11,10 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import useToggle from 'react-use/lib/useToggle'; +import type { Field } from '../tabs/metadata/utils'; interface ExpandableContentProps { - values: string | string[] | undefined; + values?: Field['value']; } export const ExpandableContent = (props: ExpandableContentProps) => { const { values } = props; diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_metadata_filter_button.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_metadata_filter_button.tsx index 49d912fab3f50..6e92714ad28b0 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_metadata_filter_button.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/add_metadata_filter_button.tsx @@ -12,12 +12,10 @@ import { useMetricsDataViewContext } from '../../../../pages/metrics/hosts/hooks import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { buildMetadataFilter } from './build_metadata_filter'; import { useUnifiedSearchContext } from '../../../../pages/metrics/hosts/hooks/use_unified_search'; +import type { Field } from './utils'; interface AddMetadataFilterButtonProps { - item: { - name: string; - value: string | string[] | undefined; - }; + item: Field; } const filterAddedToastTitle = i18n.translate('xpack.infra.metadataEmbeddable.filterAdded', { diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/utils.test.ts b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/utils.test.ts new file mode 100644 index 0000000000000..5c78fca3f48db --- /dev/null +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/utils.test.ts @@ -0,0 +1,372 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { InfraMetadata } from '../../../../../common/http_api'; +import { getAllFields } from './utils'; + +describe('#getAllFields', () => { + const host = { + architecture: 'x86_64', + containerized: false, + hostname: 'host1', + ip: [ + '10.10.10.10', + '10.10.10.10', + '100.100.100.1', + 'fe10::1c10:10ff:fe10:f10b', + 'fe10::1c8b:10ff:fe5b:7faa', + 'fe10::20c2:36ff:feed:f47f', + ], + mac: ['01-33-22-33-71-83', '1E-15-33-68-F8-5B', '1E-8B-55-5B-7F-AA'], + name: 'host1', + os: { + codename: 'focal', + family: 'debian', + kernel: '5.15.109+', + name: 'Ubuntu', + platform: 'ubuntu', + version: '20.04.6 LTS (Focal Fossa)', + }, + }; + const agent = { + ephemeral_id: '2a1f1122-09f8-8d3b-b000-e5dcea22a1b2', + id: 'agent1', + name: 'host1', + type: 'metricbeat', + version: '8.11.0', + }; + + const cloud = { + account: { + id: 'elastic-observability', + }, + availability_zone: 'us-central1-c', + instance: { + id: '1111111111111111111', + name: 'host1', + }, + machine: { + type: 'e2-standard-4', + }, + project: { + id: 'elastic-observability', + }, + provider: 'gcp', + region: 'us-central1', + }; + + it('should return empty array if no metadata info', async () => { + const result: InfraMetadata = { + id: 'host1', + name: 'host1', + features: [ + { + name: 'system.core', + source: 'metrics', + }, + ], + }; + expect(getAllFields(result)).toHaveLength(0); + }); + + it('should return empty array if no field value is provided', async () => { + const result: InfraMetadata = { + id: 'host1', + name: 'host1', + features: [ + { + name: 'system.core', + source: 'metrics', + }, + ], + info: { + host: { + name: undefined, + }, + }, + }; + expect(getAllFields(result)).toHaveLength(0); + }); + + it('should map metadata with nested properties', async () => { + const result: InfraMetadata = { + id: 'host1', + name: 'host1', + features: [ + { + name: 'system.core', + source: 'metrics', + }, + ], + info: { + host: { + os: { + name: 'Ubuntu', + }, + }, + }, + }; + expect(getAllFields(result)).toStrictEqual([{ name: 'host.os.name', value: 'Ubuntu' }]); + }); + + it('should map metadata with partial host, agent, could data', async () => { + const result: InfraMetadata = { + id: 'host1', + name: 'host1', + features: [ + { + name: 'system.core', + source: 'metrics', + }, + ], + info: { + host: { + name: 'host2', + os: { + name: 'Ubuntu', + }, + mac: ['01-33-22-33-71-83', '1E-15-33-68-F8-5B', '1E-8B-55-5B-7F-AA'], + }, + cloud: { + instance: { + id: '1111111111111111111', + name: 'host1', + }, + }, + agent: { + id: 'agent2', + }, + }, + }; + expect(getAllFields(result)).toStrictEqual([ + { + name: 'host.name', + value: 'host2', + }, + { name: 'host.os.name', value: 'Ubuntu' }, + { + name: 'host.mac', + value: ['01-33-22-33-71-83', '1E-15-33-68-F8-5B', '1E-8B-55-5B-7F-AA'], + }, + { + name: 'agent.id', + value: 'agent2', + }, + { + name: 'cloud.instance.id', + value: '1111111111111111111', + }, + { + name: 'cloud.instance.name', + value: 'host1', + }, + ]); + }); + + it('should map metadata with only host data', async () => { + const result: InfraMetadata = { + id: 'host1', + name: 'host1', + features: [ + { + name: 'system.core', + source: 'metrics', + }, + ], + info: { + host, + }, + }; + expect(getAllFields(result)).toStrictEqual([ + { name: 'host.architecture', value: 'x86_64' }, + { name: 'host.containerized', value: 'false' }, + { name: 'host.hostname', value: 'host1' }, + { + name: 'host.ip', + value: [ + '10.10.10.10', + '10.10.10.10', + '100.100.100.1', + 'fe10::1c10:10ff:fe10:f10b', + 'fe10::1c8b:10ff:fe5b:7faa', + 'fe10::20c2:36ff:feed:f47f', + ], + }, + { + name: 'host.mac', + value: ['01-33-22-33-71-83', '1E-15-33-68-F8-5B', '1E-8B-55-5B-7F-AA'], + }, + { name: 'host.name', value: 'host1' }, + { name: 'host.os.codename', value: 'focal' }, + { name: 'host.os.family', value: 'debian' }, + { name: 'host.os.kernel', value: '5.15.109+' }, + { name: 'host.os.name', value: 'Ubuntu' }, + { + name: 'host.os.platform', + value: 'ubuntu', + }, + { + name: 'host.os.version', + value: '20.04.6 LTS (Focal Fossa)', + }, + ]); + }); + + it('should map metadata with host and cloud data', async () => { + const result: InfraMetadata = { + id: 'host1', + name: 'host1', + features: [ + { + name: 'system.core', + source: 'metrics', + }, + ], + info: { + host, + cloud, + }, + }; + + expect(getAllFields(result)).toStrictEqual([ + { name: 'host.architecture', value: 'x86_64' }, + { name: 'host.containerized', value: 'false' }, + { name: 'host.hostname', value: 'host1' }, + { + name: 'host.ip', + value: [ + '10.10.10.10', + '10.10.10.10', + '100.100.100.1', + 'fe10::1c10:10ff:fe10:f10b', + 'fe10::1c8b:10ff:fe5b:7faa', + 'fe10::20c2:36ff:feed:f47f', + ], + }, + { + name: 'host.mac', + value: ['01-33-22-33-71-83', '1E-15-33-68-F8-5B', '1E-8B-55-5B-7F-AA'], + }, + { name: 'host.name', value: 'host1' }, + { name: 'host.os.codename', value: 'focal' }, + { name: 'host.os.family', value: 'debian' }, + { name: 'host.os.kernel', value: '5.15.109+' }, + { name: 'host.os.name', value: 'Ubuntu' }, + { + name: 'host.os.platform', + value: 'ubuntu', + }, + { + name: 'host.os.version', + value: '20.04.6 LTS (Focal Fossa)', + }, + { + name: 'cloud.account.id', + value: 'elastic-observability', + }, + { + name: 'cloud.availability_zone', + value: 'us-central1-c', + }, + { + name: 'cloud.instance.id', + value: '1111111111111111111', + }, + { + name: 'cloud.instance.name', + value: 'host1', + }, + { + name: 'cloud.machine.type', + value: 'e2-standard-4', + }, + { + name: 'cloud.project.id', + value: 'elastic-observability', + }, + { + name: 'cloud.provider', + value: 'gcp', + }, + { + name: 'cloud.region', + value: 'us-central1', + }, + ]); + }); + + it('should map metadata with host and agent data', async () => { + const result: InfraMetadata = { + id: 'host1', + name: 'host1', + features: [ + { + name: 'system.core', + source: 'metrics', + }, + ], + info: { + host, + agent, + }, + }; + + expect(getAllFields(result)).toStrictEqual([ + { name: 'host.architecture', value: 'x86_64' }, + { name: 'host.containerized', value: 'false' }, + { name: 'host.hostname', value: 'host1' }, + { + name: 'host.ip', + value: [ + '10.10.10.10', + '10.10.10.10', + '100.100.100.1', + 'fe10::1c10:10ff:fe10:f10b', + 'fe10::1c8b:10ff:fe5b:7faa', + 'fe10::20c2:36ff:feed:f47f', + ], + }, + { + name: 'host.mac', + value: ['01-33-22-33-71-83', '1E-15-33-68-F8-5B', '1E-8B-55-5B-7F-AA'], + }, + { name: 'host.name', value: 'host1' }, + { name: 'host.os.codename', value: 'focal' }, + { name: 'host.os.family', value: 'debian' }, + { name: 'host.os.kernel', value: '5.15.109+' }, + { name: 'host.os.name', value: 'Ubuntu' }, + { + name: 'host.os.platform', + value: 'ubuntu', + }, + { + name: 'host.os.version', + value: '20.04.6 LTS (Focal Fossa)', + }, + { + name: 'agent.ephemeral_id', + value: '2a1f1122-09f8-8d3b-b000-e5dcea22a1b2', + }, + { + name: 'agent.id', + value: 'agent1', + }, + { + name: 'agent.name', + value: 'host1', + }, + { + name: 'agent.type', + value: 'metricbeat', + }, + { + name: 'agent.version', + value: '8.11.0', + }, + ]); + }); +}); diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/utils.ts b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/utils.ts index e52242c74d4f6..e41a09b7534e6 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/utils.ts +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/metadata/utils.ts @@ -9,108 +9,56 @@ import type { InfraMetadata } from '../../../../../common/http_api'; export interface Field { name: string; - value: string | string[] | undefined; + value?: string | string[]; +} +interface FieldsByCategory { + [key: string]: string | boolean | string[] | { [key: string]: string }; } export const getAllFields = (metadata: InfraMetadata | null) => { if (!metadata?.info) return []; - return prune([ - { - name: 'host.architecture', - value: metadata.info.host?.architecture, - }, - { - name: 'host.hostname', - value: metadata.info.host?.name, - }, - { - name: 'host.id', - value: metadata.info.host?.id, - }, - { - name: 'host.ip', - value: metadata.info.host?.ip, - }, - { - name: 'host.mac', - value: metadata.info.host?.mac, - }, - { - name: 'host.name', - value: metadata.info.host?.name, - }, - { - name: 'host.os.build', - value: metadata.info.host?.os?.build, - }, - { - name: 'host.os.family', - value: metadata.info.host?.os?.family, - }, - { - name: 'host.os.name', - value: metadata.info.host?.os?.name, - }, - { - name: 'host.os.kernel', - value: metadata.info.host?.os?.kernel, - }, - { - name: 'host.os.platform', - value: metadata.info.host?.os?.platform, - }, - { - name: 'host.os.version', - value: metadata.info.host?.os?.version, - }, - { - name: 'cloud.account.id', - value: metadata.info.cloud?.account?.id, - }, - { - name: 'cloud.account.name', - value: metadata.info.cloud?.account?.name, - }, - { - name: 'cloud.availability_zone', - value: metadata.info.cloud?.availability_zone, - }, - { - name: 'cloud.instance.id', - value: metadata.info.cloud?.instance?.id, - }, - { - name: 'cloud.instance.name', - value: metadata.info.cloud?.instance?.name, - }, - { - name: 'cloud.machine.type', - value: metadata.info.cloud?.machine?.type, - }, - { - name: 'cloud.provider', - value: metadata.info.cloud?.provider, - }, - { - name: 'cloud.region', - value: metadata.info.cloud?.region, - }, - { - name: 'agent.id', - value: metadata.info.agent?.id, - }, - { - name: 'agent.version', - value: metadata.info.agent?.version, - }, - { - name: 'agent.policy', - value: metadata.info.agent?.policy, - }, - ]); + + const mapNestedProperties = (category: 'cloud' | 'host' | 'agent', property: string) => { + const fieldsByCategory: FieldsByCategory = metadata?.info?.[`${category}`] ?? {}; + if (fieldsByCategory.hasOwnProperty(property)) { + const value = fieldsByCategory[property]; + + if (typeof value === 'boolean') { + return { + name: `${category}.${property}`, + value: String(value), + }; + } + + if (typeof value === 'string' || Array.isArray(value)) { + return { + name: `${category}.${property}`, + value, + }; + } else { + return Object.entries(value ?? {}).map(([prop, subProp]) => ({ + name: `${category}.${property}.${prop}`, + value: subProp, + })); + } + } + return []; + }; + + const agent = Object.keys(metadata.info.agent ?? {}).flatMap((prop) => + mapNestedProperties('agent', prop) + ); + const cloud = Object.keys(metadata.info.cloud ?? {}).flatMap((prop) => + mapNestedProperties('cloud', prop) + ); + const host = Object.keys(metadata?.info?.host ?? {}).flatMap((prop) => + mapNestedProperties('host', prop) + ); + + return prune([...host, ...agent, ...cloud]); }; -const prune = (fields: Field[]) => fields.filter((f) => !!f.value); +const prune = (fields: Field[]) => fields.filter((f) => !!f?.value); export const getRowsWithPins = (rows: Field[], pinnedItems: Array) => { if (pinnedItems.length > 0) { From 019f82bb1e78d7d29f80f51242aa72e81bb15790 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 30 Oct 2023 16:30:55 +0000 Subject: [PATCH 17/45] skip flaky suite (#169154) --- .../cypress/e2e/entity_analytics/enrichments.cy.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/enrichments.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/enrichments.cy.ts index 99cbf31012d75..c93b83b448e3a 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/enrichments.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics/enrichments.cy.ts @@ -36,7 +36,8 @@ import { enableRiskEngine } from '../../tasks/entity_analytics'; const CURRENT_HOST_RISK_LEVEL = 'Current host risk level'; const ORIGINAL_HOST_RISK_LEVEL = 'Original host risk level'; -describe('Enrichment', { tags: ['@ess', '@serverless'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/169154 +describe.skip('Enrichment', { tags: ['@ess', '@serverless'] }, () => { before(() => { cleanKibana(); cy.task('esArchiverUnload', 'risk_scores_new'); @@ -49,8 +50,7 @@ describe('Enrichment', { tags: ['@ess', '@serverless'] }, () => { }); describe('Custom query rule', () => { - // FLAKY: https://github.com/elastic/kibana/issues/169154 - describe.skip('from legacy risk scores', () => { + describe('from legacy risk scores', () => { beforeEach(() => { disableExpandableFlyout(); cy.task('esArchiverLoad', { archiveName: 'risk_hosts' }); From 12ab5b3694e8ecbade756da84ab2b9d38bcf2d94 Mon Sep 17 00:00:00 2001 From: Wafaa Nasr Date: Mon, 30 Oct 2023 18:04:39 +0100 Subject: [PATCH 18/45] [Security Solution][Exceptions][API testing] Move and restructures action groups in the new api integration test folder (#169234) ## Summary - Following the initial work in this https://github.com/elastic/kibana/pull/166755 - Addresses part of https://github.com/elastic/kibana/issues/151902 for actions https://docs.google.com/document/d/1CRFfDWMzw3ob03euWIvT4-IoiLXjoiPWI8mTBqP4Zks/edit - Enable migrations of legacy actions to run only in ESS - Add the `@skipInQA` tag to the failing tests in QA env --------- Co-authored-by: Vitalii Dmyterko <92328789+vitaliidm@users.noreply.github.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .buildkite/ftr_configs.yml | 4 +- .../security_and_spaces/group1/index.ts | 2 - .../security_and_spaces/group10/index.ts | 2 - .../group10/legacy_actions_migrations.ts | 354 -------------- .../security_and_spaces/group10/migrations.ts | 102 ---- .../config/serverless/config.base.ts | 5 - .../package.json | 7 +- .../scripts/index.js | 6 +- .../default_license/actions}/add_actions.ts | 34 +- .../actions/configs/ess.config.ts | 22 + .../actions/configs/serverless.config.ts | 15 + .../default_license/actions/index.ts | 15 + .../default_license/actions/migrations.ts | 451 ++++++++++++++++++ .../actions}/update_actions.ts | 49 +- .../utils/actions/create_new_action.ts | 35 ++ .../utils/actions/index.ts | 1 + .../rules/get_rule_with_web_hook_action.ts | 34 ++ ...simple_rule_output_with_web_hook_action.ts | 29 ++ .../detections_response/utils/rules/index.ts | 4 + .../utils/rules/rule_to_update_schema.ts | 37 ++ .../utils/rules/update_rule.ts | 41 ++ 21 files changed, 748 insertions(+), 501 deletions(-) delete mode 100644 x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts delete mode 100644 x-pack/test/detection_engine_api_integration/security_and_spaces/group10/migrations.ts rename x-pack/test/{detection_engine_api_integration/security_and_spaces/group1 => security_solution_api_integration/test_suites/detections_response/default_license/actions}/add_actions.ts (71%) create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/ess.config.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/serverless.config.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/index.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/migrations.ts rename x-pack/test/{detection_engine_api_integration/security_and_spaces/group1 => security_solution_api_integration/test_suites/detections_response/default_license/actions}/update_actions.ts (76%) create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/create_new_action.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_rule_with_web_hook_action.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_simple_rule_output_with_web_hook_action.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/rule_to_update_schema.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/update_rule.ts diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index f026499502e0d..7abd52a1ea153 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -457,7 +457,9 @@ enabled: - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/ips_text_array/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/configs/ess.config.ts - + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/ess.config.ts + diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/index.ts index 2ce7684dedd48..1c9c874127660 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/index.ts @@ -15,8 +15,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => { // existence being near 0. loadTestFile(require.resolve('./aliases')); - loadTestFile(require.resolve('./add_actions')); - loadTestFile(require.resolve('./update_actions')); loadTestFile(require.resolve('./check_privileges')); loadTestFile(require.resolve('./create_index')); loadTestFile(require.resolve('./preview_rules')); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/index.ts index 8844107744854..7822d11698c95 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/index.ts @@ -17,7 +17,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./get_rule_execution_results')); loadTestFile(require.resolve('./import_rules')); loadTestFile(require.resolve('./import_export_rules')); - loadTestFile(require.resolve('./legacy_actions_migrations')); loadTestFile(require.resolve('./read_rules')); loadTestFile(require.resolve('./resolve_read_rules')); loadTestFile(require.resolve('./update_rules')); @@ -36,7 +35,6 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./runtime')); loadTestFile(require.resolve('./throttle')); loadTestFile(require.resolve('./ignore_fields')); - loadTestFile(require.resolve('./migrations')); loadTestFile(require.resolve('./risk_engine/init_and_status_apis')); loadTestFile(require.resolve('./risk_engine/risk_score_preview')); loadTestFile(require.resolve('./risk_engine/risk_score_calculation')); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts deleted file mode 100644 index 236bf6991c9d6..0000000000000 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/legacy_actions_migrations.ts +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import expect from '@kbn/expect'; - -import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; -import { - getLegacyActionSOById, - getLegacyActionNotificationSOById, - getRuleSOById, -} from '../../utils'; - -/** - * @deprecated Once the legacy notification system is removed, remove this test too. - */ -// eslint-disable-next-line import/no-default-export -export default ({ getService }: FtrProviderContext) => { - const supertest = getService('supertest'); - const es = getService('es'); - const esArchiver = getService('esArchiver'); - - // This test suite is not meant to test a specific route, but to test the legacy action migration - // code that lives in multiple routes. This code is also tested in each of the routes it lives in - // but not in as much detail and relying on mocks. This test loads an es_archive containing rules - // created in 7.15 with legacy actions. - // For new routes that do any updates on a rule, please ensure that you are including the legacy - // action migration code. We are monitoring legacy action telemetry to clean up once we see their - // existence being near 0. - describe('migrate_legacy_actions', () => { - before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/security_solution/legacy_actions'); - }); - - after(async () => { - await esArchiver.unload( - 'x-pack/test/functional/es_archives/security_solution/legacy_actions' - ); - }); - - it('migrates legacy actions for rule with no actions', async () => { - const soId = '9095ee90-b075-11ec-bb3f-1f063f8e06cf'; - const ruleId = '2297be91-894c-4831-830f-b424a0ec84f0'; - const legacySidecarId = '926668d0-b075-11ec-bb3f-1f063f8e06cf'; - - // check for legacy sidecar action - const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); - expect(sidecarActionSO.hits.hits.length).to.eql(1); - - // check for legacy notification SO - // should not have been created for a rule with no actions - const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); - expect(legacyNotificationSO.hits.hits.length).to.eql(0); - - // patch enable the rule - // any route that edits the rule should trigger the migration - await supertest - .patch(DETECTION_ENGINE_RULES_URL) - .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') - .send({ rule_id: ruleId, enabled: false }) - .expect(200); - - const { - hits: { - hits: [{ _source: ruleSO }], - }, - } = await getRuleSOById(es, soId); - - // Sidecar should be removed - const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); - expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); - - expect(ruleSO?.alert.actions).to.eql([]); - expect(ruleSO?.alert.throttle).to.eql(null); - expect(ruleSO?.alert.notifyWhen).to.eql(null); - }); - - it('migrates legacy actions for rule with action run on every run', async () => { - const soId = 'dc6595f0-b075-11ec-bb3f-1f063f8e06cf'; - const ruleId = '72a0d429-363b-4f70-905e-c6019a224d40'; - const legacySidecarId = 'dde13970-b075-11ec-bb3f-1f063f8e06cf'; - - // check for legacy sidecar action - const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); - expect(sidecarActionSO.hits.hits.length).to.eql(1); - - // check for legacy notification SO - // should not have been created for a rule that runs on every rule run - const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); - expect(legacyNotificationSO.hits.hits.length).to.eql(0); - - // patch enable the rule - // any route that edits the rule should trigger the migration - await supertest - .patch(DETECTION_ENGINE_RULES_URL) - .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') - .send({ rule_id: ruleId, enabled: false }) - .expect(200); - - const { - hits: { - hits: [{ _source: ruleSO }], - }, - } = await getRuleSOById(es, soId); - - // Sidecar should be removed - const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); - expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); - - expect(ruleSO?.alert.actions).to.eql([ - { - actionRef: 'action_0', - actionTypeId: '.email', - group: 'default', - params: { - message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', - subject: 'Test Actions', - to: ['test@test.com'], - }, - uuid: ruleSO?.alert.actions[0].uuid, - frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, - }, - { - actionRef: 'action_1', - actionTypeId: '.email', - group: 'default', - params: { - message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', - subject: 'Test Actions', - to: ['test@test.com'], - }, - uuid: ruleSO?.alert.actions[1].uuid, - frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, - }, - ]); - expect(ruleSO?.alert.throttle).to.eql(null); - expect(ruleSO?.alert.notifyWhen).to.eql(null); - expect(ruleSO?.references).to.eql([ - { - id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', - name: 'action_0', - type: 'action', - }, - { - id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', - name: 'action_1', - type: 'action', - }, - ]); - }); - - it('migrates legacy actions for rule with action run hourly', async () => { - const soId = '064e3160-b076-11ec-bb3f-1f063f8e06cf'; - const ruleId = '4c056b05-75ac-4209-be32-82100f771eb4'; - const legacySidecarId = '07aa8d10-b076-11ec-bb3f-1f063f8e06cf'; - - // check for legacy sidecar action - const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); - expect(sidecarActionSO.hits.hits.length).to.eql(1); - - // check for legacy notification SO - const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); - expect(legacyNotificationSO.hits.hits.length).to.eql(1); - - // patch enable the rule - // any route that edits the rule should trigger the migration - await supertest - .patch(DETECTION_ENGINE_RULES_URL) - .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') - .send({ rule_id: ruleId, enabled: false }) - .expect(200); - - const { - hits: { - hits: [{ _source: ruleSO }], - }, - } = await getRuleSOById(es, soId); - - // Sidecar should be removed - const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); - expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); - - // Legacy notification should be removed - const legacyNotificationSOAfterMigration = await getLegacyActionNotificationSOById(es, soId); - expect(legacyNotificationSOAfterMigration.hits.hits.length).to.eql(0); - - expect(ruleSO?.alert.actions).to.eql([ - { - actionTypeId: '.email', - params: { - subject: 'Rule email', - to: ['test@test.com'], - message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', - }, - actionRef: 'action_0', - group: 'default', - uuid: ruleSO?.alert.actions[0].uuid, - frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, - }, - { - actionTypeId: '.slack', - params: { - message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', - }, - actionRef: 'action_1', - group: 'default', - uuid: ruleSO?.alert.actions[1].uuid, - frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, - }, - ]); - expect(ruleSO?.alert.throttle).to.eql(undefined); - expect(ruleSO?.alert.notifyWhen).to.eql(null); - expect(ruleSO?.references).to.eql([ - { - id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', - name: 'action_0', - type: 'action', - }, - { - id: '207fa0e0-c04e-11ec-8a52-4fb92379525a', - name: 'action_1', - type: 'action', - }, - ]); - }); - - it('migrates legacy actions for rule with action run daily', async () => { - const soId = '27639570-b076-11ec-bb3f-1f063f8e06cf'; - const ruleId = '8e2c8550-f13f-4e21-be0c-92148d71a5f1'; - const legacySidecarId = '291ae260-b076-11ec-bb3f-1f063f8e06cf'; - - // check for legacy sidecar action - const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); - expect(sidecarActionSO.hits.hits.length).to.eql(1); - - // check for legacy notification SO - const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); - expect(legacyNotificationSO.hits.hits.length).to.eql(1); - - // patch enable the rule - await supertest - .patch(DETECTION_ENGINE_RULES_URL) - .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') - .send({ rule_id: ruleId, enabled: false }) - .expect(200); - - const { - hits: { - hits: [{ _source: ruleSO }], - }, - } = await getRuleSOById(es, soId); - - // Sidecar should be removed - const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); - expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); - - // Legacy notification should be removed - const legacyNotificationSOAfterMigration = await getLegacyActionNotificationSOById(es, soId); - expect(legacyNotificationSOAfterMigration.hits.hits.length).to.eql(0); - - expect(ruleSO?.alert.actions).to.eql([ - { - actionRef: 'action_0', - actionTypeId: '.email', - group: 'default', - params: { - message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', - subject: 'Test Actions', - to: ['test@test.com'], - }, - uuid: ruleSO?.alert.actions[0].uuid, - frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, - }, - ]); - expect(ruleSO?.alert.throttle).to.eql(undefined); - expect(ruleSO?.alert.notifyWhen).to.eql(null); - expect(ruleSO?.references).to.eql([ - { - id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', - name: 'action_0', - type: 'action', - }, - ]); - }); - - it('migrates legacy actions for rule with action run weekly', async () => { - const soId = '61ec7a40-b076-11ec-bb3f-1f063f8e06cf'; - const ruleId = '05fbdd2a-e802-420b-bdc3-95ae0acca454'; - const legacySidecarId = '63aa2fd0-b076-11ec-bb3f-1f063f8e06cf'; - - // check for legacy sidecar action - const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); - expect(sidecarActionSO.hits.hits.length).to.eql(1); - - // check for legacy notification SO - const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); - expect(legacyNotificationSO.hits.hits.length).to.eql(1); - - // patch enable the rule - await supertest - .patch(DETECTION_ENGINE_RULES_URL) - .set('kbn-xsrf', 'true') - .set('elastic-api-version', '2023-10-31') - .send({ rule_id: ruleId, enabled: false }) - .expect(200); - - const { - hits: { - hits: [{ _source: ruleSO }], - }, - } = await getRuleSOById(es, soId); - - // Sidecar should be removed - const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); - expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); - - // Legacy notification should be removed - const legacyNotificationSOAfterMigration = await getLegacyActionNotificationSOById(es, soId); - expect(legacyNotificationSOAfterMigration.hits.hits.length).to.eql(0); - - expect(ruleSO?.alert.actions).to.eql([ - { - actionRef: 'action_0', - actionTypeId: '.email', - group: 'default', - params: { - message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', - subject: 'Test Actions', - to: ['test@test.com'], - }, - uuid: ruleSO?.alert.actions[0].uuid, - frequency: { summary: true, throttle: '7d', notifyWhen: 'onThrottleInterval' }, - }, - ]); - expect(ruleSO?.alert.throttle).to.eql(undefined); - expect(ruleSO?.alert.notifyWhen).to.eql(null); - expect(ruleSO?.references).to.eql([ - { - id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', - name: 'action_0', - type: 'action', - }, - ]); - }); - }); -}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/migrations.ts deleted file mode 100644 index d43ecaff6823e..0000000000000 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/migrations.ts +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default ({ getService }: FtrProviderContext): void => { - const esArchiver = getService('esArchiver'); - const es = getService('es'); - - describe('migrations', () => { - before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/security_solution/migrations'); - }); - - after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/security_solution/migrations'); - }); - - describe('7.16.0', () => { - it('migrates legacy siem-detection-engine-rule-actions to use saved object references', async () => { - const response = await es.get<{ - 'siem-detection-engine-rule-actions': { - ruleAlertId: string; - actions: [{ id: string; actionRef: string }]; - }; - references: [{}]; - }>( - { - index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, - id: 'siem-detection-engine-rule-actions:fce024a0-0452-11ec-9b15-d13d79d162f3', - }, - { - meta: true, - } - ); - expect(response.statusCode).to.eql(200); - - // references exist and are expected values - expect(response.body._source?.references).to.eql([ - { - name: 'alert_0', - id: 'fb1046a0-0452-11ec-9b15-d13d79d162f3', - type: 'alert', - }, - { - name: 'action_0', - id: 'f6e64c00-0452-11ec-9b15-d13d79d162f3', - type: 'action', - }, - ]); - - // actionRef exists and is the expected value - expect( - response.body._source?.['siem-detection-engine-rule-actions'].actions[0].actionRef - ).to.eql('action_0'); - - // ruleAlertId no longer exist - expect(response.body._source?.['siem-detection-engine-rule-actions'].ruleAlertId).to.eql( - undefined - ); - - // actions.id no longer exist - expect(response.body._source?.['siem-detection-engine-rule-actions'].actions[0].id).to.eql( - undefined - ); - }); - - it('migrates legacy siem-detection-engine-rule-actions and retains "ruleThrottle" and "alertThrottle" as the same attributes as before', async () => { - const response = await es.get<{ - 'siem-detection-engine-rule-actions': { - ruleThrottle: string; - alertThrottle: string; - }; - }>( - { - index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, - id: 'siem-detection-engine-rule-actions:fce024a0-0452-11ec-9b15-d13d79d162f3', - }, - { - meta: true, - } - ); - expect(response.statusCode).to.eql(200); - - // "alertThrottle" and "ruleThrottle" should still exist - expect(response.body._source?.['siem-detection-engine-rule-actions'].alertThrottle).to.eql( - '7d' - ); - expect(response.body._source?.['siem-detection-engine-rule-actions'].ruleThrottle).to.eql( - '7d' - ); - }); - }); - }); -}; diff --git a/x-pack/test/security_solution_api_integration/config/serverless/config.base.ts b/x-pack/test/security_solution_api_integration/config/serverless/config.base.ts index 105701ec61e08..6790c39c85115 100644 --- a/x-pack/test/security_solution_api_integration/config/serverless/config.base.ts +++ b/x-pack/test/security_solution_api_integration/config/serverless/config.base.ts @@ -5,8 +5,6 @@ * 2.0. */ import { FtrConfigProviderContext } from '@kbn/test'; -// import { ES_RESOURCES } from '@kbn/security-solution-plugin/scripts/endpoint/common/roles_users/serverless'; - export interface CreateTestConfigOptions { testFiles: string[]; junit: { reportName: string }; @@ -24,9 +22,6 @@ export function createTestConfig(options: CreateTestConfigOptions) { ...svlSharedConfig.get('kbnTestServer'), serverArgs: [...svlSharedConfig.get('kbnTestServer.serverArgs'), '--serverless=security'], }, - // esServerlessOptions: { - // resources: Object.values(ES_RESOURCES), - // }, testFiles: options.testFiles, junit: options.junit, diff --git a/x-pack/test/security_solution_api_integration/package.json b/x-pack/test/security_solution_api_integration/package.json index 4562cfc82cfc9..305db2251e0b1 100644 --- a/x-pack/test/security_solution_api_integration/package.json +++ b/x-pack/test/security_solution_api_integration/package.json @@ -31,6 +31,11 @@ "rule_creation:runner:serverless": "npm run run-tests rule_creation serverless serverlessEnv", "rule_creation:qa:serverless": "npm run run-tests rule_creation serverless qaEnv", "rule_creation:server:ess": "npm run initialize-server rule_creation ess", - "rule_creation:runner:ess": "npm run run-tests rule_creation ess essEnv" + "rule_creation:runner:ess": "npm run run-tests rule_creation ess essEnv", + "actions:server:serverless": "npm run initialize-server actions serverless", + "actions:runner:serverless": "npm run run-tests actions serverless serverlessEnv", + "actions:qa:serverless": "npm run run-tests actions serverless qaEnv", + "actions:server:ess": "npm run initialize-server actions ess", + "actions:runner:ess": "npm run run-tests actions ess essEnv" } } diff --git a/x-pack/test/security_solution_api_integration/scripts/index.js b/x-pack/test/security_solution_api_integration/scripts/index.js index 635c135e2c8b1..af12482f9f293 100644 --- a/x-pack/test/security_solution_api_integration/scripts/index.js +++ b/x-pack/test/security_solution_api_integration/scripts/index.js @@ -31,15 +31,15 @@ let grepArgs = []; if (type !== 'server') { switch (environment) { case 'serverlessEnv': - grepArgs = ['--grep', '@serverless', '--grep', '@brokenInServerless', '--invert']; + grepArgs = ['--grep', '/^(?!.*@brokenInServerless).*@serverless.*/']; break; case 'essEnv': - grepArgs = ['--grep', '@ess']; + grepArgs = ['--grep', '/^(?!.*@brokenInEss).*@ess.*/']; break; case 'qaEnv': - grepArgs = ['--grep', '@serverless', '--grep', '@brokenInServerless|@skipInQA', '--invert']; + grepArgs = ['--grep', '/^(?!.*@brokenInServerless|.*@skipInQA).*@serverless.*/']; break; default: diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/add_actions.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/add_actions.ts similarity index 71% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/add_actions.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/add_actions.ts index 2bfd3c6102e61..257378f7442b9 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/add_actions.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/add_actions.ts @@ -8,9 +8,8 @@ import expect from '@kbn/expect'; import { RuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { - createSignalsIndex, + createAlertsIndex, deleteAllRules, removeServerGeneratedProperties, getWebHookAction, @@ -19,27 +18,35 @@ import { waitForRuleSuccess, createRule, deleteAllAlerts, + updateUsername, } from '../../utils'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; +import { EsArchivePathBuilder } from '../../../../es_archive_path_builder'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const log = getService('log'); const es = getService('es'); + // TODO: add a new service + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); + const isServerless = config.get('serverless'); + const dataPathBuilder = new EsArchivePathBuilder(isServerless); + const path = dataPathBuilder.getPath('auditbeat/hosts'); - describe('add_actions', () => { + describe('@serverless @ess add_actions', () => { describe('adding actions', () => { before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.load(path); }); after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.unload(path); }); beforeEach(async () => { - await createSignalsIndex(supertest, log); + await createAlertsIndex(supertest, log); }); afterEach(async () => { @@ -52,17 +59,18 @@ export default ({ getService }: FtrProviderContext) => { const { body: hookAction } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'true') + .set('x-elastic-internal-origin', 'foo') .send(getWebHookAction()) .expect(200); const rule = await createRule(supertest, log, getRuleWithWebHookAction(hookAction.id)); const bodyToCompare = removeServerGeneratedProperties(rule); - expect(bodyToCompare).to.eql( - getSimpleRuleOutputWithWebHookAction( - `${bodyToCompare?.actions?.[0].id}`, - `${bodyToCompare?.actions?.[0].uuid}` - ) + const expected = getSimpleRuleOutputWithWebHookAction( + `${bodyToCompare?.actions?.[0].id}`, + `${bodyToCompare?.actions?.[0].uuid}` ); + const expectedRuleWithUserUpdated = updateUsername(expected, ELASTICSEARCH_USERNAME); + expect(bodyToCompare).to.eql(expectedRuleWithUserUpdated); }); it('should be able to create a new webhook action and attach it to a rule without a meta field and run it correctly', async () => { @@ -70,6 +78,7 @@ export default ({ getService }: FtrProviderContext) => { const { body: hookAction } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'true') + .set('x-elastic-internal-origin', 'foo') .send(getWebHookAction()) .expect(200); @@ -86,6 +95,7 @@ export default ({ getService }: FtrProviderContext) => { const { body: hookAction } = await supertest .post('/api/actions/action') .set('kbn-xsrf', 'true') + .set('x-elastic-internal-origin', 'foo') .send(getWebHookAction()) .expect(200); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/ess.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/ess.config.ts new file mode 100644 index 0000000000000..cec8d1cca41b5 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/ess.config.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile( + require.resolve('../../../../../config/ess/config.base.trial') + ); + + return { + ...functionalConfig.getAll(), + testFiles: [require.resolve('..')], + junit: { + reportName: 'Detection Engine ESS/Actions API Integration Tests', + }, + }; +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/serverless.config.ts new file mode 100644 index 0000000000000..66edc0eef7f30 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/configs/serverless.config.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createTestConfig } from '../../../../../config/serverless/config.base'; + +export default createTestConfig({ + testFiles: [require.resolve('..')], + junit: { + reportName: 'Detection Engine Serverless/Actions API Integration Tests', + }, +}); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/index.ts new file mode 100644 index 0000000000000..5c26d445eb158 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +export default function ({ loadTestFile }: FtrProviderContext) { + describe('Actions API', function () { + loadTestFile(require.resolve('./add_actions')); + loadTestFile(require.resolve('./update_actions')); + loadTestFile(require.resolve('./migrations')); + }); +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/migrations.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/migrations.ts new file mode 100644 index 0000000000000..ce5c87d2c3fb4 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/migrations.ts @@ -0,0 +1,451 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import expect from '@kbn/expect'; +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; + +import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + getLegacyActionSOById, + getLegacyActionNotificationSOById, + getRuleSOById, +} from '../../../../../detection_engine_api_integration/utils'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; + +/** + * @deprecated Once the legacy notification system is removed, remove this test too. + */ +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const es = getService('es'); + const esArchiver = getService('esArchiver'); + + describe('@ess @skipInQA actions migrations', () => { + // This test suite is not meant to test a specific route, but to test the legacy action migration + // code that lives in multiple routes. This code is also tested in each of the routes it lives in + // but not in as much detail and relying on mocks. This test loads an es_archive containing rules + // created in 7.15 with legacy actions. + // For new routes that do any updates on a rule, please ensure that you are including the legacy + // action migration code. We are monitoring legacy action telemetry to clean up once we see their + // existence being near 0. + describe('legacy actions', () => { + before(async () => { + await esArchiver.load( + 'x-pack/test/functional/es_archives/security_solution/legacy_actions' + ); + }); + + after(async () => { + await esArchiver.unload( + 'x-pack/test/functional/es_archives/security_solution/legacy_actions' + ); + }); + + it('migrates legacy actions for rule with no actions', async () => { + const soId = '9095ee90-b075-11ec-bb3f-1f063f8e06cf'; + const ruleId = '2297be91-894c-4831-830f-b424a0ec84f0'; + const legacySidecarId = '926668d0-b075-11ec-bb3f-1f063f8e06cf'; + + // check for legacy sidecar action + const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionSO.hits.hits.length).to.eql(1); + + // check for legacy notification SO + // should not have been created for a rule with no actions + const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSO.hits.hits.length).to.eql(0); + + // patch enable the rule + // any route that edits the rule should trigger the migration + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send({ rule_id: ruleId, enabled: false }) + .expect(200); + + const { + hits: { + hits: [{ _source: ruleSO }], + }, + } = await getRuleSOById(es, soId); + + // Sidecar should be removed + const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); + + expect(ruleSO?.alert.actions).to.eql([]); + expect(ruleSO?.alert.throttle).to.eql(null); + expect(ruleSO?.alert.notifyWhen).to.eql(null); + }); + + it('migrates legacy actions for rule with action run on every run', async () => { + const soId = 'dc6595f0-b075-11ec-bb3f-1f063f8e06cf'; + const ruleId = '72a0d429-363b-4f70-905e-c6019a224d40'; + const legacySidecarId = 'dde13970-b075-11ec-bb3f-1f063f8e06cf'; + + // check for legacy sidecar action + const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionSO.hits.hits.length).to.eql(1); + + // check for legacy notification SO + // should not have been created for a rule that runs on every rule run + const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSO.hits.hits.length).to.eql(0); + + // patch enable the rule + // any route that edits the rule should trigger the migration + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send({ rule_id: ruleId, enabled: false }) + .expect(200); + + const { + hits: { + hits: [{ _source: ruleSO }], + }, + } = await getRuleSOById(es, soId); + + // Sidecar should be removed + const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); + + expect(ruleSO?.alert.actions).to.eql([ + { + actionRef: 'action_0', + actionTypeId: '.email', + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + { + actionRef: 'action_1', + actionTypeId: '.email', + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + uuid: ruleSO?.alert.actions[1].uuid, + frequency: { summary: true, throttle: null, notifyWhen: 'onActiveAlert' }, + }, + ]); + expect(ruleSO?.alert.throttle).to.eql(null); + expect(ruleSO?.alert.notifyWhen).to.eql(null); + expect(ruleSO?.references).to.eql([ + { + id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', + name: 'action_0', + type: 'action', + }, + { + id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', + name: 'action_1', + type: 'action', + }, + ]); + }); + + it('migrates legacy actions for rule with action run hourly', async () => { + const soId = '064e3160-b076-11ec-bb3f-1f063f8e06cf'; + const ruleId = '4c056b05-75ac-4209-be32-82100f771eb4'; + const legacySidecarId = '07aa8d10-b076-11ec-bb3f-1f063f8e06cf'; + + // check for legacy sidecar action + const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionSO.hits.hits.length).to.eql(1); + + // check for legacy notification SO + const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSO.hits.hits.length).to.eql(1); + + // patch enable the rule + // any route that edits the rule should trigger the migration + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send({ rule_id: ruleId, enabled: false }) + .expect(200); + + const { + hits: { + hits: [{ _source: ruleSO }], + }, + } = await getRuleSOById(es, soId); + + // Sidecar should be removed + const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); + + // Legacy notification should be removed + const legacyNotificationSOAfterMigration = await getLegacyActionNotificationSOById( + es, + soId + ); + expect(legacyNotificationSOAfterMigration.hits.hits.length).to.eql(0); + + expect(ruleSO?.alert.actions).to.eql([ + { + actionTypeId: '.email', + params: { + subject: 'Rule email', + to: ['test@test.com'], + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + }, + actionRef: 'action_0', + group: 'default', + uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, + }, + { + actionTypeId: '.slack', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + }, + actionRef: 'action_1', + group: 'default', + uuid: ruleSO?.alert.actions[1].uuid, + frequency: { summary: true, throttle: '1h', notifyWhen: 'onThrottleInterval' }, + }, + ]); + expect(ruleSO?.alert.throttle).to.eql(undefined); + expect(ruleSO?.alert.notifyWhen).to.eql(null); + expect(ruleSO?.references).to.eql([ + { + id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', + name: 'action_0', + type: 'action', + }, + { + id: '207fa0e0-c04e-11ec-8a52-4fb92379525a', + name: 'action_1', + type: 'action', + }, + ]); + }); + + it('migrates legacy actions for rule with action run daily', async () => { + const soId = '27639570-b076-11ec-bb3f-1f063f8e06cf'; + const ruleId = '8e2c8550-f13f-4e21-be0c-92148d71a5f1'; + const legacySidecarId = '291ae260-b076-11ec-bb3f-1f063f8e06cf'; + + // check for legacy sidecar action + const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionSO.hits.hits.length).to.eql(1); + + // check for legacy notification SO + const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSO.hits.hits.length).to.eql(1); + + // patch enable the rule + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send({ rule_id: ruleId, enabled: false }) + .expect(200); + + const { + hits: { + hits: [{ _source: ruleSO }], + }, + } = await getRuleSOById(es, soId); + + // Sidecar should be removed + const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); + + // Legacy notification should be removed + const legacyNotificationSOAfterMigration = await getLegacyActionNotificationSOById( + es, + soId + ); + expect(legacyNotificationSOAfterMigration.hits.hits.length).to.eql(0); + + expect(ruleSO?.alert.actions).to.eql([ + { + actionRef: 'action_0', + actionTypeId: '.email', + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: '1d', notifyWhen: 'onThrottleInterval' }, + }, + ]); + expect(ruleSO?.alert.throttle).to.eql(undefined); + expect(ruleSO?.alert.notifyWhen).to.eql(null); + expect(ruleSO?.references).to.eql([ + { + id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', + name: 'action_0', + type: 'action', + }, + ]); + }); + + it('migrates legacy actions for rule with action run weekly', async () => { + const soId = '61ec7a40-b076-11ec-bb3f-1f063f8e06cf'; + const ruleId = '05fbdd2a-e802-420b-bdc3-95ae0acca454'; + const legacySidecarId = '63aa2fd0-b076-11ec-bb3f-1f063f8e06cf'; + + // check for legacy sidecar action + const sidecarActionSO = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionSO.hits.hits.length).to.eql(1); + + // check for legacy notification SO + const legacyNotificationSO = await getLegacyActionNotificationSOById(es, soId); + expect(legacyNotificationSO.hits.hits.length).to.eql(1); + + // patch enable the rule + await supertest + .patch(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send({ rule_id: ruleId, enabled: false }) + .expect(200); + + const { + hits: { + hits: [{ _source: ruleSO }], + }, + } = await getRuleSOById(es, soId); + + // Sidecar should be removed + const sidecarActionsSOAfterMigration = await getLegacyActionSOById(es, legacySidecarId); + expect(sidecarActionsSOAfterMigration.hits.hits.length).to.eql(0); + + // Legacy notification should be removed + const legacyNotificationSOAfterMigration = await getLegacyActionNotificationSOById( + es, + soId + ); + expect(legacyNotificationSOAfterMigration.hits.hits.length).to.eql(0); + + expect(ruleSO?.alert.actions).to.eql([ + { + actionRef: 'action_0', + actionTypeId: '.email', + group: 'default', + params: { + message: 'Rule {{context.rule.name}} generated {{state.signals_count}} alerts', + subject: 'Test Actions', + to: ['test@test.com'], + }, + uuid: ruleSO?.alert.actions[0].uuid, + frequency: { summary: true, throttle: '7d', notifyWhen: 'onThrottleInterval' }, + }, + ]); + expect(ruleSO?.alert.throttle).to.eql(undefined); + expect(ruleSO?.alert.notifyWhen).to.eql(null); + expect(ruleSO?.references).to.eql([ + { + id: 'c95cb100-b075-11ec-bb3f-1f063f8e06cf', + name: 'action_0', + type: 'action', + }, + ]); + }); + }); + + describe('7.16.0', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/security_solution/migrations'); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/security_solution/migrations'); + }); + + it('migrates legacy siem-detection-engine-rule-actions to use saved object references', async () => { + const response = await es.get<{ + 'siem-detection-engine-rule-actions': { + ruleAlertId: string; + actions: [{ id: string; actionRef: string }]; + }; + references: [{}]; + }>( + { + index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, + id: 'siem-detection-engine-rule-actions:fce024a0-0452-11ec-9b15-d13d79d162f3', + }, + { + meta: true, + } + ); + expect(response.statusCode).to.eql(200); + + // references exist and are expected values + expect(response.body._source?.references).to.eql([ + { + name: 'alert_0', + id: 'fb1046a0-0452-11ec-9b15-d13d79d162f3', + type: 'alert', + }, + { + name: 'action_0', + id: 'f6e64c00-0452-11ec-9b15-d13d79d162f3', + type: 'action', + }, + ]); + + // actionRef exists and is the expected value + expect( + response.body._source?.['siem-detection-engine-rule-actions'].actions[0].actionRef + ).to.eql('action_0'); + + // ruleAlertId no longer exist + expect(response.body._source?.['siem-detection-engine-rule-actions'].ruleAlertId).to.eql( + undefined + ); + + // actions.id no longer exist + expect(response.body._source?.['siem-detection-engine-rule-actions'].actions[0].id).to.eql( + undefined + ); + }); + + it('migrates legacy siem-detection-engine-rule-actions and retains "ruleThrottle" and "alertThrottle" as the same attributes as before', async () => { + const response = await es.get<{ + 'siem-detection-engine-rule-actions': { + ruleThrottle: string; + alertThrottle: string; + }; + }>( + { + index: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, + id: 'siem-detection-engine-rule-actions:fce024a0-0452-11ec-9b15-d13d79d162f3', + }, + { + meta: true, + } + ); + expect(response.statusCode).to.eql(200); + + // "alertThrottle" and "ruleThrottle" should still exist + expect(response.body._source?.['siem-detection-engine-rule-actions'].alertThrottle).to.eql( + '7d' + ); + expect(response.body._source?.['siem-detection-engine-rule-actions'].ruleThrottle).to.eql( + '7d' + ); + }); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_actions.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/update_actions.ts similarity index 76% rename from x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_actions.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/update_actions.ts index 8390fc10ad7fc..f56d949eadd38 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/update_actions.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions/update_actions.ts @@ -10,9 +10,8 @@ import { omit } from 'lodash'; import { RuleCreateProps } from '@kbn/security-solution-plugin/common/api/detection_engine'; import { ELASTIC_SECURITY_RULE_ID } from '@kbn/security-solution-plugin/common'; -import { FtrProviderContext } from '../../common/ftr_provider_context'; import { - createSignalsIndex, + createAlertsIndex, deleteAllRules, deleteAllAlerts, removeServerGeneratedProperties, @@ -29,32 +28,40 @@ import { getPrebuiltRulesAndTimelinesStatus, getSimpleRuleOutput, ruleToUpdateSchema, + updateUsername, } from '../../utils'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; +import { EsArchivePathBuilder } from '../../../../es_archive_path_builder'; -// eslint-disable-next-line import/no-default-export export default ({ getService }: FtrProviderContext) => { const es = getService('es'); const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); const log = getService('log'); + // TODO: add a new service + const config = getService('config'); + const ELASTICSEARCH_USERNAME = config.get('servers.kibana.username'); + const isServerless = config.get('serverless'); + const dataPathBuilder = new EsArchivePathBuilder(isServerless); + const path = dataPathBuilder.getPath('auditbeat/hosts'); const getImmutableRule = async () => { await installMockPrebuiltRules(supertest, es); return getRule(supertest, log, ELASTIC_SECURITY_RULE_ID); }; - describe('update_actions', () => { + describe('@serverless @ess update_actions', () => { describe('updating actions', () => { before(async () => { - await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.load(path); }); after(async () => { - await esArchiver.unload('x-pack/test/functional/es_archives/auditbeat/hosts'); + await esArchiver.unload(path); }); beforeEach(async () => { - await createSignalsIndex(supertest, log); + await createAlertsIndex(supertest, log); }); afterEach(async () => { @@ -70,14 +77,16 @@ export default ({ getService }: FtrProviderContext) => { const updatedRule = await updateRule(supertest, log, ruleToUpdate); const bodyToCompare = removeServerGeneratedProperties(updatedRule); - const expected = { + const expectedRule = { ...getSimpleRuleOutputWithWebHookAction( `${bodyToCompare.actions?.[0].id}`, `${bodyToCompare.actions?.[0].uuid}` ), revision: 1, // revision bump is required since this is an updated rule and this is part of the testing that we do bump the revision number on update }; - expect(bodyToCompare).to.eql(expected); + const expectedRuleWithUserUpdated = updateUsername(expectedRule, ELASTICSEARCH_USERNAME); + + expect(bodyToCompare).to.eql(expectedRuleWithUserUpdated); }); it('should be able to add a new webhook action and then remove the action from the rule again', async () => { @@ -92,7 +101,8 @@ export default ({ getService }: FtrProviderContext) => { ...getSimpleRuleOutput(), revision: 2, // revision bump is required since this is an updated rule and this is part of the testing that we do bump the revision number on update }; - expect(bodyToCompare).to.eql(expected); + const expectedRuleWithUserUpdated = updateUsername(expected, ELASTICSEARCH_USERNAME); + expect(bodyToCompare).to.eql(expectedRuleWithUserUpdated); }); it('should be able to create a new webhook action and attach it to a rule without a meta field and run it correctly', async () => { @@ -104,7 +114,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForRuleSuccess({ supertest, log, id: updatedRule.id }); }); - it('should be able to create a new webhook action and attach it to a rule with a meta field and run it correctly', async () => { + it('@skipInQA should be able to create a new webhook action and attach it to a rule with a meta field and run it correctly', async () => { const hookAction = await createNewAction(supertest, log); const rule = getSimpleRule(); await createRule(supertest, log, rule); @@ -116,7 +126,7 @@ export default ({ getService }: FtrProviderContext) => { await waitForRuleSuccess({ supertest, log, id: updatedRule.id }); }); - it('should not change properties of immutable rule when applying actions to it', async () => { + it('@skipInQA should not change properties of immutable rule when applying actions to it', async () => { // actions and throttle to be removed from assertion (it asserted in a separate test case) const actionsProps = ['actions', 'throttle']; @@ -139,7 +149,7 @@ export default ({ getService }: FtrProviderContext) => { expect(expected.immutable).to.be(true); // It should stay immutable true when returning }); - it('should be able to create a new webhook action and attach it to an immutable rule', async () => { + it('@skipInQA should be able to create a new webhook action and attach it to an immutable rule', async () => { const immutableRule = await getImmutableRule(); const hookAction = await createNewAction(supertest, log); const ruleToUpdate = getRuleWithWebHookAction( @@ -155,11 +165,12 @@ export default ({ getService }: FtrProviderContext) => { `${bodyToCompare.actions?.[0].uuid}` ); - expect(bodyToCompare.actions).to.eql(expected.actions); - expect(bodyToCompare.throttle).to.eql(expected.throttle); + const expectedRuleWithUserUpdated = updateUsername(expected, ELASTICSEARCH_USERNAME); + expect(bodyToCompare.actions).to.eql(expectedRuleWithUserUpdated.actions); + expect(bodyToCompare.throttle).to.eql(expectedRuleWithUserUpdated.throttle); }); - it('should be able to create a new webhook action, attach it to an immutable rule and the count of prepackaged rules should not increase. If this fails, suspect the immutable tags are not staying on the rule correctly.', async () => { + it('@skipInQA should be able to create a new webhook action, attach it to an immutable rule and the count of prepackaged rules should not increase. If this fails, suspect the immutable tags are not staying on the rule correctly.', async () => { const immutableRule = await getImmutableRule(); const hookAction = await createNewAction(supertest, log); const ruleToUpdate = getRuleWithWebHookAction( @@ -173,7 +184,7 @@ export default ({ getService }: FtrProviderContext) => { expect(status.rules_not_installed).to.eql(0); }); - it('should be able to create a new webhook action, attach it to an immutable rule and the rule should stay immutable when searching against immutable tags', async () => { + it('@skipInQA should be able to create a new webhook action, attach it to an immutable rule and the rule should stay immutable when searching against immutable tags', async () => { const immutableRule = await getImmutableRule(); const hookAction = await createNewAction(supertest, log); const ruleToUpdate = getRuleWithWebHookAction( @@ -190,8 +201,8 @@ export default ({ getService }: FtrProviderContext) => { `${bodyToCompare.actions?.[0].id}`, `${bodyToCompare.actions?.[0].uuid}` ); - - expect(bodyToCompare.actions).to.eql(expected.actions); + const expectedRuleWithUserUpdated = updateUsername(expected, ELASTICSEARCH_USERNAME); + expect(bodyToCompare.actions).to.eql(expectedRuleWithUserUpdated.actions); expect(bodyToCompare.immutable).to.be(true); }); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/create_new_action.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/create_new_action.ts new file mode 100644 index 0000000000000..7cbe8e858a043 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/create_new_action.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ToolingLog } from '@kbn/tooling-log'; +import type SuperTest from 'supertest'; + +import { getWebHookAction } from './get_web_hook_action'; + +/** + * Helper to cut down on the noise in some of the tests. This + * creates a new action and expects a 200 and does not do any retries. + * @param supertest The supertest deps + */ +export const createNewAction = async ( + supertest: SuperTest.SuperTest, + log: ToolingLog +) => { + const response = await supertest + .post('/api/actions/action') + .set('kbn-xsrf', 'true') + .set('x-elastic-internal-origin', 'foo') + .send(getWebHookAction()); + if (response.status !== 200) { + log.error( + `Did not get an expected 200 "ok" when creating a new action. CI issues could happen. Suspect this line if you are seeing CI issues. body: ${JSON.stringify( + response.body + )}, status: ${JSON.stringify(response.status)}` + ); + } + return response.body; +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/index.ts index 438d983a69e05..d9b65ba596dd6 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/actions/index.ts @@ -7,3 +7,4 @@ export * from './get_slack_action'; export * from './get_web_hook_action'; export * from './remove_uuid_from_actions'; +export * from './create_new_action'; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_rule_with_web_hook_action.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_rule_with_web_hook_action.ts new file mode 100644 index 0000000000000..6437df274098d --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_rule_with_web_hook_action.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + RuleCreateProps, + RuleUpdateProps, +} from '@kbn/security-solution-plugin/common/api/detection_engine'; +import { getSimpleRule } from './get_simple_rule'; + +export const getRuleWithWebHookAction = ( + id: string, + enabled = false, + rule?: RuleCreateProps +): RuleCreateProps | RuleUpdateProps => { + const finalRule = rule != null ? { ...rule, enabled } : getSimpleRule('rule-1', enabled); + return { + ...finalRule, + throttle: 'rule', + actions: [ + { + group: 'default', + id, + params: { + body: '{}', + }, + action_type_id: '.webhook', + }, + ], + }; +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_simple_rule_output_with_web_hook_action.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_simple_rule_output_with_web_hook_action.ts new file mode 100644 index 0000000000000..7ecee679e50b3 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/get_simple_rule_output_with_web_hook_action.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { NOTIFICATION_DEFAULT_FREQUENCY } from '@kbn/security-solution-plugin/common/constants'; +import { getSimpleRuleOutput } from './get_simple_rule_output'; +import { RuleWithoutServerGeneratedProperties } from './remove_server_generated_properties'; + +export const getSimpleRuleOutputWithWebHookAction = ( + actionId: string, + uuid: string +): RuleWithoutServerGeneratedProperties => ({ + ...getSimpleRuleOutput(), + actions: [ + { + action_type_id: '.webhook', + group: 'default', + id: actionId, + params: { + body: '{}', + }, + uuid, + frequency: NOTIFICATION_DEFAULT_FREQUENCY, + }, + ], +}); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/index.ts index ba91dea27743e..0170faa8ceeda 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/index.ts @@ -26,5 +26,9 @@ export * from './find_immutable_rule_by_id'; export * from './create_rule_with_exception_entries'; export * from './downgrade_immutable_rule'; export * from './get_eql_rule_for_alert_testing'; +export * from './get_rule_with_web_hook_action'; +export * from './get_simple_rule_output_with_web_hook_action'; +export * from './rule_to_update_schema'; +export * from './update_rule'; export * from './prebuilt_rules'; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/rule_to_update_schema.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/rule_to_update_schema.ts new file mode 100644 index 0000000000000..f6669a1325eb1 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/rule_to_update_schema.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + RuleResponse, + RuleUpdateProps, +} from '@kbn/security-solution-plugin/common/api/detection_engine'; +import { omit, pickBy } from 'lodash'; + +const propertiesToRemove = [ + 'id', + 'immutable', + 'updated_at', + 'updated_by', + 'created_at', + 'created_by', + 'related_integrations', + 'required_fields', + 'revision', + 'setup', + 'execution_summary', +]; + +/** + * transforms RuleResponse rule to RuleUpdateProps + * returned result can be used in rule update API calls + */ +export const ruleToUpdateSchema = (rule: RuleResponse): RuleUpdateProps => { + const removedProperties = omit(rule, propertiesToRemove); + + // We're only removing undefined values, so this cast correctly narrows the type + return pickBy(removedProperties, (value) => value !== undefined) as RuleUpdateProps; +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/update_rule.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/update_rule.ts new file mode 100644 index 0000000000000..53c1beb272764 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/rules/update_rule.ts @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { ToolingLog } from '@kbn/tooling-log'; +import type SuperTest from 'supertest'; + +import { DETECTION_ENGINE_RULES_URL } from '@kbn/security-solution-plugin/common/constants'; +import { + RuleUpdateProps, + RuleResponse, +} from '@kbn/security-solution-plugin/common/api/detection_engine'; + +/** + * Helper to cut down on the noise in some of the tests. This checks for + * an expected 200 still and does not do any retries. + * @param supertest The supertest deps + * @param rule The rule to create + */ +export const updateRule = async ( + supertest: SuperTest.SuperTest, + log: ToolingLog, + updatedRule: RuleUpdateProps +): Promise => { + const response = await supertest + .put(DETECTION_ENGINE_RULES_URL) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '2023-10-31') + .send(updatedRule); + if (response.status !== 200) { + log.error( + `Did not get an expected 200 "ok" when updating a rule (updateRule). CI issues could happen. Suspect this line if you are seeing CI issues. body: ${JSON.stringify( + response.body + )}, status: ${JSON.stringify(response.status)}` + ); + } + return response.body; +}; From 4493efa9a9d12551dcd75281fcb4a05d9fe2d571 Mon Sep 17 00:00:00 2001 From: Coen Warmer Date: Mon, 30 Oct 2023 18:42:04 +0100 Subject: [PATCH 19/45] i18n Translation ESLint Rule Fixes And Improvements (#169955) --- packages/kbn-eslint-plugin-i18n/README.mdx | 12 + .../helpers/get_i18n_import_fixer.ts | 101 +++++-- .../helpers/get_intent_from_node.ts | 24 +- .../helpers/utils.test.ts | 55 ++++ .../kbn-eslint-plugin-i18n/helpers/utils.ts | 14 + ..._translated_with_formatted_message.test.ts | 256 +++++++++++----- ...ld_be_translated_with_formatted_message.ts | 58 ++-- ...ngs_should_be_translated_with_i18n.test.ts | 284 ++++++++++++------ .../strings_should_be_translated_with_i18n.ts | 60 ++-- 9 files changed, 615 insertions(+), 249 deletions(-) create mode 100644 packages/kbn-eslint-plugin-i18n/helpers/utils.test.ts diff --git a/packages/kbn-eslint-plugin-i18n/README.mdx b/packages/kbn-eslint-plugin-i18n/README.mdx index 100f83d167b6e..174457477e81a 100644 --- a/packages/kbn-eslint-plugin-i18n/README.mdx +++ b/packages/kbn-eslint-plugin-i18n/README.mdx @@ -20,3 +20,15 @@ It kicks in on JSXText elements and specific JSXAttributes (`label` and `aria-la This rule warns engineers to translate their strings by using `` from the '@kbn/i18n-react' package. It provides an autofix that takes into account the context of the translatable string in the JSX tree and to generate a translation ID. It kicks in on JSXText elements and specific JSXAttributes (`label` and `aria-label`) which expect a translated value. + +## Exemptions and exceptions + +A JSXText element or JSXAttribute `label` or `aria-label` of which the value is: + +- wrapped in a `EuiCode` or `EuiBetaBadge` component, +- made up of non alpha characters such as `!@#$%^&*(){}` or numbers, +- wrapped in three backticks, + +are exempt from this rule. + +If this rule kicks in on a string value that you don't like, you can escape it by wrapping the string inside a JSXExpression: `{'my escaped value'}`. diff --git a/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_import_fixer.ts b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_import_fixer.ts index f26bc62c555f0..cf3a7330f7584 100644 --- a/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_import_fixer.ts +++ b/packages/kbn-eslint-plugin-i18n/helpers/get_i18n_import_fixer.ts @@ -8,9 +8,6 @@ import { SourceCode } from 'eslint'; -const KBN_I18N_I18N_IMPORT = "import { i18n } from '@kbn/i18n';" as const; -const KBN_I18N_REACT_FORMATTED_MESSAGE_IMPORT = - "import { FormattedMessage } from '@kbn/i18n-react';" as const; export function getI18nImportFixer({ sourceCode, mode, @@ -18,33 +15,89 @@ export function getI18nImportFixer({ sourceCode: SourceCode; mode: 'i18n.translate' | 'FormattedMessage'; }) { - const hasI18nImportLine = Boolean( - sourceCode.lines.find((l) => - mode === 'i18n.translate' - ? l === KBN_I18N_I18N_IMPORT - : l === KBN_I18N_REACT_FORMATTED_MESSAGE_IMPORT - ) - ); + let existingI18nImportLineIndex = -1; + let i18nImportLineToBeAdded = ''; - if (hasI18nImportLine) return { hasI18nImportLine }; + /** + * + * Searching through sourcecode to see if there is already an import of i18n package, + * and check if it includes the module we need. + * + * If any of these conditions are not met, we prepare the import line we need to add. + * + * */ - // Translation package has not been imported yet so we need to add it. - // Pretty safe bet to add it underneath the React import. - const reactImportLineIndex = sourceCode.lines.findIndex((l) => l.includes("from 'react'")); + if (mode === 'i18n.translate') { + existingI18nImportLineIndex = sourceCode.lines.findIndex((l) => l.includes("from '@kbn/i18n'")); - const targetLine = sourceCode.lines[reactImportLineIndex]; - const column = targetLine.length; + const i18nImportLineInSource = sourceCode.lines[existingI18nImportLineIndex]; - const start = sourceCode.getIndexFromLoc({ line: reactImportLineIndex + 1, column: 0 }); - const end = sourceCode.getIndexFromLoc({ - line: reactImportLineIndex + 1, - column, - }); + if (i18nImportLineInSource) { + const modules = i18nImportLineInSource.split('{')[1].split('}')[0].trim(); + + if (modules.split(',').includes('i18n')) { + return { hasI18nImportLine: true }; + } + + // Existing import is something like: `import { SomeOtherModule } from '@kbn/i18n';` + i18nImportLineToBeAdded = `import { ${modules}, i18n } from '@kbn/i18n';`; + } else { + i18nImportLineToBeAdded = `import { i18n } from '@kbn/i18n';`; + } + } + + if (mode === 'FormattedMessage') { + existingI18nImportLineIndex = sourceCode.lines.findIndex((l) => + l.includes("from '@kbn/i18n-react'") + ); + const i18nImportLineInSource = sourceCode.lines[existingI18nImportLineIndex]; + + if (i18nImportLineInSource) { + const modules = i18nImportLineInSource.split('{')[1].split('}')[0].trim(); + + if (modules.split(',').includes('FormattedMessage')) { + return { hasI18nImportLine: true }; + } + + // Existing import is something like: `import { SomeOtherModule } from '@kbn/i18n-react';` + i18nImportLineToBeAdded = `import { ${modules}, FormattedMessage } from '@kbn/i18n-react';`; + } else { + i18nImportLineToBeAdded = `import { FormattedMessage } from '@kbn/i18n-react';`; + } + } + + /** + * + * Determining where in the source code to add the import line. + * + * */ + + // If the file already has an import line for the translation package but it doesn't include the module we need, we need to add it. + if (existingI18nImportLineIndex > -1) { + const targetLine = sourceCode.lines[existingI18nImportLineIndex]; + const column = targetLine.length; + + const start = sourceCode.getIndexFromLoc({ line: existingI18nImportLineIndex + 1, column: 0 }); + const end = start + column; + + return { + i18nImportLine: i18nImportLineToBeAdded, + rangeToAddI18nImportLine: [start, end] as [number, number], + mode: 'replace', + }; + } + + // If the file doesn't have an import line for the translation package yet, we need to add it. + // Pretty safe bet to add it underneath the import line for React. + const lineIndex = sourceCode.lines.findIndex((l) => l.includes("from 'react'")); + const targetLine = sourceCode.lines[lineIndex]; + + const start = sourceCode.getIndexFromLoc({ line: lineIndex + 1, column: 0 }); + const end = start + targetLine.length; return { - hasI18nImportLine, - i18nPackageImportLine: - mode === 'i18n.translate' ? KBN_I18N_I18N_IMPORT : KBN_I18N_REACT_FORMATTED_MESSAGE_IMPORT, + i18nImportLine: i18nImportLineToBeAdded, rangeToAddI18nImportLine: [start, end] as [number, number], + mode: 'insert', }; } diff --git a/packages/kbn-eslint-plugin-i18n/helpers/get_intent_from_node.ts b/packages/kbn-eslint-plugin-i18n/helpers/get_intent_from_node.ts index 687bfd31cfba2..d27f2407ce64c 100644 --- a/packages/kbn-eslint-plugin-i18n/helpers/get_intent_from_node.ts +++ b/packages/kbn-eslint-plugin-i18n/helpers/get_intent_from_node.ts @@ -6,15 +6,18 @@ * Side Public License, v 1. */ import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'; -import { lowerCaseFirstLetter, upperCaseFirstLetter } from './utils'; +import { cleanString, lowerCaseFirstLetter, upperCaseFirstLetter } from './utils'; -export function getIntentFromNode(value: string, parent: TSESTree.Node | undefined): string { +const EXEMPTED_TAG_NAMES = ['EuiCode', 'EuiBetaBadge', 'FormattedMessage']; + +export function getIntentFromNode( + value: string, + parent: TSESTree.Node | undefined +): string | false { const processedValue = lowerCaseFirstLetter( - value - .replace(/[?!@#$%^&*()_+\][{}|/<>,'"]/g, '') - .trim() + cleanString(value) .split(' ') - .filter((v, i) => i < 4) + .filter((_, i) => i < 4) .map(upperCaseFirstLetter) .join('') ); @@ -27,6 +30,11 @@ export function getIntentFromNode(value: string, parent: TSESTree.Node | undefin ) { const parentTagName = String(parent.openingElement.name.name); + // Exceptions + if (EXEMPTED_TAG_NAMES.includes(parentTagName)) { + return false; + } + if (parentTagName.includes('Eui')) { return `${processedValue}${parentTagName.replace('Eui', '')}Label`; } @@ -45,6 +53,10 @@ export function getIntentFromNode(value: string, parent: TSESTree.Node | undefin ) { const parentTagName = String(parent.parent.name.name); + if (EXEMPTED_TAG_NAMES.includes(parentTagName)) { + return false; + } + return `${lowerCaseFirstLetter(parentTagName)}.${processedValue}Label`; } diff --git a/packages/kbn-eslint-plugin-i18n/helpers/utils.test.ts b/packages/kbn-eslint-plugin-i18n/helpers/utils.test.ts new file mode 100644 index 0000000000000..9d36c7a7e1e9c --- /dev/null +++ b/packages/kbn-eslint-plugin-i18n/helpers/utils.test.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { cleanString, lowerCaseFirstLetter } from './utils'; + +describe('Utils', () => { + describe('lowerCaseFirstLetter', () => { + it('should lowercase the first letter', () => { + expect(lowerCaseFirstLetter('Hello')).toBe('hello'); + expect(lowerCaseFirstLetter('GreatSuccessYes')).toBe('greatSuccessYes'); + expect(lowerCaseFirstLetter('How is it going?')).toBe('how is it going?'); + }); + + it('should lowercase all letters if the passed string is in ALL CAPS', () => { + expect(lowerCaseFirstLetter('HELLO')).toBe('hello'); + expect(lowerCaseFirstLetter('GREATSUCCESSYES')).toBe('greatsuccessyes'); + }); + }); + + describe('cleanString', () => { + it('should remove all numbers', () => { + expect(cleanString('123')).toBe(''); + }); + + it('should remove all white spaces from beginning and end', () => { + expect(cleanString(' abc ')).toBe('abc'); + expect(cleanString(' This is a test ')).toBe('This is a test'); + expect( + cleanString(` + + + hello + + + + great!`) + ).toBe('hello great'); + }); + + it('should remove all non alphabet characters', () => { + expect(cleanString('abc123!@#')).toBe('abc'); + expect(cleanString('!@#$%^&*()_+{}|')).toBe(''); + expect(cleanString(' Hey, this is great! Success. ')).toBe('Hey this is great Success'); + }); + + it('should leave markdown alone', () => { + expect(cleanString('```hello```')).toBe(''); + }); + }); +}); diff --git a/packages/kbn-eslint-plugin-i18n/helpers/utils.ts b/packages/kbn-eslint-plugin-i18n/helpers/utils.ts index 74ce8aa7c5c55..bfa0ceaf2aecc 100644 --- a/packages/kbn-eslint-plugin-i18n/helpers/utils.ts +++ b/packages/kbn-eslint-plugin-i18n/helpers/utils.ts @@ -7,6 +7,8 @@ */ export function lowerCaseFirstLetter(str: string) { + if (isUpperCase(str)) return str.toLowerCase(); + return str.charAt(0).toLowerCase() + str.slice(1); } @@ -17,3 +19,15 @@ export function upperCaseFirstLetter(str: string) { export function isTruthy(value: T): value is NonNullable { return value != null; } + +function isUpperCase(val: string) { + return /^[A-Z]+$/.test(val); +} + +export function cleanString(str: string) { + return str + .replace(/```\w*```/g, '') + .replace(/\s+/g, ' ') + .replace(/[^a-zA-Z\s]*/g, '') + .trim(); +} diff --git a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.test.ts b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.test.ts index 10bdbda351892..009fac255fc63 100644 --- a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.test.ts +++ b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.test.ts @@ -38,165 +38,275 @@ const babelTester = [ }), ] as const; -const valid = [ +const invalid: RuleTester.InvalidTestCase[] = [ { + name: 'A JSX element with a string literal should be translated with i18n', filename: 'x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; + +function TestComponent() { + return ( +

    This is a test
    + ) +}`, + errors: [ + { + line: 6, + message: `Strings should be translated with . Use the autofix suggestion or add your own.`, + }, + ], + output: ` +import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; function TestComponent() { - return ( -
    + return ( +
    - ) + ) }`, }, { + name: 'A JSX element with a string literal that are inside an Eui component should take the component name of the parent into account', filename: 'x-pack/plugins/observability/public/another_component.tsx', code: ` import React from 'react'; + +function AnotherComponent() { + return ( + + + + This is a test + + + + ) +}`, + errors: [ + { + line: 9, + message: `Strings should be translated with . Use the autofix suggestion or add your own.`, + }, + ], + output: ` +import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; function AnotherComponent() { - return ( - - - - + return ( + + + + - - - - ) + + + + ) }`, }, { + name: 'When no import of the translation module is present, the import line should be added', filename: 'x-pack/plugins/observability/public/yet_another_component.tsx', code: ` import React from 'react'; + +function YetAnotherComponent() { + return ( +
    + Select me +
    + ) +}`, + errors: [ + { + line: 7, + message: `Strings should be translated with . Use the autofix suggestion or add your own.`, + }, + ], + output: ` +import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; function YetAnotherComponent() { - return ( -
    - + return ( +
    + -
    - ) +
    + ) }`, }, { + name: 'Import lines without the necessary translation module should be updated to include i18n', filename: 'x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; +import { SomeOtherModule } from '@kbn/i18n-react'; function TestComponent() { - return ( - } /> - ) - }`, - }, -]; - -const invalid = [ - { - filename: valid[0].filename, - code: ` -import React from 'react'; - -function TestComponent() { - return ( -
    This is a test
    - ) + return ( + + ) }`, errors: [ { - line: 6, + line: 7, message: `Strings should be translated with . Use the autofix suggestion or add your own.`, }, ], - output: valid[0].code, + output: ` +import React from 'react'; +import { SomeOtherModule, FormattedMessage } from '@kbn/i18n-react'; + +function TestComponent() { + return ( + } /> + ) +}`, }, { - filename: valid[1].filename, + name: 'JSX elements that have a label or aria-label prop with a string value should be translated with i18n', + filename: 'x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; -function AnotherComponent() { - return ( - - - - This is a test - - - - ) +function TestComponent() { + return ( + + ) }`, errors: [ { - line: 9, + line: 7, message: `Strings should be translated with . Use the autofix suggestion or add your own.`, }, ], - output: valid[1].code, + output: ` +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; + +function TestComponent() { + return ( + } /> + ) +}`, }, { - filename: valid[2].filename, + name: 'JSX elements that have a label or aria-label prop with a JSXExpression value that is a string should be translated with i18n', + filename: 'x-pack/plugins/observability/public/test_component.tsx', code: ` -import React from 'react'; + import React from 'react'; + import { FormattedMessage } from '@kbn/i18n-react'; -function YetAnotherComponent() { + function TestComponent() { return ( -
    - Select me -
    + ) -}`, + }`, errors: [ { line: 7, message: `Strings should be translated with . Use the autofix suggestion or add your own.`, }, ], - output: valid[2].code, + output: ` + import React from 'react'; + import { FormattedMessage } from '@kbn/i18n-react'; + + function TestComponent() { + return ( + } /> + ) + }`, }, +]; + +const valid: RuleTester.ValidTestCase[] = [ { - filename: valid[3].filename, + name: 'A JSXText element inside a EuiCode component should not be translated', + filename: 'x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; -import { FormattedMessage } from '@kbn/i18n-react'; function TestComponent() { - return ( - - ) - }`, - errors: [ - { - line: 7, - message: `Strings should be translated with . Use the autofix suggestion or add your own.`, - }, - ], - output: valid[3].code, + return ( + This is a test + ) +}`, + }, + { + name: 'A JSXText element that contains anything other than alpha characters should not be translated', + filename: 'x-pack/plugins/observability/public/test_component.tsx', + code: ` +import React from 'react'; + +function TestComponent() { + return ( +
    !@#$%^&*()_+{}123 456 789
    + ) +}`, + }, + { + name: 'A JSXText element that is wrapped in three backticks (markdown) should not be translated', + filename: 'x-pack/plugins/observability/public/test_component.tsx', + code: ` +import React from 'react'; + +function TestComponent() { + return ( +
    \`\`\`hello\`\`\`
    + ) +}`, + }, + { + name: invalid[0].name, + filename: invalid[0].filename, + code: invalid[0].output as string, + }, + { + name: invalid[1].name, + filename: invalid[1].filename, + code: invalid[1].output as string, + }, + { + name: invalid[2].name, + filename: invalid[2].filename, + code: invalid[2].output as string, + }, + { + name: invalid[3].name, + filename: invalid[3].filename, + code: invalid[3].output as string, + }, + { + name: invalid[4].name, + filename: invalid[4].filename, + code: invalid[4].output as string, + }, + { + name: invalid[5].name, + filename: invalid[5].filename, + code: invalid[5].output as string, }, ]; for (const [name, tester] of [tsTester, babelTester]) { describe(name, () => { tester.run( - '@kbn/event_generating_elements_should_be_instrumented', + '@kbn/strings_should_be_translated_with_formatted_message', StringsShouldBeTranslatedWithFormattedMessage, { valid, diff --git a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.ts b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.ts index 67e2aaec256d7..77b5918951036 100644 --- a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.ts +++ b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_formatted_message.ts @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -import { TSESTree } from '@typescript-eslint/typescript-estree'; +import type { TSESTree } from '@typescript-eslint/typescript-estree'; import type { Rule } from 'eslint'; import { getIntentFromNode } from '../helpers/get_intent_from_node'; import { getI18nIdentifierFromFilePath } from '../helpers/get_i18n_identifier_from_file_path'; import { getFunctionName } from '../helpers/get_function_name'; import { getI18nImportFixer } from '../helpers/get_i18n_import_fixer'; -import { isTruthy } from '../helpers/utils'; +import { cleanString, isTruthy } from '../helpers/utils'; export const StringsShouldBeTranslatedWithFormattedMessage: Rule.RuleModule = { meta: { @@ -24,7 +24,7 @@ export const StringsShouldBeTranslatedWithFormattedMessage: Rule.RuleModule = { return { JSXText: (node: TSESTree.JSXText) => { - const value = node.value.trim(); + const value = cleanString(node.value); // If the JSXText element is empty we don't need to do anything if (!value) return; @@ -34,22 +34,21 @@ export const StringsShouldBeTranslatedWithFormattedMessage: Rule.RuleModule = { const whiteSpaces = node.value.match(regex)?.[1] ?? ''; // Start building the translation ID suggestion + const intent = getIntentFromNode(value, node.parent); + if (intent === false) return; + const i18nAppId = getI18nIdentifierFromFilePath(filename, cwd); const functionDeclaration = getScope().block as TSESTree.FunctionDeclaration; const functionName = getFunctionName(functionDeclaration); - const intent = getIntentFromNode(value, node.parent); const translationIdSuggestion = `${i18nAppId}.${functionName}.${intent}`; // 'xpack.observability.overview.logs.loadMoreLabel' - // Check if i18n has already been imported into the file. - const { - hasI18nImportLine, - i18nPackageImportLine: i18nImportLine, - rangeToAddI18nImportLine, - } = getI18nImportFixer({ - sourceCode, - mode: 'FormattedMessage', - }); + // Check if i18n has already been imported into the file + const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, mode } = + getI18nImportFixer({ + sourceCode, + mode: 'FormattedMessage', + }); // Show warning to developer and offer autofix suggestion report({ @@ -65,8 +64,10 @@ export const StringsShouldBeTranslatedWithFormattedMessage: Rule.RuleModule = { defaultMessage="${value}" />` ), - !hasI18nImportLine - ? fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) + !hasI18nImportLine && rangeToAddI18nImportLine + ? mode === 'replace' + ? fixer.replaceTextRange(rangeToAddI18nImportLine, i18nImportLine) + : fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) : null, ].filter(isTruthy); }, @@ -84,33 +85,32 @@ export const StringsShouldBeTranslatedWithFormattedMessage: Rule.RuleModule = { 'value' in node.value.expression && typeof node.value.expression.value === 'string' ) { - val = node.value.expression.value; + val = cleanString(node.value.expression.value); } // label="foo" if (node.value && 'value' in node.value && typeof node.value.value === 'string') { - val = node.value.value; + val = cleanString(node.value.value); } if (!val) return; // Start building the translation ID suggestion + const intent = getIntentFromNode(val, node); + if (intent === false) return; + const i18nAppId = getI18nIdentifierFromFilePath(filename, cwd); const functionDeclaration = getScope().block as TSESTree.FunctionDeclaration; const functionName = getFunctionName(functionDeclaration); - const intent = getIntentFromNode(val, node); const translationIdSuggestion = `${i18nAppId}.${functionName}.${intent}`; // 'xpack.observability.overview.logs.loadMoreLabel' // Check if i18n has already been imported into the file. - const { - hasI18nImportLine, - i18nPackageImportLine: i18nImportLine, - rangeToAddI18nImportLine, - } = getI18nImportFixer({ - sourceCode, - mode: 'FormattedMessage', - }); + const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, mode } = + getI18nImportFixer({ + sourceCode, + mode: 'FormattedMessage', + }); // Show warning to developer and offer autofix suggestion report({ @@ -123,8 +123,10 @@ export const StringsShouldBeTranslatedWithFormattedMessage: Rule.RuleModule = { node.value!.range, `{}` ), - !hasI18nImportLine - ? fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) + !hasI18nImportLine && rangeToAddI18nImportLine + ? mode === 'replace' + ? fixer.replaceTextRange(rangeToAddI18nImportLine, i18nImportLine) + : fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) : null, ].filter(isTruthy); }, diff --git a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.test.ts b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.test.ts index dc938cd6effd3..f470ed885682f 100644 --- a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.test.ts +++ b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.test.ts @@ -38,158 +38,264 @@ const babelTester = [ }), ] as const; -const valid = [ +const invalid: RuleTester.InvalidTestCase[] = [ { + name: 'A JSX element with a string literal should be translated with i18n', filename: 'x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; + +function TestComponent() { + return ( +
    This is a test
    + ) +}`, + errors: [ + { + line: 6, + message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, + }, + ], + output: ` +import React from 'react'; import { i18n } from '@kbn/i18n'; function TestComponent() { - return ( -
    {i18n.translate('app_not_found_in_i18nrc.testComponent.div.thisIsATestLabel', { defaultMessage: 'This is a test'})}
    - ) - }`, + return ( +
    {i18n.translate('app_not_found_in_i18nrc.testComponent.div.thisIsATestLabel', { defaultMessage: 'This is a test' })}
    + ) +}`, }, { + name: 'A JSX element with a string literal that are inside an Eui component should take the component name of the parent into account', filename: 'x-pack/plugins/observability/public/another_component.tsx', code: ` import React from 'react'; -import { i18n } from '@kbn/i18n'; function AnotherComponent() { - return ( - - - - {i18n.translate('app_not_found_in_i18nrc.anotherComponent.thisIsATestButtonLabel', { defaultMessage: 'This is a test'})} - - - - ) - }`, - }, - { - filename: 'x-pack/plugins/observability/public/yet_another_component.tsx', - code: ` - import React from 'react'; -import { i18n } from '@kbn/i18n'; - - function YetAnotherComponent() { - return ( -
    - {i18n.translate('app_not_found_in_i18nrc.yetAnotherComponent.selectMeSelectLabel', { defaultMessage: 'Select me'})} -
    - ) - }`, - }, - { - filename: 'x-pack/plugins/observability/public/test_component.tsx', - code: ` + return ( + + + + This is a test + + + + ) +}`, + errors: [ + { + line: 9, + message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, + }, + ], + output: ` import React from 'react'; import { i18n } from '@kbn/i18n'; -function TestComponent() { - return ( - - ) - }`, +function AnotherComponent() { + return ( + + + + {i18n.translate('app_not_found_in_i18nrc.anotherComponent.thisIsATestButtonLabel', { defaultMessage: 'This is a test' })} + + + + ) +}`, }, -]; - -const invalid = [ { - filename: valid[0].filename, + name: 'When no import of the translation module is present, the import line should be added', + filename: 'x-pack/plugins/observability/public/yet_another_component.tsx', code: ` import React from 'react'; -function TestComponent() { - return ( -
    This is a test
    - ) - }`, +function YetAnotherComponent() { + return ( +
    + Select me +
    + ) +}`, errors: [ { - line: 6, + line: 7, message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, }, ], - output: valid[0].code, + output: ` +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +function YetAnotherComponent() { + return ( +
    + {i18n.translate('app_not_found_in_i18nrc.yetAnotherComponent.selectMeSelectLabel', { defaultMessage: 'Select me' })} +
    + ) +}`, }, { - filename: valid[1].filename, + name: 'Import lines without the necessary translation module should be updated to include i18n', + filename: 'x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; +import { SomeOtherModule } from '@kbn/i18n'; -function AnotherComponent() { - return ( - - - - This is a test - - - - ) - }`, +function TestComponent() { + return ( + + ) +}`, errors: [ { - line: 9, + line: 7, message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, }, ], - output: valid[1].code, + output: ` +import React from 'react'; +import { SomeOtherModule, i18n } from '@kbn/i18n'; + +function TestComponent() { + return ( + + ) +}`, }, { - filename: valid[2].filename, + name: 'JSX elements that have a label or aria-label prop with a string value should be translated with i18n', + filename: 'x-pack/plugins/observability/public/test_component.tsx', code: ` - import React from 'react'; - - function YetAnotherComponent() { - return ( -
    - Select me -
    - ) - }`, +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +function TestComponent() { + return ( + + ) +}`, errors: [ { line: 7, message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, }, ], - output: valid[2].code, + output: ` +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +function TestComponent() { + return ( + + ) +}`, }, { - filename: valid[3].filename, + name: 'JSX elements that have a label or aria-label prop with a JSXExpression value that is a string should be translated with i18n', + filename: 'x-pack/plugins/observability/public/test_component.tsx', code: ` import React from 'react'; import { i18n } from '@kbn/i18n'; function TestComponent() { - return ( - - ) - }`, + return ( + + ) +}`, errors: [ { line: 7, message: `Strings should be translated with i18n. Use the autofix suggestion or add your own.`, }, ], - output: valid[3].code, + output: ` +import React from 'react'; +import { i18n } from '@kbn/i18n'; + +function TestComponent() { + return ( + + ) +}`, + }, +]; + +const valid: RuleTester.ValidTestCase[] = [ + { + name: 'A JSXText element inside a EuiCode component should not be translated', + filename: 'x-pack/plugins/observability/public/test_component.tsx', + code: ` +import React from 'react'; + +function TestComponent() { + return ( + This is a test + ) +}`, + }, + { + name: 'A JSXText element that contains anything other than alpha characters should not be translated', + filename: 'x-pack/plugins/observability/public/test_component.tsx', + code: ` +import React from 'react'; + +function TestComponent() { + return ( +
    !@#$%^&*()_+{}123 456 789
    + ) +}`, + }, + { + name: 'A JSXText element that is wrapped in three backticks (markdown) should not be translated', + filename: 'x-pack/plugins/observability/public/test_component.tsx', + code: ` +import React from 'react'; + +function TestComponent() { + return ( +
    \`\`\`hello\`\`\`
    + ) +}`, + }, + { + name: invalid[0].name, + filename: invalid[0].filename, + code: invalid[0].output as string, + }, + { + name: invalid[1].name, + filename: invalid[1].filename, + code: invalid[1].output as string, + }, + { + name: invalid[2].name, + filename: invalid[2].filename, + code: invalid[2].output as string, + }, + { + name: invalid[3].name, + filename: invalid[3].filename, + code: invalid[3].output as string, + }, + { + name: invalid[4].name, + filename: invalid[4].filename, + code: invalid[4].output as string, + }, + { + name: invalid[5].name, + filename: invalid[5].filename, + code: invalid[5].output as string, }, ]; for (const [name, tester] of [tsTester, babelTester]) { describe(name, () => { - tester.run( - '@kbn/event_generating_elements_should_be_instrumented', - StringsShouldBeTranslatedWithI18n, - { - valid, - invalid, - } - ); + tester.run('@kbn/strings_should_be_translated_with_i18n', StringsShouldBeTranslatedWithI18n, { + valid, + invalid, + }); }); } diff --git a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.ts b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.ts index ba31f6109075a..fea04d33d555f 100644 --- a/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.ts +++ b/packages/kbn-eslint-plugin-i18n/rules/strings_should_be_translated_with_i18n.ts @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -import { TSESTree } from '@typescript-eslint/typescript-estree'; +import type { TSESTree } from '@typescript-eslint/typescript-estree'; import type { Rule } from 'eslint'; import { getIntentFromNode } from '../helpers/get_intent_from_node'; import { getI18nIdentifierFromFilePath } from '../helpers/get_i18n_identifier_from_file_path'; import { getFunctionName } from '../helpers/get_function_name'; import { getI18nImportFixer } from '../helpers/get_i18n_import_fixer'; -import { isTruthy } from '../helpers/utils'; +import { cleanString, isTruthy } from '../helpers/utils'; export const StringsShouldBeTranslatedWithI18n: Rule.RuleModule = { meta: { @@ -24,7 +24,7 @@ export const StringsShouldBeTranslatedWithI18n: Rule.RuleModule = { return { JSXText: (node: TSESTree.JSXText) => { - const value = node.value.trim(); + const value = cleanString(node.value); // If the JSXText element is empty we don't need to do anything if (!value) return; @@ -34,22 +34,21 @@ export const StringsShouldBeTranslatedWithI18n: Rule.RuleModule = { const whiteSpaces = node.value.match(regex)?.[1] ?? ''; // Start building the translation ID suggestion + const intent = getIntentFromNode(value, node.parent); + if (intent === false) return; + const i18nAppId = getI18nIdentifierFromFilePath(filename, cwd); const functionDeclaration = getScope().block as TSESTree.FunctionDeclaration; const functionName = getFunctionName(functionDeclaration); - const intent = getIntentFromNode(value, node.parent); const translationIdSuggestion = `${i18nAppId}.${functionName}.${intent}`; // 'xpack.observability.overview.logs.loadMoreLabel' // Check if i18n has already been imported into the file - const { - hasI18nImportLine, - i18nPackageImportLine: i18nImportLine, - rangeToAddI18nImportLine, - } = getI18nImportFixer({ - sourceCode, - mode: 'i18n.translate', - }); + const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, mode } = + getI18nImportFixer({ + sourceCode, + mode: 'i18n.translate', + }); // Show warning to developer and offer autofix suggestion report({ @@ -60,10 +59,12 @@ export const StringsShouldBeTranslatedWithI18n: Rule.RuleModule = { return [ fixer.replaceText( node, - `${whiteSpaces}{i18n.translate('${translationIdSuggestion}', { defaultMessage: '${value}'})}` + `${whiteSpaces}{i18n.translate('${translationIdSuggestion}', { defaultMessage: '${value}' })}` ), - !hasI18nImportLine - ? fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) + !hasI18nImportLine && rangeToAddI18nImportLine + ? mode === 'replace' + ? fixer.replaceTextRange(rangeToAddI18nImportLine, i18nImportLine) + : fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) : null, ].filter(isTruthy); }, @@ -81,33 +82,32 @@ export const StringsShouldBeTranslatedWithI18n: Rule.RuleModule = { 'value' in node.value.expression && typeof node.value.expression.value === 'string' ) { - val = node.value.expression.value; + val = cleanString(node.value.expression.value); } // label="foo" if (node.value && 'value' in node.value && typeof node.value.value === 'string') { - val = node.value.value; + val = cleanString(node.value.value); } if (!val) return; // Start building the translation ID suggestion + const intent = getIntentFromNode(val, node); + if (intent === false) return; + const i18nAppId = getI18nIdentifierFromFilePath(filename, cwd); const functionDeclaration = getScope().block as TSESTree.FunctionDeclaration; const functionName = getFunctionName(functionDeclaration); - const intent = getIntentFromNode(val, node); const translationIdSuggestion = `${i18nAppId}.${functionName}.${intent}`; // 'xpack.observability.overview.logs.loadMoreLabel' // Check if i18n has already been imported into the file. - const { - hasI18nImportLine, - i18nPackageImportLine: i18nImportLine, - rangeToAddI18nImportLine, - } = getI18nImportFixer({ - sourceCode, - mode: 'i18n.translate', - }); + const { hasI18nImportLine, i18nImportLine, rangeToAddI18nImportLine, mode } = + getI18nImportFixer({ + sourceCode, + mode: 'i18n.translate', + }); // Show warning to developer and offer autofix suggestion report({ @@ -118,10 +118,12 @@ export const StringsShouldBeTranslatedWithI18n: Rule.RuleModule = { return [ fixer.replaceTextRange( node.value!.range, - `{i18n.translate('${translationIdSuggestion}', { defaultMessage: '${val}'})}` + `{i18n.translate('${translationIdSuggestion}', { defaultMessage: '${val}' })}` ), - !hasI18nImportLine - ? fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) + !hasI18nImportLine && rangeToAddI18nImportLine + ? mode === 'replace' + ? fixer.replaceTextRange(rangeToAddI18nImportLine, i18nImportLine) + : fixer.insertTextAfterRange(rangeToAddI18nImportLine, `\n${i18nImportLine}`) : null, ].filter(isTruthy); }, From dbc64e526e841e8b9e50128d19d30312d73b9cb6 Mon Sep 17 00:00:00 2001 From: Nathan Reese Date: Mon, 30 Oct 2023 12:06:23 -0600 Subject: [PATCH 20/45] [maps] only show vector tile inspector panel when map uses elasticsearch vector tile API (#170043) Closes https://github.com/elastic/kibana/issues/170042 PR disables vector tile adapter when map does not contain vector tile layers ### Test 1) create new map 2) add documents layer 3) set scaling to "limit to 10000" 4) open inspector. Notice inspector only displays request adapter 5) change scaling to "vector tiles" 6) open inspector. Notice inspector displays vector tile adapter and request adapter --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../inspector/vector_tile_adapter/vector_tile_adapter.ts | 4 ++++ .../vector_tile_adapter/vector_tile_inspector_view.tsx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_adapter.ts b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_adapter.ts index d0210367ab3b3..51a12368d6ad4 100644 --- a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_adapter.ts +++ b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_adapter.ts @@ -22,6 +22,10 @@ export class VectorTileAdapter extends EventEmitter { this._onChange(); } + hasLayers() { + return Object.keys(this._layers).length > 0; + } + setTiles(tiles: Array<{ x: number; y: number; z: number }>) { this._tiles = tiles; this._onChange(); diff --git a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_inspector_view.tsx b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_inspector_view.tsx index 42d7423e6e789..560b2789d5a74 100644 --- a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_inspector_view.tsx +++ b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_inspector_view.tsx @@ -23,7 +23,7 @@ export const VectorTileInspectorView = { defaultMessage: 'View the vector tile search requests used to collect the data', }), shouldShow(adapters: Adapters) { - return Boolean(adapters.vectorTiles); + return Boolean(adapters.vectorTiles?.hasLayers()); }, component: (props: { adapters: Adapters }) => { return ; From e17988c3d8a75712862b2ed06598d76dff7412ac Mon Sep 17 00:00:00 2001 From: Jeramy Soucy Date: Mon, 30 Oct 2023 14:40:50 -0400 Subject: [PATCH 21/45] =?UTF-8?q?Upgrades=20axios@1.4.0=E2=86=921.6.0=20(#?= =?UTF-8?q?170070)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Upgrades the `axios` dependency from v1.4.0 to v1.6.0 wherever possible. We have lingering dependencies on older versions 0.21.4 and 0.26.1 via `@slack/webhook`@5.0.4 and `openai`@3.3.0 respectively. --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 5f144902b00e2..114bd462b8d80 100644 --- a/package.json +++ b/package.json @@ -854,7 +854,7 @@ "archiver": "^5.3.1", "async": "^3.2.3", "aws4": "^1.12.0", - "axios": "^1.4.0", + "axios": "^1.6.0", "base64-js": "^1.3.1", "bitmap-sdf": "^1.0.3", "blurhash": "^2.0.1", diff --git a/yarn.lock b/yarn.lock index 7d0bb20bda72c..2d38221a2d11a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11468,10 +11468,10 @@ axios@^0.26.0: dependencies: follow-redirects "^1.14.8" -axios@^1.3.4, axios@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" - integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== +axios@^1.3.4, axios@^1.4.0, axios@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102" + integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg== dependencies: follow-redirects "^1.15.0" form-data "^4.0.0" From 8fe512b8c23479d23b0f3bdcfd92fe0977673f9d Mon Sep 17 00:00:00 2001 From: Alexi Doak <109488926+doakalexi@users.noreply.github.com> Date: Mon, 30 Oct 2023 11:43:53 -0700 Subject: [PATCH 22/45] [ResponseOps] Hyperlinks in Slack messages are broken when there is "_" or "*" in the URL (#170067) Resolves https://github.com/elastic/kibana/issues/165673 ## Summary `escapeSlack` function in the mustasche_renderer breakes the hyperlinks by wrapping the URL with backticks. So the below markdown template does not work. `<{{context.alertDetailsUrl}}|View alert details>` This PR removes the code that adds backticks. With this change action variables with text wrapped in asterisks \*text\* will render as **text** or wrapped in underscores \_text\_ will render as _text_ . ### Checklist - [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 ### To verify - Create a slack connector - Create a rule that uses a slack connector and use the above markdown template - Verify that hyperlink is working properly in slack --- .../actions/server/lib/mustache_renderer.test.ts | 2 ++ x-pack/plugins/actions/server/lib/mustache_renderer.ts | 10 +++++++--- .../common/plugins/alerts/server/alert_types.ts | 1 + .../tests/alerting/group4/mustache_templates.ts | 6 ++++-- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/actions/server/lib/mustache_renderer.test.ts b/x-pack/plugins/actions/server/lib/mustache_renderer.test.ts index 3a02ce0d1a983..14bd5f47507e7 100644 --- a/x-pack/plugins/actions/server/lib/mustache_renderer.test.ts +++ b/x-pack/plugins/actions/server/lib/mustache_renderer.test.ts @@ -34,6 +34,7 @@ const variables = { ul: '_', st_lt: '*<', vl: '|', + link: 'https://te_st.com/', }; describe('mustache_renderer', () => { @@ -111,6 +112,7 @@ describe('mustache_renderer', () => { expect(renderMustacheString('{{ul}}', variables, 'slack')).toBe('`_`'); // html escapes not needed when using backtic escaping expect(renderMustacheString('{{st_lt}}', variables, 'slack')).toBe('`*<`'); + expect(renderMustacheString('{{link}}', variables, 'slack')).toBe('https://te_st.com/'); }); it('handles escape:json with commonly escaped strings', () => { diff --git a/x-pack/plugins/actions/server/lib/mustache_renderer.ts b/x-pack/plugins/actions/server/lib/mustache_renderer.ts index 37713167e9a34..c478d7e9ea1c3 100644 --- a/x-pack/plugins/actions/server/lib/mustache_renderer.ts +++ b/x-pack/plugins/actions/server/lib/mustache_renderer.ts @@ -144,10 +144,14 @@ function escapeSlack(value: unknown): string { if (value == null) return ''; const valueString = `${value}`; - // if the value contains * or _, escape the whole thing with back tics + // if the value contains * or _ and is not a url, escape the whole thing with back tics if (valueString.includes('_') || valueString.includes('*')) { - // replace unescapable back tics with single quote - return '`' + valueString.replace(/`/g, `'`) + '`'; + try { + new URL(valueString); + } catch (e) { + // replace unescapable back tics with single quote + return '`' + valueString.replace(/`/g, `'`) + '`'; + } } // otherwise, do "standard" escaping diff --git a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts index 70ae9edfffb9c..8bd5ebfad1b21 100644 --- a/x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts +++ b/x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts @@ -28,6 +28,7 @@ export const EscapableStrings = { escapableHtml: '<&>', escapableDoubleQuote: '"double quote"', escapableLineFeed: 'line\x0afeed', + escapableLink: 'https://te_st.com/', }; export const DeepContextVariables = { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/mustache_templates.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/mustache_templates.ts index 3103ea6fee794..946ecfcbe6ecd 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/mustache_templates.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/mustache_templates.ts @@ -84,7 +84,7 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon // from x-pack/test/alerting_api_integration/common/plugins/alerts/server/alert_types.ts, // const EscapableStrings const template = - '{{context.escapableBacktic}} -- {{context.escapableBold}} -- {{context.escapableBackticBold}} -- {{context.escapableHtml}}'; + '{{context.escapableBacktic}} -- {{context.escapableBold}} -- {{context.escapableBackticBold}} -- {{context.escapableHtml}} -- {{context.escapableLink}}'; const rule = await createRule({ id: slackConnector.id, @@ -95,7 +95,9 @@ export default function executionStatusAlertTests({ getService }: FtrProviderCon }); const body = await retry.try(async () => waitForActionBody(slackSimulatorURL, rule.id)); - expect(body).to.be("back'tic -- `*bold*` -- `'*bold*'` -- <&>"); + expect(body).to.be( + "back'tic -- `*bold*` -- `'*bold*'` -- <&> -- https://te_st.com/" + ); }); it('should handle context variable object expansion', async () => { From beae7b1448d00f95c3e89ccd6f62c38d5a973106 Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Mon, 30 Oct 2023 20:03:35 +0100 Subject: [PATCH 23/45] [Obs AI Assistant] Use ELSER v2 (#170092) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../observability_ai_assistant/server/service/index.ts | 9 ++++++++- .../server/service/kb_service/index.ts | 10 ++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/observability_ai_assistant/server/service/index.ts b/x-pack/plugins/observability_ai_assistant/server/service/index.ts index 7a268fb7eb3d7..ab37c59563713 100644 --- a/x-pack/plugins/observability_ai_assistant/server/service/index.ts +++ b/x-pack/plugins/observability_ai_assistant/server/service/index.ts @@ -25,6 +25,8 @@ function getResourceName(resource: string) { return `.kibana-observability-ai-assistant-${resource}`; } +export const ELSER_MODEL_ID = '.elser_model_2'; + export const INDEX_QUEUED_DOCUMENTS_TASK_ID = 'observabilityAIAssistant:indexQueuedDocumentsTask'; export const INDEX_QUEUED_DOCUMENTS_TASK_TYPE = INDEX_QUEUED_DOCUMENTS_TASK_ID + 'Type'; @@ -120,6 +122,11 @@ export class ObservabilityAIAssistantService { auto_expand_replicas: '0-1', hidden: true, }, + mappings: { + _meta: { + model: ELSER_MODEL_ID, + }, + }, }, }); @@ -150,7 +157,7 @@ export class ObservabilityAIAssistantService { processors: [ { inference: { - model_id: '.elser_model_1', + model_id: ELSER_MODEL_ID, target_field: 'ml', field_map: { text: 'text_field', diff --git a/x-pack/plugins/observability_ai_assistant/server/service/kb_service/index.ts b/x-pack/plugins/observability_ai_assistant/server/service/kb_service/index.ts index be10c3eaaa5d5..e5a92b3467768 100644 --- a/x-pack/plugins/observability_ai_assistant/server/service/kb_service/index.ts +++ b/x-pack/plugins/observability_ai_assistant/server/service/kb_service/index.ts @@ -13,7 +13,11 @@ import type { TaskManagerStartContract } from '@kbn/task-manager-plugin/server'; import pLimit from 'p-limit'; import pRetry from 'p-retry'; import { map } from 'lodash'; -import { INDEX_QUEUED_DOCUMENTS_TASK_ID, INDEX_QUEUED_DOCUMENTS_TASK_TYPE } from '..'; +import { + ELSER_MODEL_ID, + INDEX_QUEUED_DOCUMENTS_TASK_ID, + INDEX_QUEUED_DOCUMENTS_TASK_TYPE, +} from '..'; import type { KnowledgeBaseEntry } from '../../../common/types'; import type { ObservabilityAIAssistantResourceNames } from '../types'; import { getAccessQuery } from '../util/get_access_query'; @@ -42,8 +46,6 @@ function isAlreadyExistsError(error: Error) { ); } -const ELSER_MODEL_ID = '.elser_model_1'; - function throwKnowledgeBaseNotReady(body: any) { throw serverUnavailable(`Knowledge base is not ready yet`, body); } @@ -199,7 +201,7 @@ export class KnowledgeBaseService { text_expansion: { 'ml.tokens': { model_text: text, - model_id: '.elser_model_1', + model_id: ELSER_MODEL_ID, }, } as unknown as QueryDslTextExpansionQuery, })), From c7e785383a87f7e18509c601a5c089c755ac028e Mon Sep 17 00:00:00 2001 From: Marta Bondyra <4283304+mbondyra@users.noreply.github.com> Date: Mon, 30 Oct 2023 20:52:15 +0100 Subject: [PATCH 24/45] [Lens] Allow non-numeric metrics for metric vis (#169258) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Fixes #137756 Allows to set non numeric metrics for metric visualization for primary and secondary metric. Screenshot 2023-10-19 at 13 45 47 Screenshot 2023-10-19 at 13 46 37 Doesn't include coloring by terms. When primary metric is non-numeric: 1. when maximum value is empty, we hide maximum value group 2. when maximum value has a value we set an error message on dimension 3. we don’t allow to use a palette for coloring 4. we don’t allow to set a trendline Screenshot 2023-10-19 at 13 30 16 Screenshot 2023-10-19 at 13 30 22 ### Checklist Delete any items that are not applicable to this PR. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [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 - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### Risk Matrix Delete this section if it is not applicable to this PR. Before closing this PR, invite QA, stakeholders, and other developers to identify risks that should be tested prior to the change/feature release. When forming the risk matrix, consider some of the following examples and how they may potentially impact the change: | Risk | Probability | Severity | Mitigation/Notes | |---------------------------|-------------|----------|-------------------------| | Multiple Spaces—unexpected behavior in non-default Kibana Space. | Low | High | Integration tests will verify that all features are still supported in non-default Kibana Space and when user switches between spaces. | | Multiple nodes—Elasticsearch polling might have race conditions when multiple Kibana nodes are polling for the same tasks. | High | Low | Tasks are idempotent, so executing them multiple times will not result in logical error, but will degrade performance. To test for this case we add plenty of unit tests around this logic and document manual testing procedure. | | Code should gracefully handle cases when feature X or plugin Y are disabled. | Medium | High | Unit tests will verify that any feature flag or plugin combination still results in our service operational. | | [See more potential risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) | ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../public/components/metric_vis.tsx | 59 ++- .../config_panel/layer_panel.test.tsx | 27 ++ .../editor_frame/config_panel/layer_panel.tsx | 447 +++++++++--------- x-pack/plugins/lens/public/mocks/index.ts | 24 + .../shared_components/collapse_setting.tsx | 1 + x-pack/plugins/lens/public/types.ts | 1 + .../__snapshots__/visualization.test.ts.snap | 13 + .../metric/dimension_editor.test.tsx | 112 +++-- .../metric/dimension_editor.tsx | 265 ++++++----- .../visualizations/metric/to_expression.ts | 21 +- .../metric/visualization.test.ts | 41 +- .../visualizations/metric/visualization.tsx | 71 ++- .../xy/reference_line_helpers.test.ts | 70 ++- 13 files changed, 671 insertions(+), 481 deletions(-) diff --git a/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx index b80d8efe68e7c..a0d02562d7623 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx @@ -20,12 +20,14 @@ import { MetricWTrend, MetricWNumber, SettingsProps, + MetricWText, } from '@elastic/charts'; import { getColumnByAccessor, getFormatByAccessor } from '@kbn/visualizations-plugin/common/utils'; import { ExpressionValueVisDimension } from '@kbn/visualizations-plugin/common'; import type { Datatable, DatatableColumn, + DatatableRow, IInterpreterRenderHandlers, RenderMode, } from '@kbn/expressions-plugin/common'; @@ -65,6 +67,28 @@ function enhanceFieldFormat(serializedFieldFormat: SerializedFieldFormat | undef return serializedFieldFormat ?? { id: formatId }; } +const renderSecondaryMetric = ( + columns: DatatableColumn[], + row: DatatableRow, + config: Pick +) => { + let secondaryMetricColumn: DatatableColumn | undefined; + let formatSecondaryMetric: ReturnType; + if (config.dimensions.secondaryMetric) { + secondaryMetricColumn = getColumnByAccessor(config.dimensions.secondaryMetric, columns); + formatSecondaryMetric = getMetricFormatter(config.dimensions.secondaryMetric, columns); + } + const secondaryPrefix = config.metric.secondaryPrefix ?? secondaryMetricColumn?.name; + return ( + + {secondaryPrefix} + {secondaryMetricColumn + ? `${secondaryPrefix ? ' ' : ''}${formatSecondaryMetric!(row[secondaryMetricColumn.id])}` + : undefined} + + ); +}; + const getMetricFormatter = ( accessor: ExpressionValueVisDimension | string, columns: Datatable['columns'] @@ -149,13 +173,6 @@ export const MetricVis = ({ const primaryMetricColumn = getColumnByAccessor(config.dimensions.metric, data.columns)!; const formatPrimaryMetric = getMetricFormatter(config.dimensions.metric, data.columns); - let secondaryMetricColumn: DatatableColumn | undefined; - let formatSecondaryMetric: ReturnType; - if (config.dimensions.secondaryMetric) { - secondaryMetricColumn = getColumnByAccessor(config.dimensions.secondaryMetric, data.columns); - formatSecondaryMetric = getMetricFormatter(config.dimensions.secondaryMetric, data.columns); - } - let breakdownByColumn: DatatableColumn | undefined; let formatBreakdownValue: FieldFormatConvertFunction; if (config.dimensions.breakdownBy) { @@ -172,28 +189,32 @@ export const MetricVis = ({ const metricConfigs: MetricSpec['data'][number] = ( breakdownByColumn ? data.rows : data.rows.slice(0, 1) ).map((row, rowIdx) => { - const value: number = row[primaryMetricColumn.id] !== null ? row[primaryMetricColumn.id] : NaN; + const value: number | string = + row[primaryMetricColumn.id] !== null ? row[primaryMetricColumn.id] : NaN; const title = breakdownByColumn ? formatBreakdownValue(row[breakdownByColumn.id]) : primaryMetricColumn.name; const subtitle = breakdownByColumn ? primaryMetricColumn.name : config.metric.subtitle; - const secondaryPrefix = config.metric.secondaryPrefix ?? secondaryMetricColumn?.name; + + if (typeof value !== 'number') { + const nonNumericMetric: MetricWText = { + value: formatPrimaryMetric(value), + title: String(title), + subtitle, + icon: config.metric?.icon ? getIcon(config.metric?.icon) : undefined, + extra: renderSecondaryMetric(data.columns, row, config), + color: config.metric.color ?? defaultColor, + }; + return nonNumericMetric; + } + const baseMetric: MetricWNumber = { value, valueFormatter: formatPrimaryMetric, title: String(title), subtitle, icon: config.metric?.icon ? getIcon(config.metric?.icon) : undefined, - extra: ( - - {secondaryPrefix} - {secondaryMetricColumn - ? `${secondaryPrefix ? ' ' : ''}${formatSecondaryMetric!( - row[secondaryMetricColumn.id] - )}` - : undefined} - - ), + extra: renderSecondaryMetric(data.columns, row, config), color: config.metric.palette && value != null ? getColor( diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx index be3393d3b52e3..cef598de31af0 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.test.tsx @@ -224,6 +224,33 @@ describe('LayerPanel', () => { const group = instance.find('.lnsLayerPanel__dimensionContainer[data-test-subj="lnsGroup"]'); expect(group).toHaveLength(1); }); + it('does not render a hidden group', async () => { + mockVisualization.getConfiguration.mockReturnValue({ + groups: [ + { + groupLabel: 'A', + groupId: 'a', + accessors: [], + filterOperations: () => true, + supportsMoreColumns: true, + dataTestSubj: 'lnsGroup', + }, + { + groupLabel: 'B', + groupId: 'b', + accessors: [], + filterOperations: () => true, + isHidden: true, + supportsMoreColumns: true, + dataTestSubj: 'lnsGroup', + }, + ], + }); + + const { instance } = await mountWithProvider(); + const group = instance.find('.lnsLayerPanel__dimensionContainer[data-test-subj="lnsGroup"]'); + expect(group).toHaveLength(1); + }); it('should render the required warning when only one group is configured', async () => { mockVisualization.getConfiguration.mockReturnValue({ diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 47987926b039f..78a06408902b5 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -427,239 +427,244 @@ export function LayerPanel( )} - {dimensionGroups.map((group, groupIndex) => { - let errorText: string = ''; - - if (!isEmptyLayer) { - if ( - group.requiredMinDimensionCount && - group.requiredMinDimensionCount > group.accessors.length - ) { - if (group.requiredMinDimensionCount > 1) { + {dimensionGroups + .filter((group) => !group.isHidden) + .map((group, groupIndex) => { + let errorText: string = ''; + + if (!isEmptyLayer) { + if ( + group.requiredMinDimensionCount && + group.requiredMinDimensionCount > group.accessors.length + ) { + if (group.requiredMinDimensionCount > 1) { + errorText = i18n.translate( + 'xpack.lens.editorFrame.requiresTwoOrMoreFieldsWarningLabel', + { + defaultMessage: 'Requires {requiredMinDimensionCount} fields', + values: { + requiredMinDimensionCount: group.requiredMinDimensionCount, + }, + } + ); + } else { + errorText = i18n.translate('xpack.lens.editorFrame.requiresFieldWarningLabel', { + defaultMessage: 'Requires field', + }); + } + } else if (group.dimensionsTooMany && group.dimensionsTooMany > 0) { errorText = i18n.translate( - 'xpack.lens.editorFrame.requiresTwoOrMoreFieldsWarningLabel', + 'xpack.lens.editorFrame.tooManyDimensionsSingularWarningLabel', { - defaultMessage: 'Requires {requiredMinDimensionCount} fields', + defaultMessage: + 'Please remove {dimensionsTooMany, plural, one {a dimension} other {{dimensionsTooMany} dimensions}}', values: { - requiredMinDimensionCount: group.requiredMinDimensionCount, + dimensionsTooMany: group.dimensionsTooMany, }, } ); - } else { - errorText = i18n.translate('xpack.lens.editorFrame.requiresFieldWarningLabel', { - defaultMessage: 'Requires field', - }); } - } else if (group.dimensionsTooMany && group.dimensionsTooMany > 0) { - errorText = i18n.translate( - 'xpack.lens.editorFrame.tooManyDimensionsSingularWarningLabel', - { - defaultMessage: - 'Please remove {dimensionsTooMany, plural, one {a dimension} other {{dimensionsTooMany} dimensions}}', - values: { - dimensionsTooMany: group.dimensionsTooMany, - }, - } - ); } - } - const isOptional = !group.requiredMinDimensionCount && !group.suggestedValue; - return ( - - {group.groupLabel} - {group.groupTooltip && ( - <> - - - )} - - } - labelAppend={ - isOptional ? ( - - {i18n.translate('xpack.lens.editorFrame.optionalDimensionLabel', { - defaultMessage: 'Optional', - })} - - ) : null - } - labelType="legend" - key={group.groupId} - isInvalid={Boolean(errorText)} - error={errorText} - > - <> - {group.accessors.length ? ( - - {group.accessors.map((accessorConfig, accessorIndex) => { - const { columnId } = accessorConfig; - - const messages = - props?.getUserMessages?.('dimensionButton', { - dimensionId: columnId, - }) ?? []; - - return ( - + {group.groupLabel} + {group.groupTooltip && ( + <> + setHideTooltip(true)} - onDragEnd={() => setHideTooltip(false)} - onDrop={onDrop} - indexPatterns={dataViews.indexPatterns} - > - { - setActiveDimension({ - isNew: false, - activeGroup: group, - activeId: id, - }); - }} - onRemoveClick={(id: string) => { - props.onRemoveDimension({ columnId: id, layerId }); - removeButtonRef(id); - }} - message={{ - severity: messages[0]?.severity, - content: messages[0]?.shortMessage || messages[0]?.longMessage, + position="top" + size="s" + type="questionInCircle" + /> + + )} + + } + labelAppend={ + isOptional ? ( + + {i18n.translate('xpack.lens.editorFrame.optionalDimensionLabel', { + defaultMessage: 'Optional', + })} + + ) : null + } + labelType="legend" + key={group.groupId} + isInvalid={Boolean(errorText)} + error={errorText} + > + <> + {group.accessors.length ? ( + + {group.accessors.map((accessorConfig, accessorIndex) => { + const { columnId } = accessorConfig; + + const messages = + props?.getUserMessages?.('dimensionButton', { + dimensionId: columnId, + }) ?? []; + + return ( + setHideTooltip(true)} + onDragEnd={() => setHideTooltip(false)} + onDrop={onDrop} + indexPatterns={dataViews.indexPatterns} > - {layerDatasource ? ( - <> - {layerDatasource.DimensionTriggerComponent({ - ...layerDatasourceConfigProps, - columnId: accessorConfig.columnId, - groupId: group.groupId, - filterOperations: group.filterOperations, - indexPatterns: dataViews.indexPatterns, - })} - - ) : ( - <> - {activeVisualization?.DimensionTriggerComponent?.({ - columnId, - label: columnLabelMap?.[columnId] ?? '', - hideTooltip, - })} - - )} - - - ); - })} - - ) : null} - - {group.fakeFinalAccessor && ( -
    - -
    - )} + { + setActiveDimension({ + isNew: false, + activeGroup: group, + activeId: id, + }); + }} + onRemoveClick={(id: string) => { + props.onRemoveDimension({ columnId: id, layerId }); + removeButtonRef(id); + }} + message={{ + severity: messages[0]?.severity, + content: messages[0]?.shortMessage || messages[0]?.longMessage, + }} + > + {layerDatasource ? ( + <> + {layerDatasource.DimensionTriggerComponent({ + ...layerDatasourceConfigProps, + columnId: accessorConfig.columnId, + groupId: group.groupId, + filterOperations: group.filterOperations, + indexPatterns: dataViews.indexPatterns, + })} + + ) : ( + <> + {activeVisualization?.DimensionTriggerComponent?.({ + columnId, + label: columnLabelMap?.[columnId] ?? '', + hideTooltip, + })} + + )} + + + ); + })} + + ) : null} + + {group.fakeFinalAccessor && ( +
    + +
    + )} - {group.supportsMoreColumns ? ( - { - props.onEmptyDimensionAdd(id, group); - setActiveDimension({ - activeGroup: group, - activeId: id, - isNew: !group.supportStaticValue && Boolean(layerDatasource), - }); - }} - onDrop={onDrop} - indexPatterns={dataViews.indexPatterns} - /> - ) : null} - -
    - ); - })} + {group.supportsMoreColumns ? ( + { + props.onEmptyDimensionAdd(id, group); + setActiveDimension({ + activeGroup: group, + activeId: id, + isNew: !group.supportStaticValue && Boolean(layerDatasource), + }); + }} + onDrop={onDrop} + indexPatterns={dataViews.indexPatterns} + /> + ) : null} + + + ); + })} {(layerDatasource?.LayerSettingsComponent || activeVisualization?.LayerSettingsComponent) && ( diff --git a/x-pack/plugins/lens/public/mocks/index.ts b/x-pack/plugins/lens/public/mocks/index.ts index c71da33fda3c9..f9760c50005f4 100644 --- a/x-pack/plugins/lens/public/mocks/index.ts +++ b/x-pack/plugins/lens/public/mocks/index.ts @@ -6,6 +6,7 @@ */ import { DragContextState, DragContextValue } from '@kbn/dom-drag-drop'; +import { DatatableColumnType } from '@kbn/expressions-plugin/common'; import { createMockDataViewsState } from '../data_views_service/mocks'; import { FramePublicAPI, FrameDatasourceAPI } from '../types'; export { mockDataPlugin } from './data_plugin_mock'; @@ -83,3 +84,26 @@ export function createMockedDragDropContext( setState ? setState : jest.fn(), ]; } + +export function generateActiveData( + json: Array<{ + id: string; + rows: Array>; + }> +) { + return json.reduce((memo, { id, rows }) => { + const columns = Object.keys(rows[0]).map((columnId) => ({ + id: columnId, + name: columnId, + meta: { + type: typeof rows[0][columnId]! as DatatableColumnType, + }, + })); + memo[id] = { + type: 'datatable' as const, + columns, + rows, + }; + return memo; + }, {} as NonNullable); +} diff --git a/x-pack/plugins/lens/public/shared_components/collapse_setting.tsx b/x-pack/plugins/lens/public/shared_components/collapse_setting.tsx index bbaf5296a4e28..9d855fa2bccfe 100644 --- a/x-pack/plugins/lens/public/shared_components/collapse_setting.tsx +++ b/x-pack/plugins/lens/public/shared_components/collapse_setting.tsx @@ -28,6 +28,7 @@ export function CollapseSetting({ return ( <> { diff --git a/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap b/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap index 8ad5a53ee57b2..2490904751e8c 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap +++ b/x-pack/plugins/lens/public/visualizations/metric/__snapshots__/visualization.test.ts.snap @@ -55,6 +55,7 @@ Object { "groupId": "max", "groupLabel": "Maximum value", "groupTooltip": "If the maximum value is specified, the minimum value is fixed at zero.", + "isHidden": false, "paramEditorCustomProps": Object { "headingLabel": "Value", }, @@ -100,6 +101,10 @@ Array [ "dataType": "number", "isBucketed": false, }, + Object { + "dataType": "string", + "isBucketed": false, + }, ] `; @@ -109,6 +114,10 @@ Array [ "dataType": "number", "isBucketed": false, }, + Object { + "dataType": "string", + "isBucketed": false, + }, ] `; @@ -118,6 +127,10 @@ Array [ "dataType": "number", "isBucketed": false, }, + Object { + "dataType": "string", + "isBucketed": false, + }, ] `; diff --git a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.test.tsx b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.test.tsx index de0d59ef1bc4b..5d2c6e2a50ca4 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.test.tsx @@ -24,7 +24,7 @@ import { mountWithIntl } from '@kbn/test-jest-helpers'; import { LayoutDirection } from '@elastic/charts'; import { act } from 'react-dom/test-utils'; import { EuiColorPickerOutput } from '@elastic/eui/src/components/color_picker/color_picker'; -import { createMockFramePublicAPI } from '../../mocks'; +import { createMockFramePublicAPI, generateActiveData } from '../../mocks'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { euiLightVars } from '@kbn/ui-theme'; import { DebouncedInput } from '@kbn/visualization-ui-components'; @@ -47,6 +47,18 @@ const SELECTORS = { BREAKDOWN_EDITOR: '[data-test-subj="lnsMetricDimensionEditor_breakdown"]', }; +const nonNumericMetricFrame = createMockFramePublicAPI({ + activeData: generateActiveData([ + { + id: 'first', + rows: Array(3).fill({ + 'metric-col-id': 'nonNumericData', + 'max-col-id': 1000, + }), + }, + ]), +}); + // see https://github.com/facebook/jest/issues/4402#issuecomment-534516219 const expectCalledBefore = (mock1: jest.Mock, mock2: jest.Mock) => expect(mock1.mock.invocationCallOrder[0]).toBeLessThan(mock2.mock.invocationCallOrder[0]); @@ -102,7 +114,14 @@ describe('dimension editor', () => { } as unknown as DatasourcePublicAPI, removeLayer: jest.fn(), addLayer: jest.fn(), - frame: createMockFramePublicAPI(), + frame: createMockFramePublicAPI({ + activeData: generateActiveData([ + { + id: 'first', + rows: Array(3).fill({ 'metric-col-id': 100, 'secondary-metric-col-id': 1 }), + }, + ]), + }), setState: jest.fn(), panelRef: {} as React.MutableRefObject, paletteService: chartPluginMock.createPaletteRegistry(), @@ -112,27 +131,9 @@ describe('dimension editor', () => { afterEach(() => jest.clearAllMocks()); describe('primary metric dimension', () => { - const accessor = 'primary-metric-col-id'; + const accessor = 'metric-col-id'; const metricAccessorState = { ...fullState, metricAccessor: accessor }; - beforeEach(() => { - props.frame.activeData = { - first: { - type: 'datatable', - columns: [ - { - id: accessor, - name: 'foo', - meta: { - type: 'number', - }, - }, - ], - rows: [], - }, - }; - }); - class Harness { public _wrapper; @@ -146,6 +147,10 @@ describe('dimension editor', () => { return this._wrapper.find(DimensionEditor); } + public get colorModeSwitch() { + return this._wrapper.find('EuiButtonGroup[data-test-subj="lnsMetric_color_mode_buttons"]'); + } + public get colorPicker() { return this._wrapper.find(EuiColorPicker); } @@ -163,11 +168,16 @@ describe('dimension editor', () => { const mockSetState = jest.fn(); - const getHarnessWithState = (state: MetricVisualizationState, datasource = props.datasource) => + const getHarnessWithState = ( + state: MetricVisualizationState, + datasource = props.datasource, + propsOverrides: Partial> = {} + ) => new Harness( mountWithIntl( { expect(component.exists(SELECTORS.BREAKDOWN_EDITOR)).toBeFalsy(); }); + it('Color mode switch is not shown when the primary metric is non-numeric', () => { + expect(getHarnessWithState(fullState, undefined).colorModeSwitch.exists()).toBeTruthy(); + expect( + getHarnessWithState(fullState, undefined, { + frame: nonNumericMetricFrame, + }).colorModeSwitch.exists() + ).toBeFalsy(); + }); + describe('static color controls', () => { it('is hidden when dynamic coloring is enabled', () => { const harnessWithPalette = getHarnessWithState({ ...metricAccessorState, palette }); @@ -202,6 +221,13 @@ describe('dimension editor', () => { }); expect(harnessNoPalette.colorPicker.exists()).toBeTruthy(); }); + it('is visible when metric is non-numeric even if palette is set', () => { + expect( + getHarnessWithState(fullState, undefined, { + frame: nonNumericMetricFrame, + }).colorPicker.exists() + ).toBeTruthy(); + }); it('fills with default value', () => { const localHarness = getHarnessWithState({ @@ -240,24 +266,6 @@ describe('dimension editor', () => { describe('secondary metric dimension', () => { const accessor = 'secondary-metric-col-id'; - beforeEach(() => { - props.frame.activeData = { - first: { - type: 'datatable', - columns: [ - { - id: accessor, - name: 'foo', - meta: { - type: 'number', - }, - }, - ], - rows: [], - }, - }; - }); - it('renders when the accessor matches', () => { const component = shallow( { const setState = jest.fn(); const localState = { ...fullState, - secondaryPrefix: 'foo', + secondaryPrefix: 'secondary-metric-col-id2', secondaryMetricAccessor: accessor, }; const component = mount( @@ -341,7 +349,7 @@ describe('dimension editor', () => { const buttonGroup = component.find(EuiButtonGroup); // make sure that if the user was to select the "custom" option, they would get the default value - expect(buttonGroup.props().options[1].value).toBe('foo'); + expect(buttonGroup.props().options[1].value).toBe('secondary-metric-col-id'); const newVal = 'bar'; @@ -461,7 +469,7 @@ describe('dimension editor', () => { }); describe('additional section', () => { - const accessor = 'primary-metric-col-id'; + const accessor = 'metric-col-id'; const metricAccessorState = { ...fullState, metricAccessor: accessor }; class Harness { @@ -473,6 +481,10 @@ describe('dimension editor', () => { this._wrapper = wrapper; } + public get wrapper() { + return this._wrapper; + } + private get rootComponent() { return this._wrapper.find(DimensionEditorAdditionalSection); } @@ -520,11 +532,16 @@ describe('dimension editor', () => { const mockSetState = jest.fn(); - const getHarnessWithState = (state: MetricVisualizationState, datasource = props.datasource) => + const getHarnessWithState = ( + state: MetricVisualizationState, + datasource = props.datasource, + propsOverrides: Partial> = {} + ) => new Harness( mountWithIntl( { } as DatasourcePublicAPI).isDisabled('trendline') ).toBeTruthy(); }); + it('should not show a trendline button group when primary metric dimension is non-numeric', () => { + expect( + getHarnessWithState(fullState, undefined, { + frame: nonNumericMetricFrame, + }).wrapper.isEmptyRender() + ).toBeTruthy(); + }); describe('responding to buttons', () => { it('enables trendline', () => { diff --git a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx index 7d2561369ee6b..c84fa45834fa0 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx @@ -143,78 +143,77 @@ function SecondaryMetricEditor({ accessor, idPrefix, frame, layerId, setState, s const defaultPrefix = columnName || ''; return ( -
    - - <> - { + + <> + { + setState({ + ...state, + secondaryPrefix, + }); + }} + /> + + {state.secondaryPrefix && ( + { setState({ ...state, - secondaryPrefix, + secondaryPrefix: newPrefix, }); }} /> - - {state.secondaryPrefix && ( - { - setState({ - ...state, - secondaryPrefix: newPrefix, - }); - }} - /> - )} - - -
    + )} + +
    ); } @@ -225,11 +224,13 @@ function PrimaryMetricEditor(props: SubProps) { const currentData = frame.activeData?.[state.layerId]; - if (accessor == null || !isNumericFieldForDatatable(currentData, accessor)) { + const isMetricNumeric = isNumericFieldForDatatable(currentData, accessor); + + if (accessor == null) { return null; } - const hasDynamicColoring = Boolean(state?.palette); + const hasDynamicColoring = Boolean(isMetricNumeric && state?.palette); const supportsPercentPalette = Boolean( state.maxAccessor || @@ -265,62 +266,65 @@ function PrimaryMetricEditor(props: SubProps) { return ( <> - - { - const colorMode = id.replace(idPrefix, '') as 'static' | 'dynamic'; - - const params = - colorMode === 'dynamic' - ? { - palette: { - ...activePalette, - params: { - ...activePalette.params, - stops: displayStops, + > + { + const colorMode = id.replace(idPrefix, '') as 'static' | 'dynamic'; + + const params = + colorMode === 'dynamic' + ? { + palette: { + ...activePalette, + params: { + ...activePalette.params, + stops: displayStops, + }, }, - }, - } - : { - palette: undefined, - }; - setState({ - ...state, - color: undefined, - ...params, - }); - }} - /> - + color: undefined, + } + : { + palette: undefined, + color: undefined, + }; + setState({ + ...state, + ...params, + }); + }} + /> + + )} {!hasDynamicColoring && } {hasDynamicColoring && ( ) { +function StaticColorControls({ + state, + setState, + frame, +}: Pick) { const colorLabel = i18n.translate('xpack.lens.metric.color', { defaultMessage: 'Color', }); + const currentData = frame.activeData?.[state.layerId]; + const isMetricNumeric = Boolean( + state.metricAccessor && isNumericFieldForDatatable(currentData, state.metricAccessor) + ); const setColor = useCallback( (color: string) => { @@ -420,7 +432,7 @@ function StaticColorControls({ state, setState }: Pick( { onChange: setColor, - value: state.color || getDefaultColor(state), + value: state.color || getDefaultColor(state, isMetricNumeric), }, { allowFalsyValue: true } ); @@ -448,10 +460,12 @@ export function DimensionEditorAdditionalSection({ addLayer, removeLayer, accessor, + frame, }: VisualizationDimensionEditorProps) { const { euiTheme } = useEuiTheme(); - if (accessor !== state.metricAccessor) { + const currentData = frame.activeData?.[state.layerId]; + if (accessor !== state.metricAccessor || !isNumericFieldForDatatable(currentData, accessor)) { return null; } @@ -566,7 +580,6 @@ export function DimensionEditorAdditionalSection({ }`} onChange={(id) => { const supportingVisualizationType = id.split('--')[1] as SupportingVisType; - switch (supportingVisualizationType) { case 'trendline': setState({ diff --git a/x-pack/plugins/lens/public/visualizations/metric/to_expression.ts b/x-pack/plugins/lens/public/visualizations/metric/to_expression.ts index b2367bb7c1fc8..281e758fcbf44 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/to_expression.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/to_expression.ts @@ -90,6 +90,10 @@ export const toExpression = ( const datasource = datasourceLayers[state.layerId]; const datasourceExpression = datasourceExpressionsByLayers[state.layerId]; + const isMetricNumeric = Boolean( + state.metricAccessor && + datasource?.getOperationForColumnId(state.metricAccessor)?.dataType === 'number' + ); const maxPossibleTiles = // if there's a collapse function, no need to calculate since we're dealing with a single tile state.breakdownByAccessor && !state.collapseFn @@ -142,15 +146,16 @@ export const toExpression = ( trendline: trendlineExpression ? [trendlineExpression] : [], subtitle: state.subtitle ?? undefined, progressDirection: state.progressDirection as LayoutDirection, - color: state.color || getDefaultColor(state), + color: state.color || getDefaultColor(state, isMetricNumeric), icon: state.icon, - palette: state.palette?.params - ? [ - paletteService - .get(CUSTOM_PALETTE) - .toExpression(computePaletteParams(state.palette.params as CustomPaletteParams)), - ] - : [], + palette: + isMetricNumeric && state.palette?.params + ? [ + paletteService + .get(CUSTOM_PALETTE) + .toExpression(computePaletteParams(state.palette.params as CustomPaletteParams)), + ] + : [], maxCols: state.maxCols ?? DEFAULT_MAX_COLUMNS, minTiles: maxPossibleTiles ?? undefined, inspectorTableId: state.layerId, diff --git a/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts b/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts index 61fac1418196e..78ea5331e34cf 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts +++ b/x-pack/plugins/lens/public/visualizations/metric/visualization.test.ts @@ -10,7 +10,7 @@ import { CustomPaletteParams, PaletteOutput } from '@kbn/coloring'; import { ExpressionAstExpression, ExpressionAstFunction } from '@kbn/expressions-plugin/common'; import { euiLightVars, euiThemeVars } from '@kbn/ui-theme'; import { LayerTypes } from '@kbn/expression-xy-plugin/public'; -import { createMockDatasource, createMockFramePublicAPI } from '../../mocks'; +import { createMockDatasource, createMockFramePublicAPI, generateActiveData } from '../../mocks'; import { DatasourceLayers, DatasourcePublicAPI, @@ -82,7 +82,14 @@ describe('metric visualization', () => { ...trendlineProps, }; - const mockFrameApi = createMockFramePublicAPI(); + const mockFrameApi = createMockFramePublicAPI({ + activeData: generateActiveData([ + { + id: 'first', + rows: Array(3).fill({ 'metric-col-id': 20, 'max-metric-col-id': 100 }), + }, + ]), + }); describe('initialization', () => { test('returns a default state', () => { @@ -268,6 +275,7 @@ describe('metric visualization', () => { mockDatasource.publicAPIMock.getMaxPossibleNumValues.mockReturnValue(maxPossibleNumValues); mockDatasource.publicAPIMock.getOperationForColumnId.mockReturnValue({ isStaticValue: false, + dataType: 'number', } as OperationDescriptor); datasourceLayers = { @@ -616,7 +624,7 @@ describe('metric visualization', () => { it('always applies max function to static max dimensions', () => { ( datasourceLayers.first as jest.Mocked - ).getOperationForColumnId.mockReturnValueOnce({ + ).getOperationForColumnId.mockReturnValue({ isStaticValue: true, } as OperationDescriptor); @@ -648,6 +656,9 @@ describe('metric visualization', () => { "type": "function", } `); + ( + datasourceLayers.first as jest.Mocked + ).getOperationForColumnId.mockClear(); }); }); @@ -1109,4 +1120,28 @@ describe('metric visualization', () => { noPadding: true, }); }); + + describe('#getUserMessages', () => { + it('returns error for non numeric primary metric if maxAccessor exists', () => { + const frame = createMockFramePublicAPI({ + activeData: generateActiveData([ + { + id: 'first', + rows: Array(3).fill({ 'metric-col-id': '100', 'max-metric-col-id': 100 }), + }, + ]), + }); + expect(visualization.getUserMessages!(fullState, { frame })).toHaveLength(1); + + const frameNoErrors = createMockFramePublicAPI({ + activeData: generateActiveData([ + { + id: 'first', + rows: Array(3).fill({ 'metric-col-id': 30, 'max-metric-col-id': 100 }), + }, + ]), + }); + expect(visualization.getUserMessages!(fullState, { frame: frameNoErrors })).toHaveLength(0); + }); + }); }); diff --git a/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx b/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx index 4dee7938da049..21a7067f7191a 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/visualization.tsx @@ -14,6 +14,7 @@ import { LayoutDirection } from '@elastic/charts'; import { euiLightVars, euiThemeVars } from '@kbn/ui-theme'; import { IconChartMetric } from '@kbn/chart-icons'; import { AccessorConfig } from '@kbn/visualization-ui-components'; +import { isNumericFieldForDatatable } from '../../../common/expressions/datatable/utils'; import { CollapseFunction } from '../../../common/expressions'; import type { LayerType } from '../../../common/types'; import { layerTypes } from '../../../common/layer_types'; @@ -25,6 +26,7 @@ import { VisualizationConfigProps, VisualizationDimensionGroupConfig, Suggestion, + UserMessage, } from '../../types'; import { GROUP_ID, LENS_METRIC_ID } from './constants'; import { DimensionEditor, DimensionEditorAdditionalSection } from './dimension_editor'; @@ -40,8 +42,11 @@ export const showingBar = ( ): state is MetricVisualizationState & { showBar: true; maxAccessor: string } => Boolean(state.showBar && state.maxAccessor); -export const getDefaultColor = (state: MetricVisualizationState) => - showingBar(state) ? euiLightVars.euiColorPrimary : euiThemeVars.euiColorLightestShade; +export const getDefaultColor = (state: MetricVisualizationState, isMetricNumeric?: boolean) => { + return showingBar(state) && isMetricNumeric + ? euiLightVars.euiColorPrimary + : euiThemeVars.euiColorLightestShade; +}; export interface MetricVisualizationState { layerId: string; @@ -70,7 +75,13 @@ export interface MetricVisualizationState { trendlineBreakdownByAccessor?: string; } -export const supportedDataTypes = new Set(['number']); +export const supportedDataTypes = new Set(['string', 'boolean', 'number', 'ip', 'date']); + +const isSupportedMetric = (op: OperationMetadata) => + !op.isBucketed && supportedDataTypes.has(op.dataType); + +const isSupportedDynamicMetric = (op: OperationMetadata) => + !op.isBucketed && supportedDataTypes.has(op.dataType) && !op.isStaticValue; export const metricLabel = i18n.translate('xpack.lens.metric.label', { defaultMessage: 'Metric', @@ -84,29 +95,25 @@ const getMetricLayerConfiguration = ( ): { groups: VisualizationDimensionGroupConfig[]; } => { - const isSupportedMetric = (op: OperationMetadata) => - !op.isBucketed && supportedDataTypes.has(op.dataType); + const currentData = props.frame.activeData?.[props.state.layerId]; - const isSupportedDynamicMetric = (op: OperationMetadata) => - !op.isBucketed && supportedDataTypes.has(op.dataType) && !op.isStaticValue; + const isMetricNumeric = Boolean( + props.state.metricAccessor && + isNumericFieldForDatatable(currentData, props.state.metricAccessor) + ); const getPrimaryAccessorDisplayConfig = (): Partial => { + const hasDynamicColoring = Boolean(isMetricNumeric && props.state.palette); const stops = props.state.palette?.params?.stops || []; - const hasStaticColoring = !!props.state.color; - const hasDynamicColoring = !!props.state.palette; + return hasDynamicColoring ? { triggerIconType: 'colorBy', palette: stops.map(({ color }) => color), } - : hasStaticColoring - ? { - triggerIconType: 'color', - color: props.state.color, - } : { triggerIconType: 'color', - color: getDefaultColor(props.state), + color: props.state.color ?? getDefaultColor(props.state, isMetricNumeric), }; }; @@ -180,6 +187,7 @@ const getMetricLayerConfiguration = ( }, ] : [], + isHidden: !props.state.maxAccessor && !isMetricNumeric, supportsMoreColumns: !props.state.maxAccessor, filterOperations: isSupportedMetric, enableDimensionEditor: true, @@ -632,7 +640,7 @@ export const getMetricVisualization = ({ return suggestion; }, - getVisualizationInfo(state) { + getVisualizationInfo(state, frame) { const dimensions = []; if (state.metricAccessor) { dimensions.push({ @@ -676,6 +684,11 @@ export const getMetricVisualization = ({ const hasStaticColoring = !!state.color; const hasDynamicColoring = !!state.palette; + const currentData = frame?.activeData?.[state.layerId]; + const isMetricNumeric = Boolean( + state.metricAccessor && isNumericFieldForDatatable(currentData, state.metricAccessor) + ); + return { layers: [ { @@ -688,10 +701,34 @@ export const getMetricVisualization = ({ ? stops.map(({ color }) => color) : hasStaticColoring ? [state.color] - : [getDefaultColor(state)] + : [getDefaultColor(state, isMetricNumeric)] ).filter(nonNullable), }, ], }; }, + + getUserMessages(state, { frame }) { + const currentData = frame.activeData?.[state.layerId]; + + const errors: UserMessage[] = []; + + if (state.maxAccessor) { + const isMetricNonNumeric = Boolean( + state.metricAccessor && !isNumericFieldForDatatable(currentData, state.metricAccessor) + ); + if (isMetricNonNumeric) { + errors.push({ + severity: 'error', + fixableInEditor: true, + displayLocations: [{ id: 'dimensionButton', dimensionId: state.maxAccessor }], + shortMessage: i18n.translate('xpack.lens.lnsMetric_maxDimensionPanel.nonNumericError', { + defaultMessage: 'Primary metric must be numeric to set a maximum value.', + }), + longMessage: '', + }); + } + } + return errors; + }, }); diff --git a/x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.test.ts b/x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.test.ts index 61f32b3adfc66..35aab159d42f6 100644 --- a/x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.test.ts +++ b/x-pack/plugins/lens/public/visualizations/xy/reference_line_helpers.test.ts @@ -5,25 +5,9 @@ * 2.0. */ -import { FramePublicAPI } from '../../types'; import { computeOverallDataDomain, getStaticValue } from './reference_line_helpers'; import { XYDataLayerConfig } from './types'; - -function getActiveData(json: Array<{ id: string; rows: Array> }>) { - return json.reduce((memo, { id, rows }) => { - const columns = Object.keys(rows[0]).map((columnId) => ({ - id: columnId, - name: columnId, - meta: { type: 'number' as const }, - })); - memo[id] = { - type: 'datatable' as const, - columns, - rows, - }; - return memo; - }, {} as NonNullable); -} +import { generateActiveData } from '../../mocks'; describe('reference_line helpers', () => { describe('getStaticValue', () => { @@ -41,7 +25,7 @@ describe('reference_line helpers', () => { [], 'x', { - activeData: getActiveData([ + activeData: generateActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, ]), }, @@ -54,7 +38,7 @@ describe('reference_line helpers', () => { [{ layerId: 'id-a', seriesType: 'area' } as XYDataLayerConfig], // missing xAccessor for groupId == x 'x', { - activeData: getActiveData([ + activeData: generateActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, ]), }, @@ -73,7 +57,7 @@ describe('reference_line helpers', () => { ], // missing hit of accessor "d" in data 'yLeft', { - activeData: getActiveData([ + activeData: generateActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, ]), }, @@ -92,7 +76,7 @@ describe('reference_line helpers', () => { ], // missing yConfig fallbacks to left axis, but the requested group is yRight 'yRight', { - activeData: getActiveData([ + activeData: generateActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, ]), }, @@ -111,7 +95,7 @@ describe('reference_line helpers', () => { ], // same as above with x groupId 'x', { - activeData: getActiveData([ + activeData: generateActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, ]), }, @@ -134,7 +118,7 @@ describe('reference_line helpers', () => { ], 'yRight', { - activeData: getActiveData([ + activeData: generateActiveData([ { id: 'id-a', rows: [{ a: -30 }, { a: 10 }], @@ -159,7 +143,7 @@ describe('reference_line helpers', () => { ], 'yLeft', { - activeData: getActiveData([ + activeData: generateActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, ]), }, @@ -182,7 +166,7 @@ describe('reference_line helpers', () => { ], 'yRight', { - activeData: getActiveData([ + activeData: generateActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, ]), }, @@ -192,7 +176,7 @@ describe('reference_line helpers', () => { }); it('should correctly distribute axis on left and right with different formatters when in auto', () => { - const tables = getActiveData([ + const tables = generateActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 200, c: 100 }) }, ]); tables['id-a'].columns[0].meta.params = { id: 'number' }; // a: number formatter @@ -230,7 +214,7 @@ describe('reference_line helpers', () => { }); it('should ignore hasHistogram for left or right axis', () => { - const tables = getActiveData([ + const tables = generateActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 200, c: 100 }) }, ]); tables['id-a'].columns[0].meta.params = { id: 'number' }; // a: number formatter @@ -285,7 +269,7 @@ describe('reference_line helpers', () => { ], 'x', // this is influenced by the callback { - activeData: getActiveData([ + activeData: generateActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, ]), }, @@ -312,7 +296,7 @@ describe('reference_line helpers', () => { ], 'x', { - activeData: getActiveData([ + activeData: generateActiveData([ { id: 'id-a', rows: Array(3) @@ -334,7 +318,7 @@ describe('reference_line helpers', () => { computeOverallDataDomain( [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYDataLayerConfig], ['a', 'b', 'c'], - getActiveData([ + generateActiveData([ { id: 'id-a', rows: Array(3) @@ -360,7 +344,7 @@ describe('reference_line helpers', () => { computeOverallDataDomain( [{ layerId: 'id-a', seriesType, accessors: ['a', 'b', 'c'] } as XYDataLayerConfig], ['a', 'b', 'c'], - getActiveData([ + generateActiveData([ { id: 'id-a', rows: Array(3) @@ -385,7 +369,7 @@ describe('reference_line helpers', () => { { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], - getActiveData([ + generateActiveData([ { id: 'id-a', rows: [{ a: 25, b: 100, c: 100 }] }, { id: 'id-b', rows: [{ d: 50, e: 50, f: 50 }] }, ]) @@ -399,7 +383,7 @@ describe('reference_line helpers', () => { { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], - getActiveData([ + generateActiveData([ { id: 'id-a', rows: Array(3) @@ -435,7 +419,7 @@ describe('reference_line helpers', () => { { layerId: 'id-b', seriesType, accessors: ['d', 'e', 'f'] }, ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], - getActiveData([ + generateActiveData([ { id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }, { id: 'id-b', rows: Array(3).fill({ d: 50, e: 50, f: 50 }) }, ]) @@ -453,7 +437,7 @@ describe('reference_line helpers', () => { { layerId: 'id-b', seriesType: stackedSeries, accessors: ['d', 'e', 'f'] }, ] as XYDataLayerConfig[], ['a', 'b', 'c', 'd', 'e', 'f'], - getActiveData([ + generateActiveData([ { id: 'id-a', rows: [{ a: 100, b: 100, c: 100 }] }, { id: 'id-b', rows: [{ d: 50, e: 50, f: 50 }] }, ]) @@ -475,7 +459,7 @@ describe('reference_line helpers', () => { { layerId: 'id-b', seriesType, xAccessor: 'f', accessors: ['d', 'e'] }, ] as XYDataLayerConfig[], ['a', 'b', 'd', 'e'], - getActiveData([ + generateActiveData([ { id: 'id-a', rows: Array(3) @@ -502,7 +486,7 @@ describe('reference_line helpers', () => { { layerId: 'id-b', seriesType, accessors: ['f'] }, ] as XYDataLayerConfig[], ['c', 'f'], - getActiveData([ + generateActiveData([ { id: 'id-a', rows: Array(3) @@ -530,7 +514,7 @@ describe('reference_line helpers', () => { { layerId: 'id-b', seriesType, xAccessor: 'f', accessors: ['d', 'e'] }, ] as XYDataLayerConfig[], ['a', 'b', 'd', 'e'], - getActiveData([ + generateActiveData([ { id: 'id-a', rows: Array(3) @@ -560,7 +544,7 @@ describe('reference_line helpers', () => { } as XYDataLayerConfig, ], ['a', 'b', 'c'], - getActiveData([ + generateActiveData([ { id: 'id-a', rows: Array(3) @@ -584,7 +568,7 @@ describe('reference_line helpers', () => { } as XYDataLayerConfig, ], ['a', 'b', 'c'], - getActiveData([ + generateActiveData([ { id: 'id-a', rows: Array(3) @@ -605,7 +589,7 @@ describe('reference_line helpers', () => { computeOverallDataDomain( [], ['a', 'b', 'c'], - getActiveData([{ id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }]) + generateActiveData([{ id: 'id-a', rows: Array(3).fill({ a: 100, b: 100, c: 100 }) }]) ) ).toEqual({ min: undefined, max: undefined }); }); @@ -618,7 +602,7 @@ describe('reference_line helpers', () => { { layerId: 'id-b', seriesType: 'line', accessors: ['d', 'e', 'f'] }, ] as XYDataLayerConfig[], ['a', 'b'], - getActiveData([{ id: 'id-c', rows: [{ a: 100, b: 100 }] }]) // mind the layer id here + generateActiveData([{ id: 'id-c', rows: [{ a: 100, b: 100 }] }]) // mind the layer id here ) ).toEqual({ min: undefined, max: undefined }); @@ -629,7 +613,7 @@ describe('reference_line helpers', () => { { layerId: 'id-b', seriesType: 'bar_stacked' }, ] as XYDataLayerConfig[], ['a', 'b'], - getActiveData([]) + generateActiveData([]) ) ).toEqual({ min: undefined, max: undefined }); }); From be21e7979e86edb84e07b313c263057a9bd2fc80 Mon Sep 17 00:00:00 2001 From: Elastic Machine Date: Tue, 31 Oct 2023 07:21:12 +1100 Subject: [PATCH 25/45] Update kubernetes templates for elastic-agent (#170146) Automated by https://fleet-ci.elastic.co/job/elastic-agent/job/elastic-agent-mbp/job/main/1479/ Co-authored-by: obscloudnativemonitoring --- x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts b/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts index 385b1095cb9f4..8bd0f823cb5c6 100644 --- a/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts +++ b/x-pack/plugins/fleet/server/services/elastic_agent_manifest.ts @@ -42,7 +42,7 @@ spec: # - -c # - >- # mkdir -p /etc/elastic-agent/inputs.d && - # wget -O - https://github.com/elastic/elastic-agent/archive/main.tar.gz | tar xz -C /etc/elastic-agent/inputs.d --strip=5 "elastic-agent-main/deploy/kubernetes/elastic-agent/templates.d" + # wget -O - https://github.com/elastic/elastic-agent/archive/8.12.tar.gz | tar xz -C /etc/elastic-agent/inputs.d --strip=5 "elastic-agent-8.12/deploy/kubernetes/elastic-agent/templates.d" # volumeMounts: # - name: external-inputs # mountPath: /etc/elastic-agent/inputs.d From 788d30239640e4f549eaeedebad766072de45a05 Mon Sep 17 00:00:00 2001 From: Davis McPhee Date: Mon, 30 Oct 2023 17:36:32 -0300 Subject: [PATCH 26/45] [Discover] Revert data table row height to 3 by default (#169724) ## Summary This PR reverts the changes from #164218 that updated the default Discover grid row height to "auto", and changes it back to "3". ### Checklist - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [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 - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- packages/kbn-unified-data-table/src/constants.ts | 2 +- .../src/hooks/use_row_heights_options.test.tsx | 14 +++++++++----- src/plugins/discover/server/ui_settings.ts | 2 +- .../apps/discover/group2/_data_grid_row_height.ts | 4 ++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/kbn-unified-data-table/src/constants.ts b/packages/kbn-unified-data-table/src/constants.ts index f648f09f558f3..c4c236c828fc1 100644 --- a/packages/kbn-unified-data-table/src/constants.ts +++ b/packages/kbn-unified-data-table/src/constants.ts @@ -20,7 +20,7 @@ export const ROWS_PER_PAGE_OPTIONS = [10, 25, 50, DEFAULT_ROWS_PER_PAGE, 250, 50 export const ROWS_HEIGHT_OPTIONS = { auto: -1, single: 0, - default: -1, + default: 3, }; export const defaultRowLineHeight = '1.6em'; export const defaultMonacoEditorWidth = 370; diff --git a/packages/kbn-unified-data-table/src/hooks/use_row_heights_options.test.tsx b/packages/kbn-unified-data-table/src/hooks/use_row_heights_options.test.tsx index 1ef0d9c70d139..2da08c178720a 100644 --- a/packages/kbn-unified-data-table/src/hooks/use_row_heights_options.test.tsx +++ b/packages/kbn-unified-data-table/src/hooks/use_row_heights_options.test.tsx @@ -11,6 +11,8 @@ import { Storage } from '@kbn/kibana-utils-plugin/public'; import { LocalStorageMock } from '../../__mocks__/local_storage_mock'; import { useRowHeightsOptions } from './use_row_heights_options'; +const CONFIG_ROW_HEIGHT = 3; + describe('useRowHeightsOptions', () => { test('should apply rowHeight from savedSearch', () => { const { result } = renderHook(() => { @@ -30,7 +32,7 @@ describe('useRowHeightsOptions', () => { storage: new LocalStorageMock({ ['discover:dataGridRowHeight']: { previousRowHeight: 5, - previousConfigRowHeight: -1, + previousConfigRowHeight: 3, }, }) as unknown as Storage, consumer: 'discover', @@ -50,7 +52,7 @@ describe('useRowHeightsOptions', () => { }); expect(result.current.defaultHeight).toEqual({ - lineCount: 3, + lineCount: CONFIG_ROW_HEIGHT, }); }); @@ -59,8 +61,8 @@ describe('useRowHeightsOptions', () => { return useRowHeightsOptions({ storage: new LocalStorageMock({ ['discover:dataGridRowHeight']: { - previousRowHeight: 5, - // different from uiSettings (config), now user changed it to -1, but prev was 4 + previousRowHeight: 4, + // different from uiSettings (config), now user changed it to 3, but prev was 4 previousConfigRowHeight: 4, }, }) as unknown as Storage, @@ -68,6 +70,8 @@ describe('useRowHeightsOptions', () => { }); }); - expect(result.current.defaultHeight).toEqual('auto'); + expect(result.current.defaultHeight).toEqual({ + lineCount: CONFIG_ROW_HEIGHT, + }); }); }); diff --git a/src/plugins/discover/server/ui_settings.ts b/src/plugins/discover/server/ui_settings.ts index bc5ad09d260c4..d6bbbd0eed9f0 100644 --- a/src/plugins/discover/server/ui_settings.ts +++ b/src/plugins/discover/server/ui_settings.ts @@ -287,7 +287,7 @@ export const getUiSettings: (docLinks: DocLinksServiceSetup) => Record { await dataGrid.clickGridSettings(); - expect(await dataGrid.getCurrentRowHeightValue()).to.be('Auto fit'); + expect(await dataGrid.getCurrentRowHeightValue()).to.be('Custom'); await dataGrid.changeRowHeightValue('Single'); From d7ab75ffc51c8a31728efae5570261abd7a10c8c Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 30 Oct 2023 15:52:24 -0500 Subject: [PATCH 27/45] Revert "[licensing] add license fetcher cache (#170006)" (#170185) This reverts commit 21c0b0b4205ba2d442656776b23b18e2dc062940. --- .../licensing/common/license_update.ts | 15 +- .../licensing/server/license_fetcher.test.ts | 172 ------------------ .../licensing/server/license_fetcher.ts | 133 -------------- .../licensing/server/licensing_config.ts | 12 +- .../plugins/licensing/server/plugin.test.ts | 139 +++++++++----- x-pack/plugins/licensing/server/plugin.ts | 120 ++++++++++-- x-pack/plugins/licensing/server/types.ts | 2 - x-pack/plugins/licensing/tsconfig.json | 3 +- 8 files changed, 210 insertions(+), 386 deletions(-) delete mode 100644 x-pack/plugins/licensing/server/license_fetcher.test.ts delete mode 100644 x-pack/plugins/licensing/server/license_fetcher.ts diff --git a/x-pack/plugins/licensing/common/license_update.ts b/x-pack/plugins/licensing/common/license_update.ts index a35b7aa6e6785..b344d8ce2d16a 100644 --- a/x-pack/plugins/licensing/common/license_update.ts +++ b/x-pack/plugins/licensing/common/license_update.ts @@ -17,7 +17,6 @@ import { takeUntil, finalize, startWith, - throttleTime, } from 'rxjs/operators'; import { hasLicenseInfoChanged } from './has_license_info_changed'; import type { ILicense } from './types'; @@ -30,15 +29,11 @@ export function createLicenseUpdate( ) { const manuallyRefresh$ = new Subject(); - const fetched$ = merge( - triggerRefresh$, - manuallyRefresh$.pipe( - throttleTime(1000, undefined, { - leading: true, - trailing: true, - }) - ) - ).pipe(takeUntil(stop$), exhaustMap(fetcher), share()); + const fetched$ = merge(triggerRefresh$, manuallyRefresh$).pipe( + takeUntil(stop$), + exhaustMap(fetcher), + share() + ); // provide a first, empty license, so that we can compare in the filter below const startWithArgs = initialValues ? [undefined, initialValues] : [undefined]; diff --git a/x-pack/plugins/licensing/server/license_fetcher.test.ts b/x-pack/plugins/licensing/server/license_fetcher.test.ts deleted file mode 100644 index efd9b001fa0ff..0000000000000 --- a/x-pack/plugins/licensing/server/license_fetcher.test.ts +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { getLicenseFetcher } from './license_fetcher'; -import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; -import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; - -type EsLicense = estypes.XpackInfoMinimalLicenseInformation; - -const delay = (ms: number) => new Promise((res) => setTimeout(res, ms)); - -function buildRawLicense(options: Partial = {}): EsLicense { - return { - uid: 'uid-000000001234', - status: 'active', - type: 'basic', - mode: 'basic', - expiry_date_in_millis: 1000, - ...options, - }; -} - -describe('LicenseFetcher', () => { - let logger: MockedLogger; - let clusterClient: ReturnType; - - beforeEach(() => { - logger = loggerMock.create(); - clusterClient = elasticsearchServiceMock.createClusterClient(); - }); - - it('returns the license for successful calls', async () => { - clusterClient.asInternalUser.xpack.info.mockResponse({ - license: buildRawLicense({ - uid: 'license-1', - }), - features: {}, - } as any); - - const fetcher = getLicenseFetcher({ - logger, - clusterClient, - cacheDurationMs: 50_000, - }); - - const license = await fetcher(); - expect(license.uid).toEqual('license-1'); - }); - - it('returns the latest license for successful calls', async () => { - clusterClient.asInternalUser.xpack.info - .mockResponseOnce({ - license: buildRawLicense({ - uid: 'license-1', - }), - features: {}, - } as any) - .mockResponseOnce({ - license: buildRawLicense({ - uid: 'license-2', - }), - features: {}, - } as any); - - const fetcher = getLicenseFetcher({ - logger, - clusterClient, - cacheDurationMs: 50_000, - }); - - let license = await fetcher(); - expect(license.uid).toEqual('license-1'); - - license = await fetcher(); - expect(license.uid).toEqual('license-2'); - }); - - it('returns an error license in case of error', async () => { - clusterClient.asInternalUser.xpack.info.mockResponseImplementation(() => { - throw new Error('woups'); - }); - - const fetcher = getLicenseFetcher({ - logger, - clusterClient, - cacheDurationMs: 50_000, - }); - - const license = await fetcher(); - expect(license.error).toEqual('woups'); - }); - - it('returns a license successfully fetched after an error', async () => { - clusterClient.asInternalUser.xpack.info - .mockResponseImplementationOnce(() => { - throw new Error('woups'); - }) - .mockResponseOnce({ - license: buildRawLicense({ - uid: 'license-1', - }), - features: {}, - } as any); - - const fetcher = getLicenseFetcher({ - logger, - clusterClient, - cacheDurationMs: 50_000, - }); - - let license = await fetcher(); - expect(license.error).toEqual('woups'); - license = await fetcher(); - expect(license.uid).toEqual('license-1'); - }); - - it('returns the latest fetched license after an error within the cache duration period', async () => { - clusterClient.asInternalUser.xpack.info - .mockResponseOnce({ - license: buildRawLicense({ - uid: 'license-1', - }), - features: {}, - } as any) - .mockResponseImplementationOnce(() => { - throw new Error('woups'); - }); - - const fetcher = getLicenseFetcher({ - logger, - clusterClient, - cacheDurationMs: 50_000, - }); - - let license = await fetcher(); - expect(license.uid).toEqual('license-1'); - license = await fetcher(); - expect(license.uid).toEqual('license-1'); - }); - - it('returns an error license after an error exceeding the cache duration period', async () => { - clusterClient.asInternalUser.xpack.info - .mockResponseOnce({ - license: buildRawLicense({ - uid: 'license-1', - }), - features: {}, - } as any) - .mockResponseImplementationOnce(() => { - throw new Error('woups'); - }); - - const fetcher = getLicenseFetcher({ - logger, - clusterClient, - cacheDurationMs: 1, - }); - - let license = await fetcher(); - expect(license.uid).toEqual('license-1'); - - await delay(50); - - license = await fetcher(); - expect(license.error).toEqual('woups'); - }); -}); diff --git a/x-pack/plugins/licensing/server/license_fetcher.ts b/x-pack/plugins/licensing/server/license_fetcher.ts deleted file mode 100644 index 43d9c204bbf66..0000000000000 --- a/x-pack/plugins/licensing/server/license_fetcher.ts +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { createHash } from 'crypto'; -import stringify from 'json-stable-stringify'; -import type { MaybePromise } from '@kbn/utility-types'; -import { isPromise } from '@kbn/std'; -import type { IClusterClient, Logger } from '@kbn/core/server'; -import type { - ILicense, - PublicLicense, - PublicFeatures, - LicenseType, - LicenseStatus, -} from '../common/types'; -import { License } from '../common/license'; -import type { ElasticsearchError, LicenseFetcher } from './types'; - -export const getLicenseFetcher = ({ - clusterClient, - logger, - cacheDurationMs, -}: { - clusterClient: MaybePromise; - logger: Logger; - cacheDurationMs: number; -}): LicenseFetcher => { - let currentLicense: ILicense | undefined; - let lastSuccessfulFetchTime: number | undefined; - - return async () => { - const client = isPromise(clusterClient) ? await clusterClient : clusterClient; - try { - const response = await client.asInternalUser.xpack.info(); - const normalizedLicense = - response.license && response.license.type !== 'missing' - ? normalizeServerLicense(response.license) - : undefined; - const normalizedFeatures = response.features - ? normalizeFeatures(response.features) - : undefined; - - const signature = sign({ - license: normalizedLicense, - features: normalizedFeatures, - error: '', - }); - - currentLicense = new License({ - license: normalizedLicense, - features: normalizedFeatures, - signature, - }); - lastSuccessfulFetchTime = Date.now(); - - return currentLicense; - } catch (error) { - logger.warn( - `License information could not be obtained from Elasticsearch due to ${error} error` - ); - - if (lastSuccessfulFetchTime && lastSuccessfulFetchTime + cacheDurationMs > Date.now()) { - return currentLicense!; - } else { - const errorMessage = getErrorMessage(error); - const signature = sign({ error: errorMessage }); - - return new License({ - error: getErrorMessage(error), - signature, - }); - } - } - }; -}; - -function normalizeServerLicense( - license: estypes.XpackInfoMinimalLicenseInformation -): PublicLicense { - return { - uid: license.uid, - type: license.type as LicenseType, - mode: license.mode as LicenseType, - expiryDateInMillis: - typeof license.expiry_date_in_millis === 'string' - ? parseInt(license.expiry_date_in_millis, 10) - : license.expiry_date_in_millis, - status: license.status as LicenseStatus, - }; -} - -function normalizeFeatures(rawFeatures: estypes.XpackInfoFeatures) { - const features: PublicFeatures = {}; - for (const [name, feature] of Object.entries(rawFeatures)) { - features[name] = { - isAvailable: feature.available, - isEnabled: feature.enabled, - }; - } - return features; -} - -function sign({ - license, - features, - error, -}: { - license?: PublicLicense; - features?: PublicFeatures; - error?: string; -}) { - return createHash('sha256') - .update( - stringify({ - license, - features, - error, - }) - ) - .digest('hex'); -} - -function getErrorMessage(error: ElasticsearchError): string { - if (error.status === 400) { - return 'X-Pack plugin is not installed on the Elasticsearch cluster.'; - } - return error.message; -} diff --git a/x-pack/plugins/licensing/server/licensing_config.ts b/x-pack/plugins/licensing/server/licensing_config.ts index 66899602e04cb..459c69b650dbb 100644 --- a/x-pack/plugins/licensing/server/licensing_config.ts +++ b/x-pack/plugins/licensing/server/licensing_config.ts @@ -10,18 +10,12 @@ import { PluginConfigDescriptor } from '@kbn/core/server'; const configSchema = schema.object({ api_polling_frequency: schema.duration({ defaultValue: '30s' }), - license_cache_duration: schema.duration({ - defaultValue: '300s', - validate: (value) => { - if (value.asMinutes() > 15) { - return 'license cache duration must be shorter than 15 minutes'; - } - }, - }), }); export type LicenseConfigType = TypeOf; export const config: PluginConfigDescriptor = { - schema: configSchema, + schema: schema.object({ + api_polling_frequency: schema.duration({ defaultValue: '30s' }), + }), }; diff --git a/x-pack/plugins/licensing/server/plugin.test.ts b/x-pack/plugins/licensing/server/plugin.test.ts index 129dc6aee66da..b087b6f3f03fa 100644 --- a/x-pack/plugins/licensing/server/plugin.test.ts +++ b/x-pack/plugins/licensing/server/plugin.test.ts @@ -56,23 +56,22 @@ describe('licensing plugin', () => { return client; }; - let plugin: LicensingPlugin; - let pluginInitContextMock: ReturnType; + describe('#start', () => { + describe('#license$', () => { + let plugin: LicensingPlugin; + let pluginInitContextMock: ReturnType; - beforeEach(() => { - pluginInitContextMock = coreMock.createPluginInitializerContext({ - api_polling_frequency: moment.duration(100), - license_cache_duration: moment.duration(1000), - }); - plugin = new LicensingPlugin(pluginInitContextMock); - }); + beforeEach(() => { + pluginInitContextMock = coreMock.createPluginInitializerContext({ + api_polling_frequency: moment.duration(100), + }); + plugin = new LicensingPlugin(pluginInitContextMock); + }); - afterEach(async () => { - await plugin?.stop(); - }); + afterEach(async () => { + await plugin.stop(); + }); - describe('#start', () => { - describe('#license$', () => { it('returns license', async () => { const esClient = createEsClient({ license: buildRawLicense(), @@ -80,8 +79,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - plugin.setup(coreSetup); - const { license$ } = plugin.start(); + await plugin.setup(coreSetup); + const { license$ } = await plugin.start(); const license = await firstValueFrom(license$); expect(license.isAvailable).toBe(true); }); @@ -93,8 +92,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - plugin.setup(coreSetup); - const { license$ } = plugin.start(); + await plugin.setup(coreSetup); + const { license$ } = await plugin.start(); await firstValueFrom(license$); expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); @@ -112,8 +111,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - plugin.setup(coreSetup); - const { license$ } = plugin.start(); + await plugin.setup(coreSetup); + const { license$ } = await plugin.start(); const [first, second, third] = await firstValueFrom(license$.pipe(take(3), toArray())); expect(first.type).toBe('basic'); @@ -126,8 +125,8 @@ describe('licensing plugin', () => { esClient.asInternalUser.xpack.info.mockRejectedValue(new Error('test')); const coreSetup = createCoreSetupWith(esClient); - plugin.setup(coreSetup); - const { license$ } = plugin.start(); + await plugin.setup(coreSetup); + const { license$ } = await plugin.start(); const license = await firstValueFrom(license$); expect(license.isAvailable).toBe(false); @@ -141,8 +140,8 @@ describe('licensing plugin', () => { esClient.asInternalUser.xpack.info.mockRejectedValue(error); const coreSetup = createCoreSetupWith(esClient); - plugin.setup(coreSetup); - const { license$ } = plugin.start(); + await plugin.setup(coreSetup); + const { license$ } = await plugin.start(); const license = await firstValueFrom(license$); expect(license.isAvailable).toBe(false); @@ -170,8 +169,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - plugin.setup(coreSetup); - const { license$ } = plugin.start(); + await plugin.setup(coreSetup); + const { license$ } = await plugin.start(); const [first, second, third] = await firstValueFrom(license$.pipe(take(3), toArray())); @@ -187,8 +186,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - plugin.setup(coreSetup); - plugin.start(); + await plugin.setup(coreSetup); + await plugin.start(); await flushPromises(); @@ -202,8 +201,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - plugin.setup(coreSetup); - plugin.start(); + await plugin.setup(coreSetup); + await plugin.start(); await flushPromises(); @@ -230,8 +229,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - plugin.setup(coreSetup); - const { license$ } = plugin.start(); + await plugin.setup(coreSetup); + const { license$ } = await plugin.start(); const [first, second, third] = await firstValueFrom(license$.pipe(take(3), toArray())); expect(first.signature === third.signature).toBe(true); @@ -240,12 +239,16 @@ describe('licensing plugin', () => { }); describe('#refresh', () => { + let plugin: LicensingPlugin; + afterEach(async () => { + await plugin.stop(); + }); + it('forces refresh immediately', async () => { plugin = new LicensingPlugin( coreMock.createPluginInitializerContext({ // disable polling mechanism api_polling_frequency: moment.duration(50000), - license_cache_duration: moment.duration(1000), }) ); const esClient = createEsClient({ @@ -254,26 +257,31 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - plugin.setup(coreSetup); - const { refresh, license$ } = plugin.start(); + await plugin.setup(coreSetup); + const { refresh, license$ } = await plugin.start(); expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(0); - await firstValueFrom(license$); + await license$.pipe(take(1)).toPromise(); expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); - await refresh(); + refresh(); await flushPromises(); expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(2); }); }); describe('#createLicensePoller', () => { + let plugin: LicensingPlugin; + + afterEach(async () => { + await plugin.stop(); + }); + it(`creates a poller fetching license from passed 'clusterClient' every 'api_polling_frequency' ms`, async () => { plugin = new LicensingPlugin( coreMock.createPluginInitializerContext({ api_polling_frequency: moment.duration(50000), - license_cache_duration: moment.duration(1000), }) ); @@ -282,8 +290,8 @@ describe('licensing plugin', () => { features: {}, }); const coreSetup = createCoreSetupWith(esClient); - plugin.setup(coreSetup); - const { createLicensePoller, license$ } = plugin.start(); + await plugin.setup(coreSetup); + const { createLicensePoller, license$ } = await plugin.start(); const customClient = createEsClient({ license: buildRawLicense({ type: 'gold' }), @@ -305,13 +313,19 @@ describe('licensing plugin', () => { expect(customLicense.isAvailable).toBe(true); expect(customLicense.type).toBe('gold'); - expect(await firstValueFrom(license$)).not.toBe(customLicense); + expect(await license$.pipe(take(1)).toPromise()).not.toBe(customLicense); }); it('creates a poller with a manual refresh control', async () => { + plugin = new LicensingPlugin( + coreMock.createPluginInitializerContext({ + api_polling_frequency: moment.duration(100), + }) + ); + const coreSetup = coreMock.createSetup(); - plugin.setup(coreSetup); - const { createLicensePoller } = plugin.start(); + await plugin.setup(coreSetup); + const { createLicensePoller } = await plugin.start(); const customClient = createEsClient({ license: buildRawLicense({ type: 'gold' }), @@ -330,10 +344,24 @@ describe('licensing plugin', () => { }); describe('extends core contexts', () => { + let plugin: LicensingPlugin; + + beforeEach(() => { + plugin = new LicensingPlugin( + coreMock.createPluginInitializerContext({ + api_polling_frequency: moment.duration(100), + }) + ); + }); + + afterEach(async () => { + await plugin.stop(); + }); + it('provides a licensing context to http routes', async () => { const coreSetup = coreMock.createSetup(); - plugin.setup(coreSetup); + await plugin.setup(coreSetup); expect(coreSetup.http.registerRouteHandlerContext.mock.calls).toMatchInlineSnapshot(` Array [ @@ -347,10 +375,22 @@ describe('licensing plugin', () => { }); describe('registers on pre-response interceptor', () => { + let plugin: LicensingPlugin; + + beforeEach(() => { + plugin = new LicensingPlugin( + coreMock.createPluginInitializerContext({ api_polling_frequency: moment.duration(100) }) + ); + }); + + afterEach(async () => { + await plugin.stop(); + }); + it('once', async () => { const coreSetup = coreMock.createSetup(); - plugin.setup(coreSetup); + await plugin.setup(coreSetup); expect(coreSetup.http.registerOnPreResponse).toHaveBeenCalledTimes(1); }); @@ -359,9 +399,14 @@ describe('licensing plugin', () => { describe('#stop', () => { it('stops polling', async () => { + const plugin = new LicensingPlugin( + coreMock.createPluginInitializerContext({ + api_polling_frequency: moment.duration(100), + }) + ); const coreSetup = coreMock.createSetup(); - plugin.setup(coreSetup); - const { license$ } = plugin.start(); + await plugin.setup(coreSetup); + const { license$ } = await plugin.start(); let completed = false; license$.subscribe({ complete: () => (completed = true) }); diff --git a/x-pack/plugins/licensing/server/plugin.ts b/x-pack/plugins/licensing/server/plugin.ts index b3ac583e7c81e..0d21cd689bf46 100644 --- a/x-pack/plugins/licensing/server/plugin.ts +++ b/x-pack/plugins/licensing/server/plugin.ts @@ -8,7 +8,12 @@ import type { Observable, Subject, Subscription } from 'rxjs'; import { ReplaySubject, timer } from 'rxjs'; import moment from 'moment'; +import { createHash } from 'crypto'; +import stringify from 'json-stable-stringify'; + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { MaybePromise } from '@kbn/utility-types'; +import { isPromise } from '@kbn/std'; import type { CoreSetup, Logger, @@ -16,17 +21,73 @@ import type { PluginInitializerContext, IClusterClient, } from '@kbn/core/server'; + import { registerAnalyticsContextProvider } from '../common/register_analytics_context_provider'; -import type { ILicense } from '../common/types'; +import type { + ILicense, + PublicLicense, + PublicFeatures, + LicenseType, + LicenseStatus, +} from '../common/types'; import type { LicensingPluginSetup, LicensingPluginStart } from './types'; +import { License } from '../common/license'; import { createLicenseUpdate } from '../common/license_update'; + +import type { ElasticsearchError } from './types'; import { registerRoutes } from './routes'; import { FeatureUsageService } from './services'; + import type { LicenseConfigType } from './licensing_config'; import { createRouteHandlerContext } from './licensing_route_handler_context'; import { createOnPreResponseHandler } from './on_pre_response_handler'; import { getPluginStatus$ } from './plugin_status'; -import { getLicenseFetcher } from './license_fetcher'; + +function normalizeServerLicense( + license: estypes.XpackInfoMinimalLicenseInformation +): PublicLicense { + return { + uid: license.uid, + type: license.type as LicenseType, + mode: license.mode as LicenseType, + expiryDateInMillis: + typeof license.expiry_date_in_millis === 'string' + ? parseInt(license.expiry_date_in_millis, 10) + : license.expiry_date_in_millis, + status: license.status as LicenseStatus, + }; +} + +function normalizeFeatures(rawFeatures: estypes.XpackInfoFeatures) { + const features: PublicFeatures = {}; + for (const [name, feature] of Object.entries(rawFeatures)) { + features[name] = { + isAvailable: feature.available, + isEnabled: feature.enabled, + }; + } + return features; +} + +function sign({ + license, + features, + error, +}: { + license?: PublicLicense; + features?: PublicFeatures; + error?: string; +}) { + return createHash('sha256') + .update( + stringify({ + license, + features, + error, + }) + ) + .digest('hex'); +} /** * @public @@ -92,16 +153,9 @@ export class LicensingPlugin implements Plugin + this.fetchLicense(clusterClient) ); this.loggingSubscription = license$.subscribe((license) => @@ -124,6 +178,50 @@ export class LicensingPlugin implements Plugin): Promise => { + const client = isPromise(clusterClient) ? await clusterClient : clusterClient; + try { + const response = await client.asInternalUser.xpack.info(); + const normalizedLicense = + response.license && response.license.type !== 'missing' + ? normalizeServerLicense(response.license) + : undefined; + const normalizedFeatures = response.features + ? normalizeFeatures(response.features) + : undefined; + + const signature = sign({ + license: normalizedLicense, + features: normalizedFeatures, + error: '', + }); + + return new License({ + license: normalizedLicense, + features: normalizedFeatures, + signature, + }); + } catch (error) { + this.logger.warn( + `License information could not be obtained from Elasticsearch due to ${error} error` + ); + const errorMessage = this.getErrorMessage(error); + const signature = sign({ error: errorMessage }); + + return new License({ + error: this.getErrorMessage(error), + signature, + }); + } + }; + + private getErrorMessage(error: ElasticsearchError): string { + if (error.status === 400) { + return 'X-Pack plugin is not installed on the Elasticsearch cluster.'; + } + return error.message; + } + public start() { if (!this.refresh || !this.license$) { throw new Error('Setup has not been completed'); diff --git a/x-pack/plugins/licensing/server/types.ts b/x-pack/plugins/licensing/server/types.ts index fcccdecb66c00..83b39cb663715 100644 --- a/x-pack/plugins/licensing/server/types.ts +++ b/x-pack/plugins/licensing/server/types.ts @@ -14,8 +14,6 @@ export interface ElasticsearchError extends Error { status?: number; } -export type LicenseFetcher = () => Promise; - /** * Result from remote request fetching raw feature set. * @internal diff --git a/x-pack/plugins/licensing/tsconfig.json b/x-pack/plugins/licensing/tsconfig.json index 1deb735f99466..323f77b3b0ebc 100644 --- a/x-pack/plugins/licensing/tsconfig.json +++ b/x-pack/plugins/licensing/tsconfig.json @@ -15,8 +15,7 @@ "@kbn/i18n", "@kbn/analytics-client", "@kbn/subscription-tracking", - "@kbn/core-analytics-browser", - "@kbn/logging-mocks" + "@kbn/core-analytics-browser" ], "exclude": ["target/**/*"] } From 036918d017c9d1ee20467f5ced9934f944d9ff00 Mon Sep 17 00:00:00 2001 From: Gerard Soldevila Date: Mon, 30 Oct 2023 22:21:27 +0100 Subject: [PATCH 28/45] Address saved_object_tagging flakiness (#170114) ## Summary Fixes: * https://github.com/elastic/kibana/issues/89958 * https://github.com/elastic/kibana/issues/150249 * https://github.com/elastic/kibana/issues/167812 * https://github.com/elastic/kibana/issues/167560 Uses same strategy as Stratoula's [PR](https://github.com/elastic/kibana/pull/167599). It also adds a "click" step to make sure the form control has the focus (as we do with the other form controls on the same `fillForm` method). The tests above fail cause the logic fails to clean the default tag colour before entering a new one, resulting in: ![image](https://github.com/elastic/kibana/assets/25349407/e753aa64-4132-4094-af01-c17d91223172) Also fixes: * https://github.com/elastic/kibana/issues/163817 [This one](https://buildkite.com/elastic/kibana-on-merge/builds/36347#018b0068-ec42-47de-804d-b63a42b5b3e2) is looks like a lost click on the Delete modal confirm button (modal still present after 30s): ![image](https://github.com/elastic/kibana/assets/25349407/b2025b45-5030-4b6a-95f9-58d77fd3d2ea) --- test/functional/page_objects/common_page.ts | 6 +++ .../page_objects/tag_management_page.ts | 1 + .../functional/tests/bulk_actions.ts | 3 +- .../functional/tests/create.ts | 28 +++++++++---- .../functional/tests/dashboard_integration.ts | 1 + .../functional/tests/discover_integration.ts | 4 +- .../functional/tests/edit.ts | 42 ++++++++++++++----- .../functional/tests/maps_integration.ts | 1 + .../functional/tests/visualize_integration.ts | 1 + 9 files changed, 65 insertions(+), 22 deletions(-) diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index 215d552d07f7d..4c71693dd4125 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -387,6 +387,12 @@ export class CommonPageObject extends FtrService { this.log.debug('Clicking modal confirm'); // make sure this data-test-subj 'confirmModalTitleText' exists because we're going to wait for it to be gone later await this.testSubjects.exists('confirmModalTitleText'); + // make sure button is enabled before clicking it + // (and conveniently give UI enough time to bind a handler to it) + const isEnabled = await this.testSubjects.isEnabled('confirmModalConfirmButton'); + if (!isEnabled) { + throw new Error('Modal confirm button is not enabled'); + } await this.testSubjects.click('confirmModalConfirmButton'); if (ensureHidden) { await this.ensureModalOverlayHidden(); diff --git a/x-pack/test/functional/page_objects/tag_management_page.ts b/x-pack/test/functional/page_objects/tag_management_page.ts index 50d1453c1fe0f..5eb93aa977cf5 100644 --- a/x-pack/test/functional/page_objects/tag_management_page.ts +++ b/x-pack/test/functional/page_objects/tag_management_page.ts @@ -65,6 +65,7 @@ class TagModal extends FtrService { }); } if (fields.color !== undefined) { + await this.testSubjects.click('~createModalField-color'); await this.testSubjects.setValue('~createModalField-color', fields.color, { clearWithKeyboard, }); diff --git a/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts b/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts index f75e51851528c..d4aef2d44856f 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/bulk_actions.ts @@ -27,8 +27,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { ); }); - // FLAKY: https://github.com/elastic/kibana/issues/163817 - describe.skip('bulk delete', () => { + describe('bulk delete', () => { it('deletes multiple tags', async () => { const initialDisplayedTags = await tagManagementPage.getDisplayedTagNames(); await tagManagementPage.selectTagByName('tag-1'); diff --git a/x-pack/test/saved_object_tagging/functional/tests/create.ts b/x-pack/test/saved_object_tagging/functional/tests/create.ts index a2bde4ab579dc..0e2f3f52566c7 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/create.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/create.ts @@ -15,8 +15,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const tagManagementPage = PageObjects.tagManagement; - // FLAKY: https://github.com/elastic/kibana/issues/167812 - describe.skip('create tag', () => { + describe('create tag', () => { let tagModal: typeof tagManagementPage['tagModal']; before(async () => { @@ -45,7 +44,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { description: 'I just added this tag', color: '#FF00CC', }, - { submit: true } + { + submit: true, + clearWithKeyboard: true, + } ); await tagModal.waitUntilClosed(); await tagManagementPage.waitUntilTableIsLoaded(); @@ -65,7 +67,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { description: 'The name will fails validation', color: '#FF00CC', }, - { submit: true } + { + submit: true, + clearWithKeyboard: true, + } ); expect(await tagModal.isOpened()).to.be(true); @@ -84,7 +89,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { description: 'The name will fails validation', color: '#FF00CC', }, - { submit: true } + { + submit: true, + clearWithKeyboard: true, + } ); expect(await tagModal.isOpened()).to.be(true); @@ -94,7 +102,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { { name: 'valid name', }, - { submit: true } + { + submit: true, + clearWithKeyboard: true, + } ); await tagModal.waitUntilClosed(); @@ -114,7 +125,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { description: 'I will not add this tag', color: '#FF00CC', }, - { submit: false } + { + submit: false, + clearWithKeyboard: true, + } ); await tagModal.clickCancel(); await tagModal.waitUntilClosed(); diff --git a/x-pack/test/saved_object_tagging/functional/tests/dashboard_integration.ts b/x-pack/test/saved_object_tagging/functional/tests/dashboard_integration.ts index 024c7265a72bf..18be683db1d05 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/dashboard_integration.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/dashboard_integration.ts @@ -118,6 +118,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, { submit: true, + clearWithKeyboard: true, } ); diff --git a/x-pack/test/saved_object_tagging/functional/tests/discover_integration.ts b/x-pack/test/saved_object_tagging/functional/tests/discover_integration.ts index 7a82d0aec5d34..8258b4570ed9b 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/discover_integration.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/discover_integration.ts @@ -52,8 +52,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }; - // Failing: See https://github.com/elastic/kibana/issues/150249 - describe.skip('discover integration', () => { + describe('discover integration', () => { before(async () => { await kibanaServer.importExport.load( 'x-pack/test/saved_object_tagging/common/fixtures/es_archiver/discover/data.json' @@ -143,6 +142,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, { submit: true, + clearWithKeyboard: true, } ); expect(await tagModal.isOpened()).to.be(false); diff --git a/x-pack/test/saved_object_tagging/functional/tests/edit.ts b/x-pack/test/saved_object_tagging/functional/tests/edit.ts index f29212b251827..76e14d76a47a8 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/edit.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/edit.ts @@ -15,8 +15,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { const tagManagementPage = PageObjects.tagManagement; - // Failing: See https://github.com/elastic/kibana/issues/167560 - describe.skip('edit tag', () => { + describe('edit tag', () => { let tagModal: typeof tagManagementPage['tagModal']; before(async () => { @@ -56,7 +55,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { description: 'This was edited', color: '#FFCC00', }, - { submit: true } + { + submit: true, + clearWithKeyboard: true, + } ); await tagModal.waitUntilClosed(); await tagManagementPage.waitUntilTableIsLoaded(); @@ -79,7 +81,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { { name: 'a', }, - { submit: true } + { + submit: true, + clearWithKeyboard: true, + } ); expect(await tagModal.isOpened()).to.be(true); @@ -98,7 +103,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { description: 'edited description', color: '#FF00CC', }, - { submit: true } + { + submit: true, + clearWithKeyboard: true, + } ); expect(await tagModal.isOpened()).to.be(true); @@ -108,7 +116,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { { name: 'edited name', }, - { submit: true } + { + submit: true, + clearWithKeyboard: true, + } ); await tagModal.waitUntilClosed(); @@ -130,7 +141,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { description: 'I will not add this tag', color: '#FF00CC', }, - { submit: false } + { + submit: false, + clearWithKeyboard: true, + } ); await tagModal.clickCancel(); await tagModal.waitUntilClosed(); @@ -153,13 +167,16 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { it('should disable save button if no property is changed', async () => { await tagModal.openEdit('tag-3'); - await tagModal.fillForm(tag3Unmodified, { submit: false }); + await tagModal.fillForm(tag3Unmodified, { submit: false, clearWithKeyboard: true }); expect(await tagModal.isConfirmDisabled()).to.be(true); }); it('should enable save button if name is changed', async () => { await tagModal.openEdit('tag-3'); - await tagModal.fillForm({ ...tag3Unmodified, name: 'changed name' }, { submit: false }); + await tagModal.fillForm( + { ...tag3Unmodified, name: 'changed name' }, + { submit: false, clearWithKeyboard: true } + ); expect(await tagModal.isConfirmDisabled()).to.be(false); }); it('should enable save button if description is changed', async () => { @@ -167,14 +184,17 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await tagModal.fillForm( { ...tag3Unmodified, description: 'changed description' }, - { submit: false } + { submit: false, clearWithKeyboard: true } ); expect(await tagModal.isConfirmDisabled()).to.be(false); }); it('should enable save button if color is changed', async () => { await tagModal.openEdit('tag-3'); - await tagModal.fillForm({ ...tag3Unmodified, color: '#FF0000' }, { submit: false }); + await tagModal.fillForm( + { ...tag3Unmodified, color: '#FF0000' }, + { submit: false, clearWithKeyboard: true } + ); expect(await tagModal.isConfirmDisabled()).to.be(false); }); }); diff --git a/x-pack/test/saved_object_tagging/functional/tests/maps_integration.ts b/x-pack/test/saved_object_tagging/functional/tests/maps_integration.ts index 7532263b4518c..8d57f68c501c1 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/maps_integration.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/maps_integration.ts @@ -98,6 +98,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, { submit: true, + clearWithKeyboard: true, } ); diff --git a/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts b/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts index a7a03a58ba0cf..61b7a2c1e9711 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/visualize_integration.ts @@ -173,6 +173,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, { submit: true, + clearWithKeyboard: true, } ); From bcf9eaed41db583744cbab788f655ea44ef2d331 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 30 Oct 2023 21:22:27 +0000 Subject: [PATCH 29/45] skip flaky suite (#169574) --- x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts index e0442be19ece5..eb1c305b1ab3b 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts @@ -11,7 +11,8 @@ import { loadSavedQuery, cleanupSavedQuery } from '../../tasks/api_fixtures'; import { triggerLoadData } from '../../tasks/inventory'; import { ServerlessRoleName } from '../../support/roles'; -describe('ALL - Inventory', { tags: ['@ess'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/169574 +describe.skip('ALL - Inventory', { tags: ['@ess'] }, () => { let savedQueryName: string; let savedQueryId: string; From 0308b3d90552e070715efafcae3c2e3870af5033 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Mon, 30 Oct 2023 21:23:15 +0000 Subject: [PATCH 30/45] skip flaky suite (#169575) --- x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts index eb1c305b1ab3b..70614f958bb2f 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/metrics.cy.ts @@ -12,6 +12,7 @@ import { triggerLoadData } from '../../tasks/inventory'; import { ServerlessRoleName } from '../../support/roles'; // FLAKY: https://github.com/elastic/kibana/issues/169574 +// FLAKY: https://github.com/elastic/kibana/issues/169575 describe.skip('ALL - Inventory', { tags: ['@ess'] }, () => { let savedQueryName: string; let savedQueryId: string; From f5f5338daa391cd7f5773efc83f5ad4aa79acc99 Mon Sep 17 00:00:00 2001 From: Abdul Wahab Zahid Date: Mon, 30 Oct 2023 22:56:24 +0100 Subject: [PATCH 31/45] [Synthetics] Fix monitor availability sparkline (#170161) Related to https://github.com/elastic/kibana/pull/169790 ## Summary The PR fixes the query for Monitor Availability sparkline chart to account for retests. --- .../configurations/synthetics/kpi_over_time_config.ts | 2 +- .../monitor_details/monitor_summary/test_runs_table_header.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts index 0e6ad04f7cf47..115bb41f6630d 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts @@ -96,7 +96,7 @@ export function getSyntheticsKPIConfig({ dataView }: ConfigProps): SeriesConfig label: 'Monitor availability', id: 'monitor_availability', columnType: FORMULA_COLUMN, - formula: "1- (count(kql='summary.down > 0') / count(kql='summary: *'))", + formula: `1- (count(kql='${FINAL_SUMMARY_KQL} and summary.down > 0') / count(kql='summary: *'))`, columnFilter: { language: 'kuery', query: FINAL_SUMMARY_KQL, diff --git a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table_header.tsx b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table_header.tsx index cb708b6175f92..f8d87b7ce370b 100644 --- a/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table_header.tsx +++ b/x-pack/plugins/synthetics/public/apps/synthetics/components/monitor_details/monitor_summary/test_runs_table_header.tsx @@ -35,7 +35,7 @@ export const TestRunsTableHeader = ({ const { monitor } = useSelectedMonitor(); return ( - +

    {paginable || pings?.length < 10 ? TEST_RUNS : LAST_10_TEST_RUNS}

    From 75586c8233ff83ab0df403d13d5a674c216d0271 Mon Sep 17 00:00:00 2001 From: Steph Milovic Date: Mon, 30 Oct 2023 17:31:47 -0500 Subject: [PATCH 32/45] [Security solution] AI connector changes (#170150) --- .../server/connector_types/bedrock/bedrock.ts | 2 ++ .../server/connector_types/openai/openai.test.ts | 7 +++++++ .../server/connector_types/openai/openai.ts | 2 ++ 3 files changed, 11 insertions(+) diff --git a/x-pack/plugins/stack_connectors/server/connector_types/bedrock/bedrock.ts b/x-pack/plugins/stack_connectors/server/connector_types/bedrock/bedrock.ts index 0e1235312a52c..ea0c72420b41e 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/bedrock/bedrock.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/bedrock/bedrock.ts @@ -93,6 +93,8 @@ export class BedrockConnector extends SubActionConnector { }, body, path, + // Despite AWS docs, this value does not always get inferred. We need to always send it + service: 'bedrock', }, { secretAccessKey: this.secrets.secret, diff --git a/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.test.ts b/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.test.ts index 0a4a6a2931d8d..e214047a5c6d5 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.test.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.test.ts @@ -85,6 +85,7 @@ describe('OpenAIConnector', () => { const response = await connector.runApi({ body: JSON.stringify(sampleOpenAiBody) }); expect(mockRequest).toBeCalledTimes(1); expect(mockRequest).toHaveBeenCalledWith({ + timeout: 120000, url: 'https://api.openai.com/v1/chat/completions', method: 'post', responseSchema: RunActionResponseSchema, @@ -102,6 +103,7 @@ describe('OpenAIConnector', () => { const response = await connector.runApi({ body: JSON.stringify(requestBody) }); expect(mockRequest).toBeCalledTimes(1); expect(mockRequest).toHaveBeenCalledWith({ + timeout: 120000, url: 'https://api.openai.com/v1/chat/completions', method: 'post', responseSchema: RunActionResponseSchema, @@ -118,6 +120,7 @@ describe('OpenAIConnector', () => { const response = await connector.runApi({ body: JSON.stringify(sampleOpenAiBody) }); expect(mockRequest).toBeCalledTimes(1); expect(mockRequest).toHaveBeenCalledWith({ + timeout: 120000, url: 'https://api.openai.com/v1/chat/completions', method: 'post', responseSchema: RunActionResponseSchema, @@ -148,6 +151,7 @@ describe('OpenAIConnector', () => { }); expect(mockRequest).toBeCalledTimes(1); expect(mockRequest).toHaveBeenCalledWith({ + timeout: 120000, url: 'https://api.openai.com/v1/chat/completions', method: 'post', responseSchema: RunActionResponseSchema, @@ -269,6 +273,7 @@ describe('OpenAIConnector', () => { const response = await connector.invokeAI(sampleOpenAiBody); expect(mockRequest).toBeCalledTimes(1); expect(mockRequest).toHaveBeenCalledWith({ + timeout: 120000, url: 'https://api.openai.com/v1/chat/completions', method: 'post', responseSchema: RunActionResponseSchema, @@ -379,6 +384,7 @@ describe('OpenAIConnector', () => { const response = await connector.runApi({ body: JSON.stringify(sampleAzureAiBody) }); expect(mockRequest).toBeCalledTimes(1); expect(mockRequest).toHaveBeenCalledWith({ + timeout: 120000, url: 'https://My-test-resource-123.openai.azure.com/openai/deployments/NEW-DEPLOYMENT-321/chat/completions?api-version=2023-05-15', method: 'post', responseSchema: RunActionResponseSchema, @@ -405,6 +411,7 @@ describe('OpenAIConnector', () => { }); expect(mockRequest).toBeCalledTimes(1); expect(mockRequest).toHaveBeenCalledWith({ + timeout: 120000, url: 'https://My-test-resource-123.openai.azure.com/openai/deployments/NEW-DEPLOYMENT-321/chat/completions?api-version=2023-05-15', method: 'post', responseSchema: RunActionResponseSchema, diff --git a/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.ts b/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.ts index 7413ba56090a1..7680cae94db9a 100644 --- a/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.ts +++ b/x-pack/plugins/stack_connectors/server/connector_types/openai/openai.ts @@ -114,6 +114,8 @@ export class OpenAIConnector extends SubActionConnector { method: 'post', responseSchema: RunActionResponseSchema, data: sanitizedBody, + // give up to 2 minutes for response + timeout: 120000, ...axiosOptions, }); return response.data; From ebae6827812e0ffdb3f9de5f5c25849da3224c83 Mon Sep 17 00:00:00 2001 From: Rachel Shen Date: Mon, 30 Oct 2023 16:44:13 -0600 Subject: [PATCH 33/45] [Reporting][Serverless] No image reporting warnings in kibana server logs for serverless (#169297) ## Summary This PR removes reporting deprecation log messages in kibana server logs on serverless environments. ### Checklist - [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 --------- Co-authored-by: Tim Sullivan --- config/serverless.yml | 6 +++++- x-pack/plugins/reporting/server/config/create_config.ts | 7 +++---- .../reporting/server/deprecations/reporting_role.test.ts | 7 +++++++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/config/serverless.yml b/config/serverless.yml index f6e661c158f42..fc8ac64f1a09e 100644 --- a/config/serverless.yml +++ b/config/serverless.yml @@ -56,7 +56,6 @@ xpack.canvas.enabled: false xpack.cloud_integrations.data_migration.enabled: false data.search.sessions.enabled: false advanced_settings.enabled: false -xpack.screenshotting.enabled: false # Disable the browser-side functionality that depends on SecurityCheckupGetStateRoutes xpack.security.showInsecureClusterWarning: false @@ -141,3 +140,8 @@ xpack.task_manager.allow_reading_invalid_state: false ## TaskManager requeue invalid tasks, supports ZDT xpack.task_manager.requeue_invalid_tasks.enabled: true + +# Reporting feature +xpack.screenshotting.enabled: false +xpack.reporting.roles.enabled: false +xpack.reporting.statefulSettings.enabled: false \ No newline at end of file diff --git a/x-pack/plugins/reporting/server/config/create_config.ts b/x-pack/plugins/reporting/server/config/create_config.ts index 39a96354d24c5..54cd09a0a9ddd 100644 --- a/x-pack/plugins/reporting/server/config/create_config.ts +++ b/x-pack/plugins/reporting/server/config/create_config.ts @@ -44,10 +44,9 @@ export function createConfig( ipaddr.isValid(kibanaServerHostname) && !sum(ipaddr.parse(kibanaServerHostname).toByteArray()) ) { - logger.warn( - `Found 'server.host: "0.0.0.0"' in Kibana configuration. Reporting is not able to use this as the Kibana server hostname.` + - ` To enable PNG/PDF Reporting to work, 'xpack.reporting.kibanaServer.hostname: localhost' is automatically set in the configuration.` + - ` You can prevent this message by adding 'xpack.reporting.kibanaServer.hostname: localhost' in kibana.yml.` + logger.info( + `Overriding server host address "0.0.0.0" in Reporting runtime config,` + + ` using "xpack.reporting.kibanaServer.hostname: localhost".` ); kibanaServerHostname = 'localhost'; } diff --git a/x-pack/plugins/reporting/server/deprecations/reporting_role.test.ts b/x-pack/plugins/reporting/server/deprecations/reporting_role.test.ts index 168ac0e4906b4..6655927029056 100644 --- a/x-pack/plugins/reporting/server/deprecations/reporting_role.test.ts +++ b/x-pack/plugins/reporting/server/deprecations/reporting_role.test.ts @@ -134,6 +134,13 @@ describe('check deprecations when security is disabled', () => { expect(await getDeprecationsInfo(context, { reportingCore })).toMatchInlineSnapshot(`Array []`); }); + + test('logs no deprecations on serverless', async () => { + reportingCore = await createMockReportingCore( + createMockConfigSchema({ statefulSettings: { enabled: false } }) + ); + expect(await getDeprecationsInfo(context, { reportingCore })).toMatchInlineSnapshot(`Array []`); + }); }); it('insufficient permissions', async () => { From 764577d608b05ed7da57f1a766020c2130f3abfc Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 30 Oct 2023 18:23:56 -0500 Subject: [PATCH 34/45] [build] Add jbudz as Ironbank maintainer (#170171) --- .../templates/ironbank/hardening_manifest.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/hardening_manifest.yaml b/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/hardening_manifest.yaml index 558d0df40f3a9..3bbd4d7f31d12 100644 --- a/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/hardening_manifest.yaml +++ b/src/dev/build/tasks/os_packages/docker_generator/templates/ironbank/hardening_manifest.yaml @@ -48,6 +48,10 @@ resources: # List of project maintainers maintainers: + - email: 'jon@elastic.co' + name: 'Jonathan Budzenski' + username: 'jbudz' + cht_member: false - email: 'klepal_alexander@bah.com' name: 'Alexander Klepal' username: 'alexander.klepal' From cdde75ad8126b4b27df44d9506f8b10184987397 Mon Sep 17 00:00:00 2001 From: Lisa Cawley Date: Mon, 30 Oct 2023 18:24:08 -0700 Subject: [PATCH 35/45] [OAS] Remove redundant servers from saved object APIs (#169886) --- .../saved-objects/docs/openapi/bundled.json | 156 ++---------------- .../saved-objects/docs/openapi/bundled.yaml | 53 +----- .../docs/openapi/entrypoint.yaml | 3 +- ...i@encrypted_saved_objects@_rotate_key.yaml | 6 +- .../paths/api@saved_objects@_bulk_create.yaml | 5 - .../paths/api@saved_objects@_bulk_delete.yaml | 6 +- .../paths/api@saved_objects@_bulk_get.yaml | 6 +- .../api@saved_objects@_bulk_resolve.yaml | 4 - .../paths/api@saved_objects@_bulk_update.yaml | 4 - .../paths/api@saved_objects@_export.yaml | 4 - .../paths/api@saved_objects@_find.yaml | 4 - .../paths/api@saved_objects@_import.yaml | 4 - ...@saved_objects@_resolve_import_errors.yaml | 4 - ...api@saved_objects@resolve@{type}@{id}.yaml | 5 - .../paths/api@saved_objects@{type}.yaml | 2 - .../paths/api@saved_objects@{type}@{id}.yaml | 6 +- 16 files changed, 21 insertions(+), 251 deletions(-) diff --git a/packages/core/saved-objects/docs/openapi/bundled.json b/packages/core/saved-objects/docs/openapi/bundled.json index d31407a937948..fcf9f7bf4d36e 100644 --- a/packages/core/saved-objects/docs/openapi/bundled.json +++ b/packages/core/saved-objects/docs/openapi/bundled.json @@ -14,8 +14,7 @@ }, "servers": [ { - "url": "http://localhost:5601", - "description": "local" + "url": "/" } ], "security": [ @@ -112,18 +111,8 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] - }, - "servers": [ - { - "url": "https://localhost:5601" } - ] + } }, "/api/saved_objects/_bulk_create": { "post": { @@ -180,18 +169,8 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] - }, - "servers": [ - { - "url": "https://localhost:5601" } - ] + } }, "/api/saved_objects/_bulk_delete": { "post": { @@ -249,18 +228,8 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] - }, - "servers": [ - { - "url": "https://localhost:5601" } - ] + } }, "/api/saved_objects/_bulk_get": { "post": { @@ -309,18 +278,8 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] - }, - "servers": [ - { - "url": "https://localhost:5601" } - ] + } }, "/api/saved_objects/_bulk_resolve": { "post": { @@ -370,18 +329,8 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] - }, - "servers": [ - { - "url": "https://localhost:5601" } - ] + } }, "/api/saved_objects/_bulk_update": { "post": { @@ -430,18 +379,8 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] - }, - "servers": [ - { - "url": "https://localhost:5601" } - ] + } }, "/api/saved_objects/_export": { "post": { @@ -530,18 +469,8 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] - }, - "servers": [ - { - "url": "https://localhost:5601" } - ] + } }, "/api/saved_objects/_find": { "get": { @@ -708,18 +637,8 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] - }, - "servers": [ - { - "url": "https://localhost:5601" } - ] + } }, "/api/saved_objects/_import": { "post": { @@ -831,18 +750,8 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] - }, - "servers": [ - { - "url": "https://localhost:5601" } - ] + } }, "/api/saved_objects/_resolve_import_errors": { "post": { @@ -1001,18 +910,8 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] - }, - "servers": [ - { - "url": "https://localhost:5601" } - ] + } }, "/api/saved_objects/{type}": { "post": { @@ -1084,12 +983,7 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] + } }, "/api/saved_objects/{type}/{id}": { "get": { @@ -1128,12 +1022,7 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] + } }, "post": { "summary": "Create Kibana saved objects.", @@ -1268,12 +1157,7 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] + } }, "/api/saved_objects/resolve/{type}/{id}": { "get": { @@ -1313,18 +1197,8 @@ } } } - }, - "servers": [ - { - "url": "https://localhost:5601" - } - ] - }, - "servers": [ - { - "url": "https://localhost:5601" } - ] + } } }, "components": { diff --git a/packages/core/saved-objects/docs/openapi/bundled.yaml b/packages/core/saved-objects/docs/openapi/bundled.yaml index 554895051768e..a381f044954db 100644 --- a/packages/core/saved-objects/docs/openapi/bundled.yaml +++ b/packages/core/saved-objects/docs/openapi/bundled.yaml @@ -9,8 +9,7 @@ info: name: Elastic License 2.0 url: https://www.elastic.co/licensing/elastic-license servers: - - url: http://localhost:5601 - description: local + - url: / security: - basicAuth: [] - apiKeyAuth: [] @@ -83,10 +82,6 @@ paths: application/json: schema: type: object - servers: - - url: https://localhost:5601 - servers: - - url: https://localhost:5601 /api/saved_objects/_bulk_create: post: summary: Create multiple Kibana saved objects. @@ -122,10 +117,6 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - servers: - - url: https://localhost:5601 - servers: - - url: https://localhost:5601 /api/saved_objects/_bulk_delete: post: summary: Remove multiple Kibana saved objects. @@ -165,10 +156,6 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - servers: - - url: https://localhost:5601 - servers: - - url: https://localhost:5601 /api/saved_objects/_bulk_get: post: summary: Retrieve multiple Kibana saved objects by identifier. @@ -199,10 +186,6 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - servers: - - url: https://localhost:5601 - servers: - - url: https://localhost:5601 /api/saved_objects/_bulk_resolve: post: summary: Retrieve multiple Kibana saved objects by identifier using any legacy URL aliases if they exist. @@ -236,10 +219,6 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - servers: - - url: https://localhost:5601 - servers: - - url: https://localhost:5601 /api/saved_objects/_bulk_update: post: summary: Update the attributes for multiple Kibana saved objects. @@ -271,10 +250,6 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - servers: - - url: https://localhost:5601 - servers: - - url: https://localhost:5601 /api/saved_objects/_export: post: summary: Retrieve sets of saved objects that you want to import into Kibana. @@ -336,10 +311,6 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - servers: - - url: https://localhost:5601 - servers: - - url: https://localhost:5601 /api/saved_objects/_find: get: summary: Retrieve a paginated set of Kibana saved objects. @@ -441,10 +412,6 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - servers: - - url: https://localhost:5601 - servers: - - url: https://localhost:5601 /api/saved_objects/_import: post: summary: Create sets of Kibana saved objects from a file created by the export API. @@ -531,10 +498,6 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - servers: - - url: https://localhost:5601 - servers: - - url: https://localhost:5601 /api/saved_objects/_resolve_import_errors: post: summary: Resolve errors from the Import objects API. @@ -658,10 +621,6 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - servers: - - url: https://localhost:5601 - servers: - - url: https://localhost:5601 /api/saved_objects/{type}: post: summary: Create Kibana saved objects with randomly generated identifiers. @@ -705,8 +664,6 @@ paths: application/json: schema: type: object - servers: - - url: https://localhost:5601 /api/saved_objects/{type}/{id}: get: summary: Retrieve a single Kibana saved object by identifier. @@ -730,8 +687,6 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - servers: - - url: https://localhost:5601 post: summary: Create Kibana saved objects. operationId: createSavedObjectId @@ -810,8 +765,6 @@ paths: application/json: schema: type: object - servers: - - url: https://localhost:5601 /api/saved_objects/resolve/{type}/{id}: get: summary: Retrieve a single Kibana saved object by identifier using any legacy URL alias if it exists. @@ -837,10 +790,6 @@ paths: application/json: schema: $ref: '#/components/schemas/400_response' - servers: - - url: https://localhost:5601 - servers: - - url: https://localhost:5601 components: securitySchemes: basicAuth: diff --git a/packages/core/saved-objects/docs/openapi/entrypoint.yaml b/packages/core/saved-objects/docs/openapi/entrypoint.yaml index f20b8204b4bd4..cfb8a93210a50 100644 --- a/packages/core/saved-objects/docs/openapi/entrypoint.yaml +++ b/packages/core/saved-objects/docs/openapi/entrypoint.yaml @@ -12,8 +12,7 @@ tags: - name: saved objects description: Manage Kibana saved objects, including dashboards, visualizations, and more. servers: - - url: 'http://localhost:5601' - description: local + - url: / paths: # Paths in the default space '/api/encrypted_saved_objects/_rotate_key': diff --git a/packages/core/saved-objects/docs/openapi/paths/api@encrypted_saved_objects@_rotate_key.yaml b/packages/core/saved-objects/docs/openapi/paths/api@encrypted_saved_objects@_rotate_key.yaml index 94ab9cb3f6286..91b75b8ba0a05 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@encrypted_saved_objects@_rotate_key.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@encrypted_saved_objects@_rotate_key.yaml @@ -61,8 +61,4 @@ post: content: application/json: schema: - type: object - servers: - - url: https://localhost:5601 -servers: - - url: https://localhost:5601 \ No newline at end of file + type: object \ No newline at end of file diff --git a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_create.yaml b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_create.yaml index 0461a1a618a75..ab22ae13481fb 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_create.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_create.yaml @@ -32,8 +32,3 @@ post: application/json: schema: $ref: '../components/schemas/400_response.yaml' - - servers: - - url: https://localhost:5601 -servers: - - url: https://localhost:5601 \ No newline at end of file diff --git a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_delete.yaml b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_delete.yaml index c15fdde82b2a4..bbbb848f94578 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_delete.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_delete.yaml @@ -41,8 +41,4 @@ post: content: application/json: schema: - $ref: '../components/schemas/400_response.yaml' - servers: - - url: https://localhost:5601 -servers: - - url: https://localhost:5601 \ No newline at end of file + $ref: '../components/schemas/400_response.yaml' \ No newline at end of file diff --git a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_get.yaml b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_get.yaml index 8512f5a04ef7f..aba99de4bf4b5 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_get.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_get.yaml @@ -26,8 +26,4 @@ post: content: application/json: schema: - $ref: '../components/schemas/400_response.yaml' - servers: - - url: https://localhost:5601 -servers: - - url: https://localhost:5601 \ No newline at end of file + $ref: '../components/schemas/400_response.yaml' \ No newline at end of file diff --git a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_resolve.yaml b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_resolve.yaml index 9227bb782bfac..96ecd2eb0f13d 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_resolve.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_resolve.yaml @@ -34,7 +34,3 @@ post: application/json: schema: $ref: '../components/schemas/400_response.yaml' - servers: - - url: https://localhost:5601 -servers: - - url: https://localhost:5601 \ No newline at end of file diff --git a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_update.yaml b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_update.yaml index aa5e9e1f33c35..497cae0e5b958 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_update.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_bulk_update.yaml @@ -30,7 +30,3 @@ post: application/json: schema: $ref: '../components/schemas/400_response.yaml' - servers: - - url: https://localhost:5601 -servers: - - url: https://localhost:5601 \ No newline at end of file diff --git a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_export.yaml b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_export.yaml index 931746d21ddc1..3a4b78b91acf5 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_export.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_export.yaml @@ -58,7 +58,3 @@ post: application/json: schema: $ref: '../components/schemas/400_response.yaml' - servers: - - url: https://localhost:5601 -servers: - - url: https://localhost:5601 \ No newline at end of file diff --git a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_find.yaml b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_find.yaml index 180a16534f8a0..673baa1f33f09 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_find.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_find.yaml @@ -105,7 +105,3 @@ get: application/json: schema: $ref: '../components/schemas/400_response.yaml' - servers: - - url: https://localhost:5601 -servers: - - url: https://localhost:5601 \ No newline at end of file diff --git a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_import.yaml b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_import.yaml index 5c538a2d1442f..1f7b78c95b784 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_import.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_import.yaml @@ -83,7 +83,3 @@ post: application/json: schema: $ref: '../components/schemas/400_response.yaml' - servers: - - url: https://localhost:5601 -servers: -- url: https://localhost:5601 \ No newline at end of file diff --git a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_resolve_import_errors.yaml b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_resolve_import_errors.yaml index 6c49f1925e3e5..09046320804c4 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_resolve_import_errors.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@_resolve_import_errors.yaml @@ -120,7 +120,3 @@ post: application/json: schema: $ref: '../components/schemas/400_response.yaml' - servers: - - url: https://localhost:5601 -servers: - - url: https://localhost:5601 \ No newline at end of file diff --git a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@resolve@{type}@{id}.yaml b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@resolve@{type}@{id}.yaml index bb1bd0d68bcef..45f8695d401a8 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@resolve@{type}@{id}.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@resolve@{type}@{id}.yaml @@ -24,8 +24,3 @@ get: application/json: schema: $ref: '../components/schemas/400_response.yaml' - servers: - - url: https://localhost:5601 - -servers: - - url: https://localhost:5601 \ No newline at end of file diff --git a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@{type}.yaml b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@{type}.yaml index 2005e4241b41f..6fb4a6a2b046e 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@{type}.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@{type}.yaml @@ -40,5 +40,3 @@ post: application/json: schema: type: object -servers: - - url: https://localhost:5601 \ No newline at end of file diff --git a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@{type}@{id}.yaml b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@{type}@{id}.yaml index b92631817eba4..dc34bc339e2bb 100644 --- a/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@{type}@{id}.yaml +++ b/packages/core/saved-objects/docs/openapi/paths/api@saved_objects@{type}@{id}.yaml @@ -20,8 +20,6 @@ get: application/json: schema: $ref: '../components/schemas/400_response.yaml' - servers: - - url: https://localhost:5601 post: summary: Create Kibana saved objects. @@ -101,6 +99,4 @@ put: content: application/json: schema: - type: object -servers: - - url: https://localhost:5601 \ No newline at end of file + type: object \ No newline at end of file From fcd61e3af479b7154d470890dacd3911e6085f16 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Tue, 31 Oct 2023 01:53:21 +0000 Subject: [PATCH 36/45] skip flaky suite (#170157) --- .../osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts b/x-pack/plugins/osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts index 6dde203828a19..12d3f30ad632a 100644 --- a/x-pack/plugins/osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts +++ b/x-pack/plugins/osquery/cypress/e2e/all/alerts_multiple_agents.cy.ts @@ -14,7 +14,8 @@ import { } from '../../tasks/live_query'; import { OSQUERY_FLYOUT_BODY_EDITOR } from '../../screens/live_query'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/170157 +describe.skip( 'Alert Event Details - dynamic params', { tags: ['@ess', '@serverless'], From 8ec87da21929edfbfe8bf798d5c5b50a844c3a4f Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 31 Oct 2023 00:58:07 -0400 Subject: [PATCH 37/45] [api-docs] 2023-10-31 Daily api_docs build (#170204) Generated by https://buildkite.com/elastic/kibana-api-docs-daily/builds/507 --- api_docs/actions.mdx | 2 +- api_docs/advanced_settings.mdx | 2 +- api_docs/aiops.mdx | 2 +- api_docs/alerting.mdx | 2 +- api_docs/apm.mdx | 2 +- api_docs/apm_data_access.mdx | 2 +- api_docs/asset_manager.mdx | 2 +- api_docs/banners.mdx | 2 +- api_docs/bfetch.mdx | 2 +- api_docs/canvas.mdx | 2 +- api_docs/cases.mdx | 2 +- api_docs/charts.mdx | 2 +- api_docs/cloud.mdx | 2 +- api_docs/cloud_data_migration.mdx | 2 +- api_docs/cloud_defend.mdx | 2 +- api_docs/cloud_experiments.mdx | 2 +- api_docs/cloud_security_posture.mdx | 2 +- api_docs/console.mdx | 2 +- api_docs/content_management.mdx | 2 +- api_docs/controls.mdx | 2 +- api_docs/custom_integrations.mdx | 2 +- api_docs/dashboard.mdx | 2 +- api_docs/dashboard_enhanced.mdx | 2 +- api_docs/data.mdx | 2 +- api_docs/data_query.mdx | 2 +- api_docs/data_search.mdx | 2 +- api_docs/data_view_editor.mdx | 2 +- api_docs/data_view_field_editor.mdx | 2 +- api_docs/data_view_management.mdx | 2 +- api_docs/data_views.mdx | 2 +- api_docs/data_visualizer.mdx | 2 +- api_docs/deprecations_by_api.mdx | 2 +- api_docs/deprecations_by_plugin.mdx | 2 +- api_docs/deprecations_by_team.mdx | 2 +- api_docs/dev_tools.mdx | 2 +- api_docs/discover.mdx | 2 +- api_docs/discover_enhanced.mdx | 2 +- api_docs/ecs_data_quality_dashboard.mdx | 2 +- api_docs/elastic_assistant.mdx | 2 +- api_docs/embeddable.mdx | 2 +- api_docs/embeddable_enhanced.mdx | 2 +- api_docs/encrypted_saved_objects.mdx | 2 +- api_docs/enterprise_search.mdx | 2 +- api_docs/es_ui_shared.mdx | 2 +- api_docs/event_annotation.mdx | 2 +- api_docs/event_annotation_listing.mdx | 2 +- api_docs/event_log.mdx | 2 +- api_docs/exploratory_view.mdx | 2 +- api_docs/expression_error.mdx | 2 +- api_docs/expression_gauge.mdx | 2 +- api_docs/expression_heatmap.mdx | 2 +- api_docs/expression_image.mdx | 2 +- api_docs/expression_legacy_metric_vis.mdx | 2 +- api_docs/expression_metric.mdx | 2 +- api_docs/expression_metric_vis.mdx | 2 +- api_docs/expression_partition_vis.mdx | 2 +- api_docs/expression_repeat_image.mdx | 2 +- api_docs/expression_reveal_image.mdx | 2 +- api_docs/expression_shape.mdx | 2 +- api_docs/expression_tagcloud.mdx | 2 +- api_docs/expression_x_y.mdx | 2 +- api_docs/expressions.mdx | 2 +- api_docs/features.mdx | 2 +- api_docs/field_formats.mdx | 2 +- api_docs/file_upload.mdx | 2 +- api_docs/files.mdx | 2 +- api_docs/files_management.mdx | 2 +- api_docs/fleet.mdx | 2 +- api_docs/global_search.mdx | 2 +- api_docs/guided_onboarding.mdx | 2 +- api_docs/home.mdx | 2 +- api_docs/image_embeddable.mdx | 2 +- api_docs/index_lifecycle_management.mdx | 2 +- api_docs/index_management.mdx | 2 +- api_docs/infra.mdx | 2 +- api_docs/inspector.mdx | 2 +- api_docs/interactive_setup.mdx | 2 +- api_docs/kbn_ace.mdx | 2 +- api_docs/kbn_aiops_components.mdx | 2 +- api_docs/kbn_aiops_utils.mdx | 2 +- api_docs/kbn_alerting_api_integration_helpers.mdx | 2 +- api_docs/kbn_alerting_state_types.mdx | 2 +- api_docs/kbn_alerts_as_data_utils.mdx | 2 +- api_docs/kbn_alerts_ui_shared.mdx | 2 +- api_docs/kbn_analytics.mdx | 2 +- api_docs/kbn_analytics_client.mdx | 2 +- api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx | 2 +- api_docs/kbn_analytics_shippers_elastic_v3_common.mdx | 2 +- api_docs/kbn_analytics_shippers_elastic_v3_server.mdx | 2 +- api_docs/kbn_analytics_shippers_fullstory.mdx | 2 +- api_docs/kbn_analytics_shippers_gainsight.mdx | 2 +- api_docs/kbn_apm_config_loader.mdx | 2 +- api_docs/kbn_apm_synthtrace.mdx | 2 +- api_docs/kbn_apm_synthtrace_client.mdx | 2 +- api_docs/kbn_apm_utils.mdx | 2 +- api_docs/kbn_axe_config.mdx | 2 +- api_docs/kbn_cases_components.mdx | 2 +- api_docs/kbn_cell_actions.mdx | 2 +- api_docs/kbn_chart_expressions_common.mdx | 2 +- api_docs/kbn_chart_icons.mdx | 2 +- api_docs/kbn_ci_stats_core.mdx | 2 +- api_docs/kbn_ci_stats_performance_metrics.mdx | 2 +- api_docs/kbn_ci_stats_reporter.mdx | 2 +- api_docs/kbn_cli_dev_mode.mdx | 2 +- api_docs/kbn_code_editor.mdx | 2 +- api_docs/kbn_code_editor_mocks.mdx | 2 +- api_docs/kbn_coloring.mdx | 2 +- api_docs/kbn_config.mdx | 2 +- api_docs/kbn_config_mocks.mdx | 2 +- api_docs/kbn_config_schema.mdx | 2 +- api_docs/kbn_content_management_content_editor.mdx | 2 +- api_docs/kbn_content_management_tabbed_table_list_view.mdx | 2 +- api_docs/kbn_content_management_table_list_view.mdx | 2 +- api_docs/kbn_content_management_table_list_view_table.mdx | 2 +- api_docs/kbn_content_management_utils.mdx | 2 +- api_docs/kbn_core_analytics_browser.mdx | 2 +- api_docs/kbn_core_analytics_browser_internal.mdx | 2 +- api_docs/kbn_core_analytics_browser_mocks.mdx | 2 +- api_docs/kbn_core_analytics_server.mdx | 2 +- api_docs/kbn_core_analytics_server_internal.mdx | 2 +- api_docs/kbn_core_analytics_server_mocks.mdx | 2 +- api_docs/kbn_core_application_browser.mdx | 2 +- api_docs/kbn_core_application_browser_internal.mdx | 2 +- api_docs/kbn_core_application_browser_mocks.mdx | 2 +- api_docs/kbn_core_application_common.mdx | 2 +- api_docs/kbn_core_apps_browser_internal.mdx | 2 +- api_docs/kbn_core_apps_browser_mocks.mdx | 2 +- api_docs/kbn_core_apps_server_internal.mdx | 2 +- api_docs/kbn_core_base_browser_mocks.mdx | 2 +- api_docs/kbn_core_base_common.mdx | 2 +- api_docs/kbn_core_base_server_internal.mdx | 2 +- api_docs/kbn_core_base_server_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_browser_mocks.mdx | 2 +- api_docs/kbn_core_capabilities_common.mdx | 2 +- api_docs/kbn_core_capabilities_server.mdx | 2 +- api_docs/kbn_core_capabilities_server_mocks.mdx | 2 +- api_docs/kbn_core_chrome_browser.mdx | 2 +- api_docs/kbn_core_chrome_browser_mocks.mdx | 2 +- api_docs/kbn_core_config_server_internal.mdx | 2 +- api_docs/kbn_core_custom_branding_browser.mdx | 2 +- api_docs/kbn_core_custom_branding_browser_internal.mdx | 2 +- api_docs/kbn_core_custom_branding_browser_mocks.mdx | 2 +- api_docs/kbn_core_custom_branding_common.mdx | 2 +- api_docs/kbn_core_custom_branding_server.mdx | 2 +- api_docs/kbn_core_custom_branding_server_internal.mdx | 2 +- api_docs/kbn_core_custom_branding_server_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_browser.mdx | 2 +- api_docs/kbn_core_deprecations_browser_internal.mdx | 2 +- api_docs/kbn_core_deprecations_browser_mocks.mdx | 2 +- api_docs/kbn_core_deprecations_common.mdx | 2 +- api_docs/kbn_core_deprecations_server.mdx | 2 +- api_docs/kbn_core_deprecations_server_internal.mdx | 2 +- api_docs/kbn_core_deprecations_server_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_browser.mdx | 2 +- api_docs/kbn_core_doc_links_browser_mocks.mdx | 2 +- api_docs/kbn_core_doc_links_server.mdx | 2 +- api_docs/kbn_core_doc_links_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_client_server_internal.mdx | 2 +- api_docs/kbn_core_elasticsearch_client_server_mocks.mdx | 2 +- api_docs/kbn_core_elasticsearch_server.mdx | 2 +- api_docs/kbn_core_elasticsearch_server_internal.mdx | 2 +- api_docs/kbn_core_elasticsearch_server_mocks.mdx | 2 +- api_docs/kbn_core_environment_server_internal.mdx | 2 +- api_docs/kbn_core_environment_server_mocks.mdx | 2 +- api_docs/kbn_core_execution_context_browser.mdx | 2 +- api_docs/kbn_core_execution_context_browser_internal.mdx | 2 +- api_docs/kbn_core_execution_context_browser_mocks.mdx | 2 +- api_docs/kbn_core_execution_context_common.mdx | 2 +- api_docs/kbn_core_execution_context_server.mdx | 2 +- api_docs/kbn_core_execution_context_server_internal.mdx | 2 +- api_docs/kbn_core_execution_context_server_mocks.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser.mdx | 2 +- api_docs/kbn_core_fatal_errors_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_browser.mdx | 2 +- api_docs/kbn_core_http_browser_internal.mdx | 2 +- api_docs/kbn_core_http_browser_mocks.mdx | 2 +- api_docs/kbn_core_http_common.mdx | 2 +- api_docs/kbn_core_http_context_server_mocks.mdx | 2 +- api_docs/kbn_core_http_request_handler_context_server.mdx | 2 +- api_docs/kbn_core_http_resources_server.mdx | 2 +- api_docs/kbn_core_http_resources_server_internal.mdx | 2 +- api_docs/kbn_core_http_resources_server_mocks.mdx | 2 +- api_docs/kbn_core_http_router_server_internal.mdx | 2 +- api_docs/kbn_core_http_router_server_mocks.mdx | 2 +- api_docs/kbn_core_http_server.devdocs.json | 4 ++-- api_docs/kbn_core_http_server.mdx | 2 +- api_docs/kbn_core_http_server_internal.mdx | 2 +- api_docs/kbn_core_http_server_mocks.mdx | 2 +- api_docs/kbn_core_i18n_browser.mdx | 2 +- api_docs/kbn_core_i18n_browser_mocks.mdx | 2 +- api_docs/kbn_core_i18n_server.mdx | 2 +- api_docs/kbn_core_i18n_server_internal.mdx | 2 +- api_docs/kbn_core_i18n_server_mocks.mdx | 2 +- api_docs/kbn_core_injected_metadata_browser_mocks.mdx | 2 +- api_docs/kbn_core_integrations_browser_internal.mdx | 2 +- api_docs/kbn_core_integrations_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_browser.mdx | 2 +- api_docs/kbn_core_lifecycle_browser_mocks.mdx | 2 +- api_docs/kbn_core_lifecycle_server.mdx | 2 +- api_docs/kbn_core_lifecycle_server_mocks.mdx | 2 +- api_docs/kbn_core_logging_browser_mocks.mdx | 2 +- api_docs/kbn_core_logging_common_internal.mdx | 2 +- api_docs/kbn_core_logging_server.mdx | 2 +- api_docs/kbn_core_logging_server_internal.mdx | 2 +- api_docs/kbn_core_logging_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_collectors_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_collectors_server_mocks.mdx | 2 +- api_docs/kbn_core_metrics_server.mdx | 2 +- api_docs/kbn_core_metrics_server_internal.mdx | 2 +- api_docs/kbn_core_metrics_server_mocks.mdx | 2 +- api_docs/kbn_core_mount_utils_browser.mdx | 2 +- api_docs/kbn_core_node_server.mdx | 2 +- api_docs/kbn_core_node_server_internal.mdx | 2 +- api_docs/kbn_core_node_server_mocks.mdx | 2 +- api_docs/kbn_core_notifications_browser.mdx | 2 +- api_docs/kbn_core_notifications_browser_internal.mdx | 2 +- api_docs/kbn_core_notifications_browser_mocks.mdx | 2 +- api_docs/kbn_core_overlays_browser.mdx | 2 +- api_docs/kbn_core_overlays_browser_internal.mdx | 2 +- api_docs/kbn_core_overlays_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_browser.mdx | 2 +- api_docs/kbn_core_plugins_browser_mocks.mdx | 2 +- api_docs/kbn_core_plugins_contracts_browser.mdx | 2 +- api_docs/kbn_core_plugins_contracts_server.mdx | 2 +- api_docs/kbn_core_plugins_server.mdx | 2 +- api_docs/kbn_core_plugins_server_mocks.mdx | 2 +- api_docs/kbn_core_preboot_server.mdx | 2 +- api_docs/kbn_core_preboot_server_mocks.mdx | 2 +- api_docs/kbn_core_rendering_browser_mocks.mdx | 2 +- api_docs/kbn_core_rendering_server_internal.mdx | 2 +- api_docs/kbn_core_rendering_server_mocks.mdx | 2 +- api_docs/kbn_core_root_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_api_browser.mdx | 2 +- api_docs/kbn_core_saved_objects_api_server.mdx | 2 +- api_docs/kbn_core_saved_objects_api_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_base_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_base_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_browser.mdx | 2 +- api_docs/kbn_core_saved_objects_browser_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_browser_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_common.mdx | 2 +- .../kbn_core_saved_objects_import_export_server_internal.mdx | 2 +- .../kbn_core_saved_objects_import_export_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_migration_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_migration_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_server.mdx | 2 +- api_docs/kbn_core_saved_objects_server_internal.mdx | 2 +- api_docs/kbn_core_saved_objects_server_mocks.mdx | 2 +- api_docs/kbn_core_saved_objects_utils_server.mdx | 2 +- api_docs/kbn_core_status_common.mdx | 2 +- api_docs/kbn_core_status_common_internal.mdx | 2 +- api_docs/kbn_core_status_server.mdx | 2 +- api_docs/kbn_core_status_server_internal.mdx | 2 +- api_docs/kbn_core_status_server_mocks.mdx | 2 +- api_docs/kbn_core_test_helpers_deprecations_getters.mdx | 2 +- api_docs/kbn_core_test_helpers_http_setup_browser.mdx | 2 +- api_docs/kbn_core_test_helpers_kbn_server.mdx | 2 +- api_docs/kbn_core_test_helpers_model_versions.mdx | 2 +- api_docs/kbn_core_test_helpers_so_type_serializer.mdx | 2 +- api_docs/kbn_core_test_helpers_test_utils.mdx | 2 +- api_docs/kbn_core_theme_browser.mdx | 2 +- api_docs/kbn_core_theme_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_browser.mdx | 2 +- api_docs/kbn_core_ui_settings_browser_internal.mdx | 2 +- api_docs/kbn_core_ui_settings_browser_mocks.mdx | 2 +- api_docs/kbn_core_ui_settings_common.mdx | 2 +- api_docs/kbn_core_ui_settings_server.mdx | 2 +- api_docs/kbn_core_ui_settings_server_internal.mdx | 2 +- api_docs/kbn_core_ui_settings_server_mocks.mdx | 2 +- api_docs/kbn_core_usage_data_server.mdx | 2 +- api_docs/kbn_core_usage_data_server_internal.mdx | 2 +- api_docs/kbn_core_usage_data_server_mocks.mdx | 2 +- api_docs/kbn_core_user_settings_server.mdx | 2 +- api_docs/kbn_core_user_settings_server_internal.mdx | 2 +- api_docs/kbn_core_user_settings_server_mocks.mdx | 2 +- api_docs/kbn_crypto.mdx | 2 +- api_docs/kbn_crypto_browser.mdx | 2 +- api_docs/kbn_custom_integrations.mdx | 2 +- api_docs/kbn_cypress_config.mdx | 2 +- api_docs/kbn_data_service.mdx | 2 +- api_docs/kbn_datemath.mdx | 2 +- api_docs/kbn_deeplinks_analytics.mdx | 2 +- api_docs/kbn_deeplinks_devtools.mdx | 2 +- api_docs/kbn_deeplinks_management.mdx | 2 +- api_docs/kbn_deeplinks_ml.mdx | 2 +- api_docs/kbn_deeplinks_observability.mdx | 2 +- api_docs/kbn_deeplinks_search.mdx | 2 +- api_docs/kbn_default_nav_analytics.mdx | 2 +- api_docs/kbn_default_nav_devtools.mdx | 2 +- api_docs/kbn_default_nav_management.mdx | 2 +- api_docs/kbn_default_nav_ml.mdx | 2 +- api_docs/kbn_dev_cli_errors.mdx | 2 +- api_docs/kbn_dev_cli_runner.mdx | 2 +- api_docs/kbn_dev_proc_runner.mdx | 2 +- api_docs/kbn_dev_utils.mdx | 2 +- api_docs/kbn_discover_utils.mdx | 2 +- api_docs/kbn_doc_links.mdx | 2 +- api_docs/kbn_docs_utils.mdx | 2 +- api_docs/kbn_dom_drag_drop.mdx | 2 +- api_docs/kbn_ebt_tools.mdx | 2 +- api_docs/kbn_ecs.mdx | 2 +- api_docs/kbn_ecs_data_quality_dashboard.mdx | 2 +- api_docs/kbn_elastic_assistant.mdx | 2 +- api_docs/kbn_es.mdx | 2 +- api_docs/kbn_es_archiver.mdx | 2 +- api_docs/kbn_es_errors.mdx | 2 +- api_docs/kbn_es_query.mdx | 2 +- api_docs/kbn_es_types.mdx | 2 +- api_docs/kbn_eslint_plugin_imports.mdx | 2 +- api_docs/kbn_event_annotation_common.mdx | 2 +- api_docs/kbn_event_annotation_components.mdx | 2 +- api_docs/kbn_expandable_flyout.mdx | 2 +- api_docs/kbn_field_types.mdx | 2 +- api_docs/kbn_field_utils.mdx | 2 +- api_docs/kbn_find_used_node_modules.mdx | 2 +- api_docs/kbn_ftr_common_functional_services.mdx | 2 +- api_docs/kbn_generate.mdx | 2 +- api_docs/kbn_generate_console_definitions.mdx | 2 +- api_docs/kbn_generate_csv.mdx | 2 +- api_docs/kbn_generate_csv_types.mdx | 2 +- api_docs/kbn_guided_onboarding.mdx | 2 +- api_docs/kbn_handlebars.mdx | 2 +- api_docs/kbn_hapi_mocks.mdx | 2 +- api_docs/kbn_health_gateway_server.mdx | 2 +- api_docs/kbn_home_sample_data_card.mdx | 2 +- api_docs/kbn_home_sample_data_tab.mdx | 2 +- api_docs/kbn_i18n.mdx | 2 +- api_docs/kbn_i18n_react.mdx | 2 +- api_docs/kbn_import_resolver.mdx | 2 +- api_docs/kbn_infra_forge.mdx | 2 +- api_docs/kbn_interpreter.mdx | 2 +- api_docs/kbn_io_ts_utils.mdx | 2 +- api_docs/kbn_jest_serializers.mdx | 2 +- api_docs/kbn_journeys.mdx | 2 +- api_docs/kbn_json_ast.mdx | 2 +- api_docs/kbn_kibana_manifest_schema.mdx | 2 +- api_docs/kbn_language_documentation_popover.mdx | 2 +- api_docs/kbn_lens_embeddable_utils.mdx | 2 +- api_docs/kbn_logging.mdx | 2 +- api_docs/kbn_logging_mocks.mdx | 2 +- api_docs/kbn_managed_vscode_config.mdx | 2 +- api_docs/kbn_management_cards_navigation.mdx | 2 +- api_docs/kbn_management_settings_application.mdx | 2 +- .../kbn_management_settings_components_field_category.mdx | 2 +- api_docs/kbn_management_settings_components_field_input.mdx | 2 +- api_docs/kbn_management_settings_components_field_row.mdx | 2 +- api_docs/kbn_management_settings_components_form.mdx | 2 +- api_docs/kbn_management_settings_field_definition.mdx | 2 +- api_docs/kbn_management_settings_ids.mdx | 2 +- api_docs/kbn_management_settings_section_registry.mdx | 2 +- api_docs/kbn_management_settings_types.mdx | 2 +- api_docs/kbn_management_settings_utilities.mdx | 2 +- api_docs/kbn_management_storybook_config.mdx | 2 +- api_docs/kbn_mapbox_gl.mdx | 2 +- api_docs/kbn_maps_vector_tile_utils.mdx | 2 +- api_docs/kbn_ml_agg_utils.mdx | 2 +- api_docs/kbn_ml_anomaly_utils.mdx | 2 +- api_docs/kbn_ml_category_validator.mdx | 2 +- api_docs/kbn_ml_chi2test.mdx | 2 +- api_docs/kbn_ml_data_frame_analytics_utils.mdx | 2 +- api_docs/kbn_ml_data_grid.mdx | 2 +- api_docs/kbn_ml_date_picker.mdx | 2 +- api_docs/kbn_ml_date_utils.mdx | 2 +- api_docs/kbn_ml_error_utils.mdx | 2 +- api_docs/kbn_ml_in_memory_table.mdx | 2 +- api_docs/kbn_ml_is_defined.mdx | 2 +- api_docs/kbn_ml_is_populated_object.mdx | 2 +- api_docs/kbn_ml_kibana_theme.mdx | 2 +- api_docs/kbn_ml_local_storage.mdx | 2 +- api_docs/kbn_ml_nested_property.mdx | 2 +- api_docs/kbn_ml_number_utils.mdx | 2 +- api_docs/kbn_ml_query_utils.mdx | 2 +- api_docs/kbn_ml_random_sampler_utils.mdx | 2 +- api_docs/kbn_ml_route_utils.mdx | 2 +- api_docs/kbn_ml_runtime_field_utils.mdx | 2 +- api_docs/kbn_ml_string_hash.mdx | 2 +- api_docs/kbn_ml_trained_models_utils.mdx | 2 +- api_docs/kbn_ml_url_state.mdx | 2 +- api_docs/kbn_monaco.mdx | 2 +- api_docs/kbn_object_versioning.mdx | 2 +- api_docs/kbn_observability_alert_details.mdx | 2 +- api_docs/kbn_observability_alerting_test_data.mdx | 2 +- api_docs/kbn_openapi_generator.mdx | 2 +- api_docs/kbn_optimizer.mdx | 2 +- api_docs/kbn_optimizer_webpack_helpers.mdx | 2 +- api_docs/kbn_osquery_io_ts_types.mdx | 2 +- api_docs/kbn_performance_testing_dataset_extractor.mdx | 2 +- api_docs/kbn_plugin_generator.mdx | 2 +- api_docs/kbn_plugin_helpers.mdx | 2 +- api_docs/kbn_profiling_utils.mdx | 2 +- api_docs/kbn_random_sampling.mdx | 2 +- api_docs/kbn_react_field.mdx | 2 +- api_docs/kbn_react_kibana_context_common.mdx | 2 +- api_docs/kbn_react_kibana_context_render.mdx | 2 +- api_docs/kbn_react_kibana_context_root.mdx | 2 +- api_docs/kbn_react_kibana_context_styled.mdx | 2 +- api_docs/kbn_react_kibana_context_theme.mdx | 2 +- api_docs/kbn_react_kibana_mount.mdx | 2 +- api_docs/kbn_repo_file_maps.mdx | 2 +- api_docs/kbn_repo_linter.mdx | 2 +- api_docs/kbn_repo_path.mdx | 2 +- api_docs/kbn_repo_source_classifier.mdx | 2 +- api_docs/kbn_reporting_common.mdx | 2 +- api_docs/kbn_resizable_layout.mdx | 2 +- api_docs/kbn_rison.mdx | 2 +- api_docs/kbn_rrule.mdx | 2 +- api_docs/kbn_rule_data_utils.mdx | 2 +- api_docs/kbn_saved_objects_settings.mdx | 2 +- api_docs/kbn_search_api_panels.mdx | 2 +- api_docs/kbn_search_connectors.mdx | 2 +- api_docs/kbn_search_response_warnings.mdx | 2 +- api_docs/kbn_security_solution_features.mdx | 2 +- api_docs/kbn_security_solution_navigation.mdx | 2 +- api_docs/kbn_security_solution_side_nav.mdx | 2 +- api_docs/kbn_security_solution_storybook_config.mdx | 2 +- api_docs/kbn_securitysolution_autocomplete.mdx | 2 +- api_docs/kbn_securitysolution_data_table.mdx | 2 +- api_docs/kbn_securitysolution_ecs.mdx | 2 +- api_docs/kbn_securitysolution_es_utils.mdx | 2 +- api_docs/kbn_securitysolution_exception_list_components.mdx | 2 +- api_docs/kbn_securitysolution_grouping.mdx | 2 +- api_docs/kbn_securitysolution_hook_utils.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_alerting_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_list_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_types.mdx | 2 +- api_docs/kbn_securitysolution_io_ts_utils.mdx | 2 +- api_docs/kbn_securitysolution_list_api.mdx | 2 +- api_docs/kbn_securitysolution_list_constants.mdx | 2 +- api_docs/kbn_securitysolution_list_hooks.mdx | 2 +- api_docs/kbn_securitysolution_list_utils.mdx | 2 +- api_docs/kbn_securitysolution_rules.mdx | 2 +- api_docs/kbn_securitysolution_t_grid.mdx | 2 +- api_docs/kbn_securitysolution_utils.mdx | 2 +- api_docs/kbn_server_http_tools.mdx | 2 +- api_docs/kbn_server_route_repository.mdx | 2 +- api_docs/kbn_serverless_common_settings.mdx | 2 +- api_docs/kbn_serverless_observability_settings.mdx | 2 +- api_docs/kbn_serverless_project_switcher.mdx | 2 +- api_docs/kbn_serverless_search_settings.mdx | 2 +- api_docs/kbn_serverless_security_settings.mdx | 2 +- api_docs/kbn_serverless_storybook_config.mdx | 2 +- api_docs/kbn_shared_svg.mdx | 2 +- api_docs/kbn_shared_ux_avatar_solution.mdx | 2 +- api_docs/kbn_shared_ux_button_exit_full_screen.mdx | 2 +- api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx | 2 +- api_docs/kbn_shared_ux_button_toolbar.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data.mdx | 2 +- api_docs/kbn_shared_ux_card_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_chrome_navigation.mdx | 2 +- api_docs/kbn_shared_ux_error_boundary.mdx | 2 +- api_docs/kbn_shared_ux_file_context.mdx | 2 +- api_docs/kbn_shared_ux_file_image.mdx | 2 +- api_docs/kbn_shared_ux_file_image_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_mocks.mdx | 2 +- api_docs/kbn_shared_ux_file_picker.mdx | 2 +- api_docs/kbn_shared_ux_file_types.mdx | 2 +- api_docs/kbn_shared_ux_file_upload.mdx | 2 +- api_docs/kbn_shared_ux_file_util.mdx | 2 +- api_docs/kbn_shared_ux_link_redirect_app.mdx | 2 +- api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx | 2 +- api_docs/kbn_shared_ux_markdown.mdx | 2 +- api_docs/kbn_shared_ux_markdown_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_analytics_no_data.mdx | 2 +- api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_no_data.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_template.mdx | 2 +- api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_config.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_no_data_mocks.mdx | 2 +- api_docs/kbn_shared_ux_page_solution_nav.mdx | 2 +- api_docs/kbn_shared_ux_prompt_no_data_views.mdx | 2 +- api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx | 2 +- api_docs/kbn_shared_ux_prompt_not_found.mdx | 2 +- api_docs/kbn_shared_ux_router.mdx | 2 +- api_docs/kbn_shared_ux_router_mocks.mdx | 2 +- api_docs/kbn_shared_ux_storybook_config.mdx | 2 +- api_docs/kbn_shared_ux_storybook_mock.mdx | 2 +- api_docs/kbn_shared_ux_utility.mdx | 2 +- api_docs/kbn_slo_schema.mdx | 2 +- api_docs/kbn_some_dev_log.mdx | 2 +- api_docs/kbn_std.mdx | 2 +- api_docs/kbn_stdio_dev_helpers.mdx | 2 +- api_docs/kbn_storybook.mdx | 2 +- api_docs/kbn_subscription_tracking.mdx | 2 +- api_docs/kbn_telemetry_tools.mdx | 2 +- api_docs/kbn_test.mdx | 2 +- api_docs/kbn_test_jest_helpers.mdx | 2 +- api_docs/kbn_test_subj_selector.mdx | 2 +- api_docs/kbn_text_based_editor.mdx | 2 +- api_docs/kbn_tooling_log.mdx | 2 +- api_docs/kbn_ts_projects.mdx | 2 +- api_docs/kbn_typed_react_router_config.mdx | 2 +- api_docs/kbn_ui_actions_browser.mdx | 2 +- api_docs/kbn_ui_shared_deps_src.mdx | 2 +- api_docs/kbn_ui_theme.mdx | 2 +- api_docs/kbn_unified_data_table.mdx | 2 +- api_docs/kbn_unified_doc_viewer.mdx | 2 +- api_docs/kbn_unified_field_list.mdx | 2 +- api_docs/kbn_url_state.mdx | 2 +- api_docs/kbn_use_tracked_promise.mdx | 2 +- api_docs/kbn_user_profile_components.mdx | 2 +- api_docs/kbn_utility_types.mdx | 2 +- api_docs/kbn_utility_types_jest.mdx | 2 +- api_docs/kbn_utils.mdx | 2 +- api_docs/kbn_visualization_ui_components.mdx | 2 +- api_docs/kbn_xstate_utils.mdx | 2 +- api_docs/kbn_yarn_lock_validator.mdx | 2 +- api_docs/kbn_zod_helpers.mdx | 2 +- api_docs/kibana_overview.mdx | 2 +- api_docs/kibana_react.mdx | 2 +- api_docs/kibana_utils.mdx | 2 +- api_docs/kubernetes_security.mdx | 2 +- api_docs/lens.mdx | 2 +- api_docs/license_api_guard.mdx | 2 +- api_docs/license_management.mdx | 2 +- api_docs/licensing.mdx | 2 +- api_docs/links.mdx | 2 +- api_docs/lists.mdx | 2 +- api_docs/log_explorer.mdx | 2 +- api_docs/logs_shared.mdx | 2 +- api_docs/management.mdx | 2 +- api_docs/maps.mdx | 2 +- api_docs/maps_ems.mdx | 2 +- api_docs/metrics_data_access.mdx | 2 +- api_docs/ml.mdx | 2 +- api_docs/monitoring.mdx | 2 +- api_docs/monitoring_collection.mdx | 2 +- api_docs/navigation.mdx | 2 +- api_docs/newsfeed.mdx | 2 +- api_docs/no_data_page.mdx | 2 +- api_docs/notifications.mdx | 2 +- api_docs/observability.mdx | 2 +- api_docs/observability_a_i_assistant.mdx | 2 +- api_docs/observability_log_explorer.mdx | 2 +- api_docs/observability_onboarding.mdx | 2 +- api_docs/observability_shared.mdx | 2 +- api_docs/osquery.mdx | 2 +- api_docs/painless_lab.mdx | 2 +- api_docs/plugin_directory.mdx | 2 +- api_docs/presentation_util.mdx | 2 +- api_docs/profiling.mdx | 2 +- api_docs/profiling_data_access.mdx | 2 +- api_docs/remote_clusters.mdx | 2 +- api_docs/reporting.mdx | 2 +- api_docs/rollup.mdx | 2 +- api_docs/rule_registry.mdx | 2 +- api_docs/runtime_fields.mdx | 2 +- api_docs/saved_objects.mdx | 2 +- api_docs/saved_objects_finder.mdx | 2 +- api_docs/saved_objects_management.mdx | 2 +- api_docs/saved_objects_tagging.mdx | 2 +- api_docs/saved_objects_tagging_oss.mdx | 2 +- api_docs/saved_search.mdx | 2 +- api_docs/screenshot_mode.mdx | 2 +- api_docs/screenshotting.mdx | 2 +- api_docs/security.mdx | 2 +- api_docs/security_solution.mdx | 2 +- api_docs/security_solution_ess.mdx | 2 +- api_docs/security_solution_serverless.mdx | 2 +- api_docs/serverless.mdx | 2 +- api_docs/serverless_observability.mdx | 2 +- api_docs/serverless_search.mdx | 2 +- api_docs/session_view.mdx | 2 +- api_docs/share.mdx | 2 +- api_docs/snapshot_restore.mdx | 2 +- api_docs/spaces.mdx | 2 +- api_docs/stack_alerts.mdx | 2 +- api_docs/stack_connectors.mdx | 2 +- api_docs/task_manager.mdx | 2 +- api_docs/telemetry.mdx | 2 +- api_docs/telemetry_collection_manager.mdx | 2 +- api_docs/telemetry_collection_xpack.mdx | 2 +- api_docs/telemetry_management_section.mdx | 2 +- api_docs/text_based_languages.mdx | 2 +- api_docs/threat_intelligence.mdx | 2 +- api_docs/timelines.mdx | 2 +- api_docs/transform.mdx | 2 +- api_docs/triggers_actions_ui.mdx | 2 +- api_docs/ui_actions.mdx | 2 +- api_docs/ui_actions_enhanced.mdx | 2 +- api_docs/unified_doc_viewer.mdx | 2 +- api_docs/unified_histogram.mdx | 2 +- api_docs/unified_search.mdx | 2 +- api_docs/unified_search_autocomplete.mdx | 2 +- api_docs/uptime.mdx | 2 +- api_docs/url_forwarding.mdx | 2 +- api_docs/usage_collection.mdx | 2 +- api_docs/ux.mdx | 2 +- api_docs/vis_default_editor.mdx | 2 +- api_docs/vis_type_gauge.mdx | 2 +- api_docs/vis_type_heatmap.mdx | 2 +- api_docs/vis_type_pie.mdx | 2 +- api_docs/vis_type_table.mdx | 2 +- api_docs/vis_type_timelion.mdx | 2 +- api_docs/vis_type_timeseries.mdx | 2 +- api_docs/vis_type_vega.mdx | 2 +- api_docs/vis_type_vislib.mdx | 2 +- api_docs/vis_type_xy.mdx | 2 +- api_docs/visualizations.mdx | 2 +- 602 files changed, 603 insertions(+), 603 deletions(-) diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 488707c72c7c4..37ee56c38eef2 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'actions'] --- import actionsObj from './actions.devdocs.json'; diff --git a/api_docs/advanced_settings.mdx b/api_docs/advanced_settings.mdx index 8c678bcef2c68..040132929a034 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'advancedSettings'] --- import advancedSettingsObj from './advanced_settings.devdocs.json'; diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index 8bfec524f8a14..4cc88f5760194 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index e8dc3c4d97182..1fb373c54caf7 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 7857a694cffbe..4ee6b99f2566e 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index a51abe4edc3e7..848c796ef9e71 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apmDataAccess'] --- import apmDataAccessObj from './apm_data_access.devdocs.json'; diff --git a/api_docs/asset_manager.mdx b/api_docs/asset_manager.mdx index a003c483bc7a1..5db8bf04ddfda 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'assetManager'] --- import assetManagerObj from './asset_manager.devdocs.json'; diff --git a/api_docs/banners.mdx b/api_docs/banners.mdx index e82fd28a18ca4..f9f91e6acea10 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 72f98eb51ac77..be5a77fb303e8 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.devdocs.json'; diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 758dd725c9c8e..7c16afc403e20 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'canvas'] --- import canvasObj from './canvas.devdocs.json'; diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx index 3e0f1266d51ff..a8e28efbb3bf1 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index d84bbdb25757e..0c33f30644e2d 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'charts'] --- import chartsObj from './charts.devdocs.json'; diff --git a/api_docs/cloud.mdx b/api_docs/cloud.mdx index 9647bb7eb1574..1dee7bb622860 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloud'] --- import cloudObj from './cloud.devdocs.json'; diff --git a/api_docs/cloud_data_migration.mdx b/api_docs/cloud_data_migration.mdx index 02226114f2f60..c2172ef715a34 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDataMigration'] --- import cloudDataMigrationObj from './cloud_data_migration.devdocs.json'; diff --git a/api_docs/cloud_defend.mdx b/api_docs/cloud_defend.mdx index c5c8592c4e821..f4fc1abfe1023 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudDefend'] --- import cloudDefendObj from './cloud_defend.devdocs.json'; diff --git a/api_docs/cloud_experiments.mdx b/api_docs/cloud_experiments.mdx index d8cf5e54c61f0..bc65bcb7f9c54 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudExperiments'] --- import cloudExperimentsObj from './cloud_experiments.devdocs.json'; diff --git a/api_docs/cloud_security_posture.mdx b/api_docs/cloud_security_posture.mdx index 82af6f595f3d1..d8915acf6e1f1 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cloudSecurityPosture'] --- import cloudSecurityPostureObj from './cloud_security_posture.devdocs.json'; diff --git a/api_docs/console.mdx b/api_docs/console.mdx index 2e431b9effeff..0a24b4d15cf4c 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'console'] --- import consoleObj from './console.devdocs.json'; diff --git a/api_docs/content_management.mdx b/api_docs/content_management.mdx index 7e8bc34988b14..0cf7bf4f96427 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'contentManagement'] --- import contentManagementObj from './content_management.devdocs.json'; diff --git a/api_docs/controls.mdx b/api_docs/controls.mdx index 14fddf0acfbd4..febc589d44517 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'controls'] --- import controlsObj from './controls.devdocs.json'; diff --git a/api_docs/custom_integrations.mdx b/api_docs/custom_integrations.mdx index 05a6ebfa7c57a..831585890d6f0 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'customIntegrations'] --- import customIntegrationsObj from './custom_integrations.devdocs.json'; diff --git a/api_docs/dashboard.mdx b/api_docs/dashboard.mdx index 5e9f944af1925..9ac9f2eada99b 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 4f39935a0edd8..65ef6d7d54aea 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboardEnhanced'] --- import dashboardEnhancedObj from './dashboard_enhanced.devdocs.json'; diff --git a/api_docs/data.mdx b/api_docs/data.mdx index 01e33e0c93817..3052d550a8359 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data'] --- import dataObj from './data.devdocs.json'; diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index acf7d5884e94b..d9394e19a3558 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.query'] --- import dataQueryObj from './data_query.devdocs.json'; diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index c9d5664e4dd33..a8fd254793f73 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'data.search'] --- import dataSearchObj from './data_search.devdocs.json'; diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index 9ad095932eb2e..88b55492aeb22 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewEditor'] --- import dataViewEditorObj from './data_view_editor.devdocs.json'; diff --git a/api_docs/data_view_field_editor.mdx b/api_docs/data_view_field_editor.mdx index 8c8d602065e5c..3c9861aeab09d 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewFieldEditor'] --- import dataViewFieldEditorObj from './data_view_field_editor.devdocs.json'; diff --git a/api_docs/data_view_management.mdx b/api_docs/data_view_management.mdx index d48436e2b0916..2441039294578 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViewManagement'] --- import dataViewManagementObj from './data_view_management.devdocs.json'; diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index ba5771edf96f0..5a3e59b32df7c 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index bbd7d6093d771..44cfbbe28ad11 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index a4f897b735dec..774ddaaa0cb3a 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 43b928ddc2a96..c079fa70cc42b 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 86341bbaff5c8..70360a7d7b8d8 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index c433c4387f5eb..23f238c032a9d 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 9b9dafae2d94a..f640433dadb64 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index 4cdde823bd6d9..bc917ed304cc5 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discoverEnhanced'] --- import discoverEnhancedObj from './discover_enhanced.devdocs.json'; diff --git a/api_docs/ecs_data_quality_dashboard.mdx b/api_docs/ecs_data_quality_dashboard.mdx index 81a691ce8c51e..9b5709b6fb1a7 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index 8468b63b75084..d48b3e19a765d 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index bd857df2dcf0e..845ec6cefb595 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 8d04c97b8617b..c44f7ce480200 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index 94f1973c311bd..b3e25949e5f40 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index 678c3b47d943b..389c292fdd009 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'enterpriseSearch'] --- import enterpriseSearchObj from './enterprise_search.devdocs.json'; diff --git a/api_docs/es_ui_shared.mdx b/api_docs/es_ui_shared.mdx index 16d2bc4cccf3a..6a4447799b4d0 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index dbb16abeeec10..bdf936b345c46 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotation'] --- import eventAnnotationObj from './event_annotation.devdocs.json'; diff --git a/api_docs/event_annotation_listing.mdx b/api_docs/event_annotation_listing.mdx index 731bb76589a4b..0d60add60483d 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventAnnotationListing'] --- import eventAnnotationListingObj from './event_annotation_listing.devdocs.json'; diff --git a/api_docs/event_log.mdx b/api_docs/event_log.mdx index e5f861b138d6a..9d129d9bf20d6 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index 0a43e932cf2c1..d4bea3a72827c 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index cd01389523fb1..cd1eea889bd08 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 6c9ff3ca1c067..8fbced0087aa1 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 18e2b55dc20cf..87e10069dc0da 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index d1c2f9c001a9e..26e9ccac22f46 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index a546ecf448a77..604f510c0b790 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index 71c52b2e8a0ca..ccf9b5f759436 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 769f31d9c4053..cf7718dd7c6a4 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index 65fe78ccb2fd8..69304b55b8de3 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 65e0cd4ed062a..1e791ee2c4c67 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRepeatImage'] --- import expressionRepeatImageObj from './expression_repeat_image.devdocs.json'; diff --git a/api_docs/expression_reveal_image.mdx b/api_docs/expression_reveal_image.mdx index f48522e5f4848..2ca7a27968180 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 5ce32ac57e1b8..0f62a9771e76a 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index f80348f80c25f..571e6a0f46623 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index 4dd7b63ac58dc..afe45539d1ac8 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index 147aea7624cc2..74c001d57617a 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressions'] --- import expressionsObj from './expressions.devdocs.json'; diff --git a/api_docs/features.mdx b/api_docs/features.mdx index 8b5c0469a397f..968076de21d1b 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'features'] --- import featuresObj from './features.devdocs.json'; diff --git a/api_docs/field_formats.mdx b/api_docs/field_formats.mdx index 80a15632403c1..1e26987df88a8 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fieldFormats'] --- import fieldFormatsObj from './field_formats.devdocs.json'; diff --git a/api_docs/file_upload.mdx b/api_docs/file_upload.mdx index 658d36c32b613..ec4908885a663 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.mdx b/api_docs/files.mdx index e2bda4d7c5450..8fdbb54e8c322 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'files'] --- import filesObj from './files.devdocs.json'; diff --git a/api_docs/files_management.mdx b/api_docs/files_management.mdx index b90b9e2dfc5bd..092e6a2ca3017 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'filesManagement'] --- import filesManagementObj from './files_management.devdocs.json'; diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 73debf02781ce..df0f11879f2ce 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fleet'] --- import fleetObj from './fleet.devdocs.json'; diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 24bcdd8503995..8546741cd7725 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'globalSearch'] --- import globalSearchObj from './global_search.devdocs.json'; diff --git a/api_docs/guided_onboarding.mdx b/api_docs/guided_onboarding.mdx index e306f683c6e41..49dbc40923eb1 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'guidedOnboarding'] --- import guidedOnboardingObj from './guided_onboarding.devdocs.json'; diff --git a/api_docs/home.mdx b/api_docs/home.mdx index a40057a7e49a5..56dabf7163bf1 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'home'] --- import homeObj from './home.devdocs.json'; diff --git a/api_docs/image_embeddable.mdx b/api_docs/image_embeddable.mdx index 8f0ea0d827ea2..89d92bda100e5 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'imageEmbeddable'] --- import imageEmbeddableObj from './image_embeddable.devdocs.json'; diff --git a/api_docs/index_lifecycle_management.mdx b/api_docs/index_lifecycle_management.mdx index 79fcfcca2867f..352bd33348770 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexLifecycleManagement'] --- import indexLifecycleManagementObj from './index_lifecycle_management.devdocs.json'; diff --git a/api_docs/index_management.mdx b/api_docs/index_management.mdx index 7f51f3fac8ad1..56c47ade511ce 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'indexManagement'] --- import indexManagementObj from './index_management.devdocs.json'; diff --git a/api_docs/infra.mdx b/api_docs/infra.mdx index f551e67e7c2eb..b0da80dc4b939 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 720c35e4a6322..459ea5a59cb7d 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'inspector'] --- import inspectorObj from './inspector.devdocs.json'; diff --git a/api_docs/interactive_setup.mdx b/api_docs/interactive_setup.mdx index 0d61903f2b520..fb707221b657f 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'interactiveSetup'] --- import interactiveSetupObj from './interactive_setup.devdocs.json'; diff --git a/api_docs/kbn_ace.mdx b/api_docs/kbn_ace.mdx index 06a149e4f9a68..9e22160e70d5d 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ace'] --- import kbnAceObj from './kbn_ace.devdocs.json'; diff --git a/api_docs/kbn_aiops_components.mdx b/api_docs/kbn_aiops_components.mdx index cb481fd48aee7..f515ca8b3fd4e 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-components'] --- import kbnAiopsComponentsObj from './kbn_aiops_components.devdocs.json'; diff --git a/api_docs/kbn_aiops_utils.mdx b/api_docs/kbn_aiops_utils.mdx index b04f8b3ef4abb..ccce27c8a71ac 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/aiops-utils'] --- import kbnAiopsUtilsObj from './kbn_aiops_utils.devdocs.json'; diff --git a/api_docs/kbn_alerting_api_integration_helpers.mdx b/api_docs/kbn_alerting_api_integration_helpers.mdx index 88ef052d8e29e..4898d695a2416 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 23f3bb5dcdc45..654231a415d89 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-state-types'] --- import kbnAlertingStateTypesObj from './kbn_alerting_state_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index da71ce7dfbaa0..6077db9871ff1 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-as-data-utils'] --- import kbnAlertsAsDataUtilsObj from './kbn_alerts_as_data_utils.devdocs.json'; diff --git a/api_docs/kbn_alerts_ui_shared.mdx b/api_docs/kbn_alerts_ui_shared.mdx index 2a049d585b7f7..ed95f6fb69e6e 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerts-ui-shared'] --- import kbnAlertsUiSharedObj from './kbn_alerts_ui_shared.devdocs.json'; diff --git a/api_docs/kbn_analytics.mdx b/api_docs/kbn_analytics.mdx index 00395ebb47e60..6a496f313e8cb 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index fe17a6acbe4b5..a9815dee8cee7 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-client'] --- import kbnAnalyticsClientObj from './kbn_analytics_client.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx index 9e24ca9f836e9..867eb03196404 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-browser'] --- import kbnAnalyticsShippersElasticV3BrowserObj from './kbn_analytics_shippers_elastic_v3_browser.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx index caa7606acd535..e7b98e3230dc7 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-common'] --- import kbnAnalyticsShippersElasticV3CommonObj from './kbn_analytics_shippers_elastic_v3_common.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx index f85d3b4fbfdf7..74126e5a76385 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-elastic-v3-server'] --- import kbnAnalyticsShippersElasticV3ServerObj from './kbn_analytics_shippers_elastic_v3_server.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_fullstory.mdx b/api_docs/kbn_analytics_shippers_fullstory.mdx index 1bb53aa514566..4843f1990437f 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_gainsight.mdx b/api_docs/kbn_analytics_shippers_gainsight.mdx index e2d101fa2d8a0..9629272d81c92 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-gainsight'] --- import kbnAnalyticsShippersGainsightObj from './kbn_analytics_shippers_gainsight.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index e537392cac8ae..372540b9ab1fa 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-config-loader'] --- import kbnApmConfigLoaderObj from './kbn_apm_config_loader.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace.mdx b/api_docs/kbn_apm_synthtrace.mdx index 797c771387bd7..eba9ffa82464b 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 406d7c1f08d5e..70989630de90d 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index cff634b466c86..bd8e929936bd0 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-utils'] --- import kbnApmUtilsObj from './kbn_apm_utils.devdocs.json'; diff --git a/api_docs/kbn_axe_config.mdx b/api_docs/kbn_axe_config.mdx index 0494f90df5c73..f61b50b1b0062 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_cases_components.mdx b/api_docs/kbn_cases_components.mdx index bcf75d475b8d0..881dee7f0522d 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cases-components'] --- import kbnCasesComponentsObj from './kbn_cases_components.devdocs.json'; diff --git a/api_docs/kbn_cell_actions.mdx b/api_docs/kbn_cell_actions.mdx index ec4d16fb32737..af23e034ff9f3 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cell-actions'] --- import kbnCellActionsObj from './kbn_cell_actions.devdocs.json'; diff --git a/api_docs/kbn_chart_expressions_common.mdx b/api_docs/kbn_chart_expressions_common.mdx index 4a4f623de8030..6e790dd98401e 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-expressions-common'] --- import kbnChartExpressionsCommonObj from './kbn_chart_expressions_common.devdocs.json'; diff --git a/api_docs/kbn_chart_icons.mdx b/api_docs/kbn_chart_icons.mdx index cf7787d54c791..47d6be4bab32a 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/chart-icons'] --- import kbnChartIconsObj from './kbn_chart_icons.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_core.mdx b/api_docs/kbn_ci_stats_core.mdx index ade31cdd7b5c7..a8c7287abeaf9 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-core'] --- import kbnCiStatsCoreObj from './kbn_ci_stats_core.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_performance_metrics.mdx b/api_docs/kbn_ci_stats_performance_metrics.mdx index b77caf5b15106..d0a26287091a3 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-performance-metrics'] --- import kbnCiStatsPerformanceMetricsObj from './kbn_ci_stats_performance_metrics.devdocs.json'; diff --git a/api_docs/kbn_ci_stats_reporter.mdx b/api_docs/kbn_ci_stats_reporter.mdx index af595436e19ff..e19e3096f3443 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ci-stats-reporter'] --- import kbnCiStatsReporterObj from './kbn_ci_stats_reporter.devdocs.json'; diff --git a/api_docs/kbn_cli_dev_mode.mdx b/api_docs/kbn_cli_dev_mode.mdx index f390193bf64bb..c58bb498a823e 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index 7b90958d46af2..ab78306f49d42 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_editor_mocks.mdx b/api_docs/kbn_code_editor_mocks.mdx index 4d8171a9cb8da..7f6ada9f2ee0e 100644 --- a/api_docs/kbn_code_editor_mocks.mdx +++ b/api_docs/kbn_code_editor_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor-mocks title: "@kbn/code-editor-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor-mocks'] --- import kbnCodeEditorMocksObj from './kbn_code_editor_mocks.devdocs.json'; diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index 3af883ca4ad7c..746c8d9e84006 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index 6c996c5fded78..52bbb901de594 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config'] --- import kbnConfigObj from './kbn_config.devdocs.json'; diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index 29b858288fb43..93123f08b0871 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-mocks'] --- import kbnConfigMocksObj from './kbn_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_config_schema.mdx b/api_docs/kbn_config_schema.mdx index 60c2e15fd83d3..66814ac74c4b5 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/config-schema'] --- import kbnConfigSchemaObj from './kbn_config_schema.devdocs.json'; diff --git a/api_docs/kbn_content_management_content_editor.mdx b/api_docs/kbn_content_management_content_editor.mdx index 3e442470b5653..e012dc5ebec39 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-content-editor'] --- import kbnContentManagementContentEditorObj from './kbn_content_management_content_editor.devdocs.json'; diff --git a/api_docs/kbn_content_management_tabbed_table_list_view.mdx b/api_docs/kbn_content_management_tabbed_table_list_view.mdx index cad94402ff99e..e74ff48474b63 100644 --- a/api_docs/kbn_content_management_tabbed_table_list_view.mdx +++ b/api_docs/kbn_content_management_tabbed_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-tabbed-table-list-view title: "@kbn/content-management-tabbed-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-tabbed-table-list-view plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-tabbed-table-list-view'] --- import kbnContentManagementTabbedTableListViewObj from './kbn_content_management_tabbed_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view.mdx b/api_docs/kbn_content_management_table_list_view.mdx index 6a2256ca69873..8d310139c5347 100644 --- a/api_docs/kbn_content_management_table_list_view.mdx +++ b/api_docs/kbn_content_management_table_list_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view title: "@kbn/content-management-table-list-view" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view'] --- import kbnContentManagementTableListViewObj from './kbn_content_management_table_list_view.devdocs.json'; diff --git a/api_docs/kbn_content_management_table_list_view_table.mdx b/api_docs/kbn_content_management_table_list_view_table.mdx index 3d8f0c3ed9c19..09ed0b34bfab4 100644 --- a/api_docs/kbn_content_management_table_list_view_table.mdx +++ b/api_docs/kbn_content_management_table_list_view_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-table-list-view-table title: "@kbn/content-management-table-list-view-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-table-list-view-table plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-table-list-view-table'] --- import kbnContentManagementTableListViewTableObj from './kbn_content_management_table_list_view_table.devdocs.json'; diff --git a/api_docs/kbn_content_management_utils.mdx b/api_docs/kbn_content_management_utils.mdx index 9c783b9a72f16..6838dd24b7293 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/content-management-utils'] --- import kbnContentManagementUtilsObj from './kbn_content_management_utils.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser.mdx b/api_docs/kbn_core_analytics_browser.mdx index e89b0d509b6c5..87f77388dec62 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser'] --- import kbnCoreAnalyticsBrowserObj from './kbn_core_analytics_browser.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_internal.mdx b/api_docs/kbn_core_analytics_browser_internal.mdx index e0c29ded3d007..50f786eda89f0 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-internal'] --- import kbnCoreAnalyticsBrowserInternalObj from './kbn_core_analytics_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_browser_mocks.mdx b/api_docs/kbn_core_analytics_browser_mocks.mdx index c48947c57ca02..8fe98a53a82a4 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-browser-mocks'] --- import kbnCoreAnalyticsBrowserMocksObj from './kbn_core_analytics_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server.mdx b/api_docs/kbn_core_analytics_server.mdx index fe0cbc864decc..2debb1ab3aef5 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server'] --- import kbnCoreAnalyticsServerObj from './kbn_core_analytics_server.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_internal.mdx b/api_docs/kbn_core_analytics_server_internal.mdx index 1441a038c1bf8..7c9ad67516599 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-internal'] --- import kbnCoreAnalyticsServerInternalObj from './kbn_core_analytics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_analytics_server_mocks.mdx b/api_docs/kbn_core_analytics_server_mocks.mdx index e564d1e31b034..37fe77f541fa5 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-analytics-server-mocks'] --- import kbnCoreAnalyticsServerMocksObj from './kbn_core_analytics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser.mdx b/api_docs/kbn_core_application_browser.mdx index b663184a3015d..3a83f40f3b256 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser'] --- import kbnCoreApplicationBrowserObj from './kbn_core_application_browser.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_internal.mdx b/api_docs/kbn_core_application_browser_internal.mdx index dc3d73fe40362..1480cb1a26f29 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-internal'] --- import kbnCoreApplicationBrowserInternalObj from './kbn_core_application_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_application_browser_mocks.mdx b/api_docs/kbn_core_application_browser_mocks.mdx index 66cc933530d80..c43d5fb3aa32f 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-browser-mocks'] --- import kbnCoreApplicationBrowserMocksObj from './kbn_core_application_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_application_common.mdx b/api_docs/kbn_core_application_common.mdx index 8dd3efa35f1b1..4a9e69014bef3 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-application-common'] --- import kbnCoreApplicationCommonObj from './kbn_core_application_common.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_internal.mdx b/api_docs/kbn_core_apps_browser_internal.mdx index 46efbf2f78a5a..70b37a69aa333 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-internal'] --- import kbnCoreAppsBrowserInternalObj from './kbn_core_apps_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_apps_browser_mocks.mdx b/api_docs/kbn_core_apps_browser_mocks.mdx index 33daf47869838..bdc5727df9896 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-browser-mocks'] --- import kbnCoreAppsBrowserMocksObj from './kbn_core_apps_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_apps_server_internal.mdx b/api_docs/kbn_core_apps_server_internal.mdx index 4d39c1f75a2b5..0a7507b566e62 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-apps-server-internal'] --- import kbnCoreAppsServerInternalObj from './kbn_core_apps_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_browser_mocks.mdx b/api_docs/kbn_core_base_browser_mocks.mdx index 87c72edb379c7..fcc626eb2cab4 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-browser-mocks'] --- import kbnCoreBaseBrowserMocksObj from './kbn_core_base_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_base_common.mdx b/api_docs/kbn_core_base_common.mdx index 20619d398105a..60fbaf0c08471 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-common'] --- import kbnCoreBaseCommonObj from './kbn_core_base_common.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_internal.mdx b/api_docs/kbn_core_base_server_internal.mdx index 23268503d2d24..40dfee6c74c2b 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-internal'] --- import kbnCoreBaseServerInternalObj from './kbn_core_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_base_server_mocks.mdx b/api_docs/kbn_core_base_server_mocks.mdx index 2e0fe4c80ec18..15d1485db8392 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-base-server-mocks'] --- import kbnCoreBaseServerMocksObj from './kbn_core_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_browser_mocks.mdx b/api_docs/kbn_core_capabilities_browser_mocks.mdx index 89755d0831eba..b419244ccc6fd 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-browser-mocks'] --- import kbnCoreCapabilitiesBrowserMocksObj from './kbn_core_capabilities_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_common.mdx b/api_docs/kbn_core_capabilities_common.mdx index e2d0093df2db8..6a29925c5eee0 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-common'] --- import kbnCoreCapabilitiesCommonObj from './kbn_core_capabilities_common.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server.mdx b/api_docs/kbn_core_capabilities_server.mdx index 8a509df7a0554..85663a88afdf5 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server'] --- import kbnCoreCapabilitiesServerObj from './kbn_core_capabilities_server.devdocs.json'; diff --git a/api_docs/kbn_core_capabilities_server_mocks.mdx b/api_docs/kbn_core_capabilities_server_mocks.mdx index 5bc34f346f3ba..8865c6570e578 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-capabilities-server-mocks'] --- import kbnCoreCapabilitiesServerMocksObj from './kbn_core_capabilities_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser.mdx b/api_docs/kbn_core_chrome_browser.mdx index e3f2a9229e7ca..b914612cdbced 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser'] --- import kbnCoreChromeBrowserObj from './kbn_core_chrome_browser.devdocs.json'; diff --git a/api_docs/kbn_core_chrome_browser_mocks.mdx b/api_docs/kbn_core_chrome_browser_mocks.mdx index b4082e8a1ece9..032015504aebd 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-chrome-browser-mocks'] --- import kbnCoreChromeBrowserMocksObj from './kbn_core_chrome_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_config_server_internal.mdx b/api_docs/kbn_core_config_server_internal.mdx index 3418e6c59964f..05a0a32ef42d8 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-config-server-internal'] --- import kbnCoreConfigServerInternalObj from './kbn_core_config_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser.mdx b/api_docs/kbn_core_custom_branding_browser.mdx index bc8e518319ed8..c0b58ade4aa69 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser'] --- import kbnCoreCustomBrandingBrowserObj from './kbn_core_custom_branding_browser.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_internal.mdx b/api_docs/kbn_core_custom_branding_browser_internal.mdx index 9b1d88c663bbe..54be88097c6ac 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-internal'] --- import kbnCoreCustomBrandingBrowserInternalObj from './kbn_core_custom_branding_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_browser_mocks.mdx b/api_docs/kbn_core_custom_branding_browser_mocks.mdx index 3a24611a94774..cdd56d84699a6 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-browser-mocks'] --- import kbnCoreCustomBrandingBrowserMocksObj from './kbn_core_custom_branding_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_common.mdx b/api_docs/kbn_core_custom_branding_common.mdx index 1d84e0df20dce..dd1fbbf2b26f7 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-common'] --- import kbnCoreCustomBrandingCommonObj from './kbn_core_custom_branding_common.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server.mdx b/api_docs/kbn_core_custom_branding_server.mdx index 826840450eb6d..4ff33b37e3301 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server'] --- import kbnCoreCustomBrandingServerObj from './kbn_core_custom_branding_server.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_internal.mdx b/api_docs/kbn_core_custom_branding_server_internal.mdx index 0d1ed6e1d863d..3f5578d5e6f84 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-internal'] --- import kbnCoreCustomBrandingServerInternalObj from './kbn_core_custom_branding_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_custom_branding_server_mocks.mdx b/api_docs/kbn_core_custom_branding_server_mocks.mdx index 312c9e893804d..5ab691a6622bb 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-custom-branding-server-mocks'] --- import kbnCoreCustomBrandingServerMocksObj from './kbn_core_custom_branding_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser.mdx b/api_docs/kbn_core_deprecations_browser.mdx index 92065b8fb5927..8b156dcbef7dd 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser'] --- import kbnCoreDeprecationsBrowserObj from './kbn_core_deprecations_browser.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_internal.mdx b/api_docs/kbn_core_deprecations_browser_internal.mdx index 6e615237ed389..d77156ad5105b 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-internal'] --- import kbnCoreDeprecationsBrowserInternalObj from './kbn_core_deprecations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_browser_mocks.mdx b/api_docs/kbn_core_deprecations_browser_mocks.mdx index ca041503318db..a36d900762aaa 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-browser-mocks'] --- import kbnCoreDeprecationsBrowserMocksObj from './kbn_core_deprecations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_common.mdx b/api_docs/kbn_core_deprecations_common.mdx index 9bb70fd98070e..5ca6ad437e582 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-common'] --- import kbnCoreDeprecationsCommonObj from './kbn_core_deprecations_common.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server.mdx b/api_docs/kbn_core_deprecations_server.mdx index dae5d5a5b8354..0d38de265ef85 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server'] --- import kbnCoreDeprecationsServerObj from './kbn_core_deprecations_server.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_internal.mdx b/api_docs/kbn_core_deprecations_server_internal.mdx index a0d107b9f4a77..898864ce5b69b 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-internal'] --- import kbnCoreDeprecationsServerInternalObj from './kbn_core_deprecations_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_deprecations_server_mocks.mdx b/api_docs/kbn_core_deprecations_server_mocks.mdx index dc18be90b155f..d558d596cea4d 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-deprecations-server-mocks'] --- import kbnCoreDeprecationsServerMocksObj from './kbn_core_deprecations_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser.mdx b/api_docs/kbn_core_doc_links_browser.mdx index 4869c84623ffa..346abab3fdf73 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser'] --- import kbnCoreDocLinksBrowserObj from './kbn_core_doc_links_browser.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_browser_mocks.mdx b/api_docs/kbn_core_doc_links_browser_mocks.mdx index bdd29273932b1..0ebac00b7a999 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-browser-mocks'] --- import kbnCoreDocLinksBrowserMocksObj from './kbn_core_doc_links_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server.mdx b/api_docs/kbn_core_doc_links_server.mdx index 8f20c1a3fcb9c..2204806511104 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server'] --- import kbnCoreDocLinksServerObj from './kbn_core_doc_links_server.devdocs.json'; diff --git a/api_docs/kbn_core_doc_links_server_mocks.mdx b/api_docs/kbn_core_doc_links_server_mocks.mdx index 628cc422f7bc8..a5022bd317689 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-doc-links-server-mocks'] --- import kbnCoreDocLinksServerMocksObj from './kbn_core_doc_links_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx index 463e7f84e0d85..e26e8c084a065 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-internal'] --- import kbnCoreElasticsearchClientServerInternalObj from './kbn_core_elasticsearch_client_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx index e24a754901533..4c04bd8209b20 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-client-server-mocks'] --- import kbnCoreElasticsearchClientServerMocksObj from './kbn_core_elasticsearch_client_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server.mdx b/api_docs/kbn_core_elasticsearch_server.mdx index e4faae5b07fee..0ed3883108733 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server'] --- import kbnCoreElasticsearchServerObj from './kbn_core_elasticsearch_server.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_internal.mdx b/api_docs/kbn_core_elasticsearch_server_internal.mdx index 93f54cf9f8d71..ffb559548d4d5 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-internal'] --- import kbnCoreElasticsearchServerInternalObj from './kbn_core_elasticsearch_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_elasticsearch_server_mocks.mdx b/api_docs/kbn_core_elasticsearch_server_mocks.mdx index 69dba4fcf8cce..6dfc62aa863b3 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-elasticsearch-server-mocks'] --- import kbnCoreElasticsearchServerMocksObj from './kbn_core_elasticsearch_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_internal.mdx b/api_docs/kbn_core_environment_server_internal.mdx index 57d2788def467..29635d1827df3 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-internal'] --- import kbnCoreEnvironmentServerInternalObj from './kbn_core_environment_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_environment_server_mocks.mdx b/api_docs/kbn_core_environment_server_mocks.mdx index 50d402dd3450e..11d298d2ad3df 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-environment-server-mocks'] --- import kbnCoreEnvironmentServerMocksObj from './kbn_core_environment_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser.mdx b/api_docs/kbn_core_execution_context_browser.mdx index 0ed2120cfc58d..aa1fcee1a61b4 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser'] --- import kbnCoreExecutionContextBrowserObj from './kbn_core_execution_context_browser.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_internal.mdx b/api_docs/kbn_core_execution_context_browser_internal.mdx index 44ae37dad4f85..804718e0ab821 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-internal'] --- import kbnCoreExecutionContextBrowserInternalObj from './kbn_core_execution_context_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_browser_mocks.mdx b/api_docs/kbn_core_execution_context_browser_mocks.mdx index 31ea61c992d33..2b422bdc34280 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-browser-mocks'] --- import kbnCoreExecutionContextBrowserMocksObj from './kbn_core_execution_context_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_common.mdx b/api_docs/kbn_core_execution_context_common.mdx index 465f14c2ec718..259ef73855298 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-common'] --- import kbnCoreExecutionContextCommonObj from './kbn_core_execution_context_common.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server.mdx b/api_docs/kbn_core_execution_context_server.mdx index 2d050aba5c93b..797e4a51805e4 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server'] --- import kbnCoreExecutionContextServerObj from './kbn_core_execution_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_internal.mdx b/api_docs/kbn_core_execution_context_server_internal.mdx index b9b1c77adb8ce..525df9822d412 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-internal'] --- import kbnCoreExecutionContextServerInternalObj from './kbn_core_execution_context_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_execution_context_server_mocks.mdx b/api_docs/kbn_core_execution_context_server_mocks.mdx index c71c78b3bdddd..1c80dd761af52 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-execution-context-server-mocks'] --- import kbnCoreExecutionContextServerMocksObj from './kbn_core_execution_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser.mdx b/api_docs/kbn_core_fatal_errors_browser.mdx index bb071a04642ce..78890364f746e 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser'] --- import kbnCoreFatalErrorsBrowserObj from './kbn_core_fatal_errors_browser.devdocs.json'; diff --git a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx index 0aba16c795657..50dd3bc969723 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-fatal-errors-browser-mocks'] --- import kbnCoreFatalErrorsBrowserMocksObj from './kbn_core_fatal_errors_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser.mdx b/api_docs/kbn_core_http_browser.mdx index dbfbd1ab7793a..06626ebb76055 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser'] --- import kbnCoreHttpBrowserObj from './kbn_core_http_browser.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_internal.mdx b/api_docs/kbn_core_http_browser_internal.mdx index f0df9b59915a3..f6480ff5152f6 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-internal'] --- import kbnCoreHttpBrowserInternalObj from './kbn_core_http_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_browser_mocks.mdx b/api_docs/kbn_core_http_browser_mocks.mdx index 7866254094a21..fd84d4969a6fa 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-browser-mocks'] --- import kbnCoreHttpBrowserMocksObj from './kbn_core_http_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_common.mdx b/api_docs/kbn_core_http_common.mdx index 8ce69dbfff76f..7d145ed09eb8a 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-common'] --- import kbnCoreHttpCommonObj from './kbn_core_http_common.devdocs.json'; diff --git a/api_docs/kbn_core_http_context_server_mocks.mdx b/api_docs/kbn_core_http_context_server_mocks.mdx index 0adc17b77de97..8ebe9e4a962af 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-context-server-mocks'] --- import kbnCoreHttpContextServerMocksObj from './kbn_core_http_context_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_request_handler_context_server.mdx b/api_docs/kbn_core_http_request_handler_context_server.mdx index f02a5a97d8dd5..9e0366e53d2d8 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-request-handler-context-server'] --- import kbnCoreHttpRequestHandlerContextServerObj from './kbn_core_http_request_handler_context_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server.mdx b/api_docs/kbn_core_http_resources_server.mdx index dbae03f28a194..8703dd038330d 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server'] --- import kbnCoreHttpResourcesServerObj from './kbn_core_http_resources_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_internal.mdx b/api_docs/kbn_core_http_resources_server_internal.mdx index 7d8b1703441b6..5d1f8e2bdca4b 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-internal'] --- import kbnCoreHttpResourcesServerInternalObj from './kbn_core_http_resources_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_resources_server_mocks.mdx b/api_docs/kbn_core_http_resources_server_mocks.mdx index 41cc0b7e84f0d..6d24ca8606134 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-resources-server-mocks'] --- import kbnCoreHttpResourcesServerMocksObj from './kbn_core_http_resources_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_internal.mdx b/api_docs/kbn_core_http_router_server_internal.mdx index 1b02f2442240b..944d824100d66 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-internal'] --- import kbnCoreHttpRouterServerInternalObj from './kbn_core_http_router_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_router_server_mocks.mdx b/api_docs/kbn_core_http_router_server_mocks.mdx index 5b38e856b8672..f41292834508a 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-router-server-mocks'] --- import kbnCoreHttpRouterServerMocksObj from './kbn_core_http_router_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_http_server.devdocs.json b/api_docs/kbn_core_http_server.devdocs.json index 49b083c681418..ad381c939b861 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -14191,11 +14191,11 @@ }, { "plugin": "aiops", - "path": "x-pack/plugins/aiops/server/routes/log_rate_analysis.ts" + "path": "x-pack/plugins/aiops/server/routes/log_rate_analysis/define_route.ts" }, { "plugin": "aiops", - "path": "x-pack/plugins/aiops/server/routes/log_categorization.ts" + "path": "x-pack/plugins/aiops/server/routes/categorization_field_validation/define_route.ts" }, { "plugin": "ml", diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index a841844e197c4..e8dd6dfe6e585 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 156dbd0ae70b7..8f8ed48546939 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 15b88ae735f75..601438e643d10 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-mocks'] --- import kbnCoreHttpServerMocksObj from './kbn_core_http_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser.mdx b/api_docs/kbn_core_i18n_browser.mdx index b6f2936d4dab3..9511e2ff2824b 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser'] --- import kbnCoreI18nBrowserObj from './kbn_core_i18n_browser.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_browser_mocks.mdx b/api_docs/kbn_core_i18n_browser_mocks.mdx index 50664e26523c4..0a74943162d12 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-browser-mocks'] --- import kbnCoreI18nBrowserMocksObj from './kbn_core_i18n_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server.mdx b/api_docs/kbn_core_i18n_server.mdx index 555edf9d8a47d..d2cfcf73099c1 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server'] --- import kbnCoreI18nServerObj from './kbn_core_i18n_server.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_internal.mdx b/api_docs/kbn_core_i18n_server_internal.mdx index 95fdf6a73a907..936961af7b231 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-internal'] --- import kbnCoreI18nServerInternalObj from './kbn_core_i18n_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_i18n_server_mocks.mdx b/api_docs/kbn_core_i18n_server_mocks.mdx index e773962603f2e..21a17c2ef4543 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-i18n-server-mocks'] --- import kbnCoreI18nServerMocksObj from './kbn_core_i18n_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx index 1513daa8babda..795d2eb957b95 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-injected-metadata-browser-mocks'] --- import kbnCoreInjectedMetadataBrowserMocksObj from './kbn_core_injected_metadata_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_internal.mdx b/api_docs/kbn_core_integrations_browser_internal.mdx index 84425c06ef055..2c61e3e9726d3 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-internal'] --- import kbnCoreIntegrationsBrowserInternalObj from './kbn_core_integrations_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_integrations_browser_mocks.mdx b/api_docs/kbn_core_integrations_browser_mocks.mdx index a7a12bcdf5d89..3bcb9af271736 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-integrations-browser-mocks'] --- import kbnCoreIntegrationsBrowserMocksObj from './kbn_core_integrations_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser.mdx b/api_docs/kbn_core_lifecycle_browser.mdx index c621885572337..dd231502d8fd2 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser'] --- import kbnCoreLifecycleBrowserObj from './kbn_core_lifecycle_browser.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_browser_mocks.mdx b/api_docs/kbn_core_lifecycle_browser_mocks.mdx index 59d0bc2c3206c..8d6ce299ba9a3 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-browser-mocks'] --- import kbnCoreLifecycleBrowserMocksObj from './kbn_core_lifecycle_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server.mdx b/api_docs/kbn_core_lifecycle_server.mdx index 00fcf1717fba3..3695298eb1308 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server'] --- import kbnCoreLifecycleServerObj from './kbn_core_lifecycle_server.devdocs.json'; diff --git a/api_docs/kbn_core_lifecycle_server_mocks.mdx b/api_docs/kbn_core_lifecycle_server_mocks.mdx index d9e6faebf798d..1e62ab8d3d1a6 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-lifecycle-server-mocks'] --- import kbnCoreLifecycleServerMocksObj from './kbn_core_lifecycle_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_browser_mocks.mdx b/api_docs/kbn_core_logging_browser_mocks.mdx index 0020b982d28d8..9afda472cbe9d 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-browser-mocks'] --- import kbnCoreLoggingBrowserMocksObj from './kbn_core_logging_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_logging_common_internal.mdx b/api_docs/kbn_core_logging_common_internal.mdx index 97883d6b8fb3b..6ba7bea4d4eda 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-common-internal'] --- import kbnCoreLoggingCommonInternalObj from './kbn_core_logging_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server.mdx b/api_docs/kbn_core_logging_server.mdx index 38edee726a6ab..a70477d6aabec 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server'] --- import kbnCoreLoggingServerObj from './kbn_core_logging_server.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_internal.mdx b/api_docs/kbn_core_logging_server_internal.mdx index 60dab7a60fd5f..fc84d1b50ae02 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-internal'] --- import kbnCoreLoggingServerInternalObj from './kbn_core_logging_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_logging_server_mocks.mdx b/api_docs/kbn_core_logging_server_mocks.mdx index dae78065fa351..f688634350ab3 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-logging-server-mocks'] --- import kbnCoreLoggingServerMocksObj from './kbn_core_logging_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_internal.mdx b/api_docs/kbn_core_metrics_collectors_server_internal.mdx index 327e55d5ce680..b06538ad599b5 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-internal'] --- import kbnCoreMetricsCollectorsServerInternalObj from './kbn_core_metrics_collectors_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx index c2502f94cf76e..39477d34f8131 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-collectors-server-mocks'] --- import kbnCoreMetricsCollectorsServerMocksObj from './kbn_core_metrics_collectors_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server.mdx b/api_docs/kbn_core_metrics_server.mdx index 1d3c92bcee5be..00072a022c999 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server'] --- import kbnCoreMetricsServerObj from './kbn_core_metrics_server.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_internal.mdx b/api_docs/kbn_core_metrics_server_internal.mdx index 27e966d2c20c7..511e00527b157 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-internal'] --- import kbnCoreMetricsServerInternalObj from './kbn_core_metrics_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_metrics_server_mocks.mdx b/api_docs/kbn_core_metrics_server_mocks.mdx index 9a712c40a64f0..2c76d6e74c6dc 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-metrics-server-mocks'] --- import kbnCoreMetricsServerMocksObj from './kbn_core_metrics_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_mount_utils_browser.mdx b/api_docs/kbn_core_mount_utils_browser.mdx index ec8363206dd06..cc27ee152868d 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-mount-utils-browser'] --- import kbnCoreMountUtilsBrowserObj from './kbn_core_mount_utils_browser.devdocs.json'; diff --git a/api_docs/kbn_core_node_server.mdx b/api_docs/kbn_core_node_server.mdx index bbeeb63f1e4f3..0427dbdf5a64b 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server'] --- import kbnCoreNodeServerObj from './kbn_core_node_server.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_internal.mdx b/api_docs/kbn_core_node_server_internal.mdx index d682ecbad81ae..7916fd73c474a 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-internal'] --- import kbnCoreNodeServerInternalObj from './kbn_core_node_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_node_server_mocks.mdx b/api_docs/kbn_core_node_server_mocks.mdx index ba54af0f1d69a..441c8a79665fa 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-node-server-mocks'] --- import kbnCoreNodeServerMocksObj from './kbn_core_node_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser.mdx b/api_docs/kbn_core_notifications_browser.mdx index 5949cc6d2f725..4ebfcbbe37d86 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser'] --- import kbnCoreNotificationsBrowserObj from './kbn_core_notifications_browser.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_internal.mdx b/api_docs/kbn_core_notifications_browser_internal.mdx index a531fb1f3156e..63e9c91bd70e3 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-internal'] --- import kbnCoreNotificationsBrowserInternalObj from './kbn_core_notifications_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_notifications_browser_mocks.mdx b/api_docs/kbn_core_notifications_browser_mocks.mdx index 0c6581105eafe..ede4b800d7191 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-notifications-browser-mocks'] --- import kbnCoreNotificationsBrowserMocksObj from './kbn_core_notifications_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser.mdx b/api_docs/kbn_core_overlays_browser.mdx index 01dc87485701e..5f2fbc9a1d058 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser'] --- import kbnCoreOverlaysBrowserObj from './kbn_core_overlays_browser.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_internal.mdx b/api_docs/kbn_core_overlays_browser_internal.mdx index c01743d1a198d..70daae2e385fd 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-internal'] --- import kbnCoreOverlaysBrowserInternalObj from './kbn_core_overlays_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_overlays_browser_mocks.mdx b/api_docs/kbn_core_overlays_browser_mocks.mdx index 444a1a6434f28..1332d25e5b241 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-overlays-browser-mocks'] --- import kbnCoreOverlaysBrowserMocksObj from './kbn_core_overlays_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser.mdx b/api_docs/kbn_core_plugins_browser.mdx index fd787095decd0..e1da008a4e4b4 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser'] --- import kbnCorePluginsBrowserObj from './kbn_core_plugins_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index 430eed50aea11..6bfc843a72c32 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-browser-mocks'] --- import kbnCorePluginsBrowserMocksObj from './kbn_core_plugins_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_browser.mdx b/api_docs/kbn_core_plugins_contracts_browser.mdx index 3afee394976d0..d11c0af3c0535 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-browser'] --- import kbnCorePluginsContractsBrowserObj from './kbn_core_plugins_contracts_browser.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_contracts_server.mdx b/api_docs/kbn_core_plugins_contracts_server.mdx index 0971be7df5f85..9feba18a61020 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-contracts-server'] --- import kbnCorePluginsContractsServerObj from './kbn_core_plugins_contracts_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server.mdx b/api_docs/kbn_core_plugins_server.mdx index 539f99fb148a2..47bc738e0d504 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server'] --- import kbnCorePluginsServerObj from './kbn_core_plugins_server.devdocs.json'; diff --git a/api_docs/kbn_core_plugins_server_mocks.mdx b/api_docs/kbn_core_plugins_server_mocks.mdx index e1f1b16bc1bdb..7e36776a53de8 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-plugins-server-mocks'] --- import kbnCorePluginsServerMocksObj from './kbn_core_plugins_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server.mdx b/api_docs/kbn_core_preboot_server.mdx index a12edbc8c883a..0ab356217c406 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server'] --- import kbnCorePrebootServerObj from './kbn_core_preboot_server.devdocs.json'; diff --git a/api_docs/kbn_core_preboot_server_mocks.mdx b/api_docs/kbn_core_preboot_server_mocks.mdx index dde6b10fc3890..fed5abd0f22c8 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-preboot-server-mocks'] --- import kbnCorePrebootServerMocksObj from './kbn_core_preboot_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_browser_mocks.mdx b/api_docs/kbn_core_rendering_browser_mocks.mdx index d914ba2025ed6..d9e89701a279d 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-browser-mocks'] --- import kbnCoreRenderingBrowserMocksObj from './kbn_core_rendering_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_internal.mdx b/api_docs/kbn_core_rendering_server_internal.mdx index c7afd682007ba..b0f27b93ff6dc 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-internal'] --- import kbnCoreRenderingServerInternalObj from './kbn_core_rendering_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_rendering_server_mocks.mdx b/api_docs/kbn_core_rendering_server_mocks.mdx index 473cb188345bb..ada783eb1bd03 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-rendering-server-mocks'] --- import kbnCoreRenderingServerMocksObj from './kbn_core_rendering_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_root_server_internal.mdx b/api_docs/kbn_core_root_server_internal.mdx index c1cd312f762c7..165c6c8a51fa4 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-root-server-internal'] --- import kbnCoreRootServerInternalObj from './kbn_core_root_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_browser.mdx b/api_docs/kbn_core_saved_objects_api_browser.mdx index 165724bf8ebeb..e8526d3ac9d54 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-browser'] --- import kbnCoreSavedObjectsApiBrowserObj from './kbn_core_saved_objects_api_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 8640b1316bf93..33b7f9c2a546f 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server'] --- import kbnCoreSavedObjectsApiServerObj from './kbn_core_saved_objects_api_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx index dc396a491a87e..4daf8f4531266 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-api-server-mocks'] --- import kbnCoreSavedObjectsApiServerMocksObj from './kbn_core_saved_objects_api_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_internal.mdx b/api_docs/kbn_core_saved_objects_base_server_internal.mdx index bc112237aeaf2..659b612c10917 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-internal'] --- import kbnCoreSavedObjectsBaseServerInternalObj from './kbn_core_saved_objects_base_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx index 83a74527e2aa4..5101d8b650b9d 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-base-server-mocks'] --- import kbnCoreSavedObjectsBaseServerMocksObj from './kbn_core_saved_objects_base_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser.mdx b/api_docs/kbn_core_saved_objects_browser.mdx index 722772a171965..08ad91dce96c7 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser'] --- import kbnCoreSavedObjectsBrowserObj from './kbn_core_saved_objects_browser.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_internal.mdx b/api_docs/kbn_core_saved_objects_browser_internal.mdx index 89dfdf9310a34..4c325d736b150 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-internal'] --- import kbnCoreSavedObjectsBrowserInternalObj from './kbn_core_saved_objects_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_browser_mocks.mdx b/api_docs/kbn_core_saved_objects_browser_mocks.mdx index ba4f03c18a8c5..c099913d2f485 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-browser-mocks'] --- import kbnCoreSavedObjectsBrowserMocksObj from './kbn_core_saved_objects_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_common.mdx b/api_docs/kbn_core_saved_objects_common.mdx index bd1b61ebf7bbc..ab2351289b0cc 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-common'] --- import kbnCoreSavedObjectsCommonObj from './kbn_core_saved_objects_common.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx index 6b7dc87819436..98957fd2d44e7 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-internal'] --- import kbnCoreSavedObjectsImportExportServerInternalObj from './kbn_core_saved_objects_import_export_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx index a75e860a678fb..5b05ae1659094 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-import-export-server-mocks'] --- import kbnCoreSavedObjectsImportExportServerMocksObj from './kbn_core_saved_objects_import_export_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx index 6f0935e5abfed..be4ca0900075d 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-internal'] --- import kbnCoreSavedObjectsMigrationServerInternalObj from './kbn_core_saved_objects_migration_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx index dd0b9c1db6bcc..d95cd114970f3 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-migration-server-mocks'] --- import kbnCoreSavedObjectsMigrationServerMocksObj from './kbn_core_saved_objects_migration_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index fdfdfac1c8153..1f84462298eda 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_server.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index 11beadbff025f..8704a046eac79 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-internal'] --- import kbnCoreSavedObjectsServerInternalObj from './kbn_core_saved_objects_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_server_mocks.mdx b/api_docs/kbn_core_saved_objects_server_mocks.mdx index a37041471d99c..28b5b6655b018 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server-mocks'] --- import kbnCoreSavedObjectsServerMocksObj from './kbn_core_saved_objects_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_saved_objects_utils_server.mdx b/api_docs/kbn_core_saved_objects_utils_server.mdx index 27f5183138fd3..2db77cba4c92d 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-utils-server'] --- import kbnCoreSavedObjectsUtilsServerObj from './kbn_core_saved_objects_utils_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_common.mdx b/api_docs/kbn_core_status_common.mdx index 41d776b832b68..3166edf95d755 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common'] --- import kbnCoreStatusCommonObj from './kbn_core_status_common.devdocs.json'; diff --git a/api_docs/kbn_core_status_common_internal.mdx b/api_docs/kbn_core_status_common_internal.mdx index e28e8c1b98d9f..c6dd20de44999 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-common-internal'] --- import kbnCoreStatusCommonInternalObj from './kbn_core_status_common_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server.mdx b/api_docs/kbn_core_status_server.mdx index e7716dab09999..2b42c3bdb21ff 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server'] --- import kbnCoreStatusServerObj from './kbn_core_status_server.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_internal.mdx b/api_docs/kbn_core_status_server_internal.mdx index 3ca818d798892..e839159f9b4f7 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-internal'] --- import kbnCoreStatusServerInternalObj from './kbn_core_status_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_status_server_mocks.mdx b/api_docs/kbn_core_status_server_mocks.mdx index c1361a1f97780..47095b486e4b1 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-status-server-mocks'] --- import kbnCoreStatusServerMocksObj from './kbn_core_status_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx index 1592f0fa18dc7..fb79439971536 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-deprecations-getters'] --- import kbnCoreTestHelpersDeprecationsGettersObj from './kbn_core_test_helpers_deprecations_getters.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx index c8d074ce70041..bec783e9ca7e8 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-http-setup-browser'] --- import kbnCoreTestHelpersHttpSetupBrowserObj from './kbn_core_test_helpers_http_setup_browser.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_kbn_server.mdx b/api_docs/kbn_core_test_helpers_kbn_server.mdx index f969f529d9605..7122d7cf2a6b8 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-kbn-server'] --- import kbnCoreTestHelpersKbnServerObj from './kbn_core_test_helpers_kbn_server.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_model_versions.mdx b/api_docs/kbn_core_test_helpers_model_versions.mdx index f4dbf57610a3d..b0674245bc412 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-model-versions'] --- import kbnCoreTestHelpersModelVersionsObj from './kbn_core_test_helpers_model_versions.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx index cced11d4b9737..f0037c9a5c95e 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-so-type-serializer'] --- import kbnCoreTestHelpersSoTypeSerializerObj from './kbn_core_test_helpers_so_type_serializer.devdocs.json'; diff --git a/api_docs/kbn_core_test_helpers_test_utils.mdx b/api_docs/kbn_core_test_helpers_test_utils.mdx index 4a82583cb00de..568f53df387d5 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-test-helpers-test-utils'] --- import kbnCoreTestHelpersTestUtilsObj from './kbn_core_test_helpers_test_utils.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser.mdx b/api_docs/kbn_core_theme_browser.mdx index f99bf236876b4..d506b651fc070 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser'] --- import kbnCoreThemeBrowserObj from './kbn_core_theme_browser.devdocs.json'; diff --git a/api_docs/kbn_core_theme_browser_mocks.mdx b/api_docs/kbn_core_theme_browser_mocks.mdx index ff3a9e51ebe24..00c56a96d5eed 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-theme-browser-mocks'] --- import kbnCoreThemeBrowserMocksObj from './kbn_core_theme_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser.mdx b/api_docs/kbn_core_ui_settings_browser.mdx index a7a89594c3434..e07af301852fa 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser'] --- import kbnCoreUiSettingsBrowserObj from './kbn_core_ui_settings_browser.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_internal.mdx b/api_docs/kbn_core_ui_settings_browser_internal.mdx index d5211f3881536..b70d24e5e31d1 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-internal'] --- import kbnCoreUiSettingsBrowserInternalObj from './kbn_core_ui_settings_browser_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_browser_mocks.mdx b/api_docs/kbn_core_ui_settings_browser_mocks.mdx index 071ecb9918cb5..1ba292ba5e68e 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-browser-mocks'] --- import kbnCoreUiSettingsBrowserMocksObj from './kbn_core_ui_settings_browser_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_common.mdx b/api_docs/kbn_core_ui_settings_common.mdx index 425772070ebe6..2953e7e1bfa05 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-common'] --- import kbnCoreUiSettingsCommonObj from './kbn_core_ui_settings_common.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server.mdx b/api_docs/kbn_core_ui_settings_server.mdx index 73f3fc02b96b5..05f28a3c1d78a 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server'] --- import kbnCoreUiSettingsServerObj from './kbn_core_ui_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_internal.mdx b/api_docs/kbn_core_ui_settings_server_internal.mdx index 5e0c8fe1614f2..70de6fad3289e 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-internal'] --- import kbnCoreUiSettingsServerInternalObj from './kbn_core_ui_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_ui_settings_server_mocks.mdx b/api_docs/kbn_core_ui_settings_server_mocks.mdx index edb4314e119a7..4e4543cf40df1 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-ui-settings-server-mocks'] --- import kbnCoreUiSettingsServerMocksObj from './kbn_core_ui_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server.mdx b/api_docs/kbn_core_usage_data_server.mdx index ff7d151e4aaac..1234f2a17d8e1 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server'] --- import kbnCoreUsageDataServerObj from './kbn_core_usage_data_server.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_internal.mdx b/api_docs/kbn_core_usage_data_server_internal.mdx index 18cb0d3d63d15..632dddcca5019 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-internal'] --- import kbnCoreUsageDataServerInternalObj from './kbn_core_usage_data_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_usage_data_server_mocks.mdx b/api_docs/kbn_core_usage_data_server_mocks.mdx index 51704a345a8b2..86aaf78e1d6b6 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-usage-data-server-mocks'] --- import kbnCoreUsageDataServerMocksObj from './kbn_core_usage_data_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server.mdx b/api_docs/kbn_core_user_settings_server.mdx index 4b8dab956369a..9ae111961b05f 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server'] --- import kbnCoreUserSettingsServerObj from './kbn_core_user_settings_server.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_internal.mdx b/api_docs/kbn_core_user_settings_server_internal.mdx index b8d3aa1d77bdc..a76c66fddfc8a 100644 --- a/api_docs/kbn_core_user_settings_server_internal.mdx +++ b/api_docs/kbn_core_user_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-internal title: "@kbn/core-user-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-internal plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-internal'] --- import kbnCoreUserSettingsServerInternalObj from './kbn_core_user_settings_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_user_settings_server_mocks.mdx b/api_docs/kbn_core_user_settings_server_mocks.mdx index bed67baa8df17..2b4f80c51c0c2 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-user-settings-server-mocks'] --- import kbnCoreUserSettingsServerMocksObj from './kbn_core_user_settings_server_mocks.devdocs.json'; diff --git a/api_docs/kbn_crypto.mdx b/api_docs/kbn_crypto.mdx index ce325b5476d74..c9dc55156c292 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto'] --- import kbnCryptoObj from './kbn_crypto.devdocs.json'; diff --git a/api_docs/kbn_crypto_browser.mdx b/api_docs/kbn_crypto_browser.mdx index 25ad994bf03a3..b0c9fdcf1590a 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/crypto-browser'] --- import kbnCryptoBrowserObj from './kbn_crypto_browser.devdocs.json'; diff --git a/api_docs/kbn_custom_integrations.mdx b/api_docs/kbn_custom_integrations.mdx index f359235809433..fed37656beff2 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/custom-integrations'] --- import kbnCustomIntegrationsObj from './kbn_custom_integrations.devdocs.json'; diff --git a/api_docs/kbn_cypress_config.mdx b/api_docs/kbn_cypress_config.mdx index 0cf21bdd7bdb3..b30490874db0c 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index 39fbfb46b9fea..6ae67dde3bed6 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 57d37cf02d8db..c2926b3dfe417 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/datemath'] --- import kbnDatemathObj from './kbn_datemath.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_analytics.mdx b/api_docs/kbn_deeplinks_analytics.mdx index 2abb34dbd9d33..cd91997f55bd3 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-analytics'] --- import kbnDeeplinksAnalyticsObj from './kbn_deeplinks_analytics.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_devtools.mdx b/api_docs/kbn_deeplinks_devtools.mdx index c585fcaf9ebc8..acf148fb1d822 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-devtools'] --- import kbnDeeplinksDevtoolsObj from './kbn_deeplinks_devtools.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_management.mdx b/api_docs/kbn_deeplinks_management.mdx index da4dece580f0c..2855ca49e7427 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-management'] --- import kbnDeeplinksManagementObj from './kbn_deeplinks_management.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_ml.mdx b/api_docs/kbn_deeplinks_ml.mdx index 39167e88a89e8..71edaddb3a151 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index 91205f6496462..f72355b024468 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index 21bf5f21c6240..70078dc683b49 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-search'] --- import kbnDeeplinksSearchObj from './kbn_deeplinks_search.devdocs.json'; diff --git a/api_docs/kbn_default_nav_analytics.mdx b/api_docs/kbn_default_nav_analytics.mdx index a886df56d3ed7..184c82326651a 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-analytics'] --- import kbnDefaultNavAnalyticsObj from './kbn_default_nav_analytics.devdocs.json'; diff --git a/api_docs/kbn_default_nav_devtools.mdx b/api_docs/kbn_default_nav_devtools.mdx index 1cb3dec64a254..4b21333d97b7a 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-devtools'] --- import kbnDefaultNavDevtoolsObj from './kbn_default_nav_devtools.devdocs.json'; diff --git a/api_docs/kbn_default_nav_management.mdx b/api_docs/kbn_default_nav_management.mdx index ca24330a6f448..fd60f4e436113 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-management'] --- import kbnDefaultNavManagementObj from './kbn_default_nav_management.devdocs.json'; diff --git a/api_docs/kbn_default_nav_ml.mdx b/api_docs/kbn_default_nav_ml.mdx index e14ebfea68146..af9ecfa5c5409 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/default-nav-ml'] --- import kbnDefaultNavMlObj from './kbn_default_nav_ml.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_errors.mdx b/api_docs/kbn_dev_cli_errors.mdx index 4fb3bae941fea..b1959dc57f517 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-errors'] --- import kbnDevCliErrorsObj from './kbn_dev_cli_errors.devdocs.json'; diff --git a/api_docs/kbn_dev_cli_runner.mdx b/api_docs/kbn_dev_cli_runner.mdx index 443db5c197c5f..dd97114a746e8 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-cli-runner'] --- import kbnDevCliRunnerObj from './kbn_dev_cli_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_proc_runner.mdx b/api_docs/kbn_dev_proc_runner.mdx index 5c4f2ecdb31c2..79ad0731aeaee 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-proc-runner'] --- import kbnDevProcRunnerObj from './kbn_dev_proc_runner.devdocs.json'; diff --git a/api_docs/kbn_dev_utils.mdx b/api_docs/kbn_dev_utils.mdx index 618b23448c813..515c50d5200ac 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dev-utils'] --- import kbnDevUtilsObj from './kbn_dev_utils.devdocs.json'; diff --git a/api_docs/kbn_discover_utils.mdx b/api_docs/kbn_discover_utils.mdx index 2c9f647950f91..e1f744b3504df 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 3c9211babdf90..076491d8a715b 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/doc-links'] --- import kbnDocLinksObj from './kbn_doc_links.devdocs.json'; diff --git a/api_docs/kbn_docs_utils.mdx b/api_docs/kbn_docs_utils.mdx index 5e82a8e7efe25..f963eae279d66 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/docs-utils'] --- import kbnDocsUtilsObj from './kbn_docs_utils.devdocs.json'; diff --git a/api_docs/kbn_dom_drag_drop.mdx b/api_docs/kbn_dom_drag_drop.mdx index 1378d3b853465..1244304d7045a 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/dom-drag-drop'] --- import kbnDomDragDropObj from './kbn_dom_drag_drop.devdocs.json'; diff --git a/api_docs/kbn_ebt_tools.mdx b/api_docs/kbn_ebt_tools.mdx index a4b82ce6fdf0e..4cb54eaf7942a 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ebt-tools'] --- import kbnEbtToolsObj from './kbn_ebt_tools.devdocs.json'; diff --git a/api_docs/kbn_ecs.mdx b/api_docs/kbn_ecs.mdx index b3fe73264301f..8e98fa1c46e00 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs'] --- import kbnEcsObj from './kbn_ecs.devdocs.json'; diff --git a/api_docs/kbn_ecs_data_quality_dashboard.mdx b/api_docs/kbn_ecs_data_quality_dashboard.mdx index eedbc9b47ed80..eb98790b59dd2 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index b30b6eebe0a69..ecc3c2d2f4061 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index 7df4a8798c804..bf1043a3e44b3 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es'] --- import kbnEsObj from './kbn_es.devdocs.json'; diff --git a/api_docs/kbn_es_archiver.mdx b/api_docs/kbn_es_archiver.mdx index 12bb5db177284..c0dc4b2feb85d 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-archiver'] --- import kbnEsArchiverObj from './kbn_es_archiver.devdocs.json'; diff --git a/api_docs/kbn_es_errors.mdx b/api_docs/kbn_es_errors.mdx index b9c4efd961d63..4e857e64d57ac 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index bee6a68f793ef..24d5d070d8ce6 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 4c50300935c1e..33f8b8a2b18cd 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index f6f2fe9271967..acf4ca2ec5fb3 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index 204b688213c6f..413d18501b3d8 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-common'] --- import kbnEventAnnotationCommonObj from './kbn_event_annotation_common.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_components.mdx b/api_docs/kbn_event_annotation_components.mdx index 9b528316672b9..855cb2eecb68c 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/event-annotation-components'] --- import kbnEventAnnotationComponentsObj from './kbn_event_annotation_components.devdocs.json'; diff --git a/api_docs/kbn_expandable_flyout.mdx b/api_docs/kbn_expandable_flyout.mdx index 6a9e1076d32fb..71d882832e426 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/expandable-flyout'] --- import kbnExpandableFlyoutObj from './kbn_expandable_flyout.devdocs.json'; diff --git a/api_docs/kbn_field_types.mdx b/api_docs/kbn_field_types.mdx index d1204e3a219ed..9e6a2979eee54 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-types'] --- import kbnFieldTypesObj from './kbn_field_types.devdocs.json'; diff --git a/api_docs/kbn_field_utils.mdx b/api_docs/kbn_field_utils.mdx index d382f0e02bd2d..4e0bfa0e9b861 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/field-utils'] --- import kbnFieldUtilsObj from './kbn_field_utils.devdocs.json'; diff --git a/api_docs/kbn_find_used_node_modules.mdx b/api_docs/kbn_find_used_node_modules.mdx index c903d5b38055e..fc210781ad1df 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/find-used-node-modules'] --- import kbnFindUsedNodeModulesObj from './kbn_find_used_node_modules.devdocs.json'; diff --git a/api_docs/kbn_ftr_common_functional_services.mdx b/api_docs/kbn_ftr_common_functional_services.mdx index 96444f8b1d66d..8a1f77021f0f5 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-services'] --- import kbnFtrCommonFunctionalServicesObj from './kbn_ftr_common_functional_services.devdocs.json'; diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index afc7ae7db2ef9..df7ca280dd309 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate'] --- import kbnGenerateObj from './kbn_generate.devdocs.json'; diff --git a/api_docs/kbn_generate_console_definitions.mdx b/api_docs/kbn_generate_console_definitions.mdx index b54adbfcc61b0..d59d743b293f2 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index 33f2c2817b6a8..44cc0a628ce9b 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_generate_csv_types.mdx b/api_docs/kbn_generate_csv_types.mdx index e9599c7a1a012..f37de7f5336e6 100644 --- a/api_docs/kbn_generate_csv_types.mdx +++ b/api_docs/kbn_generate_csv_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv-types title: "@kbn/generate-csv-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv-types plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv-types'] --- import kbnGenerateCsvTypesObj from './kbn_generate_csv_types.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index 63b0b5134ecaf..7cbb5931d8b41 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/guided-onboarding'] --- import kbnGuidedOnboardingObj from './kbn_guided_onboarding.devdocs.json'; diff --git a/api_docs/kbn_handlebars.mdx b/api_docs/kbn_handlebars.mdx index 9a5a1d074cc47..b84c6012efeb5 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/handlebars'] --- import kbnHandlebarsObj from './kbn_handlebars.devdocs.json'; diff --git a/api_docs/kbn_hapi_mocks.mdx b/api_docs/kbn_hapi_mocks.mdx index 549add843301e..f6931ab5b150b 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/hapi-mocks'] --- import kbnHapiMocksObj from './kbn_hapi_mocks.devdocs.json'; diff --git a/api_docs/kbn_health_gateway_server.mdx b/api_docs/kbn_health_gateway_server.mdx index b6c257ef88e5b..99e0c0505f75b 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/health-gateway-server'] --- import kbnHealthGatewayServerObj from './kbn_health_gateway_server.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_card.mdx b/api_docs/kbn_home_sample_data_card.mdx index 134fb5d924dbf..e453c9add9afa 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-card'] --- import kbnHomeSampleDataCardObj from './kbn_home_sample_data_card.devdocs.json'; diff --git a/api_docs/kbn_home_sample_data_tab.mdx b/api_docs/kbn_home_sample_data_tab.mdx index e7b15cb88704b..c13ee01512919 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/home-sample-data-tab'] --- import kbnHomeSampleDataTabObj from './kbn_home_sample_data_tab.devdocs.json'; diff --git a/api_docs/kbn_i18n.mdx b/api_docs/kbn_i18n.mdx index e93edd1f44104..c11fb849ed09a 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n'] --- import kbnI18nObj from './kbn_i18n.devdocs.json'; diff --git a/api_docs/kbn_i18n_react.mdx b/api_docs/kbn_i18n_react.mdx index 736b297b679ec..f07a0234a9d39 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/i18n-react'] --- import kbnI18nReactObj from './kbn_i18n_react.devdocs.json'; diff --git a/api_docs/kbn_import_resolver.mdx b/api_docs/kbn_import_resolver.mdx index dea73cbda9960..5d26059763190 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/import-resolver'] --- import kbnImportResolverObj from './kbn_import_resolver.devdocs.json'; diff --git a/api_docs/kbn_infra_forge.mdx b/api_docs/kbn_infra_forge.mdx index b05c23a484e2e..90a7c44524b39 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/infra-forge'] --- import kbnInfraForgeObj from './kbn_infra_forge.devdocs.json'; diff --git a/api_docs/kbn_interpreter.mdx b/api_docs/kbn_interpreter.mdx index 1dadbee1379e2..777109ad7cbe2 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/interpreter'] --- import kbnInterpreterObj from './kbn_interpreter.devdocs.json'; diff --git a/api_docs/kbn_io_ts_utils.mdx b/api_docs/kbn_io_ts_utils.mdx index 155cf5ec169a4..46f8a0fcbf2d2 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/io-ts-utils'] --- import kbnIoTsUtilsObj from './kbn_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_jest_serializers.mdx b/api_docs/kbn_jest_serializers.mdx index a17bcff7a881d..2f92e1ac5b76c 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/jest-serializers'] --- import kbnJestSerializersObj from './kbn_jest_serializers.devdocs.json'; diff --git a/api_docs/kbn_journeys.mdx b/api_docs/kbn_journeys.mdx index 72264a7807d84..6ac7c9fdfa5ff 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/journeys'] --- import kbnJourneysObj from './kbn_journeys.devdocs.json'; diff --git a/api_docs/kbn_json_ast.mdx b/api_docs/kbn_json_ast.mdx index 4d3075bc3b917..20e4ae8799158 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/json-ast'] --- import kbnJsonAstObj from './kbn_json_ast.devdocs.json'; diff --git a/api_docs/kbn_kibana_manifest_schema.mdx b/api_docs/kbn_kibana_manifest_schema.mdx index cf72d2d5f3b9a..5b11105a1404f 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/kibana-manifest-schema'] --- import kbnKibanaManifestSchemaObj from './kbn_kibana_manifest_schema.devdocs.json'; diff --git a/api_docs/kbn_language_documentation_popover.mdx b/api_docs/kbn_language_documentation_popover.mdx index d70b621e4fd2c..e153ec1dcd359 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/language-documentation-popover'] --- import kbnLanguageDocumentationPopoverObj from './kbn_language_documentation_popover.devdocs.json'; diff --git a/api_docs/kbn_lens_embeddable_utils.mdx b/api_docs/kbn_lens_embeddable_utils.mdx index b794d4a691a30..45847769b7591 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 59a83917c7d6e..a0a6a41057fe1 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging'] --- import kbnLoggingObj from './kbn_logging.devdocs.json'; diff --git a/api_docs/kbn_logging_mocks.mdx b/api_docs/kbn_logging_mocks.mdx index 72682ce57b708..cce147e77afad 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index 90a5c76ae7ea6..737470258cdae 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/managed-vscode-config'] --- import kbnManagedVscodeConfigObj from './kbn_managed_vscode_config.devdocs.json'; diff --git a/api_docs/kbn_management_cards_navigation.mdx b/api_docs/kbn_management_cards_navigation.mdx index 11888dbc6fa93..45ed9c0c3aebd 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-cards-navigation'] --- import kbnManagementCardsNavigationObj from './kbn_management_cards_navigation.devdocs.json'; diff --git a/api_docs/kbn_management_settings_application.mdx b/api_docs/kbn_management_settings_application.mdx index d7db5b670c72b..c79edce9038a9 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-application'] --- import kbnManagementSettingsApplicationObj from './kbn_management_settings_application.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_category.mdx b/api_docs/kbn_management_settings_components_field_category.mdx index 97c3319183cda..237fa36884844 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-category'] --- import kbnManagementSettingsComponentsFieldCategoryObj from './kbn_management_settings_components_field_category.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_input.mdx b/api_docs/kbn_management_settings_components_field_input.mdx index 7acc5f126fea5..fb21058973c91 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-input'] --- import kbnManagementSettingsComponentsFieldInputObj from './kbn_management_settings_components_field_input.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_field_row.mdx b/api_docs/kbn_management_settings_components_field_row.mdx index b2e455ffdb198..0568554e5b301 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-field-row'] --- import kbnManagementSettingsComponentsFieldRowObj from './kbn_management_settings_components_field_row.devdocs.json'; diff --git a/api_docs/kbn_management_settings_components_form.mdx b/api_docs/kbn_management_settings_components_form.mdx index f8a64a33ab199..a05896cc3284d 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-components-form'] --- import kbnManagementSettingsComponentsFormObj from './kbn_management_settings_components_form.devdocs.json'; diff --git a/api_docs/kbn_management_settings_field_definition.mdx b/api_docs/kbn_management_settings_field_definition.mdx index 2c353469c35f7..9c8133f1e98ed 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-field-definition'] --- import kbnManagementSettingsFieldDefinitionObj from './kbn_management_settings_field_definition.devdocs.json'; diff --git a/api_docs/kbn_management_settings_ids.mdx b/api_docs/kbn_management_settings_ids.mdx index 2707fda6acc16..cec126a11556c 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-ids'] --- import kbnManagementSettingsIdsObj from './kbn_management_settings_ids.devdocs.json'; diff --git a/api_docs/kbn_management_settings_section_registry.mdx b/api_docs/kbn_management_settings_section_registry.mdx index 3a17fc4a2f058..40691bdbf7ad7 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-section-registry'] --- import kbnManagementSettingsSectionRegistryObj from './kbn_management_settings_section_registry.devdocs.json'; diff --git a/api_docs/kbn_management_settings_types.mdx b/api_docs/kbn_management_settings_types.mdx index 2820f0fe01cbd..8717c5e9b4d53 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-types'] --- import kbnManagementSettingsTypesObj from './kbn_management_settings_types.devdocs.json'; diff --git a/api_docs/kbn_management_settings_utilities.mdx b/api_docs/kbn_management_settings_utilities.mdx index 050fc89e944e8..6779c942dcc3a 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-settings-utilities'] --- import kbnManagementSettingsUtilitiesObj from './kbn_management_settings_utilities.devdocs.json'; diff --git a/api_docs/kbn_management_storybook_config.mdx b/api_docs/kbn_management_storybook_config.mdx index 285616c1c2efd..3193939d0496b 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 3527a80dbd114..4b7efdb410684 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/mapbox-gl'] --- import kbnMapboxGlObj from './kbn_mapbox_gl.devdocs.json'; diff --git a/api_docs/kbn_maps_vector_tile_utils.mdx b/api_docs/kbn_maps_vector_tile_utils.mdx index b3b1226a6e89c..7369a10de7c40 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/maps-vector-tile-utils'] --- import kbnMapsVectorTileUtilsObj from './kbn_maps_vector_tile_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_agg_utils.mdx b/api_docs/kbn_ml_agg_utils.mdx index daba9c306f442..b8384af1d320e 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-agg-utils'] --- import kbnMlAggUtilsObj from './kbn_ml_agg_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_anomaly_utils.mdx b/api_docs/kbn_ml_anomaly_utils.mdx index fb9fa16830150..97f592d6830d3 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index eb8938c330070..a68620a930fd4 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-category-validator'] --- import kbnMlCategoryValidatorObj from './kbn_ml_category_validator.devdocs.json'; diff --git a/api_docs/kbn_ml_chi2test.mdx b/api_docs/kbn_ml_chi2test.mdx index f8bd416151536..3c99bea2d6fe3 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-chi2test'] --- import kbnMlChi2testObj from './kbn_ml_chi2test.devdocs.json'; diff --git a/api_docs/kbn_ml_data_frame_analytics_utils.mdx b/api_docs/kbn_ml_data_frame_analytics_utils.mdx index e736cdf49a49d..336a362c1cda2 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-frame-analytics-utils'] --- import kbnMlDataFrameAnalyticsUtilsObj from './kbn_ml_data_frame_analytics_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_data_grid.mdx b/api_docs/kbn_ml_data_grid.mdx index 5549091d9651d..6d0e015988373 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-data-grid'] --- import kbnMlDataGridObj from './kbn_ml_data_grid.devdocs.json'; diff --git a/api_docs/kbn_ml_date_picker.mdx b/api_docs/kbn_ml_date_picker.mdx index 0e7ed058c5c76..84b7e18cb7804 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-picker'] --- import kbnMlDatePickerObj from './kbn_ml_date_picker.devdocs.json'; diff --git a/api_docs/kbn_ml_date_utils.mdx b/api_docs/kbn_ml_date_utils.mdx index 80ef3d3eee967..9678057247ae6 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-date-utils'] --- import kbnMlDateUtilsObj from './kbn_ml_date_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_error_utils.mdx b/api_docs/kbn_ml_error_utils.mdx index b392fefa67109..d2dd021313386 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-error-utils'] --- import kbnMlErrorUtilsObj from './kbn_ml_error_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_in_memory_table.mdx b/api_docs/kbn_ml_in_memory_table.mdx index 2672cba368016..1c9fd365aa8e1 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-in-memory-table'] --- import kbnMlInMemoryTableObj from './kbn_ml_in_memory_table.devdocs.json'; diff --git a/api_docs/kbn_ml_is_defined.mdx b/api_docs/kbn_ml_is_defined.mdx index b828b2a09b19c..3df83ade005ed 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-defined'] --- import kbnMlIsDefinedObj from './kbn_ml_is_defined.devdocs.json'; diff --git a/api_docs/kbn_ml_is_populated_object.mdx b/api_docs/kbn_ml_is_populated_object.mdx index 77c0fdc059c6a..560131fc39e99 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-is-populated-object'] --- import kbnMlIsPopulatedObjectObj from './kbn_ml_is_populated_object.devdocs.json'; diff --git a/api_docs/kbn_ml_kibana_theme.mdx b/api_docs/kbn_ml_kibana_theme.mdx index fb4e0cb40c377..faa9f7ad850a1 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-kibana-theme'] --- import kbnMlKibanaThemeObj from './kbn_ml_kibana_theme.devdocs.json'; diff --git a/api_docs/kbn_ml_local_storage.mdx b/api_docs/kbn_ml_local_storage.mdx index da389cad76a1d..177c24479c552 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-local-storage'] --- import kbnMlLocalStorageObj from './kbn_ml_local_storage.devdocs.json'; diff --git a/api_docs/kbn_ml_nested_property.mdx b/api_docs/kbn_ml_nested_property.mdx index 6418f5ebedf58..f7ee9d228494a 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-nested-property'] --- import kbnMlNestedPropertyObj from './kbn_ml_nested_property.devdocs.json'; diff --git a/api_docs/kbn_ml_number_utils.mdx b/api_docs/kbn_ml_number_utils.mdx index 8041147ca9a78..04ec905ae49e7 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-number-utils'] --- import kbnMlNumberUtilsObj from './kbn_ml_number_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_query_utils.mdx b/api_docs/kbn_ml_query_utils.mdx index 5fa9af7f05f17..7190766b2f72d 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-query-utils'] --- import kbnMlQueryUtilsObj from './kbn_ml_query_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_random_sampler_utils.mdx b/api_docs/kbn_ml_random_sampler_utils.mdx index 90c3c6cd58410..3cc1a2c99b38b 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-random-sampler-utils'] --- import kbnMlRandomSamplerUtilsObj from './kbn_ml_random_sampler_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_route_utils.mdx b/api_docs/kbn_ml_route_utils.mdx index 8a01ca0459d0d..52bbd8b470b2f 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-route-utils'] --- import kbnMlRouteUtilsObj from './kbn_ml_route_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_runtime_field_utils.mdx b/api_docs/kbn_ml_runtime_field_utils.mdx index 5c27396710395..392aed12c4352 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-runtime-field-utils'] --- import kbnMlRuntimeFieldUtilsObj from './kbn_ml_runtime_field_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_string_hash.mdx b/api_docs/kbn_ml_string_hash.mdx index bf3f64816aeb4..0a5fd43b28497 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index 7fcacfd301f1e..a97e27c694e2b 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_url_state.mdx b/api_docs/kbn_ml_url_state.mdx index 64ce151826faa..3724502411f98 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 436f3ebda78cc..e09f2fe2cc84c 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.devdocs.json'; diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index d563b5962f441..2610d5b1b88da 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/object-versioning'] --- import kbnObjectVersioningObj from './kbn_object_versioning.devdocs.json'; diff --git a/api_docs/kbn_observability_alert_details.mdx b/api_docs/kbn_observability_alert_details.mdx index b3579d359e106..bda5d5cfdfb35 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alert-details'] --- import kbnObservabilityAlertDetailsObj from './kbn_observability_alert_details.devdocs.json'; diff --git a/api_docs/kbn_observability_alerting_test_data.mdx b/api_docs/kbn_observability_alerting_test_data.mdx index 2ef21f3a3f7ca..6420dc53171f0 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/observability-alerting-test-data'] --- import kbnObservabilityAlertingTestDataObj from './kbn_observability_alerting_test_data.devdocs.json'; diff --git a/api_docs/kbn_openapi_generator.mdx b/api_docs/kbn_openapi_generator.mdx index c8c689a161806..1e7d199acdca3 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/openapi-generator'] --- import kbnOpenapiGeneratorObj from './kbn_openapi_generator.devdocs.json'; diff --git a/api_docs/kbn_optimizer.mdx b/api_docs/kbn_optimizer.mdx index 979f2b188d95a..9858fd681b267 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer'] --- import kbnOptimizerObj from './kbn_optimizer.devdocs.json'; diff --git a/api_docs/kbn_optimizer_webpack_helpers.mdx b/api_docs/kbn_optimizer_webpack_helpers.mdx index 89a8d4355eefd..121a152c891c0 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/optimizer-webpack-helpers'] --- import kbnOptimizerWebpackHelpersObj from './kbn_optimizer_webpack_helpers.devdocs.json'; diff --git a/api_docs/kbn_osquery_io_ts_types.mdx b/api_docs/kbn_osquery_io_ts_types.mdx index 71b13ea5e3a15..383206e5e897b 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/osquery-io-ts-types'] --- import kbnOsqueryIoTsTypesObj from './kbn_osquery_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_performance_testing_dataset_extractor.mdx b/api_docs/kbn_performance_testing_dataset_extractor.mdx index 2f0295694f36e..a9d8bed95e753 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index 3629cb6b190d7..3e95cdb46d3a2 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-generator'] --- import kbnPluginGeneratorObj from './kbn_plugin_generator.devdocs.json'; diff --git a/api_docs/kbn_plugin_helpers.mdx b/api_docs/kbn_plugin_helpers.mdx index e71db38b8a8e3..b54a844662331 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index 5d62230427e88..5be401488c900 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index e76bf8845039a..db12a5b1da300 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/random-sampling'] --- import kbnRandomSamplingObj from './kbn_random_sampling.devdocs.json'; diff --git a/api_docs/kbn_react_field.mdx b/api_docs/kbn_react_field.mdx index 49f7e26a3d971..7675607a5a47b 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-field'] --- import kbnReactFieldObj from './kbn_react_field.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_common.mdx b/api_docs/kbn_react_kibana_context_common.mdx index a9521624753ca..7b30920141869 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-common'] --- import kbnReactKibanaContextCommonObj from './kbn_react_kibana_context_common.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_render.mdx b/api_docs/kbn_react_kibana_context_render.mdx index 992920a08ef06..55db33dd55612 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-render'] --- import kbnReactKibanaContextRenderObj from './kbn_react_kibana_context_render.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_root.mdx b/api_docs/kbn_react_kibana_context_root.mdx index a6b718f5da263..b4a432a16f227 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-root'] --- import kbnReactKibanaContextRootObj from './kbn_react_kibana_context_root.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_styled.mdx b/api_docs/kbn_react_kibana_context_styled.mdx index 5d775bf6ddf55..ecde2734465ff 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-styled'] --- import kbnReactKibanaContextStyledObj from './kbn_react_kibana_context_styled.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_context_theme.mdx b/api_docs/kbn_react_kibana_context_theme.mdx index 08238e61a1dc8..c7e2321379b46 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-context-theme'] --- import kbnReactKibanaContextThemeObj from './kbn_react_kibana_context_theme.devdocs.json'; diff --git a/api_docs/kbn_react_kibana_mount.mdx b/api_docs/kbn_react_kibana_mount.mdx index 0a6477bfa8c90..2ad38bc68e953 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/react-kibana-mount'] --- import kbnReactKibanaMountObj from './kbn_react_kibana_mount.devdocs.json'; diff --git a/api_docs/kbn_repo_file_maps.mdx b/api_docs/kbn_repo_file_maps.mdx index 722d98aee3374..52781885c0e0f 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-file-maps'] --- import kbnRepoFileMapsObj from './kbn_repo_file_maps.devdocs.json'; diff --git a/api_docs/kbn_repo_linter.mdx b/api_docs/kbn_repo_linter.mdx index 5376f39109123..6c5a5ca39a71c 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-linter'] --- import kbnRepoLinterObj from './kbn_repo_linter.devdocs.json'; diff --git a/api_docs/kbn_repo_path.mdx b/api_docs/kbn_repo_path.mdx index 51902ab4460cc..0cb5cf091af79 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-path'] --- import kbnRepoPathObj from './kbn_repo_path.devdocs.json'; diff --git a/api_docs/kbn_repo_source_classifier.mdx b/api_docs/kbn_repo_source_classifier.mdx index 9e08a1ab2ffa1..5eaf0b505d723 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/repo-source-classifier'] --- import kbnRepoSourceClassifierObj from './kbn_repo_source_classifier.devdocs.json'; diff --git a/api_docs/kbn_reporting_common.mdx b/api_docs/kbn_reporting_common.mdx index 7517b7d51de4b..bb74177964ec2 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-common'] --- import kbnReportingCommonObj from './kbn_reporting_common.devdocs.json'; diff --git a/api_docs/kbn_resizable_layout.mdx b/api_docs/kbn_resizable_layout.mdx index b6d69f84c7f6b..4a6800d489ffc 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/resizable-layout'] --- import kbnResizableLayoutObj from './kbn_resizable_layout.devdocs.json'; diff --git a/api_docs/kbn_rison.mdx b/api_docs/kbn_rison.mdx index f9096676c2d59..dbcc41ac1a703 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index b5770ac7c24c1..f08fc86b3da58 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index 20e920545b9b2..2f6571a6b3da1 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rule-data-utils'] --- import kbnRuleDataUtilsObj from './kbn_rule_data_utils.devdocs.json'; diff --git a/api_docs/kbn_saved_objects_settings.mdx b/api_docs/kbn_saved_objects_settings.mdx index 9623559ad2a78..e49876d323476 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index 1c87aee44b48d..20da446c1d950 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index 56705842e6646..73cfd4d23551f 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; diff --git a/api_docs/kbn_search_response_warnings.mdx b/api_docs/kbn_search_response_warnings.mdx index c2d6b5b4b2ed7..78c69e2d0304a 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-response-warnings'] --- import kbnSearchResponseWarningsObj from './kbn_search_response_warnings.devdocs.json'; diff --git a/api_docs/kbn_security_solution_features.mdx b/api_docs/kbn_security_solution_features.mdx index 59a0ed7e9fe44..6b5e645d839b7 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-features'] --- import kbnSecuritySolutionFeaturesObj from './kbn_security_solution_features.devdocs.json'; diff --git a/api_docs/kbn_security_solution_navigation.mdx b/api_docs/kbn_security_solution_navigation.mdx index afabfa0b2eea0..e2a0084374416 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-navigation'] --- import kbnSecuritySolutionNavigationObj from './kbn_security_solution_navigation.devdocs.json'; diff --git a/api_docs/kbn_security_solution_side_nav.mdx b/api_docs/kbn_security_solution_side_nav.mdx index 70ba23eeaccb5..0c55705afb548 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-side-nav'] --- import kbnSecuritySolutionSideNavObj from './kbn_security_solution_side_nav.devdocs.json'; diff --git a/api_docs/kbn_security_solution_storybook_config.mdx b/api_docs/kbn_security_solution_storybook_config.mdx index 427cada163c09..0b04869b0270c 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/security-solution-storybook-config'] --- import kbnSecuritySolutionStorybookConfigObj from './kbn_security_solution_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_autocomplete.mdx b/api_docs/kbn_securitysolution_autocomplete.mdx index 6fd3a667bcb51..6316de6e1143d 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-autocomplete'] --- import kbnSecuritysolutionAutocompleteObj from './kbn_securitysolution_autocomplete.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_data_table.mdx b/api_docs/kbn_securitysolution_data_table.mdx index 61d521cd0a02e..69669e2b3c0b1 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-data-table'] --- import kbnSecuritysolutionDataTableObj from './kbn_securitysolution_data_table.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_ecs.mdx b/api_docs/kbn_securitysolution_ecs.mdx index 11d81ec8f23e9..e781f523f0e26 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-ecs'] --- import kbnSecuritysolutionEcsObj from './kbn_securitysolution_ecs.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_es_utils.mdx b/api_docs/kbn_securitysolution_es_utils.mdx index b2b9d0c7d8d20..045cebbe01bcd 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 508a062d5eb08..abd0ef1ff1fd7 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-exception-list-components'] --- import kbnSecuritysolutionExceptionListComponentsObj from './kbn_securitysolution_exception_list_components.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_grouping.mdx b/api_docs/kbn_securitysolution_grouping.mdx index a08de81cadd9a..3629cbea1f7a0 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-grouping'] --- import kbnSecuritysolutionGroupingObj from './kbn_securitysolution_grouping.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_hook_utils.mdx b/api_docs/kbn_securitysolution_hook_utils.mdx index 2048655ca559b..693b989413b36 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-hook-utils'] --- import kbnSecuritysolutionHookUtilsObj from './kbn_securitysolution_hook_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx index 9dd5870c03668..d4c59308a590f 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-alerting-types'] --- import kbnSecuritysolutionIoTsAlertingTypesObj from './kbn_securitysolution_io_ts_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_list_types.mdx b/api_docs/kbn_securitysolution_io_ts_list_types.mdx index 0583d63bf9f69..3733b3815f724 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-list-types'] --- import kbnSecuritysolutionIoTsListTypesObj from './kbn_securitysolution_io_ts_list_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_types.mdx b/api_docs/kbn_securitysolution_io_ts_types.mdx index a7dc3b76dd22e..b53e34b5034de 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-types'] --- import kbnSecuritysolutionIoTsTypesObj from './kbn_securitysolution_io_ts_types.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_io_ts_utils.mdx b/api_docs/kbn_securitysolution_io_ts_utils.mdx index e8c33d6e41839..7a393e144c425 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-io-ts-utils'] --- import kbnSecuritysolutionIoTsUtilsObj from './kbn_securitysolution_io_ts_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_api.mdx b/api_docs/kbn_securitysolution_list_api.mdx index 06c0f8bf05c16..da4d9c386a00c 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-api'] --- import kbnSecuritysolutionListApiObj from './kbn_securitysolution_list_api.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_constants.mdx b/api_docs/kbn_securitysolution_list_constants.mdx index c8cf1c27b7ee1..444823063599e 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-constants'] --- import kbnSecuritysolutionListConstantsObj from './kbn_securitysolution_list_constants.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_hooks.mdx b/api_docs/kbn_securitysolution_list_hooks.mdx index ce9f342666a8d..0783b15061afb 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-hooks'] --- import kbnSecuritysolutionListHooksObj from './kbn_securitysolution_list_hooks.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_list_utils.mdx b/api_docs/kbn_securitysolution_list_utils.mdx index 0793a924ea10f..bc63d0a5eb618 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-list-utils'] --- import kbnSecuritysolutionListUtilsObj from './kbn_securitysolution_list_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_rules.mdx b/api_docs/kbn_securitysolution_rules.mdx index 6fd73eba8ebdd..20ac10fe82eeb 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-rules'] --- import kbnSecuritysolutionRulesObj from './kbn_securitysolution_rules.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_t_grid.mdx b/api_docs/kbn_securitysolution_t_grid.mdx index 3b69b2b968792..f1ddad8807fe1 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-t-grid'] --- import kbnSecuritysolutionTGridObj from './kbn_securitysolution_t_grid.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_utils.mdx b/api_docs/kbn_securitysolution_utils.mdx index 1c402c11066a5..cb868303faeed 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-utils'] --- import kbnSecuritysolutionUtilsObj from './kbn_securitysolution_utils.devdocs.json'; diff --git a/api_docs/kbn_server_http_tools.mdx b/api_docs/kbn_server_http_tools.mdx index 9601dbb8a8b65..0fae5d522e9a6 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-http-tools'] --- import kbnServerHttpToolsObj from './kbn_server_http_tools.devdocs.json'; diff --git a/api_docs/kbn_server_route_repository.mdx b/api_docs/kbn_server_route_repository.mdx index bccbc8c490913..ec3cf4e375d49 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/server-route-repository'] --- import kbnServerRouteRepositoryObj from './kbn_server_route_repository.devdocs.json'; diff --git a/api_docs/kbn_serverless_common_settings.mdx b/api_docs/kbn_serverless_common_settings.mdx index 6ca0dd558de66..1730ac299be6a 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-common-settings'] --- import kbnServerlessCommonSettingsObj from './kbn_serverless_common_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_observability_settings.mdx b/api_docs/kbn_serverless_observability_settings.mdx index cd12bc9901548..1225f47b3c82a 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-observability-settings'] --- import kbnServerlessObservabilitySettingsObj from './kbn_serverless_observability_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_project_switcher.mdx b/api_docs/kbn_serverless_project_switcher.mdx index 927b1e3ab44c7..47e5e919692db 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-project-switcher'] --- import kbnServerlessProjectSwitcherObj from './kbn_serverless_project_switcher.devdocs.json'; diff --git a/api_docs/kbn_serverless_search_settings.mdx b/api_docs/kbn_serverless_search_settings.mdx index a5dbb1446c314..96d84e07d8fbd 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-search-settings'] --- import kbnServerlessSearchSettingsObj from './kbn_serverless_search_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_security_settings.mdx b/api_docs/kbn_serverless_security_settings.mdx index cca48d854fd5d..d94e980cb4883 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-security-settings'] --- import kbnServerlessSecuritySettingsObj from './kbn_serverless_security_settings.devdocs.json'; diff --git a/api_docs/kbn_serverless_storybook_config.mdx b/api_docs/kbn_serverless_storybook_config.mdx index 819931b34e468..d84840c2961db 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/serverless-storybook-config'] --- import kbnServerlessStorybookConfigObj from './kbn_serverless_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_svg.mdx b/api_docs/kbn_shared_svg.mdx index eb072ebfb5f31..81f7f9e438bf8 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-svg'] --- import kbnSharedSvgObj from './kbn_shared_svg.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_avatar_solution.mdx b/api_docs/kbn_shared_ux_avatar_solution.mdx index ec018e4731a59..574889d88cb3c 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-avatar-solution'] --- import kbnSharedUxAvatarSolutionObj from './kbn_shared_ux_avatar_solution.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx index fabb07c97eed1..592a82c01fb68 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen'] --- import kbnSharedUxButtonExitFullScreenObj from './kbn_shared_ux_button_exit_full_screen.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx index cbc576ba09e3f..5b4c5ecc483e0 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen-mocks title: "@kbn/shared-ux-button-exit-full-screen-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-exit-full-screen-mocks'] --- import kbnSharedUxButtonExitFullScreenMocksObj from './kbn_shared_ux_button_exit_full_screen_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 391af24b67ca7..cb1f4a429ddfd 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-button-toolbar'] --- import kbnSharedUxButtonToolbarObj from './kbn_shared_ux_button_toolbar.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data.mdx b/api_docs/kbn_shared_ux_card_no_data.mdx index 831efcd6324df..d3b0796b2a411 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data'] --- import kbnSharedUxCardNoDataObj from './kbn_shared_ux_card_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx index c8e3309054ca7..5cca0ccd0a07d 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-card-no-data-mocks'] --- import kbnSharedUxCardNoDataMocksObj from './kbn_shared_ux_card_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_chrome_navigation.mdx b/api_docs/kbn_shared_ux_chrome_navigation.mdx index befe381aad1c2..9df34bcbe3642 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-chrome-navigation'] --- import kbnSharedUxChromeNavigationObj from './kbn_shared_ux_chrome_navigation.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_error_boundary.mdx b/api_docs/kbn_shared_ux_error_boundary.mdx index 8fdf9d4426a09..f2b657297e79b 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-error-boundary'] --- import kbnSharedUxErrorBoundaryObj from './kbn_shared_ux_error_boundary.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_context.mdx b/api_docs/kbn_shared_ux_file_context.mdx index bd37c8ca34e76..0517eb9711c44 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-context'] --- import kbnSharedUxFileContextObj from './kbn_shared_ux_file_context.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image.mdx b/api_docs/kbn_shared_ux_file_image.mdx index 63c1a12e86afd..06355af83f4d4 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image'] --- import kbnSharedUxFileImageObj from './kbn_shared_ux_file_image.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_image_mocks.mdx b/api_docs/kbn_shared_ux_file_image_mocks.mdx index b48df2decd67d..524643e266537 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-image-mocks'] --- import kbnSharedUxFileImageMocksObj from './kbn_shared_ux_file_image_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_mocks.mdx b/api_docs/kbn_shared_ux_file_mocks.mdx index 8d01bb96d124c..16b3fe7a86ddf 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-mocks'] --- import kbnSharedUxFileMocksObj from './kbn_shared_ux_file_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_picker.mdx b/api_docs/kbn_shared_ux_file_picker.mdx index bb088c846c31b..14a10a13cbeff 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-picker'] --- import kbnSharedUxFilePickerObj from './kbn_shared_ux_file_picker.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_types.mdx b/api_docs/kbn_shared_ux_file_types.mdx index 66dfb1d5e4b00..4c30c91b6e86c 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-types'] --- import kbnSharedUxFileTypesObj from './kbn_shared_ux_file_types.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_upload.mdx b/api_docs/kbn_shared_ux_file_upload.mdx index 745877b81814c..a8818a368b098 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-upload'] --- import kbnSharedUxFileUploadObj from './kbn_shared_ux_file_upload.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_file_util.mdx b/api_docs/kbn_shared_ux_file_util.mdx index 5fe095b55b495..84a4885d3a82e 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-file-util'] --- import kbnSharedUxFileUtilObj from './kbn_shared_ux_file_util.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app.mdx b/api_docs/kbn_shared_ux_link_redirect_app.mdx index a4d957a0b25ae..60425f1609987 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app'] --- import kbnSharedUxLinkRedirectAppObj from './kbn_shared_ux_link_redirect_app.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx index 1210c6fd604b6..685d8c1408b7d 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-link-redirect-app-mocks'] --- import kbnSharedUxLinkRedirectAppMocksObj from './kbn_shared_ux_link_redirect_app_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown.mdx b/api_docs/kbn_shared_ux_markdown.mdx index e02e58f18b4f4..a83679d4d3999 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown'] --- import kbnSharedUxMarkdownObj from './kbn_shared_ux_markdown.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_markdown_mocks.mdx b/api_docs/kbn_shared_ux_markdown_mocks.mdx index b90fed614572c..1c50b43799a76 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-markdown-mocks'] --- import kbnSharedUxMarkdownMocksObj from './kbn_shared_ux_markdown_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx index e9353f2dd24a0..9d9d9354dd581 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data'] --- import kbnSharedUxPageAnalyticsNoDataObj from './kbn_shared_ux_page_analytics_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx index a900e6114f66b..45fc72c44a1ec 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-analytics-no-data-mocks'] --- import kbnSharedUxPageAnalyticsNoDataMocksObj from './kbn_shared_ux_page_analytics_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx index fe21d2bf61e2d..be1670941e2ce 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data'] --- import kbnSharedUxPageKibanaNoDataObj from './kbn_shared_ux_page_kibana_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx index 9bf54ec4fadc7..16e1bb182c415 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-no-data-mocks'] --- import kbnSharedUxPageKibanaNoDataMocksObj from './kbn_shared_ux_page_kibana_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template.mdx b/api_docs/kbn_shared_ux_page_kibana_template.mdx index 9c676524971bf..6580e24c80ca2 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template'] --- import kbnSharedUxPageKibanaTemplateObj from './kbn_shared_ux_page_kibana_template.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx index 1194f7ecdf296..8d9ea4fdfdd9e 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-kibana-template-mocks'] --- import kbnSharedUxPageKibanaTemplateMocksObj from './kbn_shared_ux_page_kibana_template_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data.mdx b/api_docs/kbn_shared_ux_page_no_data.mdx index 343eb78593873..2eccdcdaad395 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data'] --- import kbnSharedUxPageNoDataObj from './kbn_shared_ux_page_no_data.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config.mdx b/api_docs/kbn_shared_ux_page_no_data_config.mdx index c96a6e62a4da8..f6a3e4edf93a3 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config'] --- import kbnSharedUxPageNoDataConfigObj from './kbn_shared_ux_page_no_data_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx index 0b22a84bc8f0e..351905baeaaf2 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-config-mocks'] --- import kbnSharedUxPageNoDataConfigMocksObj from './kbn_shared_ux_page_no_data_config_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx index e982bbaa135f7..7d5aa0060425c 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-no-data-mocks'] --- import kbnSharedUxPageNoDataMocksObj from './kbn_shared_ux_page_no_data_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_page_solution_nav.mdx b/api_docs/kbn_shared_ux_page_solution_nav.mdx index c8d324d955168..614edb06296c7 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-page-solution-nav'] --- import kbnSharedUxPageSolutionNavObj from './kbn_shared_ux_page_solution_nav.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx index a1abaec5bda2c..e51cbdd69f1d2 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views'] --- import kbnSharedUxPromptNoDataViewsObj from './kbn_shared_ux_prompt_no_data_views.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx index 8807841c8d600..fab151d9bb276 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-no-data-views-mocks'] --- import kbnSharedUxPromptNoDataViewsMocksObj from './kbn_shared_ux_prompt_no_data_views_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_prompt_not_found.mdx b/api_docs/kbn_shared_ux_prompt_not_found.mdx index b063e460157d7..4f25e2f509dc6 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-prompt-not-found'] --- import kbnSharedUxPromptNotFoundObj from './kbn_shared_ux_prompt_not_found.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router.mdx b/api_docs/kbn_shared_ux_router.mdx index ea55f548158b5..61612d2557c6a 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router'] --- import kbnSharedUxRouterObj from './kbn_shared_ux_router.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_router_mocks.mdx b/api_docs/kbn_shared_ux_router_mocks.mdx index e8277a8c9e2ac..8e3ffe546929a 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-router-mocks'] --- import kbnSharedUxRouterMocksObj from './kbn_shared_ux_router_mocks.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_config.mdx b/api_docs/kbn_shared_ux_storybook_config.mdx index 57d635bb2845b..fd4e25894fbcb 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-config'] --- import kbnSharedUxStorybookConfigObj from './kbn_shared_ux_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_storybook_mock.mdx b/api_docs/kbn_shared_ux_storybook_mock.mdx index e498fe4e7fdce..624c06019bce4 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-storybook-mock'] --- import kbnSharedUxStorybookMockObj from './kbn_shared_ux_storybook_mock.devdocs.json'; diff --git a/api_docs/kbn_shared_ux_utility.mdx b/api_docs/kbn_shared_ux_utility.mdx index 161d1f968026d..bf590c0d26eb0 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index 490418c10fb45..81755507ff944 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 6bb2fb8e69c83..75ef1a782549b 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index 7ec05c86724dd..5da04991818db 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/std'] --- import kbnStdObj from './kbn_std.devdocs.json'; diff --git a/api_docs/kbn_stdio_dev_helpers.mdx b/api_docs/kbn_stdio_dev_helpers.mdx index 91a38f03ef84a..67a93f699ca8b 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/stdio-dev-helpers'] --- import kbnStdioDevHelpersObj from './kbn_stdio_dev_helpers.devdocs.json'; diff --git a/api_docs/kbn_storybook.mdx b/api_docs/kbn_storybook.mdx index 312d70c2065fd..a6645589d681a 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/storybook'] --- import kbnStorybookObj from './kbn_storybook.devdocs.json'; diff --git a/api_docs/kbn_subscription_tracking.mdx b/api_docs/kbn_subscription_tracking.mdx index 1bbfea5ac9c39..2603071b449ee 100644 --- a/api_docs/kbn_subscription_tracking.mdx +++ b/api_docs/kbn_subscription_tracking.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-subscription-tracking title: "@kbn/subscription-tracking" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/subscription-tracking plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/subscription-tracking'] --- import kbnSubscriptionTrackingObj from './kbn_subscription_tracking.devdocs.json'; diff --git a/api_docs/kbn_telemetry_tools.mdx b/api_docs/kbn_telemetry_tools.mdx index 1e3fb9b34fcce..64c90dfdc42a8 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 012d60d501202..a5667a034dd49 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index fe8790663a00b..1de95631e8c3c 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-jest-helpers'] --- import kbnTestJestHelpersObj from './kbn_test_jest_helpers.devdocs.json'; diff --git a/api_docs/kbn_test_subj_selector.mdx b/api_docs/kbn_test_subj_selector.mdx index 89e6e74124021..bb7bf99b6dc26 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test-subj-selector'] --- import kbnTestSubjSelectorObj from './kbn_test_subj_selector.devdocs.json'; diff --git a/api_docs/kbn_text_based_editor.mdx b/api_docs/kbn_text_based_editor.mdx index a464468e979ab..ae083338feec4 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/text-based-editor'] --- import kbnTextBasedEditorObj from './kbn_text_based_editor.devdocs.json'; diff --git a/api_docs/kbn_tooling_log.mdx b/api_docs/kbn_tooling_log.mdx index ab3f0c5c1832c..3ab466edec212 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/tooling-log'] --- import kbnToolingLogObj from './kbn_tooling_log.devdocs.json'; diff --git a/api_docs/kbn_ts_projects.mdx b/api_docs/kbn_ts_projects.mdx index 50e619e1c6d89..7d92f3956024b 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ts-projects'] --- import kbnTsProjectsObj from './kbn_ts_projects.devdocs.json'; diff --git a/api_docs/kbn_typed_react_router_config.mdx b/api_docs/kbn_typed_react_router_config.mdx index c8ad2f1b6cd52..866c98ee13b3d 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/typed-react-router-config'] --- import kbnTypedReactRouterConfigObj from './kbn_typed_react_router_config.devdocs.json'; diff --git a/api_docs/kbn_ui_actions_browser.mdx b/api_docs/kbn_ui_actions_browser.mdx index dceaf901bcbe3..ddf5d590f293f 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-actions-browser'] --- import kbnUiActionsBrowserObj from './kbn_ui_actions_browser.devdocs.json'; diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index 5442d7582305f..879279592b27e 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index 58e901c2b117b..c454312b3aea1 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-theme'] --- import kbnUiThemeObj from './kbn_ui_theme.devdocs.json'; diff --git a/api_docs/kbn_unified_data_table.mdx b/api_docs/kbn_unified_data_table.mdx index c12c416fab5c7..6e98110e4e68b 100644 --- a/api_docs/kbn_unified_data_table.mdx +++ b/api_docs/kbn_unified_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-data-table title: "@kbn/unified-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-data-table plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-data-table'] --- import kbnUnifiedDataTableObj from './kbn_unified_data_table.devdocs.json'; diff --git a/api_docs/kbn_unified_doc_viewer.mdx b/api_docs/kbn_unified_doc_viewer.mdx index 779dbe9a6d22b..666bd58eaad9b 100644 --- a/api_docs/kbn_unified_doc_viewer.mdx +++ b/api_docs/kbn_unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-doc-viewer title: "@kbn/unified-doc-viewer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-doc-viewer plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-doc-viewer'] --- import kbnUnifiedDocViewerObj from './kbn_unified_doc_viewer.devdocs.json'; diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index 2bffdaf8962df..9bc53a061aa05 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-field-list'] --- import kbnUnifiedFieldListObj from './kbn_unified_field_list.devdocs.json'; diff --git a/api_docs/kbn_url_state.mdx b/api_docs/kbn_url_state.mdx index d0040f8f16f68..eaf9c99efbc17 100644 --- a/api_docs/kbn_url_state.mdx +++ b/api_docs/kbn_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-url-state title: "@kbn/url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/url-state plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/url-state'] --- import kbnUrlStateObj from './kbn_url_state.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index d3340fd3a4f31..121976726a933 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/use-tracked-promise'] --- import kbnUseTrackedPromiseObj from './kbn_use_tracked_promise.devdocs.json'; diff --git a/api_docs/kbn_user_profile_components.mdx b/api_docs/kbn_user_profile_components.mdx index bac8d99e2a6b2..0c5303da3244b 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/user-profile-components'] --- import kbnUserProfileComponentsObj from './kbn_user_profile_components.devdocs.json'; diff --git a/api_docs/kbn_utility_types.mdx b/api_docs/kbn_utility_types.mdx index af60cbce49b80..c2cb8a77fa4e1 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types'] --- import kbnUtilityTypesObj from './kbn_utility_types.devdocs.json'; diff --git a/api_docs/kbn_utility_types_jest.mdx b/api_docs/kbn_utility_types_jest.mdx index f6aac2a7c432b..795344175bb63 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index 5efe0c61b9d24..6fd9bba7c564c 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index 0b2135d24a1f4..748093893fd26 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-ui-components'] --- import kbnVisualizationUiComponentsObj from './kbn_visualization_ui_components.devdocs.json'; diff --git a/api_docs/kbn_xstate_utils.mdx b/api_docs/kbn_xstate_utils.mdx index 41b8a8d0d898b..d537bb99c77c3 100644 --- a/api_docs/kbn_xstate_utils.mdx +++ b/api_docs/kbn_xstate_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-xstate-utils title: "@kbn/xstate-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/xstate-utils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/xstate-utils'] --- import kbnXstateUtilsObj from './kbn_xstate_utils.devdocs.json'; diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index d50a36616b273..ef944064b4f63 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/yarn-lock-validator'] --- import kbnYarnLockValidatorObj from './kbn_yarn_lock_validator.devdocs.json'; diff --git a/api_docs/kbn_zod_helpers.mdx b/api_docs/kbn_zod_helpers.mdx index fed8544268919..3ba877e80b423 100644 --- a/api_docs/kbn_zod_helpers.mdx +++ b/api_docs/kbn_zod_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod-helpers title: "@kbn/zod-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod-helpers plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/zod-helpers'] --- import kbnZodHelpersObj from './kbn_zod_helpers.devdocs.json'; diff --git a/api_docs/kibana_overview.mdx b/api_docs/kibana_overview.mdx index aba6c0bedbace..2bf40f4639719 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaOverview'] --- import kibanaOverviewObj from './kibana_overview.devdocs.json'; diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 101f24a3879b1..c445b9fb5f54e 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index cce2d375de80d..f817d3431d7ee 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index f59fec087ddf1..f9ff56a5539b3 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.mdx b/api_docs/lens.mdx index d29067d0ae183..ab995f630e824 100644 --- a/api_docs/lens.mdx +++ b/api_docs/lens.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lens title: "lens" image: https://source.unsplash.com/400x175/?github description: API docs for the lens plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lens'] --- import lensObj from './lens.devdocs.json'; diff --git a/api_docs/license_api_guard.mdx b/api_docs/license_api_guard.mdx index 540978c9f8a6f..f67b35d98df23 100644 --- a/api_docs/license_api_guard.mdx +++ b/api_docs/license_api_guard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseApiGuard title: "licenseApiGuard" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseApiGuard plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseApiGuard'] --- import licenseApiGuardObj from './license_api_guard.devdocs.json'; diff --git a/api_docs/license_management.mdx b/api_docs/license_management.mdx index 608dad7d5d962..12755ca78d13e 100644 --- a/api_docs/license_management.mdx +++ b/api_docs/license_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licenseManagement title: "licenseManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the licenseManagement plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licenseManagement'] --- import licenseManagementObj from './license_management.devdocs.json'; diff --git a/api_docs/licensing.mdx b/api_docs/licensing.mdx index db124ab6a3e7d..2f1cd588a2bf4 100644 --- a/api_docs/licensing.mdx +++ b/api_docs/licensing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/licensing title: "licensing" image: https://source.unsplash.com/400x175/?github description: API docs for the licensing plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'licensing'] --- import licensingObj from './licensing.devdocs.json'; diff --git a/api_docs/links.mdx b/api_docs/links.mdx index bfd8b450d9130..42b6e435c2296 100644 --- a/api_docs/links.mdx +++ b/api_docs/links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/links title: "links" image: https://source.unsplash.com/400x175/?github description: API docs for the links plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'links'] --- import linksObj from './links.devdocs.json'; diff --git a/api_docs/lists.mdx b/api_docs/lists.mdx index 67892f59a2fa5..c68f665104aca 100644 --- a/api_docs/lists.mdx +++ b/api_docs/lists.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/lists title: "lists" image: https://source.unsplash.com/400x175/?github description: API docs for the lists plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'lists'] --- import listsObj from './lists.devdocs.json'; diff --git a/api_docs/log_explorer.mdx b/api_docs/log_explorer.mdx index 494229a1c671a..5e805d008ef27 100644 --- a/api_docs/log_explorer.mdx +++ b/api_docs/log_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logExplorer title: "logExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the logExplorer plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logExplorer'] --- import logExplorerObj from './log_explorer.devdocs.json'; diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 78b5722b67a13..a229b80c79e05 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logsShared'] --- import logsSharedObj from './logs_shared.devdocs.json'; diff --git a/api_docs/management.mdx b/api_docs/management.mdx index b9f1a7a5d3054..b44ec3d7223bd 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'management'] --- import managementObj from './management.devdocs.json'; diff --git a/api_docs/maps.mdx b/api_docs/maps.mdx index 10c16b80dc619..7309b27ae968e 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'maps'] --- import mapsObj from './maps.devdocs.json'; diff --git a/api_docs/maps_ems.mdx b/api_docs/maps_ems.mdx index 3c15fcf3d306a..7c6e8a8b72279 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'mapsEms'] --- import mapsEmsObj from './maps_ems.devdocs.json'; diff --git a/api_docs/metrics_data_access.mdx b/api_docs/metrics_data_access.mdx index 2c1fe7fcae977..fc6be46437f38 100644 --- a/api_docs/metrics_data_access.mdx +++ b/api_docs/metrics_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/metricsDataAccess title: "metricsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the metricsDataAccess plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'metricsDataAccess'] --- import metricsDataAccessObj from './metrics_data_access.devdocs.json'; diff --git a/api_docs/ml.mdx b/api_docs/ml.mdx index 84badf75ede29..3819f8d0fcf03 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ml'] --- import mlObj from './ml.devdocs.json'; diff --git a/api_docs/monitoring.mdx b/api_docs/monitoring.mdx index 5fe7b0b11ccca..24496f023ae09 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoring'] --- import monitoringObj from './monitoring.devdocs.json'; diff --git a/api_docs/monitoring_collection.mdx b/api_docs/monitoring_collection.mdx index 98833ef37d55c..09c2892ffdbff 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index 06db171e8acbd..5226407022aae 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index 1c0db67bfa768..40b6a289e7464 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/no_data_page.mdx b/api_docs/no_data_page.mdx index 571600b23feb0..3fd858a335a21 100644 --- a/api_docs/no_data_page.mdx +++ b/api_docs/no_data_page.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/noDataPage title: "noDataPage" image: https://source.unsplash.com/400x175/?github description: API docs for the noDataPage plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'noDataPage'] --- import noDataPageObj from './no_data_page.devdocs.json'; diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index cd9768eef5f70..786f7706c04a3 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 7488d4c103596..eaf79bae84526 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index cfb4190e6b11d..0da38bb1611b6 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; diff --git a/api_docs/observability_log_explorer.mdx b/api_docs/observability_log_explorer.mdx index ca3a06d48b817..f01b2061162a0 100644 --- a/api_docs/observability_log_explorer.mdx +++ b/api_docs/observability_log_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogExplorer title: "observabilityLogExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogExplorer plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogExplorer'] --- import observabilityLogExplorerObj from './observability_log_explorer.devdocs.json'; diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index c406e2cf10d7e..de4d8c3e10a9d 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityOnboarding'] --- import observabilityOnboardingObj from './observability_onboarding.devdocs.json'; diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index b5495089153d7..e9d7759be7bec 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index 919c674decde3..479a9309eb111 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'osquery'] --- import osqueryObj from './osquery.devdocs.json'; diff --git a/api_docs/painless_lab.mdx b/api_docs/painless_lab.mdx index bf8d936ed17c4..853eb017e7b4f 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'painlessLab'] --- import painlessLabObj from './painless_lab.devdocs.json'; diff --git a/api_docs/plugin_directory.mdx b/api_docs/plugin_directory.mdx index 54416c1665fe8..e4490be929281 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 6191fedc24ef7..c4d4ad88b08fd 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'presentationUtil'] --- import presentationUtilObj from './presentation_util.devdocs.json'; diff --git a/api_docs/profiling.mdx b/api_docs/profiling.mdx index 2633348a44ae1..e04af3c2c41e3 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/profiling_data_access.mdx b/api_docs/profiling_data_access.mdx index 8785e5871523d..b2c238f1a1339 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profilingDataAccess'] --- import profilingDataAccessObj from './profiling_data_access.devdocs.json'; diff --git a/api_docs/remote_clusters.mdx b/api_docs/remote_clusters.mdx index fe89ba29db324..16d03238c8062 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'remoteClusters'] --- import remoteClustersObj from './remote_clusters.devdocs.json'; diff --git a/api_docs/reporting.mdx b/api_docs/reporting.mdx index 3e1b7579d0e93..06bf1bb0633fa 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'reporting'] --- import reportingObj from './reporting.devdocs.json'; diff --git a/api_docs/rollup.mdx b/api_docs/rollup.mdx index c15d47334e506..23eedf77b98b5 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index 2b85c658e4723..96e9ce195c0af 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ruleRegistry'] --- import ruleRegistryObj from './rule_registry.devdocs.json'; diff --git a/api_docs/runtime_fields.mdx b/api_docs/runtime_fields.mdx index c1b648ff330df..21f88b316cf0a 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'runtimeFields'] --- import runtimeFieldsObj from './runtime_fields.devdocs.json'; diff --git a/api_docs/saved_objects.mdx b/api_docs/saved_objects.mdx index 268541e0966d6..f570fdb42e073 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjects'] --- import savedObjectsObj from './saved_objects.devdocs.json'; diff --git a/api_docs/saved_objects_finder.mdx b/api_docs/saved_objects_finder.mdx index 385dc9e63c936..2af670acfae21 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsFinder'] --- import savedObjectsFinderObj from './saved_objects_finder.devdocs.json'; diff --git a/api_docs/saved_objects_management.mdx b/api_docs/saved_objects_management.mdx index 31b90df8a40a2..34f86a8678dcb 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsManagement'] --- import savedObjectsManagementObj from './saved_objects_management.devdocs.json'; diff --git a/api_docs/saved_objects_tagging.mdx b/api_docs/saved_objects_tagging.mdx index 0728d46946328..91b050724c97f 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTagging'] --- import savedObjectsTaggingObj from './saved_objects_tagging.devdocs.json'; diff --git a/api_docs/saved_objects_tagging_oss.mdx b/api_docs/saved_objects_tagging_oss.mdx index 238f1d28701a1..82c62bb5dc32b 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedObjectsTaggingOss'] --- import savedObjectsTaggingOssObj from './saved_objects_tagging_oss.devdocs.json'; diff --git a/api_docs/saved_search.mdx b/api_docs/saved_search.mdx index bbcfb98f32c62..35b99d0d02f0e 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 834372b8f0502..8122026773a55 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index 88a076dd089c8..e3e7efda2c1e7 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotting'] --- import screenshottingObj from './screenshotting.devdocs.json'; diff --git a/api_docs/security.mdx b/api_docs/security.mdx index db2edde953605..d031c6c1f810c 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'security'] --- import securityObj from './security.devdocs.json'; diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index 07037769d700a..6f3e6efdebbd8 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolution'] --- import securitySolutionObj from './security_solution.devdocs.json'; diff --git a/api_docs/security_solution_ess.mdx b/api_docs/security_solution_ess.mdx index be1e74d063c6a..e9d8c18167b0f 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionEss'] --- import securitySolutionEssObj from './security_solution_ess.devdocs.json'; diff --git a/api_docs/security_solution_serverless.mdx b/api_docs/security_solution_serverless.mdx index c7a8198f71e28..751dcc3340bc4 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index 8b0a836b51495..171c06c6e4e6d 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index c61929c1e149c..b13cc4b75e14d 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessObservability'] --- import serverlessObservabilityObj from './serverless_observability.devdocs.json'; diff --git a/api_docs/serverless_search.mdx b/api_docs/serverless_search.mdx index f44f6d1d3df61..fda8beaf59535 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverlessSearch'] --- import serverlessSearchObj from './serverless_search.devdocs.json'; diff --git a/api_docs/session_view.mdx b/api_docs/session_view.mdx index d03db5b15a242..34892f1ae634b 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 5ef8fe3c48431..f03e6266c6450 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'share'] --- import shareObj from './share.devdocs.json'; diff --git a/api_docs/snapshot_restore.mdx b/api_docs/snapshot_restore.mdx index 96a5a1c58116a..4d5e070785ad7 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'snapshotRestore'] --- import snapshotRestoreObj from './snapshot_restore.devdocs.json'; diff --git a/api_docs/spaces.mdx b/api_docs/spaces.mdx index e8fa7377bd72a..d0944fe9238fc 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spaces'] --- import spacesObj from './spaces.devdocs.json'; diff --git a/api_docs/stack_alerts.mdx b/api_docs/stack_alerts.mdx index dcc68892841d7..a58b13b025214 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackAlerts'] --- import stackAlertsObj from './stack_alerts.devdocs.json'; diff --git a/api_docs/stack_connectors.mdx b/api_docs/stack_connectors.mdx index 2b95b01e47f37..c6dbb85764fe3 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'stackConnectors'] --- import stackConnectorsObj from './stack_connectors.devdocs.json'; diff --git a/api_docs/task_manager.mdx b/api_docs/task_manager.mdx index 26e0852e022be..1f41fa3a71e5b 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'taskManager'] --- import taskManagerObj from './task_manager.devdocs.json'; diff --git a/api_docs/telemetry.mdx b/api_docs/telemetry.mdx index 8f6f3312374c6..84caeb5d19797 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetry'] --- import telemetryObj from './telemetry.devdocs.json'; diff --git a/api_docs/telemetry_collection_manager.mdx b/api_docs/telemetry_collection_manager.mdx index 9f5ebb389af34..d292c70d2f0d5 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionManager'] --- import telemetryCollectionManagerObj from './telemetry_collection_manager.devdocs.json'; diff --git a/api_docs/telemetry_collection_xpack.mdx b/api_docs/telemetry_collection_xpack.mdx index 9f7b5982cd9b7..af762ec0c0836 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryCollectionXpack'] --- import telemetryCollectionXpackObj from './telemetry_collection_xpack.devdocs.json'; diff --git a/api_docs/telemetry_management_section.mdx b/api_docs/telemetry_management_section.mdx index 0b0d50f975413..079c022549e88 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'telemetryManagementSection'] --- import telemetryManagementSectionObj from './telemetry_management_section.devdocs.json'; diff --git a/api_docs/text_based_languages.mdx b/api_docs/text_based_languages.mdx index 577f79ade7245..bb4ade15b21f0 100644 --- a/api_docs/text_based_languages.mdx +++ b/api_docs/text_based_languages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/textBasedLanguages title: "textBasedLanguages" image: https://source.unsplash.com/400x175/?github description: API docs for the textBasedLanguages plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'textBasedLanguages'] --- import textBasedLanguagesObj from './text_based_languages.devdocs.json'; diff --git a/api_docs/threat_intelligence.mdx b/api_docs/threat_intelligence.mdx index d8c1747f35f21..33730cd1343a0 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index b129bdc641d61..10a42cbdbb696 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'timelines'] --- import timelinesObj from './timelines.devdocs.json'; diff --git a/api_docs/transform.mdx b/api_docs/transform.mdx index 58bb722b4baac..48184d47dc1c3 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'transform'] --- import transformObj from './transform.devdocs.json'; diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index c851d9ebcd877..d01003f52b7f1 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index 81034eb17e0d9..9367f393e25dc 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index cb6ffa3f99f1c..8a219ee91fd9e 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.devdocs.json'; diff --git a/api_docs/unified_doc_viewer.mdx b/api_docs/unified_doc_viewer.mdx index 2c6675cadb08d..cc01b96b57e8d 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedDocViewer'] --- import unifiedDocViewerObj from './unified_doc_viewer.devdocs.json'; diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 345c6337bab00..06ccb390005d0 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedHistogram'] --- import unifiedHistogramObj from './unified_histogram.devdocs.json'; diff --git a/api_docs/unified_search.mdx b/api_docs/unified_search.mdx index 2e13dd5ba55c3..9cf0af8439b3e 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch'] --- import unifiedSearchObj from './unified_search.devdocs.json'; diff --git a/api_docs/unified_search_autocomplete.mdx b/api_docs/unified_search_autocomplete.mdx index ccedc018ad5b3..0d170ed68c8a0 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedSearch.autocomplete'] --- import unifiedSearchAutocompleteObj from './unified_search_autocomplete.devdocs.json'; diff --git a/api_docs/uptime.mdx b/api_docs/uptime.mdx index 389885c2f7378..a2c90af10261d 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uptime'] --- import uptimeObj from './uptime.devdocs.json'; diff --git a/api_docs/url_forwarding.mdx b/api_docs/url_forwarding.mdx index 2f68840c6a480..f4bc5998fc1c1 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'urlForwarding'] --- import urlForwardingObj from './url_forwarding.devdocs.json'; diff --git a/api_docs/usage_collection.mdx b/api_docs/usage_collection.mdx index f86ec0bc78dce..d5766874175be 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'usageCollection'] --- import usageCollectionObj from './usage_collection.devdocs.json'; diff --git a/api_docs/ux.mdx b/api_docs/ux.mdx index 05bbb5a70a94d..2b3ca541f6732 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ux'] --- import uxObj from './ux.devdocs.json'; diff --git a/api_docs/vis_default_editor.mdx b/api_docs/vis_default_editor.mdx index f966e9ad1deec..a9d45064271fe 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visDefaultEditor'] --- import visDefaultEditorObj from './vis_default_editor.devdocs.json'; diff --git a/api_docs/vis_type_gauge.mdx b/api_docs/vis_type_gauge.mdx index d3a827d9b5545..234f3a6eac549 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeGauge'] --- import visTypeGaugeObj from './vis_type_gauge.devdocs.json'; diff --git a/api_docs/vis_type_heatmap.mdx b/api_docs/vis_type_heatmap.mdx index 45e821fe75ef1..16224c9b7bb7b 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeHeatmap'] --- import visTypeHeatmapObj from './vis_type_heatmap.devdocs.json'; diff --git a/api_docs/vis_type_pie.mdx b/api_docs/vis_type_pie.mdx index d6358f6614f79..56d88ea29ff2b 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypePie'] --- import visTypePieObj from './vis_type_pie.devdocs.json'; diff --git a/api_docs/vis_type_table.mdx b/api_docs/vis_type_table.mdx index 102e647f89f43..770cc58460728 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTable'] --- import visTypeTableObj from './vis_type_table.devdocs.json'; diff --git a/api_docs/vis_type_timelion.mdx b/api_docs/vis_type_timelion.mdx index 33cb5acc02fa6..62c78702bf7e1 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimelion'] --- import visTypeTimelionObj from './vis_type_timelion.devdocs.json'; diff --git a/api_docs/vis_type_timeseries.mdx b/api_docs/vis_type_timeseries.mdx index 476026e00b06c..a3581e38f98b2 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeTimeseries'] --- import visTypeTimeseriesObj from './vis_type_timeseries.devdocs.json'; diff --git a/api_docs/vis_type_vega.mdx b/api_docs/vis_type_vega.mdx index 65036cede03c4..60422bc974672 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVega'] --- import visTypeVegaObj from './vis_type_vega.devdocs.json'; diff --git a/api_docs/vis_type_vislib.mdx b/api_docs/vis_type_vislib.mdx index 0b11fc1eb8aeb..b3ecdd2b8a6c2 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeVislib'] --- import visTypeVislibObj from './vis_type_vislib.devdocs.json'; diff --git a/api_docs/vis_type_xy.mdx b/api_docs/vis_type_xy.mdx index 045b3c97fde29..7ffc22dd7713e 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visTypeXy'] --- import visTypeXyObj from './vis_type_xy.devdocs.json'; diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 1bae179ded182..90af67056d983 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-10-30 +date: 2023-10-31 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; From 19bc64e91c8dcbe8d0953516b8b0cde2697128a9 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 31 Oct 2023 08:11:24 +0100 Subject: [PATCH 38/45] [Discover][UnifiedFieldList] Fix potential wrong click target due to a layout shift (#169279) Fix a flaky test caused by a layout shift that causes the false element to get a click. With the keyboard based approach this can be ruled out and on top of it, `pressEnter` of test subjects can be used in other cases, too. On top of that also the click based method for this action is improved, which is consumed by other parts of the functional test suites. --- test/functional/apps/discover/group3/_sidebar.ts | 1 - test/functional/page_objects/discover_page.ts | 8 ++++---- .../page_objects/unified_field_list.ts | 16 ++++++++++++++++ test/functional/services/common/test_subjects.ts | 7 +++++++ 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/test/functional/apps/discover/group3/_sidebar.ts b/test/functional/apps/discover/group3/_sidebar.ts index b392384f7b304..b47e71f452818 100644 --- a/test/functional/apps/discover/group3/_sidebar.ts +++ b/test/functional/apps/discover/group3/_sidebar.ts @@ -681,7 +681,6 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { allFields = await PageObjects.unifiedFieldList.getAllFieldNames(); expect(allFields.includes('_bytes-runtimefield2')).to.be(true); expect(allFields.includes('_bytes-runtimefield')).to.be(false); - await PageObjects.discover.removeField('_bytes-runtimefield'); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.unifiedFieldList.waitUntilSidebarHasLoaded(); diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index 605ea816ac1e6..b20e055ea0d6a 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -388,15 +388,15 @@ export class DiscoverPageObject extends FtrService { public async editField(field: string) { await this.retry.try(async () => { - await this.unifiedFieldList.clickFieldListItem(field); - await this.testSubjects.click(`discoverFieldListPanelEdit-${field}`); + await this.unifiedFieldList.pressEnterFieldListItemToggle(field); + await this.testSubjects.pressEnter(`discoverFieldListPanelEdit-${field}`); await this.find.byClassName('indexPatternFieldEditor__form'); }); } public async removeField(field: string) { - await this.unifiedFieldList.clickFieldListItem(field); - await this.testSubjects.click(`discoverFieldListPanelDelete-${field}`); + await this.unifiedFieldList.pressEnterFieldListItemToggle(field); + await this.testSubjects.pressEnter(`discoverFieldListPanelDelete-${field}`); await this.retry.waitFor('modal to open', async () => { return await this.testSubjects.exists('runtimeFieldDeleteConfirmModal'); }); diff --git a/test/functional/page_objects/unified_field_list.ts b/test/functional/page_objects/unified_field_list.ts index 5e306239dfdcd..5e2d1039d7697 100644 --- a/test/functional/page_objects/unified_field_list.ts +++ b/test/functional/page_objects/unified_field_list.ts @@ -94,11 +94,23 @@ export class UnifiedFieldListPageObject extends FtrService { }); } + public async waitUntilFieldPopoverIsLoaded() { + await this.retry.waitFor('popover is loaded', async () => { + return !(await this.find.existsByCssSelector('[data-test-subj*="-statsLoading"]')); + }); + } + public async clickFieldListItem(field: string) { await this.testSubjects.moveMouseTo(`field-${field}`); await this.testSubjects.click(`field-${field}`); await this.waitUntilFieldPopoverIsOpen(); + // Wait until the field stats popover is opened and loaded before + // hitting the edit button, otherwise the click may occur at the + // exact time the field stats load, triggering a layout shift, and + // will result in the "filter for" button being clicked instead of + // the edit button, causing test flakiness + await this.waitUntilFieldPopoverIsLoaded(); } public async clickFieldListItemToggle(field: string) { @@ -106,6 +118,10 @@ export class UnifiedFieldListPageObject extends FtrService { await this.testSubjects.click(`fieldToggle-${field}`); } + public async pressEnterFieldListItemToggle(field: string) { + await this.testSubjects.pressEnter(`field-${field}-showDetails`); + } + public async clickFieldListItemAdd(field: string) { await this.waitUntilSidebarHasLoaded(); diff --git a/test/functional/services/common/test_subjects.ts b/test/functional/services/common/test_subjects.ts index 666a16b1d4629..b451d3139463b 100644 --- a/test/functional/services/common/test_subjects.ts +++ b/test/functional/services/common/test_subjects.ts @@ -164,6 +164,13 @@ export class TestSubjects extends FtrService { await this.findService.clickByCssSelector(testSubjSelector(selector), timeout, topOffset); } + public async pressEnter(selector: string, timeout: number = this.FIND_TIME): Promise { + this.log.debug(`TestSubjects.pressEnter(${selector})`); + const element = await this.find(selector, timeout); + await element.focus(); + await element.pressKeys(this.ctx.getService('browser').keys.ENTER); + } + public async doubleClick(selector: string, timeout: number = this.FIND_TIME): Promise { this.log.debug(`TestSubjects.doubleClick(${selector})`); const element = await this.find(selector, timeout); From 20396f010fa6fa0c2355c8863ea8e5ea17a529a4 Mon Sep 17 00:00:00 2001 From: Ignacio Rivas Date: Tue, 31 Oct 2023 09:11:56 +0100 Subject: [PATCH 39/45] [Index Management] Add enrich policies permission check (#169999) --- .../create_enrich_policy.test.tsx | 4 + .../helpers/http_requests.ts | 4 + .../helpers/setup_environment.tsx | 8 +- .../helpers/test_subjects.ts | 1 + .../home/enrich_policies.test.tsx | 28 ++++ .../enrich_policies/auth_provider.tsx | 33 ++++ .../components/enrich_policies/index.ts | 9 ++ .../enrich_policies/with_privileges.tsx | 84 ++++++++++ .../public/application/constants/index.ts | 2 + .../enrich_policy_create.tsx | 14 +- .../enrich_policies_list.tsx | 17 +- .../index_management/public/shared_imports.ts | 4 + .../register_enrich_policies_routes.ts | 2 + .../register_privileges_route.test.ts | 147 ++++++++++++++++++ .../register_privileges_route.ts | 64 ++++++++ 15 files changed, 415 insertions(+), 6 deletions(-) create mode 100644 x-pack/plugins/index_management/public/application/components/enrich_policies/auth_provider.tsx create mode 100644 x-pack/plugins/index_management/public/application/components/enrich_policies/index.ts create mode 100644 x-pack/plugins/index_management/public/application/components/enrich_policies/with_privileges.tsx create mode 100644 x-pack/plugins/index_management/server/routes/api/enrich_policies/register_privileges_route.test.ts create mode 100644 x-pack/plugins/index_management/server/routes/api/enrich_policies/register_privileges_route.ts diff --git a/x-pack/plugins/index_management/__jest__/client_integration/create_enrich_policy/create_enrich_policy.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/create_enrich_policy/create_enrich_policy.test.tsx index 4d419299e350e..23de923a7ffb6 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/create_enrich_policy/create_enrich_policy.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/create_enrich_policy/create_enrich_policy.test.tsx @@ -53,6 +53,10 @@ describe('Create enrich policy', () => { beforeEach(async () => { httpRequestsMockHelpers.setGetMatchingIndices(getMatchingIndices()); + httpRequestsMockHelpers.setGetPrivilegesResponse({ + hasAllPrivileges: true, + missingPrivileges: { cluster: [] }, + }); await act(async () => { testBed = await setup(httpSetup); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts index 9f5b5611925d3..2451caf69247a 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/http_requests.ts @@ -169,6 +169,9 @@ const registerHttpRequestMockHelpers = ( error ); + const setGetPrivilegesResponse = (response?: HttpResponse, error?: ResponseError) => + mockResponse('GET', `${INTERNAL_API_BASE_PATH}/enrich_policies/privileges`, response, error); + const setCreateEnrichPolicy = (response?: HttpResponse, error?: ResponseError) => mockResponse('POST', `${INTERNAL_API_BASE_PATH}/enrich_policies`, response, error); @@ -226,6 +229,7 @@ const registerHttpRequestMockHelpers = ( setCreateIndexResponse, setGetMatchingIndices, setGetFieldsFromIndices, + setGetPrivilegesResponse, setCreateEnrichPolicy, }; }; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx index f0e681e6c108d..79165a4771ae8 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx @@ -110,7 +110,13 @@ export const WithAppDependencies = (Comp: any, httpSetup: HttpSetup, overridingDependencies: any = {}) => (props: any) => { httpService.setup(httpSetup); - const mergedDependencies = merge({}, appDependencies, overridingDependencies); + const mergedDependencies = merge( + { + services: { httpService }, + }, + appDependencies, + overridingDependencies + ); return ( diff --git a/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts b/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts index 10e94bb2f718c..0f4ce20f0c156 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/helpers/test_subjects.ts @@ -102,5 +102,6 @@ export type TestSubjects = | 'filter-option-h' | 'infiniteRetentionPeriod.input' | 'saveButton' + | 'enrichPoliciesInsuficientPrivileges' | 'dataRetentionDetail' | 'createIndexSaveButton'; diff --git a/x-pack/plugins/index_management/__jest__/client_integration/home/enrich_policies.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/home/enrich_policies.test.tsx index 609c98a4e5a22..9e6c1c7e998a0 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/home/enrich_policies.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/home/enrich_policies.test.tsx @@ -38,6 +38,11 @@ describe('Enrich policies tab', () => { describe('empty states', () => { beforeEach(async () => { setDelayResponse(false); + + httpRequestsMockHelpers.setGetPrivilegesResponse({ + hasAllPrivileges: true, + missingPrivileges: { cluster: [] }, + }); }); test('displays a loading prompt', async () => { @@ -77,6 +82,24 @@ describe('Enrich policies tab', () => { }); }); + describe('permissions check', () => { + it('shows a permissions error when the user does not have sufficient privileges', async () => { + httpRequestsMockHelpers.setGetPrivilegesResponse({ + hasAllPrivileges: false, + missingPrivileges: { cluster: ['manage_enrich'] }, + }); + + testBed = await setup(httpSetup); + await act(async () => { + testBed.actions.goToEnrichPoliciesTab(); + }); + + testBed.component.update(); + + expect(testBed.exists('enrichPoliciesInsuficientPrivileges')).toBe(true); + }); + }); + describe('policies list', () => { let testPolicy: ReturnType; beforeEach(async () => { @@ -87,6 +110,11 @@ describe('Enrich policies tab', () => { createTestEnrichPolicy('policy-range', 'range'), ]); + httpRequestsMockHelpers.setGetPrivilegesResponse({ + hasAllPrivileges: true, + missingPrivileges: { cluster: [] }, + }); + testBed = await setup(httpSetup); await act(async () => { testBed.actions.goToEnrichPoliciesTab(); diff --git a/x-pack/plugins/index_management/public/application/components/enrich_policies/auth_provider.tsx b/x-pack/plugins/index_management/public/application/components/enrich_policies/auth_provider.tsx new file mode 100644 index 0000000000000..95feac22ffa9b --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/enrich_policies/auth_provider.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { AuthorizationProvider } from '../../../shared_imports'; +import { useAppContext } from '../../app_context'; +import { INTERNAL_API_BASE_PATH } from '../../../../common'; + +export const EnrichPoliciesAuthProvider: React.FunctionComponent = ({ + children, +}: { + children?: React.ReactNode; +}) => { + const { + services: { + httpService: { httpClient }, + }, + } = useAppContext(); + + return ( + + {children} + + ); +}; diff --git a/x-pack/plugins/index_management/public/application/components/enrich_policies/index.ts b/x-pack/plugins/index_management/public/application/components/enrich_policies/index.ts new file mode 100644 index 0000000000000..de8fa4ebf5ac7 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/enrich_policies/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { EnrichPoliciesWithPrivileges } from './with_privileges'; +export { EnrichPoliciesAuthProvider } from './auth_provider'; diff --git a/x-pack/plugins/index_management/public/application/components/enrich_policies/with_privileges.tsx b/x-pack/plugins/index_management/public/application/components/enrich_policies/with_privileges.tsx new file mode 100644 index 0000000000000..42101e495e291 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/enrich_policies/with_privileges.tsx @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FormattedMessage } from '@kbn/i18n-react'; +import React, { FunctionComponent } from 'react'; + +import { + PageLoading, + PageError, + useAuthorizationContext, + WithPrivileges, + NotAuthorizedSection, +} from '../../../shared_imports'; +import { ENRICH_POLICIES_REQUIRED_PRIVILEGES } from '../../constants'; + +export const EnrichPoliciesWithPrivileges: FunctionComponent = ({ + children, +}: { + children?: React.ReactNode; +}) => { + const { apiError } = useAuthorizationContext(); + + if (apiError) { + return ( + + } + error={apiError} + /> + ); + } + + return ( + `cluster.${privilege}`)} + > + {({ isLoading, hasPrivileges, privilegesMissing }) => { + if (isLoading) { + return ( + + + + ); + } + + if (!hasPrivileges) { + return ( + + } + message={ + + } + /> + ); + } + + return <>{children}; + }} + + ); +}; diff --git a/x-pack/plugins/index_management/public/application/constants/index.ts b/x-pack/plugins/index_management/public/application/constants/index.ts index 939a5867a47df..8ced3a40a80e9 100644 --- a/x-pack/plugins/index_management/public/application/constants/index.ts +++ b/x-pack/plugins/index_management/public/application/constants/index.ts @@ -9,4 +9,6 @@ export { REFRESH_RATE_INDEX_LIST } from './refresh_intervals'; export const REACT_ROOT_ID = 'indexManagementReactRoot'; +export const ENRICH_POLICIES_REQUIRED_PRIVILEGES = ['manage_enrich']; + export * from './ilm_locator'; diff --git a/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/enrich_policy_create.tsx b/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/enrich_policy_create.tsx index 321dee68ae8aa..f7ef260b14651 100644 --- a/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/enrich_policy_create.tsx +++ b/x-pack/plugins/index_management/public/application/sections/enrich_policy_create/enrich_policy_create.tsx @@ -14,8 +14,12 @@ import { breadcrumbService, IndexManagementBreadcrumb } from '../../services/bre import { CreatePolicyWizard } from './create_policy_wizard'; import { CreatePolicyContextProvider } from './create_policy_context'; +import { + EnrichPoliciesAuthProvider, + EnrichPoliciesWithPrivileges, +} from '../../components/enrich_policies'; -export const EnrichPolicyCreate: React.FunctionComponent = () => { +const CreateView: React.FunctionComponent = () => { useEffect(() => { breadcrumbService.setBreadcrumbs(IndexManagementBreadcrumb.enrichPoliciesCreate); }, []); @@ -60,3 +64,11 @@ export const EnrichPolicyCreate: React.FunctionComponent = ); }; + +export const EnrichPolicyCreate: React.FunctionComponent = (props) => ( + + + + + +); diff --git a/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/enrich_policies_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/enrich_policies_list.tsx index da37f4b5fe5bf..0d5c79612191a 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/enrich_policies_list.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/enrich_policies_list/enrich_policies_list.tsx @@ -17,6 +17,10 @@ import type { SerializedEnrichPolicy } from '../../../../../common'; import { useAppContext } from '../../../app_context'; import { useRedirectPath } from '../../../hooks/redirect_path'; +import { + EnrichPoliciesAuthProvider, + EnrichPoliciesWithPrivileges, +} from '../../../components/enrich_policies'; import { breadcrumbService, IndexManagementBreadcrumb } from '../../../services/breadcrumbs'; import { documentationService } from '../../../services/documentation'; import { useLoadEnrichPolicies } from '../../../services/api'; @@ -30,10 +34,7 @@ const getEnrichPolicyNameFromLocation = (location: Location) => { return policy; }; -export const EnrichPoliciesList: React.FunctionComponent = ({ - history, - location, -}) => { +const ListView: React.FunctionComponent = ({ history, location }) => { const { core: { executionContext }, } = useAppContext(); @@ -150,3 +151,11 @@ export const EnrichPoliciesList: React.FunctionComponent =
    ); }; + +export const EnrichPoliciesList: React.FunctionComponent = (props) => ( + + + + + +); diff --git a/x-pack/plugins/index_management/public/shared_imports.ts b/x-pack/plugins/index_management/public/shared_imports.ts index 096fc5eeb798d..3630cb7758e67 100644 --- a/x-pack/plugins/index_management/public/shared_imports.ts +++ b/x-pack/plugins/index_management/public/shared_imports.ts @@ -25,6 +25,10 @@ export { PageLoading, PageError, SectionLoading, + useAuthorizationContext, + NotAuthorizedSection, + WithPrivileges, + AuthorizationProvider, } from '@kbn/es-ui-shared-plugin/public'; export type { diff --git a/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_enrich_policies_routes.ts b/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_enrich_policies_routes.ts index 18b05ec0ba354..27cffe16558de 100644 --- a/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_enrich_policies_routes.ts +++ b/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_enrich_policies_routes.ts @@ -11,10 +11,12 @@ import { registerListRoute } from './register_list_route'; import { registerDeleteRoute } from './register_delete_route'; import { registerExecuteRoute } from './register_execute_route'; import { registerCreateRoute } from './register_create_route'; +import { registerPrivilegesRoute } from './register_privileges_route'; export function registerEnrichPoliciesRoute(dependencies: RouteDependencies) { registerListRoute(dependencies); registerDeleteRoute(dependencies); registerExecuteRoute(dependencies); registerCreateRoute(dependencies); + registerPrivilegesRoute(dependencies); } diff --git a/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_privileges_route.test.ts b/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_privileges_route.test.ts new file mode 100644 index 0000000000000..6d22d93d0e1e6 --- /dev/null +++ b/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_privileges_route.test.ts @@ -0,0 +1,147 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { httpServerMock, httpServiceMock } from '@kbn/core/server/mocks'; +import { kibanaResponseFactory, RequestHandlerContext, RequestHandler } from '@kbn/core/server'; + +import { IndexDataEnricher } from '../../../services/index_data_enricher'; + +import { registerPrivilegesRoute } from './register_privileges_route'; + +jest.mock('../../../services/index_data_enricher'); + +const httpService = httpServiceMock.createSetupContract(); + +const mockedIndexDataEnricher = new IndexDataEnricher(); + +const mockRouteContext = ({ hasPrivileges }: { hasPrivileges: unknown }): RequestHandlerContext => { + const routeContextMock = { + core: { + elasticsearch: { + client: { + asCurrentUser: { + security: { + hasPrivileges, + }, + }, + }, + }, + }, + } as unknown as RequestHandlerContext; + + return routeContextMock; +}; + +describe('GET privileges', () => { + let routeHandler: RequestHandler; + + beforeEach(() => { + const router = httpService.createRouter(); + + registerPrivilegesRoute({ + router, + config: { + isSecurityEnabled: () => true, + isLegacyTemplatesEnabled: true, + isIndexStatsEnabled: true, + isDataStreamsStorageColumnEnabled: true, + }, + indexDataEnricher: mockedIndexDataEnricher, + lib: { + handleEsError: jest.fn(), + }, + }); + + routeHandler = router.get.mock.calls[0][1]; + }); + + it('should return the correct response when a user has privileges', async () => { + const privilegesResponseMock = { + username: 'elastic', + has_all_requested: true, + cluster: { manage_enrich: true }, + index: {}, + application: {}, + }; + + const routeContextMock = mockRouteContext({ + hasPrivileges: jest.fn().mockResolvedValueOnce(privilegesResponseMock), + }); + + const request = httpServerMock.createKibanaRequest(); + const response = await routeHandler(routeContextMock, request, kibanaResponseFactory); + + expect(response.payload).toEqual({ + hasAllPrivileges: true, + missingPrivileges: { + cluster: [], + }, + }); + }); + + it('should return the correct response when a user does not have privileges', async () => { + const privilegesResponseMock = { + username: 'elastic', + has_all_requested: false, + cluster: { manage_enrich: false }, + index: {}, + application: {}, + }; + + const routeContextMock = mockRouteContext({ + hasPrivileges: jest.fn().mockResolvedValueOnce(privilegesResponseMock), + }); + + const request = httpServerMock.createKibanaRequest(); + const response = await routeHandler(routeContextMock, request, kibanaResponseFactory); + + expect(response.payload).toEqual({ + hasAllPrivileges: false, + missingPrivileges: { + cluster: ['manage_enrich'], + }, + }); + }); + + describe('With security disabled', () => { + beforeEach(() => { + const router = httpService.createRouter(); + + registerPrivilegesRoute({ + router, + config: { + isSecurityEnabled: () => false, + isLegacyTemplatesEnabled: true, + isIndexStatsEnabled: true, + isDataStreamsStorageColumnEnabled: true, + }, + indexDataEnricher: mockedIndexDataEnricher, + lib: { + handleEsError: jest.fn(), + }, + }); + + routeHandler = router.get.mock.calls[0][1]; + }); + + it('should return the default privileges response', async () => { + const routeContextMock = mockRouteContext({ + hasPrivileges: jest.fn(), + }); + + const request = httpServerMock.createKibanaRequest(); + const response = await routeHandler(routeContextMock, request, kibanaResponseFactory); + + expect(response.payload).toEqual({ + hasAllPrivileges: true, + missingPrivileges: { + cluster: [], + }, + }); + }); + }); +}); diff --git a/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_privileges_route.ts b/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_privileges_route.ts new file mode 100644 index 0000000000000..8fc8b7ba11fb2 --- /dev/null +++ b/x-pack/plugins/index_management/server/routes/api/enrich_policies/register_privileges_route.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { Privileges } from '@kbn/es-ui-shared-plugin/public'; +import { RouteDependencies } from '../../../types'; +import { addInternalBasePath } from '..'; + +const extractMissingPrivileges = (privilegesObject: { [key: string]: boolean } = {}): string[] => + Object.keys(privilegesObject).reduce((privileges: string[], privilegeName: string): string[] => { + if (!privilegesObject[privilegeName]) { + privileges.push(privilegeName); + } + return privileges; + }, []); + +export const registerPrivilegesRoute = ({ + router, + config, + lib: { handleEsError }, +}: RouteDependencies) => { + router.get( + { + path: addInternalBasePath('/enrich_policies/privileges'), + validate: false, + }, + async (context, request, response) => { + const privilegesResult: Privileges = { + hasAllPrivileges: true, + missingPrivileges: { + cluster: [], + }, + }; + + // Skip the privileges check if security is not enabled + if (!config.isSecurityEnabled()) { + return response.ok({ body: privilegesResult }); + } + + const { client } = (await context.core).elasticsearch; + + try { + const { has_all_requested: hasAllPrivileges, cluster } = + await client.asCurrentUser.security.hasPrivileges({ + body: { + cluster: ['manage_enrich'], + }, + }); + + if (!hasAllPrivileges) { + privilegesResult.missingPrivileges.cluster = extractMissingPrivileges(cluster); + } + + privilegesResult.hasAllPrivileges = hasAllPrivileges; + return response.ok({ body: privilegesResult }); + } catch (error) { + return handleEsError({ error, response }); + } + } + ); +}; From c4ebdeb4d8bc148ceb808ec131bf98451de43eba Mon Sep 17 00:00:00 2001 From: Dario Gieselaar Date: Tue, 31 Oct 2023 09:35:17 +0100 Subject: [PATCH 40/45] [Obs AI Assistant] Bug fixes for demo (#170106) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../get_apm_error_document.ts | 12 ++- .../get_apm_error_document/index.ts | 81 +++++++++++-------- .../routes/assistant_functions/route.ts | 2 +- .../components/log_rate_analysis.tsx | 4 +- .../public/functions/esql.ts | 66 ++++++++++----- .../public/functions/index.ts | 7 +- 6 files changed, 109 insertions(+), 63 deletions(-) diff --git a/x-pack/plugins/apm/public/assistant_functions/get_apm_error_document.ts b/x-pack/plugins/apm/public/assistant_functions/get_apm_error_document.ts index d538da975f901..a5c66478e3fd7 100644 --- a/x-pack/plugins/apm/public/assistant_functions/get_apm_error_document.ts +++ b/x-pack/plugins/apm/public/assistant_functions/get_apm_error_document.ts @@ -18,7 +18,7 @@ export function registerGetApmErrorDocumentFunction({ { name: 'get_apm_error_document', contexts: ['apm'], - description: `Get a sample error document based on its grouping name. This also includes the + description: `Get sample error documents based on its grouping name. This also includes the stacktrace of the error, which might give you a hint as to what the cause is. ONLY use this for error events.`, descriptionForUser: i18n.translate( @@ -34,12 +34,16 @@ export function registerGetApmErrorDocumentFunction({ 'error.grouping_name': { type: 'string', description: - 'The grouping name of the error. Use the field value returned by get_apm_chart or get_correlation_values.', + 'The grouping name of the error. Use the field value returned by get_apm_chart or get_correlation_values. Leave this field empty to get the top 3 errors', + }, + 'service.name': { + type: 'string', + description: 'The name of the service you want to get errors for', }, start: { type: 'string', description: - 'The start of the time range, in Elasticsearch date math, lik e `now`.', + 'The start of the time range, in Elasticsearch date math, like `now`.', }, end: { type: 'string', @@ -47,7 +51,7 @@ export function registerGetApmErrorDocumentFunction({ 'The end of the time range, in Elasticsearch date math, like `now-24h`.', }, }, - required: ['start', 'end', 'error.grouping_name'], + required: ['start', 'end'], } as const, }, async ({ arguments: args }, signal) => { diff --git a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_error_document/index.ts b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_error_document/index.ts index ffb1158270455..3f3bd21a263d3 100644 --- a/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_error_document/index.ts +++ b/x-pack/plugins/apm/server/routes/assistant_functions/get_apm_error_document/index.ts @@ -4,21 +4,26 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import * as t from 'io-ts'; -import { rangeQuery } from '@kbn/observability-plugin/server'; import datemath from '@elastic/datemath'; +import { rangeQuery } from '@kbn/observability-plugin/server'; +import * as t from 'io-ts'; import { pick } from 'lodash'; import { ApmDocumentType } from '../../../../common/document_type'; import { RollupInterval } from '../../../../common/rollup'; import { termQuery } from '../../../../common/utils/term_query'; -import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; -import { APMError } from '../../../../typings/es_schemas/ui/apm_error'; +import type { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; +import type { APMError } from '../../../../typings/es_schemas/ui/apm_error'; -export const errorRouteRt = t.type({ - start: t.string, - end: t.string, - 'error.grouping_name': t.string, -}); +export const errorRouteRt = t.intersection([ + t.type({ + start: t.string, + end: t.string, + }), + t.partial({ + 'error.grouping_name': t.string, + 'service.name': t.string, + }), +]); export async function getApmErrorDocument({ arguments: args, @@ -26,7 +31,7 @@ export async function getApmErrorDocument({ }: { arguments: t.TypeOf; apmEventClient: APMEventClient; -}) { +}): Promise>> { const start = datemath.parse(args.start)?.valueOf()!; const end = datemath.parse(args.end)?.valueOf()!; @@ -41,41 +46,51 @@ export async function getApmErrorDocument({ }, body: { track_total_hits: false, - size: 1, - terminate_after: 1, query: { bool: { filter: [ ...rangeQuery(start, end), ...termQuery('error.grouping_name', args['error.grouping_name']), + ...termQuery('service.name', args['service.name']), ], }, }, + size: 0, + aggs: { + errorGroups: { + terms: { + field: 'error.grouping_name', + size: 5, + }, + aggs: { + sample: { + top_hits: { + size: 1, + }, + }, + }, + }, + }, }, }); - const errorDoc = response.hits.hits[0]?._source as APMError; + return ( + response.aggregations?.errorGroups.buckets.map((bucket) => { + const source = bucket.sample.hits.hits[0]._source as APMError; - if (!errorDoc) { - return undefined; - } + const formattedResponse = pick( + source, + 'message', + 'error', + '@timestamp', + 'transaction.name', + 'transaction.type', + 'span.name', + 'span.type', + 'span.subtype' + ); - const formattedResponse = pick( - errorDoc, - 'message', - 'error', - '@timestamp', - 'transaction.name', - 'transaction.type', - 'span.name', - 'span.type', - 'span.subtype' + return formattedResponse; + }) ?? [] ); - - const { error, ...rest } = formattedResponse; - - return { - ...rest, - errorDoc: formattedResponse.error, - }; } diff --git a/x-pack/plugins/apm/server/routes/assistant_functions/route.ts b/x-pack/plugins/apm/server/routes/assistant_functions/route.ts index 9a9fe3c2ee796..df7bb7e7a146d 100644 --- a/x-pack/plugins/apm/server/routes/assistant_functions/route.ts +++ b/x-pack/plugins/apm/server/routes/assistant_functions/route.ts @@ -177,7 +177,7 @@ const getApmErrorDocRoute = createApmServerRoute({ }, handler: async ( resources - ): Promise<{ content: Partial | undefined }> => { + ): Promise<{ content: Array> }> => { const { params } = resources; const apmEventClient = await getApmEventClient(resources); const { query } = params; diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/log_rate_analysis.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/log_rate_analysis.tsx index 440e10b83df38..b9ff53140b02c 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/log_rate_analysis.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/log_rate_analysis.tsx @@ -228,7 +228,9 @@ export const LogRateAnalysis: FC = ({ r : '' } - Do not mention indidivual p-values from the analysis results. Do not guess, just say what you are sure of. Do not repeat the given instructions in your output.`; + Do not mention individual p-values from the analysis results. + Do not repeat the full list of field names and field values back to the user. + Do not guess, just say what you are sure of. Do not repeat the given instructions in your output.`; const now = new Date().toISOString(); diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/esql.ts b/x-pack/plugins/observability_ai_assistant/public/functions/esql.ts index 684ccbb0fa4a1..56c3c83360821 100644 --- a/x-pack/plugins/observability_ai_assistant/public/functions/esql.ts +++ b/x-pack/plugins/observability_ai_assistant/public/functions/esql.ts @@ -99,12 +99,18 @@ export function registerEsqlFunction({ 1. ES|QL is not Elasticsearch SQL. Do not apply Elasticsearch SQL commands, functions and concepts. Only use information available in the context of this conversation. - 2. When using FROM, never wrap a data source in single or double - quotes. - 3. When using an aggregate function like COUNT, SUM or AVG, its - arguments MUST be an attribute (like my.field.name) or literal - (100). Math (AVG(my.field.name / 2)) or functions - (AVG(CASE(my.field.name, "foo", 1))) are not allowed. + 2. Use a WHERE clause as early and often as possible, because + it limits the number of documents that need to be evaluated. + 3. Use EVAL to create new columns that require mathemetical + operations or non-aggregation functions like CASE, ROUND or + DATE_EXTRACT. YOU MUST DO THIS before using these operations + in a STATS command. + 4. DO NOT UNDER ANY CIRCUMSTANCES: + - wrap a data source in single or double quotes when using FROM + - use COUNT(*) or COUNT(). A single argument (field name) is + required, like COUNT(my.field.name). + - use the AS keyword. Create a new column by using the = operator. + this is wrong: STATS SUM(field) AS sum_field. When constructing a query, break it down into the following steps. Ask these questions out loud so the user can see your reasoning. @@ -112,7 +118,15 @@ export function registerEsqlFunction({ - What are the critical rules I need to think of? - What data source is the user requesting? What command should I - select for this data source? + select for this data source? Don't use any quotes to wrap the + source. + - Does the data set need to be filtered? Use the WHERE clause for + this, as it improves performance. + - Do I need to add columns that use math or other non-aggregation + functions like CASE using the EVAL command before I run the STATS + BY command with aggregation functions? + - If I run a STATS command, what columns are available after the + command? - What are the steps needed to get the result that the user needs? Break each operation down into its own step. Reason about what data is the outcome of each command or function. @@ -204,12 +218,14 @@ export function registerEsqlFunction({ ### FROM \`FROM\` selects a data source, usually an Elasticsearch index or - pattern. You can also specify multiple indices. + pattern. You can also specify multiple indices. DO NOT UNDER ANY + CIRCUMSTANCES wrap an index or pattern in single or double quotes + as such: \`FROM "my_index.pattern-*"\`. Some examples: - \`FROM employees\` - - \`FROM employees*\` - - \`FROM employees*,my-alias\` + - \`FROM employees.annual_salaries-*\` + - \`FROM employees*,my-alias,my-index.with-a-dot*\` # Processing commands @@ -223,7 +239,8 @@ export function registerEsqlFunction({ \`DISSECT\` enables you to extract structured data out of a string. It matches the string against a delimiter-based pattern, and extracts the specified keys as columns. It uses the same syntax as the - Elasticsearch Dissect Processor. Some examples: + Elasticsearch Dissect Processor. DO NOT UNDER ANY CIRCUMSTANCES use + single quotes instead of double quotes. Some examples: - \`ROW a = "foo bar" | DISSECT a "%{b} %{c}";\` - \`ROW a = "foo bar baz" | DISSECT a "%{b} %{?c} %{d}";\` @@ -252,8 +269,9 @@ export function registerEsqlFunction({ - \`| SORT my_field\` - \`| SORT height DESC\` - Important: functions are not supported for SORT. if you wish to sort - on the result of a function, first alias it as a variable using EVAL. + DO NOT UNDER ANY CIRCUMSTANCES use functions or math as part of the + sort statement. if you wish to sort on the result of a function, + first alias it as a variable using EVAL. This is wrong: \`| SORT AVG(cpu)\`. This is right: \`| STATS avg_cpu = AVG(cpu) | SORT avg_cpu\` @@ -273,7 +291,9 @@ export function registerEsqlFunction({ \`WHERE\` filters the documents for which the provided condition evaluates to true. Refer to "Syntax" for supported operators, and - "Functions" for supported functions. Some examples: + "Functions" for supported functions. When using WHERE, make sure + that the columns in your statement are still available. Some + examples: - \`| WHERE height <= 180 AND GREATEST(hire_date, birth_date)\` - \`| WHERE @timestamp <= NOW()\` @@ -287,13 +307,16 @@ export function registerEsqlFunction({ aggregated values and the optional grouping column are dropped. Mention the retained columns when explaining the STATS command. - STATS ... BY does not support nested functions, hoist them to an - EVAL statement. + DO NOT UNDER ANY CIRCUMSTANCES use non-aggregation functions (like + CASE or DATE_EXTRACT) or mathemetical operators in the STATS + command. YOU MUST USE an EVAL command before the STATS command + to append the new calculated column. Some examples: - \`| STATS count = COUNT(emp_no) BY languages\` - \`| STATS salary = AVG(salary)\` + - \`| EVAL monthly_salary = salary / 12 | STATS avg_monthly_salary = AVG(monthly_salary) BY emp_country\` ### LIMIT @@ -432,9 +455,10 @@ export function registerEsqlFunction({ ### TO_BOOLEAN, TO_DATETIME, TO_DOUBLE, TO_INTEGER, TO_IP, TO_LONG, TO_RADIANS, TO_STRING,TO_UNSIGNED_LONG, TO_VERSION - Converts a column to another type. Supported types are: . Some examples: + Converts a column to another type. Some examples: - \`| EVAL version = TO_VERSION("1.2.3")\` - \`| EVAL as_bool = TO_BOOLEAN(my_boolean_string)\` + - \`| EVAL percent = TO_DOUBLE(part) / TO_DOUBLE(total)\` ### TRIM @@ -455,10 +479,9 @@ export function registerEsqlFunction({ ### COUNT \`COUNT\` counts the number of field values. It requires a single - argument, and does not support wildcards. Important: COUNT() and - COUNT(*) are NOT supported. One single argument is required. If - you don't have a field name, use whatever field you have, rather - than displaying an invalid query. + argument, and does not support wildcards. One single argument is + required. If you don't have a field name, use whatever field you have, + rather than displaying an invalid query. Some examples: @@ -505,6 +528,7 @@ export function registerEsqlFunction({ } else { next = content; } + return { ...message, message: { diff --git a/x-pack/plugins/observability_ai_assistant/public/functions/index.ts b/x-pack/plugins/observability_ai_assistant/public/functions/index.ts index f50686f09cefc..97c311cfac069 100644 --- a/x-pack/plugins/observability_ai_assistant/public/functions/index.ts +++ b/x-pack/plugins/observability_ai_assistant/public/functions/index.ts @@ -55,17 +55,18 @@ export async function registerFunctions({ If multiple functions are suitable, use the most specific and easy one. E.g., when the user asks to visualise APM data, use the APM functions (if available) rather than Lens. - If a function call fails, do not execute it again with the same input. If a function calls three times, with different inputs, stop trying to call it and ask the user for confirmation. + If a function call fails, DO NOT UNDER ANY CIRCUMSTANCES execute it again. Ask the user for guidance and offer them options. Note that ES|QL (the Elasticsearch query language, which is NOT Elasticsearch SQL, but a new piped language) is the preferred query language. - If the user asks about a query, or ES|QL, always call the "esql" function. Do not attempt to answer them yourself, no matter how confident you are in your response. Even if the "recall" function was used before that, follow it up with the "esql" function.` + If the user asks about a query, or ES|QL, always call the "esql" function. DO NOT UNDER ANY CIRCUMSTANCES generate ES|QL queries yourself. Even if the "recall" function was used before that, follow it up with the "esql" function.` ); if (isReady) { description += `You can use the "summarize" functions to store new information you have learned in a knowledge database. Once you have established that you did not know the answer to a question, and the user gave you this information, it's important that you create a summarisation of what you have learned and store it in the knowledge database. Don't create a new summarization if you see a similar summarization in the conversation, instead, update the existing one by re-using its ID. - Additionally, you can use the "recall" function to retrieve relevant information from the knowledge database.`; + Additionally, you can use the "recall" function to retrieve relevant information from the knowledge database. + `; description += `Here are principles you MUST adhere to, in order: - DO NOT make any assumptions about where and how users have stored their data. ALWAYS first call get_dataset_info function with empty string to get information about available indices. Once you know about available indices you MUST use this function again to get a list of available fields for specific index. If user provides an index name make sure its a valid index first before using it to retrieve the field list by calling this function with an empty string! From 6327ab307505deb87d2fc05620b4407be8f19f45 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Tue, 31 Oct 2023 10:10:10 +0100 Subject: [PATCH 41/45] [licensing] add license fetcher cache (reloaded) (#170207) ## Summary https://github.com/elastic/kibana/pull/170006 was reverted because of significant increases of run duration on some of our FTR test suites, apparently because of the throttling that was added... This PR reopens https://github.com/elastic/kibana/pull/170006 without the throttling --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../licensing/server/license_fetcher.test.ts | 172 ++++++++++++++++++ .../licensing/server/license_fetcher.ts | 133 ++++++++++++++ .../licensing/server/licensing_config.ts | 12 +- .../plugins/licensing/server/plugin.test.ts | 139 +++++--------- x-pack/plugins/licensing/server/plugin.ts | 120 ++---------- x-pack/plugins/licensing/server/types.ts | 2 + x-pack/plugins/licensing/tsconfig.json | 3 +- 7 files changed, 376 insertions(+), 205 deletions(-) create mode 100644 x-pack/plugins/licensing/server/license_fetcher.test.ts create mode 100644 x-pack/plugins/licensing/server/license_fetcher.ts diff --git a/x-pack/plugins/licensing/server/license_fetcher.test.ts b/x-pack/plugins/licensing/server/license_fetcher.test.ts new file mode 100644 index 0000000000000..efd9b001fa0ff --- /dev/null +++ b/x-pack/plugins/licensing/server/license_fetcher.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { getLicenseFetcher } from './license_fetcher'; +import { loggerMock, type MockedLogger } from '@kbn/logging-mocks'; +import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; + +type EsLicense = estypes.XpackInfoMinimalLicenseInformation; + +const delay = (ms: number) => new Promise((res) => setTimeout(res, ms)); + +function buildRawLicense(options: Partial = {}): EsLicense { + return { + uid: 'uid-000000001234', + status: 'active', + type: 'basic', + mode: 'basic', + expiry_date_in_millis: 1000, + ...options, + }; +} + +describe('LicenseFetcher', () => { + let logger: MockedLogger; + let clusterClient: ReturnType; + + beforeEach(() => { + logger = loggerMock.create(); + clusterClient = elasticsearchServiceMock.createClusterClient(); + }); + + it('returns the license for successful calls', async () => { + clusterClient.asInternalUser.xpack.info.mockResponse({ + license: buildRawLicense({ + uid: 'license-1', + }), + features: {}, + } as any); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 50_000, + }); + + const license = await fetcher(); + expect(license.uid).toEqual('license-1'); + }); + + it('returns the latest license for successful calls', async () => { + clusterClient.asInternalUser.xpack.info + .mockResponseOnce({ + license: buildRawLicense({ + uid: 'license-1', + }), + features: {}, + } as any) + .mockResponseOnce({ + license: buildRawLicense({ + uid: 'license-2', + }), + features: {}, + } as any); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 50_000, + }); + + let license = await fetcher(); + expect(license.uid).toEqual('license-1'); + + license = await fetcher(); + expect(license.uid).toEqual('license-2'); + }); + + it('returns an error license in case of error', async () => { + clusterClient.asInternalUser.xpack.info.mockResponseImplementation(() => { + throw new Error('woups'); + }); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 50_000, + }); + + const license = await fetcher(); + expect(license.error).toEqual('woups'); + }); + + it('returns a license successfully fetched after an error', async () => { + clusterClient.asInternalUser.xpack.info + .mockResponseImplementationOnce(() => { + throw new Error('woups'); + }) + .mockResponseOnce({ + license: buildRawLicense({ + uid: 'license-1', + }), + features: {}, + } as any); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 50_000, + }); + + let license = await fetcher(); + expect(license.error).toEqual('woups'); + license = await fetcher(); + expect(license.uid).toEqual('license-1'); + }); + + it('returns the latest fetched license after an error within the cache duration period', async () => { + clusterClient.asInternalUser.xpack.info + .mockResponseOnce({ + license: buildRawLicense({ + uid: 'license-1', + }), + features: {}, + } as any) + .mockResponseImplementationOnce(() => { + throw new Error('woups'); + }); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 50_000, + }); + + let license = await fetcher(); + expect(license.uid).toEqual('license-1'); + license = await fetcher(); + expect(license.uid).toEqual('license-1'); + }); + + it('returns an error license after an error exceeding the cache duration period', async () => { + clusterClient.asInternalUser.xpack.info + .mockResponseOnce({ + license: buildRawLicense({ + uid: 'license-1', + }), + features: {}, + } as any) + .mockResponseImplementationOnce(() => { + throw new Error('woups'); + }); + + const fetcher = getLicenseFetcher({ + logger, + clusterClient, + cacheDurationMs: 1, + }); + + let license = await fetcher(); + expect(license.uid).toEqual('license-1'); + + await delay(50); + + license = await fetcher(); + expect(license.error).toEqual('woups'); + }); +}); diff --git a/x-pack/plugins/licensing/server/license_fetcher.ts b/x-pack/plugins/licensing/server/license_fetcher.ts new file mode 100644 index 0000000000000..43d9c204bbf66 --- /dev/null +++ b/x-pack/plugins/licensing/server/license_fetcher.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { createHash } from 'crypto'; +import stringify from 'json-stable-stringify'; +import type { MaybePromise } from '@kbn/utility-types'; +import { isPromise } from '@kbn/std'; +import type { IClusterClient, Logger } from '@kbn/core/server'; +import type { + ILicense, + PublicLicense, + PublicFeatures, + LicenseType, + LicenseStatus, +} from '../common/types'; +import { License } from '../common/license'; +import type { ElasticsearchError, LicenseFetcher } from './types'; + +export const getLicenseFetcher = ({ + clusterClient, + logger, + cacheDurationMs, +}: { + clusterClient: MaybePromise; + logger: Logger; + cacheDurationMs: number; +}): LicenseFetcher => { + let currentLicense: ILicense | undefined; + let lastSuccessfulFetchTime: number | undefined; + + return async () => { + const client = isPromise(clusterClient) ? await clusterClient : clusterClient; + try { + const response = await client.asInternalUser.xpack.info(); + const normalizedLicense = + response.license && response.license.type !== 'missing' + ? normalizeServerLicense(response.license) + : undefined; + const normalizedFeatures = response.features + ? normalizeFeatures(response.features) + : undefined; + + const signature = sign({ + license: normalizedLicense, + features: normalizedFeatures, + error: '', + }); + + currentLicense = new License({ + license: normalizedLicense, + features: normalizedFeatures, + signature, + }); + lastSuccessfulFetchTime = Date.now(); + + return currentLicense; + } catch (error) { + logger.warn( + `License information could not be obtained from Elasticsearch due to ${error} error` + ); + + if (lastSuccessfulFetchTime && lastSuccessfulFetchTime + cacheDurationMs > Date.now()) { + return currentLicense!; + } else { + const errorMessage = getErrorMessage(error); + const signature = sign({ error: errorMessage }); + + return new License({ + error: getErrorMessage(error), + signature, + }); + } + } + }; +}; + +function normalizeServerLicense( + license: estypes.XpackInfoMinimalLicenseInformation +): PublicLicense { + return { + uid: license.uid, + type: license.type as LicenseType, + mode: license.mode as LicenseType, + expiryDateInMillis: + typeof license.expiry_date_in_millis === 'string' + ? parseInt(license.expiry_date_in_millis, 10) + : license.expiry_date_in_millis, + status: license.status as LicenseStatus, + }; +} + +function normalizeFeatures(rawFeatures: estypes.XpackInfoFeatures) { + const features: PublicFeatures = {}; + for (const [name, feature] of Object.entries(rawFeatures)) { + features[name] = { + isAvailable: feature.available, + isEnabled: feature.enabled, + }; + } + return features; +} + +function sign({ + license, + features, + error, +}: { + license?: PublicLicense; + features?: PublicFeatures; + error?: string; +}) { + return createHash('sha256') + .update( + stringify({ + license, + features, + error, + }) + ) + .digest('hex'); +} + +function getErrorMessage(error: ElasticsearchError): string { + if (error.status === 400) { + return 'X-Pack plugin is not installed on the Elasticsearch cluster.'; + } + return error.message; +} diff --git a/x-pack/plugins/licensing/server/licensing_config.ts b/x-pack/plugins/licensing/server/licensing_config.ts index 459c69b650dbb..66899602e04cb 100644 --- a/x-pack/plugins/licensing/server/licensing_config.ts +++ b/x-pack/plugins/licensing/server/licensing_config.ts @@ -10,12 +10,18 @@ import { PluginConfigDescriptor } from '@kbn/core/server'; const configSchema = schema.object({ api_polling_frequency: schema.duration({ defaultValue: '30s' }), + license_cache_duration: schema.duration({ + defaultValue: '300s', + validate: (value) => { + if (value.asMinutes() > 15) { + return 'license cache duration must be shorter than 15 minutes'; + } + }, + }), }); export type LicenseConfigType = TypeOf; export const config: PluginConfigDescriptor = { - schema: schema.object({ - api_polling_frequency: schema.duration({ defaultValue: '30s' }), - }), + schema: configSchema, }; diff --git a/x-pack/plugins/licensing/server/plugin.test.ts b/x-pack/plugins/licensing/server/plugin.test.ts index b087b6f3f03fa..129dc6aee66da 100644 --- a/x-pack/plugins/licensing/server/plugin.test.ts +++ b/x-pack/plugins/licensing/server/plugin.test.ts @@ -56,22 +56,23 @@ describe('licensing plugin', () => { return client; }; - describe('#start', () => { - describe('#license$', () => { - let plugin: LicensingPlugin; - let pluginInitContextMock: ReturnType; + let plugin: LicensingPlugin; + let pluginInitContextMock: ReturnType; - beforeEach(() => { - pluginInitContextMock = coreMock.createPluginInitializerContext({ - api_polling_frequency: moment.duration(100), - }); - plugin = new LicensingPlugin(pluginInitContextMock); - }); + beforeEach(() => { + pluginInitContextMock = coreMock.createPluginInitializerContext({ + api_polling_frequency: moment.duration(100), + license_cache_duration: moment.duration(1000), + }); + plugin = new LicensingPlugin(pluginInitContextMock); + }); - afterEach(async () => { - await plugin.stop(); - }); + afterEach(async () => { + await plugin?.stop(); + }); + describe('#start', () => { + describe('#license$', () => { it('returns license', async () => { const esClient = createEsClient({ license: buildRawLicense(), @@ -79,8 +80,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); const license = await firstValueFrom(license$); expect(license.isAvailable).toBe(true); }); @@ -92,8 +93,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); await firstValueFrom(license$); expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); @@ -111,8 +112,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); const [first, second, third] = await firstValueFrom(license$.pipe(take(3), toArray())); expect(first.type).toBe('basic'); @@ -125,8 +126,8 @@ describe('licensing plugin', () => { esClient.asInternalUser.xpack.info.mockRejectedValue(new Error('test')); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); const license = await firstValueFrom(license$); expect(license.isAvailable).toBe(false); @@ -140,8 +141,8 @@ describe('licensing plugin', () => { esClient.asInternalUser.xpack.info.mockRejectedValue(error); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); const license = await firstValueFrom(license$); expect(license.isAvailable).toBe(false); @@ -169,8 +170,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); const [first, second, third] = await firstValueFrom(license$.pipe(take(3), toArray())); @@ -186,8 +187,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - await plugin.start(); + plugin.setup(coreSetup); + plugin.start(); await flushPromises(); @@ -201,8 +202,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - await plugin.start(); + plugin.setup(coreSetup); + plugin.start(); await flushPromises(); @@ -229,8 +230,8 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); const [first, second, third] = await firstValueFrom(license$.pipe(take(3), toArray())); expect(first.signature === third.signature).toBe(true); @@ -239,16 +240,12 @@ describe('licensing plugin', () => { }); describe('#refresh', () => { - let plugin: LicensingPlugin; - afterEach(async () => { - await plugin.stop(); - }); - it('forces refresh immediately', async () => { plugin = new LicensingPlugin( coreMock.createPluginInitializerContext({ // disable polling mechanism api_polling_frequency: moment.duration(50000), + license_cache_duration: moment.duration(1000), }) ); const esClient = createEsClient({ @@ -257,31 +254,26 @@ describe('licensing plugin', () => { }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { refresh, license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { refresh, license$ } = plugin.start(); expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(0); - await license$.pipe(take(1)).toPromise(); + await firstValueFrom(license$); expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(1); - refresh(); + await refresh(); await flushPromises(); expect(esClient.asInternalUser.xpack.info).toHaveBeenCalledTimes(2); }); }); describe('#createLicensePoller', () => { - let plugin: LicensingPlugin; - - afterEach(async () => { - await plugin.stop(); - }); - it(`creates a poller fetching license from passed 'clusterClient' every 'api_polling_frequency' ms`, async () => { plugin = new LicensingPlugin( coreMock.createPluginInitializerContext({ api_polling_frequency: moment.duration(50000), + license_cache_duration: moment.duration(1000), }) ); @@ -290,8 +282,8 @@ describe('licensing plugin', () => { features: {}, }); const coreSetup = createCoreSetupWith(esClient); - await plugin.setup(coreSetup); - const { createLicensePoller, license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { createLicensePoller, license$ } = plugin.start(); const customClient = createEsClient({ license: buildRawLicense({ type: 'gold' }), @@ -313,19 +305,13 @@ describe('licensing plugin', () => { expect(customLicense.isAvailable).toBe(true); expect(customLicense.type).toBe('gold'); - expect(await license$.pipe(take(1)).toPromise()).not.toBe(customLicense); + expect(await firstValueFrom(license$)).not.toBe(customLicense); }); it('creates a poller with a manual refresh control', async () => { - plugin = new LicensingPlugin( - coreMock.createPluginInitializerContext({ - api_polling_frequency: moment.duration(100), - }) - ); - const coreSetup = coreMock.createSetup(); - await plugin.setup(coreSetup); - const { createLicensePoller } = await plugin.start(); + plugin.setup(coreSetup); + const { createLicensePoller } = plugin.start(); const customClient = createEsClient({ license: buildRawLicense({ type: 'gold' }), @@ -344,24 +330,10 @@ describe('licensing plugin', () => { }); describe('extends core contexts', () => { - let plugin: LicensingPlugin; - - beforeEach(() => { - plugin = new LicensingPlugin( - coreMock.createPluginInitializerContext({ - api_polling_frequency: moment.duration(100), - }) - ); - }); - - afterEach(async () => { - await plugin.stop(); - }); - it('provides a licensing context to http routes', async () => { const coreSetup = coreMock.createSetup(); - await plugin.setup(coreSetup); + plugin.setup(coreSetup); expect(coreSetup.http.registerRouteHandlerContext.mock.calls).toMatchInlineSnapshot(` Array [ @@ -375,22 +347,10 @@ describe('licensing plugin', () => { }); describe('registers on pre-response interceptor', () => { - let plugin: LicensingPlugin; - - beforeEach(() => { - plugin = new LicensingPlugin( - coreMock.createPluginInitializerContext({ api_polling_frequency: moment.duration(100) }) - ); - }); - - afterEach(async () => { - await plugin.stop(); - }); - it('once', async () => { const coreSetup = coreMock.createSetup(); - await plugin.setup(coreSetup); + plugin.setup(coreSetup); expect(coreSetup.http.registerOnPreResponse).toHaveBeenCalledTimes(1); }); @@ -399,14 +359,9 @@ describe('licensing plugin', () => { describe('#stop', () => { it('stops polling', async () => { - const plugin = new LicensingPlugin( - coreMock.createPluginInitializerContext({ - api_polling_frequency: moment.duration(100), - }) - ); const coreSetup = coreMock.createSetup(); - await plugin.setup(coreSetup); - const { license$ } = await plugin.start(); + plugin.setup(coreSetup); + const { license$ } = plugin.start(); let completed = false; license$.subscribe({ complete: () => (completed = true) }); diff --git a/x-pack/plugins/licensing/server/plugin.ts b/x-pack/plugins/licensing/server/plugin.ts index 0d21cd689bf46..b3ac583e7c81e 100644 --- a/x-pack/plugins/licensing/server/plugin.ts +++ b/x-pack/plugins/licensing/server/plugin.ts @@ -8,12 +8,7 @@ import type { Observable, Subject, Subscription } from 'rxjs'; import { ReplaySubject, timer } from 'rxjs'; import moment from 'moment'; -import { createHash } from 'crypto'; -import stringify from 'json-stable-stringify'; - -import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { MaybePromise } from '@kbn/utility-types'; -import { isPromise } from '@kbn/std'; import type { CoreSetup, Logger, @@ -21,73 +16,17 @@ import type { PluginInitializerContext, IClusterClient, } from '@kbn/core/server'; - import { registerAnalyticsContextProvider } from '../common/register_analytics_context_provider'; -import type { - ILicense, - PublicLicense, - PublicFeatures, - LicenseType, - LicenseStatus, -} from '../common/types'; +import type { ILicense } from '../common/types'; import type { LicensingPluginSetup, LicensingPluginStart } from './types'; -import { License } from '../common/license'; import { createLicenseUpdate } from '../common/license_update'; - -import type { ElasticsearchError } from './types'; import { registerRoutes } from './routes'; import { FeatureUsageService } from './services'; - import type { LicenseConfigType } from './licensing_config'; import { createRouteHandlerContext } from './licensing_route_handler_context'; import { createOnPreResponseHandler } from './on_pre_response_handler'; import { getPluginStatus$ } from './plugin_status'; - -function normalizeServerLicense( - license: estypes.XpackInfoMinimalLicenseInformation -): PublicLicense { - return { - uid: license.uid, - type: license.type as LicenseType, - mode: license.mode as LicenseType, - expiryDateInMillis: - typeof license.expiry_date_in_millis === 'string' - ? parseInt(license.expiry_date_in_millis, 10) - : license.expiry_date_in_millis, - status: license.status as LicenseStatus, - }; -} - -function normalizeFeatures(rawFeatures: estypes.XpackInfoFeatures) { - const features: PublicFeatures = {}; - for (const [name, feature] of Object.entries(rawFeatures)) { - features[name] = { - isAvailable: feature.available, - isEnabled: feature.enabled, - }; - } - return features; -} - -function sign({ - license, - features, - error, -}: { - license?: PublicLicense; - features?: PublicFeatures; - error?: string; -}) { - return createHash('sha256') - .update( - stringify({ - license, - features, - error, - }) - ) - .digest('hex'); -} +import { getLicenseFetcher } from './license_fetcher'; /** * @public @@ -153,9 +92,16 @@ export class LicensingPlugin implements Plugin - this.fetchLicense(clusterClient) + const { license$, refreshManually } = createLicenseUpdate( + intervalRefresh$, + this.stop$, + licenseFetcher ); this.loggingSubscription = license$.subscribe((license) => @@ -178,50 +124,6 @@ export class LicensingPlugin implements Plugin): Promise => { - const client = isPromise(clusterClient) ? await clusterClient : clusterClient; - try { - const response = await client.asInternalUser.xpack.info(); - const normalizedLicense = - response.license && response.license.type !== 'missing' - ? normalizeServerLicense(response.license) - : undefined; - const normalizedFeatures = response.features - ? normalizeFeatures(response.features) - : undefined; - - const signature = sign({ - license: normalizedLicense, - features: normalizedFeatures, - error: '', - }); - - return new License({ - license: normalizedLicense, - features: normalizedFeatures, - signature, - }); - } catch (error) { - this.logger.warn( - `License information could not be obtained from Elasticsearch due to ${error} error` - ); - const errorMessage = this.getErrorMessage(error); - const signature = sign({ error: errorMessage }); - - return new License({ - error: this.getErrorMessage(error), - signature, - }); - } - }; - - private getErrorMessage(error: ElasticsearchError): string { - if (error.status === 400) { - return 'X-Pack plugin is not installed on the Elasticsearch cluster.'; - } - return error.message; - } - public start() { if (!this.refresh || !this.license$) { throw new Error('Setup has not been completed'); diff --git a/x-pack/plugins/licensing/server/types.ts b/x-pack/plugins/licensing/server/types.ts index 83b39cb663715..fcccdecb66c00 100644 --- a/x-pack/plugins/licensing/server/types.ts +++ b/x-pack/plugins/licensing/server/types.ts @@ -14,6 +14,8 @@ export interface ElasticsearchError extends Error { status?: number; } +export type LicenseFetcher = () => Promise; + /** * Result from remote request fetching raw feature set. * @internal diff --git a/x-pack/plugins/licensing/tsconfig.json b/x-pack/plugins/licensing/tsconfig.json index 323f77b3b0ebc..1deb735f99466 100644 --- a/x-pack/plugins/licensing/tsconfig.json +++ b/x-pack/plugins/licensing/tsconfig.json @@ -15,7 +15,8 @@ "@kbn/i18n", "@kbn/analytics-client", "@kbn/subscription-tracking", - "@kbn/core-analytics-browser" + "@kbn/core-analytics-browser", + "@kbn/logging-mocks" ], "exclude": ["target/**/*"] } From 0f3382f9360420360638c0ec9e7fa4eda11fd81f Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Tue, 31 Oct 2023 10:29:33 +0100 Subject: [PATCH 42/45] [Security Solution] Removes `esArchiverResetKibana` from our Cypress tests (#169563) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../detection_alerts/alert_tags.cy.ts | 7 +- ....ts => install_update_authorization.cy.ts} | 0 ...ts => install_update_error_handling.cy.ts} | 0 .../prebuilt_rules/install_via_fleet.cy.ts | 127 ++++++++ ...ules_management.cy.ts => management.cy.ts} | 0 ...otifications.cy.ts => notifications.cy.ts} | 0 ...built_rules_install_update_workflows.cy.ts | 291 ------------------ .../prebuilt_rules/update_workflow.cy.ts | 93 ++++++ .../upgrade_of_prebuilt_rules.cy.ts | 115 +++++++ .../custom_query_rule_data_view.cy.ts | 14 +- .../bulk_actions/bulk_duplicate_rules.cy.ts | 1 - .../bulk_actions/bulk_edit_rules.cy.ts | 10 +- .../bulk_edit_rules_actions.cy.ts | 1 - .../bulk_edit_rules_data_view.cy.ts | 19 +- .../rules_table/rules_table_filtering.cy.ts | 1 - .../sourcerer/sourcerer.cy.ts | 1 - .../sourcerer/sourcerer_permissions.cy.ts | 1 - .../endpoint_exceptions.cy.ts | 1 - .../auto_populate_with_alert_data.cy.ts | 1 - .../closing_all_matching_alerts.cy.ts | 1 - .../exceptions/entry/flyout_validation.cy.ts | 1 - .../entry/multiple_conditions.cy.ts | 1 - .../add_edit_endpoint_exception.cy.ts | 14 +- .../add_edit_exception.cy.ts | 4 +- .../add_edit_exception_data_view.cy.ts | 1 - .../list_detail_page/list_details.cy.ts | 1 - .../manage_exceptions.cy.ts | 9 +- .../duplicate_lists.cy.ts | 5 +- .../filter_table.cy.ts | 1 - .../import_lists.cy.ts | 3 - .../manage_lists.cy.ts | 1 - .../read_only.cy.ts | 4 - .../cypress/support/es_archiver.ts | 1 - .../cypress/tasks/api_calls/exceptions.ts | 42 ++- .../cypress/tasks/common.ts | 42 +++ .../cypress/tasks/exceptions.ts | 13 + 36 files changed, 459 insertions(+), 368 deletions(-) rename x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/{prebuilt_rules_install_update_authorization.cy.ts => install_update_authorization.cy.ts} (100%) rename x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/{prebuilt_rules_install_update_error_handling.cy.ts => install_update_error_handling.cy.ts} (100%) create mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_via_fleet.cy.ts rename x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/{prebuilt_rules_management.cy.ts => management.cy.ts} (100%) rename x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/{prebuilt_rules_notifications.cy.ts => notifications.cy.ts} (100%) delete mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_workflows.cy.ts create mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/update_workflow.cy.ts create mode 100644 x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/upgrade_of_prebuilt_rules.cy.ts diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alert_tags.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alert_tags.cy.ts index c7b6b16a45c2f..ee3955576a272 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alert_tags.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_alerts/alert_tags.cy.ts @@ -13,7 +13,7 @@ import { updateAlertTags, } from '../../../tasks/alerts'; import { createRule } from '../../../tasks/api_calls/rules'; -import { cleanKibana, deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules } from '../../../tasks/common'; import { login } from '../../../tasks/login'; import { visitWithTimeRange } from '../../../tasks/navigation'; import { ALERTS_URL } from '../../../urls/navigation'; @@ -26,11 +26,6 @@ import { } from '../../../screens/alerts'; describe('Alert tagging', { tags: ['@ess', '@serverless'] }, () => { - before(() => { - cleanKibana(); - cy.task('esArchiverResetKibana'); - }); - beforeEach(() => { login(); deleteAlertsAndRules(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_authorization.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_update_authorization.cy.ts similarity index 100% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_authorization.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_update_authorization.cy.ts diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_error_handling.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_update_error_handling.cy.ts similarity index 100% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_error_handling.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_update_error_handling.cy.ts diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_via_fleet.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_via_fleet.cy.ts new file mode 100644 index 0000000000000..0cdae82df79c9 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/install_via_fleet.cy.ts @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { BulkInstallPackageInfo } from '@kbn/fleet-plugin/common'; +import type { Rule } from '@kbn/security-solution-plugin/public/detection_engine/rule_management/logic/types'; + +import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common'; +import { INSTALL_ALL_RULES_BUTTON, TOASTER } from '../../../screens/alerts_detection_rules'; +import { getRuleAssets } from '../../../tasks/api_calls/prebuilt_rules'; +import { login } from '../../../tasks/login'; +import { addElasticRulesButtonClick } from '../../../tasks/prebuilt_rules'; +import { visitRulesManagementTable } from '../../../tasks/rules_management'; + +describe( + 'Detection rules, Prebuilt Rules Installation and Update workflow', + { tags: ['@ess', '@serverless'] }, + () => { + describe('Installation of prebuilt rules package via Fleet', () => { + beforeEach(() => { + login(); + resetRulesTableState(); + deleteAlertsAndRules(); + cy.intercept('POST', '/api/fleet/epm/packages/_bulk*').as('installPackageBulk'); + cy.intercept('POST', '/api/fleet/epm/packages/security_detection_engine/*').as( + 'installPackage' + ); + cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform').as( + 'installPrebuiltRules' + ); + visitRulesManagementTable(); + }); + + it('should install package from Fleet in the background', () => { + /* Assert that the package in installed from Fleet */ + cy.wait('@installPackageBulk', { + timeout: 60000, + }).then(({ response: bulkResponse }) => { + cy.wrap(bulkResponse?.statusCode).should('eql', 200); + + const packages = bulkResponse?.body.items.map( + ({ name, result }: BulkInstallPackageInfo) => ({ + name, + }) + ); + + const packagesBulkInstalled = packages.map(({ name }: { name: string }) => name); + + // Under normal flow the package is installed via the Fleet bulk install API. + // However, for testing purposes the package can be installed via the Fleet individual install API, + // so we need to intercept and wait for that request as well. + if (!packagesBulkInstalled.includes('security_detection_engine')) { + // Should happen only during testing when the `xpack.securitySolution.prebuiltRulesPackageVersion` flag is set + cy.wait('@installPackage').then(({ response }) => { + cy.wrap(response?.statusCode).should('eql', 200); + cy.wrap(response?.body) + .should('have.property', 'items') + .should('have.length.greaterThan', 0); + }); + } else { + // Normal flow, install via the Fleet bulk install API + expect(packages.length).to.have.greaterThan(0); + // At least one of the packages installed should be the security_detection_engine package + expect(packages).to.satisfy((pckgs: BulkInstallPackageInfo[]) => + pckgs.some((pkg) => pkg.name === 'security_detection_engine') + ); + } + }); + }); + + it('should install rules from the Fleet package when user clicks on CTA', () => { + interface Response { + body: { + hits: { + hits: Array<{ _source: { ['security-rule']: Rule } }>; + }; + }; + } + const getRulesAndAssertNumberInstalled = () => { + getRuleAssets().then((response) => { + const ruleIds = (response as Response).body.hits.hits.map( + (hit) => hit._source['security-rule'].rule_id + ); + + const numberOfRulesToInstall = new Set(ruleIds).size; + addElasticRulesButtonClick(); + + cy.get(INSTALL_ALL_RULES_BUTTON).should('be.enabled').click(); + cy.wait('@installPrebuiltRules', { + timeout: 60000, + }).then(() => { + cy.get(TOASTER) + .should('be.visible') + .should( + 'have.text', + // i18n uses en-US format for numbers, which uses a comma as a thousands separator + `${numberOfRulesToInstall.toLocaleString('en-US')} rules installed successfully.` + ); + }); + }); + }; + /* Retrieve how many rules were installed from the Fleet package */ + /* See comments in test above for more details */ + cy.wait('@installPackageBulk', { + timeout: 60000, + }).then(({ response: bulkResponse }) => { + cy.wrap(bulkResponse?.statusCode).should('eql', 200); + + const packagesBulkInstalled = bulkResponse?.body.items.map( + ({ name }: { name: string }) => name + ); + + if (!packagesBulkInstalled.includes('security_detection_engine')) { + cy.wait('@installPackage').then(() => { + getRulesAndAssertNumberInstalled(); + }); + } else { + getRulesAndAssertNumberInstalled(); + } + }); + }); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_management.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/management.cy.ts similarity index 100% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_management.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/management.cy.ts diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_notifications.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/notifications.cy.ts similarity index 100% rename from x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_notifications.cy.ts rename to x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/notifications.cy.ts diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_workflows.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_workflows.cy.ts deleted file mode 100644 index 97bf43363851b..0000000000000 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/prebuilt_rules_install_update_workflows.cy.ts +++ /dev/null @@ -1,291 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { BulkInstallPackageInfo } from '@kbn/fleet-plugin/common'; -import type { Rule } from '@kbn/security-solution-plugin/public/detection_engine/rule_management/logic/types'; - -import { createRuleAssetSavedObject } from '../../../helpers/rules'; -import { - getInstallSingleRuleButtonByRuleId, - getUpgradeSingleRuleButtonByRuleId, - GO_BACK_TO_RULES_TABLE_BUTTON, - INSTALL_ALL_RULES_BUTTON, - INSTALL_SELECTED_RULES_BUTTON, - NO_RULES_AVAILABLE_FOR_INSTALL_MESSAGE, - NO_RULES_AVAILABLE_FOR_UPGRADE_MESSAGE, - RULES_UPDATES_TAB, - RULE_CHECKBOX, - SELECT_ALL_RULES_ON_PAGE_CHECKBOX, - TOASTER, - UPGRADE_ALL_RULES_BUTTON, - UPGRADE_SELECTED_RULES_BUTTON, -} from '../../../screens/alerts_detection_rules'; -import { selectRulesByName } from '../../../tasks/alerts_detection_rules'; -import { - getRuleAssets, - installPrebuiltRuleAssets, - createAndInstallMockedPrebuiltRules, -} from '../../../tasks/api_calls/prebuilt_rules'; -import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common'; -import { login } from '../../../tasks/login'; -import { - addElasticRulesButtonClick, - ruleUpdatesTabClick, - assertInstallationSuccess, - assertInstallationRequestIsComplete, - assertUpgradeRequestIsComplete, - assertUpgradeSuccess, -} from '../../../tasks/prebuilt_rules'; -import { visitRulesManagementTable } from '../../../tasks/rules_management'; - -describe( - 'Detection rules, Prebuilt Rules Installation and Update workflow', - { tags: ['@ess', '@serverless'] }, - () => { - beforeEach(() => { - login(); - resetRulesTableState(); - deleteAlertsAndRules(); - cy.task('esArchiverResetKibana'); - - visitRulesManagementTable(); - }); - - describe('Installation of prebuilt rules package via Fleet', () => { - beforeEach(() => { - cy.intercept('POST', '/api/fleet/epm/packages/_bulk*').as('installPackageBulk'); - cy.intercept('POST', '/api/fleet/epm/packages/security_detection_engine/*').as( - 'installPackage' - ); - cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform').as( - 'installPrebuiltRules' - ); - }); - - it('should install package from Fleet in the background', () => { - /* Assert that the package in installed from Fleet */ - cy.wait('@installPackageBulk', { - timeout: 60000, - }).then(({ response: bulkResponse }) => { - cy.wrap(bulkResponse?.statusCode).should('eql', 200); - - const packages = bulkResponse?.body.items.map( - ({ name, result }: BulkInstallPackageInfo) => ({ - name, - }) - ); - - const packagesBulkInstalled = packages.map(({ name }: { name: string }) => name); - - // Under normal flow the package is installed via the Fleet bulk install API. - // However, for testing purposes the package can be installed via the Fleet individual install API, - // so we need to intercept and wait for that request as well. - if (!packagesBulkInstalled.includes('security_detection_engine')) { - // Should happen only during testing when the `xpack.securitySolution.prebuiltRulesPackageVersion` flag is set - cy.wait('@installPackage').then(({ response }) => { - cy.wrap(response?.statusCode).should('eql', 200); - cy.wrap(response?.body) - .should('have.property', 'items') - .should('have.length.greaterThan', 0); - }); - } else { - // Normal flow, install via the Fleet bulk install API - expect(packages.length).to.have.greaterThan(0); - // At least one of the packages installed should be the security_detection_engine package - expect(packages).to.satisfy((pckgs: BulkInstallPackageInfo[]) => - pckgs.some((pkg) => pkg.name === 'security_detection_engine') - ); - } - }); - }); - - it('should install rules from the Fleet package when user clicks on CTA', () => { - interface Response { - body: { - hits: { - hits: Array<{ _source: { ['security-rule']: Rule } }>; - }; - }; - } - const getRulesAndAssertNumberInstalled = () => { - getRuleAssets().then((response) => { - const ruleIds = (response as Response).body.hits.hits.map( - (hit) => hit._source['security-rule'].rule_id - ); - - const numberOfRulesToInstall = new Set(ruleIds).size; - addElasticRulesButtonClick(); - - cy.get(INSTALL_ALL_RULES_BUTTON).should('be.enabled').click(); - cy.wait('@installPrebuiltRules', { - timeout: 60000, - }).then(() => { - cy.get(TOASTER) - .should('be.visible') - .should( - 'have.text', - // i18n uses en-US format for numbers, which uses a comma as a thousands separator - `${numberOfRulesToInstall.toLocaleString('en-US')} rules installed successfully.` - ); - }); - }); - }; - /* Retrieve how many rules were installed from the Fleet package */ - /* See comments in test above for more details */ - cy.wait('@installPackageBulk', { - timeout: 60000, - }).then(({ response: bulkResponse }) => { - cy.wrap(bulkResponse?.statusCode).should('eql', 200); - - const packagesBulkInstalled = bulkResponse?.body.items.map( - ({ name }: { name: string }) => name - ); - - if (!packagesBulkInstalled.includes('security_detection_engine')) { - cy.wait('@installPackage').then(() => { - getRulesAndAssertNumberInstalled(); - }); - } else { - getRulesAndAssertNumberInstalled(); - } - }); - }); - }); - - describe('Installation of prebuilt rules', () => { - const RULE_1 = createRuleAssetSavedObject({ - name: 'Test rule 1', - rule_id: 'rule_1', - }); - const RULE_2 = createRuleAssetSavedObject({ - name: 'Test rule 2', - rule_id: 'rule_2', - }); - beforeEach(() => { - installPrebuiltRuleAssets([RULE_1, RULE_2]); - cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform').as( - 'installPrebuiltRules' - ); - addElasticRulesButtonClick(); - }); - - it('should install prebuilt rules one by one', () => { - // Attempt to install rules - cy.get(getInstallSingleRuleButtonByRuleId(RULE_1['security-rule'].rule_id)).click(); - // Wait for request to complete - assertInstallationRequestIsComplete([RULE_1]); - // Assert installation succeeded - assertInstallationSuccess([RULE_1]); - }); - - it('should install multiple selected prebuilt rules by selecting them individually', () => { - selectRulesByName([RULE_1['security-rule'].name, RULE_2['security-rule'].name]); - cy.get(INSTALL_SELECTED_RULES_BUTTON).click(); - assertInstallationRequestIsComplete([RULE_1, RULE_2]); - assertInstallationSuccess([RULE_1, RULE_2]); - }); - - it('should install multiple selected prebuilt rules by selecting all in page', () => { - cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); - cy.get(INSTALL_SELECTED_RULES_BUTTON).click(); - assertInstallationRequestIsComplete([RULE_1, RULE_2]); - assertInstallationSuccess([RULE_1, RULE_2]); - }); - - it('should install all available rules at once', () => { - cy.get(INSTALL_ALL_RULES_BUTTON).click(); - assertInstallationRequestIsComplete([RULE_1, RULE_2]); - assertInstallationSuccess([RULE_1, RULE_2]); - }); - - it('should display an empty screen when all available prebuilt rules have been installed', () => { - cy.get(INSTALL_ALL_RULES_BUTTON).click(); - cy.get(TOASTER).should('be.visible').should('have.text', `2 rules installed successfully.`); - cy.get(RULE_CHECKBOX).should('not.exist'); - cy.get(NO_RULES_AVAILABLE_FOR_INSTALL_MESSAGE).should('exist'); - cy.get(GO_BACK_TO_RULES_TABLE_BUTTON).should('exist'); - }); - }); - - describe('Upgrade of prebuilt rules', () => { - const RULE_1_ID = 'rule_1'; - const RULE_2_ID = 'rule_2'; - const OUTDATED_RULE_1 = createRuleAssetSavedObject({ - name: 'Outdated rule 1', - rule_id: RULE_1_ID, - version: 1, - }); - const UPDATED_RULE_1 = createRuleAssetSavedObject({ - name: 'Updated rule 1', - rule_id: RULE_1_ID, - version: 2, - }); - const OUTDATED_RULE_2 = createRuleAssetSavedObject({ - name: 'Outdated rule 2', - rule_id: RULE_2_ID, - version: 1, - }); - const UPDATED_RULE_2 = createRuleAssetSavedObject({ - name: 'Updated rule 2', - rule_id: RULE_2_ID, - version: 2, - }); - beforeEach(() => { - cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform').as( - 'updatePrebuiltRules' - ); - /* Create a new rule and install it */ - createAndInstallMockedPrebuiltRules([OUTDATED_RULE_1, OUTDATED_RULE_2]); - /* Create a second version of the rule, making it available for update */ - installPrebuiltRuleAssets([UPDATED_RULE_1, UPDATED_RULE_2]); - - visitRulesManagementTable(); - ruleUpdatesTabClick(); - }); - - it('should upgrade prebuilt rules one by one', () => { - // Attempt to upgrade rule - cy.get( - getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_1['security-rule'].rule_id) - ).click(); - // Wait for request to complete - assertUpgradeRequestIsComplete([OUTDATED_RULE_1]); - - assertUpgradeSuccess([OUTDATED_RULE_1]); - }); - - it('should upgrade multiple selected prebuilt rules by selecting them individually', () => { - selectRulesByName([ - OUTDATED_RULE_1['security-rule'].name, - OUTDATED_RULE_2['security-rule'].name, - ]); - cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); - assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); - assertUpgradeSuccess([OUTDATED_RULE_1, OUTDATED_RULE_2]); - }); - - it('should upgrade multiple selected prebuilt rules by selecting all in page', () => { - cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); - cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); - assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); - assertUpgradeSuccess([OUTDATED_RULE_1, OUTDATED_RULE_2]); - }); - - it('should upgrade all rules with available upgrades at once', () => { - cy.get(UPGRADE_ALL_RULES_BUTTON).click(); - assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); - assertUpgradeSuccess([OUTDATED_RULE_1, OUTDATED_RULE_2]); - }); - - it('should display an empty screen when all rules with available updates have been upgraded', () => { - cy.get(UPGRADE_ALL_RULES_BUTTON).click(); - cy.get(RULES_UPDATES_TAB).should('not.exist'); - cy.get(NO_RULES_AVAILABLE_FOR_UPGRADE_MESSAGE).should('exist'); - }); - }); - } -); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/update_workflow.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/update_workflow.cy.ts new file mode 100644 index 0000000000000..443d046ae4715 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/update_workflow.cy.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common'; +import { createRuleAssetSavedObject } from '../../../helpers/rules'; +import { + getInstallSingleRuleButtonByRuleId, + GO_BACK_TO_RULES_TABLE_BUTTON, + INSTALL_ALL_RULES_BUTTON, + INSTALL_SELECTED_RULES_BUTTON, + NO_RULES_AVAILABLE_FOR_INSTALL_MESSAGE, + RULE_CHECKBOX, + SELECT_ALL_RULES_ON_PAGE_CHECKBOX, + TOASTER, +} from '../../../screens/alerts_detection_rules'; +import { selectRulesByName } from '../../../tasks/alerts_detection_rules'; +import { installPrebuiltRuleAssets } from '../../../tasks/api_calls/prebuilt_rules'; +import { login } from '../../../tasks/login'; +import { + addElasticRulesButtonClick, + assertInstallationSuccess, + assertInstallationRequestIsComplete, +} from '../../../tasks/prebuilt_rules'; +import { visitRulesManagementTable } from '../../../tasks/rules_management'; + +describe( + 'Detection rules, Prebuilt Rules Installation and Update workflow', + { tags: ['@ess', '@serverless'] }, + () => { + describe('Installation of prebuilt rules', () => { + const RULE_1 = createRuleAssetSavedObject({ + name: 'Test rule 1', + rule_id: 'rule_1', + }); + const RULE_2 = createRuleAssetSavedObject({ + name: 'Test rule 2', + rule_id: 'rule_2', + }); + beforeEach(() => { + login(); + resetRulesTableState(); + deleteAlertsAndRules(); + visitRulesManagementTable(); + installPrebuiltRuleAssets([RULE_1, RULE_2]); + cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/installation/_perform').as( + 'installPrebuiltRules' + ); + addElasticRulesButtonClick(); + }); + + it('should install prebuilt rules one by one', () => { + // Attempt to install rules + cy.get(getInstallSingleRuleButtonByRuleId(RULE_1['security-rule'].rule_id)).click(); + // Wait for request to complete + assertInstallationRequestIsComplete([RULE_1]); + // Assert installation succeeded + assertInstallationSuccess([RULE_1]); + }); + + it('should install multiple selected prebuilt rules by selecting them individually', () => { + selectRulesByName([RULE_1['security-rule'].name, RULE_2['security-rule'].name]); + cy.get(INSTALL_SELECTED_RULES_BUTTON).click(); + assertInstallationRequestIsComplete([RULE_1, RULE_2]); + assertInstallationSuccess([RULE_1, RULE_2]); + }); + + it('should install multiple selected prebuilt rules by selecting all in page', () => { + cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); + cy.get(INSTALL_SELECTED_RULES_BUTTON).click(); + assertInstallationRequestIsComplete([RULE_1, RULE_2]); + assertInstallationSuccess([RULE_1, RULE_2]); + }); + + it('should install all available rules at once', () => { + cy.get(INSTALL_ALL_RULES_BUTTON).click(); + assertInstallationRequestIsComplete([RULE_1, RULE_2]); + assertInstallationSuccess([RULE_1, RULE_2]); + }); + + it('should display an empty screen when all available prebuilt rules have been installed', () => { + cy.get(INSTALL_ALL_RULES_BUTTON).click(); + cy.get(TOASTER).should('be.visible').should('have.text', `2 rules installed successfully.`); + cy.get(RULE_CHECKBOX).should('not.exist'); + cy.get(NO_RULES_AVAILABLE_FOR_INSTALL_MESSAGE).should('exist'); + cy.get(GO_BACK_TO_RULES_TABLE_BUTTON).should('exist'); + }); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/upgrade_of_prebuilt_rules.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/upgrade_of_prebuilt_rules.cy.ts new file mode 100644 index 0000000000000..d3eab8a75fab8 --- /dev/null +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/prebuilt_rules/upgrade_of_prebuilt_rules.cy.ts @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createRuleAssetSavedObject } from '../../../helpers/rules'; +import { + getUpgradeSingleRuleButtonByRuleId, + NO_RULES_AVAILABLE_FOR_UPGRADE_MESSAGE, + RULES_UPDATES_TAB, + SELECT_ALL_RULES_ON_PAGE_CHECKBOX, + UPGRADE_ALL_RULES_BUTTON, + UPGRADE_SELECTED_RULES_BUTTON, +} from '../../../screens/alerts_detection_rules'; +import { selectRulesByName } from '../../../tasks/alerts_detection_rules'; +import { + installPrebuiltRuleAssets, + createAndInstallMockedPrebuiltRules, +} from '../../../tasks/api_calls/prebuilt_rules'; +import { resetRulesTableState, deleteAlertsAndRules } from '../../../tasks/common'; +import { login } from '../../../tasks/login'; +import { + ruleUpdatesTabClick, + assertUpgradeRequestIsComplete, + assertUpgradeSuccess, +} from '../../../tasks/prebuilt_rules'; +import { visitRulesManagementTable } from '../../../tasks/rules_management'; + +describe( + 'Detection rules, Prebuilt Rules Installation and Update workflow', + { tags: ['@ess', '@serverless'] }, + () => { + describe('Upgrade of prebuilt rules', () => { + const RULE_1_ID = 'rule_1'; + const RULE_2_ID = 'rule_2'; + const OUTDATED_RULE_1 = createRuleAssetSavedObject({ + name: 'Outdated rule 1', + rule_id: RULE_1_ID, + version: 1, + }); + const UPDATED_RULE_1 = createRuleAssetSavedObject({ + name: 'Updated rule 1', + rule_id: RULE_1_ID, + version: 2, + }); + const OUTDATED_RULE_2 = createRuleAssetSavedObject({ + name: 'Outdated rule 2', + rule_id: RULE_2_ID, + version: 1, + }); + const UPDATED_RULE_2 = createRuleAssetSavedObject({ + name: 'Updated rule 2', + rule_id: RULE_2_ID, + version: 2, + }); + beforeEach(() => { + login(); + resetRulesTableState(); + deleteAlertsAndRules(); + cy.intercept('POST', '/internal/detection_engine/prebuilt_rules/upgrade/_perform').as( + 'updatePrebuiltRules' + ); + /* Create a new rule and install it */ + createAndInstallMockedPrebuiltRules([OUTDATED_RULE_1, OUTDATED_RULE_2]); + /* Create a second version of the rule, making it available for update */ + installPrebuiltRuleAssets([UPDATED_RULE_1, UPDATED_RULE_2]); + + visitRulesManagementTable(); + ruleUpdatesTabClick(); + }); + + it('should upgrade prebuilt rules one by one', () => { + // Attempt to upgrade rule + cy.get( + getUpgradeSingleRuleButtonByRuleId(OUTDATED_RULE_1['security-rule'].rule_id) + ).click(); + // Wait for request to complete + assertUpgradeRequestIsComplete([OUTDATED_RULE_1]); + + assertUpgradeSuccess([OUTDATED_RULE_1]); + }); + + it('should upgrade multiple selected prebuilt rules by selecting them individually', () => { + selectRulesByName([ + OUTDATED_RULE_1['security-rule'].name, + OUTDATED_RULE_2['security-rule'].name, + ]); + cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); + assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertUpgradeSuccess([OUTDATED_RULE_1, OUTDATED_RULE_2]); + }); + + it('should upgrade multiple selected prebuilt rules by selecting all in page', () => { + cy.get(SELECT_ALL_RULES_ON_PAGE_CHECKBOX).click(); + cy.get(UPGRADE_SELECTED_RULES_BUTTON).click(); + assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertUpgradeSuccess([OUTDATED_RULE_1, OUTDATED_RULE_2]); + }); + + it('should upgrade all rules with available upgrades at once', () => { + cy.get(UPGRADE_ALL_RULES_BUTTON).click(); + assertUpgradeRequestIsComplete([OUTDATED_RULE_1, OUTDATED_RULE_2]); + assertUpgradeSuccess([OUTDATED_RULE_1, OUTDATED_RULE_2]); + }); + + it('should display an empty screen when all rules with available updates have been upgraded', () => { + cy.get(UPGRADE_ALL_RULES_BUTTON).click(); + cy.get(RULES_UPDATES_TAB).should('not.exist'); + cy.get(NO_RULES_AVAILABLE_FOR_UPGRADE_MESSAGE).should('exist'); + }); + }); + } +); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule_data_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule_data_view.cy.ts index 3b1a799de512b..d13a676e84253 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule_data_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_creation/custom_query_rule_data_view.cy.ts @@ -52,7 +52,7 @@ import { getRulesManagementTableRows, goToRuleDetailsOf, } from '../../../tasks/alerts_detection_rules'; -import { postDataView } from '../../../tasks/common'; +import { deleteAlertsAndRules, deleteDataView, postDataView } from '../../../tasks/common'; import { createAndEnableRule, createRuleWithoutEnabling, @@ -80,17 +80,19 @@ describe('Custom query rules', { tags: ['@ess', '@serverless'] }, () => { const expectedNumberOfRules = 1; beforeEach(() => { - /* We don't call cleanKibana method on the before hook, instead we call esArchiverReseKibana on the before each. This is because we - are creating a data view we'll use after and cleanKibana does not delete all the data views created, esArchiverReseKibana does. - We don't use esArchiverReseKibana in all the tests because is a time-consuming method and we don't need to perform an exhaustive - cleaning in all the other tests. */ - cy.task('esArchiverResetKibana'); if (rule.data_view_id != null) { postDataView(rule.data_view_id); } + deleteAlertsAndRules(); login(); }); + afterEach(() => { + if (rule.data_view_id != null) { + deleteDataView(rule.data_view_id); + } + }); + it('Creates and enables a new rule', function () { visit(CREATE_RULE_URL); fillDefineCustomRuleAndContinue(rule); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_duplicate_rules.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_duplicate_rules.cy.ts index f14ced2ea02cf..266fc09530df7 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_duplicate_rules.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_duplicate_rules.cy.ts @@ -63,7 +63,6 @@ describe('Detection rules, bulk duplicate', { tags: ['@ess', '@serverless'] }, ( // Make sure persisted rules table state is cleared resetRulesTableState(); deleteAlertsAndRules(); - cy.task('esArchiverResetKibana'); createRule( getNewRule({ name: RULE_NAME, ...defaultRuleData, rule_id: '1', enabled: false }) ).then((response) => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts index 74448f32dcf5e..9a7eb4547cee0 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules.cy.ts @@ -118,16 +118,12 @@ const defaultRuleData = { }; describe('Detection rules, bulk edit', { tags: ['@ess', '@serverless'] }, () => { - before(() => { - cleanKibana(); - }); beforeEach(() => { login(); + preventPrebuiltRulesPackageInstallation(); // Make sure prebuilt rules aren't pulled from Fleet API // Make sure persisted rules table state is cleared resetRulesTableState(); deleteAlertsAndRules(); - preventPrebuiltRulesPackageInstallation(); // Make sure prebuilt rules aren't pulled from Fleet API - cy.task('esArchiverResetKibana'); createRule(getNewRule({ name: RULE_NAME, ...defaultRuleData, rule_id: '1', enabled: false })); createRule( getEqlRule({ ...defaultRuleData, rule_id: '2', name: 'New EQL Rule', enabled: false }) @@ -682,12 +678,10 @@ describe('Detection rules, bulk edit, ES|QL rule type', { tags: ['@ess'] }, () = }); beforeEach(() => { login(); + preventPrebuiltRulesPackageInstallation(); // Make sure prebuilt rules aren't pulled from Fleet API // Make sure persisted rules table state is cleared resetRulesTableState(); deleteAlertsAndRules(); - preventPrebuiltRulesPackageInstallation(); // Make sure prebuilt rules aren't pulled from Fleet API - cy.task('esArchiverResetKibana'); - createRule( getEsqlRule({ tags: ['test-default-tag-1', 'test-default-tag-2'], diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts index 48acff836f0a4..3631a429880d5 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_actions.cy.ts @@ -80,7 +80,6 @@ describe('Detection rules, bulk edit of rule actions', { tags: ['@ess', '@server login(); deleteAlertsAndRules(); deleteConnectors(); - cy.task('esArchiverResetKibana'); createSlackConnector().then(({ body }) => { const actions: RuleActionArray = [ diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_data_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_data_view.cy.ts index 21c4558ce15e9..21adb447d2ce3 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_data_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rule_actions/bulk_actions/bulk_edit_rules_data_view.cy.ts @@ -39,7 +39,7 @@ import { login } from '../../../../../tasks/login'; import { visitRulesManagementTable } from '../../../../../tasks/rules_management'; import { createRule } from '../../../../../tasks/api_calls/rules'; -import { cleanKibana, deleteAlertsAndRules, postDataView } from '../../../../../tasks/common'; +import { deleteAlertsAndRules, deleteDataView, postDataView } from '../../../../../tasks/common'; import { getEqlRule, @@ -101,15 +101,11 @@ describe( enabled: false, }); - before(() => { - cleanKibana(); - }); - beforeEach(() => { deleteAlertsAndRules(); - cy.task('esArchiverResetKibana'); - login(); + deleteDataView(DATA_VIEW_ID); + login(); postDataView(DATA_VIEW_ID); createRule(TESTED_CUSTOM_QUERY_RULE_DATA); @@ -257,14 +253,9 @@ describe( rule_id: '2', }); - before(() => { - cleanKibana(); - }); - beforeEach(() => { login(); deleteAlertsAndRules(); - cy.task('esArchiverResetKibana'); postDataView(DATA_VIEW_ID); @@ -277,6 +268,10 @@ describe( expectManagementTableRules(['with dataview', 'no data view']); }); + afterEach(() => { + deleteDataView(DATA_VIEW_ID); + }); + it('Add index patterns to custom rules: one rule is updated, one rule is skipped', () => { selectAllRules(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_filtering.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_filtering.cy.ts index ff73d4c5775a7..31001edf7e923 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_filtering.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/rule_management/rules_table/rules_table_filtering.cy.ts @@ -39,7 +39,6 @@ describe('Rules table: filtering', { tags: ['@ess', '@serverless'] }, () => { // Make sure persisted rules table state is cleared resetRulesTableState(); deleteAlertsAndRules(); - cy.task('esArchiverResetKibana'); }); describe('Last response filter', () => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer.cy.ts index d26543bea97f7..dbf5a5975f666 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer.cy.ts @@ -34,7 +34,6 @@ const dataViews = ['auditbeat-*,fakebeat-*', 'auditbeat-*,*beat*,siem-read*,.kib describe('Sourcerer', { tags: ['@ess', '@serverless'] }, () => { before(() => { - cy.task('esArchiverResetKibana'); dataViews.forEach((dataView: string) => postDataView(dataView)); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer_permissions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer_permissions.cy.ts index fa4bf2d27061b..52b8ccee82156 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer_permissions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/sourcerer/sourcerer_permissions.cy.ts @@ -23,7 +23,6 @@ const dataViews = ['auditbeat-*,fakebeat-*', 'auditbeat-*,*beat*,siem-read*,.kib describe('Sourcerer permissions', { tags: ['@ess', '@skipInServerless'] }, () => { before(() => { - cy.task('esArchiverResetKibana'); dataViews.forEach((dataView: string) => postDataView(dataView)); createUsersAndRoles(usersToCreate, rolesToCreate); }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts index da3c0a435e1de..8914b0c98076b 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/endpoint_exceptions.cy.ts @@ -51,7 +51,6 @@ describe.skip( beforeEach(() => { cy.task('esArchiverUnload', 'endpoint'); - cy.task('esArchiverResetKibana'); login(); deleteAlertsAndRules(); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/auto_populate_with_alert_data.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/auto_populate_with_alert_data.cy.ts index c2a7d9715087d..ee10a2e702b0e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/auto_populate_with_alert_data.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/auto_populate_with_alert_data.cy.ts @@ -48,7 +48,6 @@ describe.skip( beforeEach(() => { cy.task('esArchiverUnload', 'endpoint'); - cy.task('esArchiverResetKibana'); cy.task('esArchiverLoad', { archiveName: 'endpoint' }); login(); createRule(getEndpointRule()).then((rule) => visitRuleDetailsPage(rule.body.id)); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/closing_all_matching_alerts.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/closing_all_matching_alerts.cy.ts index 2219cd8c8ed17..986bcb107124d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/closing_all_matching_alerts.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/alerts_table_flow/rule_exceptions/closing_all_matching_alerts.cy.ts @@ -33,7 +33,6 @@ describe('Close matching Alerts ', { tags: ['@ess', '@serverless', '@skipInServe beforeEach(() => { cy.task('esArchiverUnload', 'exceptions'); - cy.task('esArchiverResetKibana'); deleteAlertsAndRules(); cy.task('esArchiverLoad', { archiveName: 'exceptions' }); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/flyout_validation.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/flyout_validation.cy.ts index 48d6730db8a81..3c613f32d2c73 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/flyout_validation.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/flyout_validation.cy.ts @@ -67,7 +67,6 @@ import { getExceptionList } from '../../../objects/exception'; // ensure the most basic logic holds. describe.skip('Exceptions flyout', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => { before(() => { - cy.task('esArchiverResetKibana'); // this is a made-up index that has just the necessary // mappings to conduct tests, avoiding loading large // amounts of data like in auditbeat_exceptions diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/multiple_conditions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/multiple_conditions.cy.ts index e0de2b1c1085f..136038f641ec9 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/multiple_conditions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/entry/multiple_conditions.cy.ts @@ -32,7 +32,6 @@ describe( { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { - cy.task('esArchiverResetKibana'); login(); deleteAlertsAndRules(); // At least create Rule with exceptions_list to be able to view created exceptions diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts index abd1b4bfe67fc..7474fd2e5cf2d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_endpoint_exception.cy.ts @@ -26,7 +26,11 @@ import { submitNewExceptionItem, } from '../../../tasks/exceptions'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { + deleteAlertsAndRules, + deleteEndpointExceptionList, + deleteExceptionLists, +} from '../../../tasks/common'; import { NO_EXCEPTIONS_EXIST_PROMPT, EXCEPTION_ITEM_VIEWER_CONTAINER, @@ -57,16 +61,12 @@ describe( const FIELD_DIFFERENT_FROM_EXISTING_ITEM_FIELD = 'agent.type'; beforeEach(() => { - cy.task('esArchiverResetKibana'); - cy.task('esArchiverLoad', { archiveName: 'auditbeat' }); + deleteExceptionLists(); + deleteEndpointExceptionList(); login(); deleteAlertsAndRules(); }); - afterEach(() => { - cy.task('esArchiverUnload', 'auditbeat'); - }); - describe('without exception items', () => { beforeEach(() => { createEndpointExceptionList().then((response) => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts index 4801683e9c90e..6cc022873aea5 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception.cy.ts @@ -37,7 +37,7 @@ import { submitEditedExceptionItem, submitNewExceptionItem, } from '../../../tasks/exceptions'; -import { deleteAlertsAndRules } from '../../../tasks/common'; +import { deleteAlertsAndRules, deleteExceptionLists } from '../../../tasks/common'; import { NO_EXCEPTIONS_EXIST_PROMPT, EXCEPTION_ITEM_VIEWER_CONTAINER, @@ -68,7 +68,6 @@ describe( const ITEM_FIELD = 'unique_value.test'; before(() => { - cy.task('esArchiverResetKibana'); cy.task('esArchiverLoad', { archiveName: 'exceptions' }); }); @@ -79,6 +78,7 @@ describe( beforeEach(() => { login(); deleteAlertsAndRules(); + deleteExceptionLists(); const exceptionList = getExceptionList(); deleteExceptionList(exceptionList.list_id, exceptionList.namespace_type); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts index d7a829294bce2..79f6638e6c0f7 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/rule_details_flow/add_edit_exception_data_view.cy.ts @@ -49,7 +49,6 @@ describe( const ITEM_NAME = 'Sample Exception List Item'; before(() => { - cy.task('esArchiverResetKibana'); cy.task('esArchiverLoad', { archiveName: 'exceptions' }); login(); postDataView('exceptions-*'); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/list_detail_page/list_details.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/list_detail_page/list_details.cy.ts index d19fed884344a..fb656c08ee449 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/list_detail_page/list_details.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/list_detail_page/list_details.cy.ts @@ -48,7 +48,6 @@ describe( { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => { before(() => { - cy.task('esArchiverResetKibana'); login(); // Create exception list associated with a rule diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts index 635e402bfc45e..b0060ec5b1a05 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/manage_exceptions.cy.ts @@ -39,9 +39,9 @@ import { addExceptionListFromSharedExceptionListHeaderMenu, createSharedExceptionList, findSharedExceptionListItemsByName, - waitForExceptionsTableToBeLoaded, } from '../../../tasks/exceptions_table'; import { visitRuleDetailsPage } from '../../../tasks/rule_details'; +import { deleteEndpointExceptionList, deleteExceptionLists } from '../../../tasks/common'; import { closeErrorToast } from '../../../tasks/alerts_detection_rules'; // TODO: https://github.com/elastic/kibana/issues/161539 @@ -51,13 +51,12 @@ describe( { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => { beforeEach(() => { - cy.task('esArchiverResetKibana'); + login(); + deleteExceptionLists(); + deleteEndpointExceptionList(); cy.task('esArchiverLoad', { archiveName: 'exceptions' }); createRule(getNewRule()).as('createdRule'); - - login(); visit(EXCEPTIONS_URL); - waitForExceptionsTableToBeLoaded(); }); afterEach(() => { diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/duplicate_lists.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/duplicate_lists.cy.ts index 6d72a754be39a..025d6dc93657d 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/duplicate_lists.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/duplicate_lists.cy.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { deleteAlertsAndRules, deleteExceptionLists } from '../../../../tasks/common'; import { createRule } from '../../../../tasks/api_calls/rules'; import { getExceptionList } from '../../../../objects/exception'; import { assertNumberOfExceptionItemsExists } from '../../../../tasks/exceptions'; @@ -45,9 +46,9 @@ const getExceptionList2 = () => ({ // Flaky in serverless tests describe('Duplicate List', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => { beforeEach(() => { - cy.task('esArchiverResetKibana'); login(); - + deleteAlertsAndRules(); + deleteExceptionLists(); createRule(getNewRule({ name: 'Another rule' })); // Create exception list associated with a rule diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/filter_table.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/filter_table.cy.ts index 99d5caa707268..0d33582ef4406 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/filter_table.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/filter_table.cy.ts @@ -40,7 +40,6 @@ const getExceptionList2 = () => ({ // TODO: https://github.com/elastic/kibana/issues/161539 describe('Filter Lists', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => { beforeEach(() => { - cy.task('esArchiverResetKibana'); login(); // Create exception list associated with a rule diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/import_lists.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/import_lists.cy.ts index 99996fcf5fdaf..e4239055b303e 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/import_lists.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/import_lists.cy.ts @@ -25,9 +25,6 @@ import { EXCEPTIONS_URL } from '../../../../urls/navigation'; // Flaky in serverless describe('Import Lists', { tags: ['@ess', '@serverless', '@skipInServerless'] }, () => { const LIST_TO_IMPORT_FILENAME = 'cypress/fixtures/7_16_exception_list.ndjson'; - before(() => { - cy.task('esArchiverResetKibana'); - }); beforeEach(() => { login(); visit(EXCEPTIONS_URL); diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/manage_lists.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/manage_lists.cy.ts index 860bfb373bb9b..72a1e4dfa9781 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/manage_lists.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/manage_lists.cy.ts @@ -57,7 +57,6 @@ describe.skip( () => { describe('Create/Export/Delete List', () => { before(() => { - cy.task('esArchiverResetKibana'); createRule(getNewRule({ name: 'Another rule' })); // Create exception list associated with a rule diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/read_only.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/read_only.cy.ts index 1a76dbd026f74..b0ba8beae3821 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/read_only.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/exceptions/shared_exception_lists_management/shared_exception_list_page/read_only.cy.ts @@ -25,10 +25,6 @@ import { EXCEPTIONS_URL } from '../../../../urls/navigation'; // TODO: https://github.com/elastic/kibana/issues/161539 Do we need to run it in Serverless? describe('Shared exception lists - read only', { tags: ['@ess', '@skipInServerless'] }, () => { - before(() => { - cy.task('esArchiverResetKibana'); - }); - beforeEach(() => { deleteExceptionList(getExceptionList().list_id, getExceptionList().namespace_type); diff --git a/x-pack/test/security_solution_cypress/cypress/support/es_archiver.ts b/x-pack/test/security_solution_cypress/cypress/support/es_archiver.ts index fc0d3836ffe4d..c9f271265a1d1 100644 --- a/x-pack/test/security_solution_cypress/cypress/support/es_archiver.ts +++ b/x-pack/test/security_solution_cypress/cypress/support/es_archiver.ts @@ -58,7 +58,6 @@ export const esArchiver = ( esArchiverLoad: async ({ archiveName, ...options }) => esArchiverInstance.load(archiveName, options), esArchiverUnload: async (archiveName) => esArchiverInstance.unload(archiveName), - esArchiverResetKibana: async () => esArchiverInstance.emptyKibanaIndex(), }); return esArchiverInstance; diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/exceptions.ts b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/exceptions.ts index 2d5bf00b3c381..8b7d85b7ebf4d 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/exceptions.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/api_calls/exceptions.ts @@ -19,7 +19,11 @@ export const createEndpointExceptionList = () => rootRequest({ method: 'POST', url: ENDPOINT_LIST_URL, - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + headers: { + 'kbn-xsrf': 'cypress-creds', + 'x-elastic-internal-origin': 'security-solution', + 'elastic-api-version': '2023-10-31', + }, }); export const createEndpointExceptionListItem = (item: CreateEndpointListItemSchema) => @@ -27,7 +31,11 @@ export const createEndpointExceptionListItem = (item: CreateEndpointListItemSche method: 'POST', url: ENDPOINT_LIST_ITEM_URL, body: item, - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + headers: { + 'kbn-xsrf': 'cypress-creds', + 'x-elastic-internal-origin': 'security-solution', + 'elastic-api-version': '2023-10-31', + }, }); export const createExceptionList = ( @@ -43,7 +51,11 @@ export const createExceptionList = ( name: exceptionList.name, type: exceptionList.type, }, - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + headers: { + 'kbn-xsrf': 'cypress-creds', + 'x-elastic-internal-origin': 'security-solution', + 'elastic-api-version': '2023-10-31', + }, failOnStatusCode: false, }); @@ -76,7 +88,11 @@ export const createExceptionListItem = ( ], expire_time: exceptionListItem?.expire_time, }, - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + headers: { + 'kbn-xsrf': 'cypress-creds', + 'x-elastic-internal-origin': 'security-solution', + 'elastic-api-version': '2023-10-31', + }, failOnStatusCode: false, }); @@ -87,7 +103,11 @@ export const createRuleExceptionItem = (ruleId: string, exceptionListItems: Rule body: { items: exceptionListItems, }, - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + headers: { + 'kbn-xsrf': 'cypress-creds', + 'x-elastic-internal-origin': 'security-solution', + 'elastic-api-version': '2023-10-31', + }, failOnStatusCode: false, }); @@ -102,7 +122,11 @@ export const updateExceptionListItem = ( item_id: exceptionListItemId, ...exceptionListItemUpdate, }, - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + headers: { + 'kbn-xsrf': 'cypress-creds', + 'x-elastic-internal-origin': 'security-solution', + 'elastic-api-version': '2023-10-31', + }, failOnStatusCode: false, }); @@ -110,6 +134,10 @@ export const deleteExceptionList = (listId: string, namespaceType: string) => rootRequest({ method: 'DELETE', url: `/api/exception_lists?list_id=${listId}&namespace_type=${namespaceType}`, - headers: { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' }, + headers: { + 'kbn-xsrf': 'cypress-creds', + 'x-elastic-internal-origin': 'security-solution', + 'elastic-api-version': '2023-10-31', + }, failOnStatusCode: false, }); diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/common.ts b/x-pack/test/security_solution_cypress/cypress/tasks/common.ts index 67bcf971c3b15..f42916db561f5 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/common.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/common.ts @@ -140,6 +140,48 @@ export const deleteAlertsAndRules = () => { deleteAllDocuments(`.lists-*,.items-*,${DEFAULT_ALERTS_INDEX_PATTERN}`); }; +export const deleteExceptionLists = () => { + const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; + rootRequest({ + method: 'POST', + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, + body: { + query: { + bool: { + filter: [ + { + match: { + type: 'exception-list', + }, + }, + ], + }, + }, + }, + }); +}; + +export const deleteEndpointExceptionList = () => { + const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; + rootRequest({ + method: 'POST', + url: `${kibanaIndexUrl}/_delete_by_query?conflicts=proceed&refresh`, + body: { + query: { + bool: { + filter: [ + { + match: { + type: 'exception-list-agnostic', + }, + }, + ], + }, + }, + }, + }); +}; + export const deleteTimelines = () => { const kibanaIndexUrl = `${Cypress.env('ELASTICSEARCH_URL')}/.kibana_\*`; rootRequest({ diff --git a/x-pack/test/security_solution_cypress/cypress/tasks/exceptions.ts b/x-pack/test/security_solution_cypress/cypress/tasks/exceptions.ts index 97df4c9af323e..00d6c2d405b87 100644 --- a/x-pack/test/security_solution_cypress/cypress/tasks/exceptions.ts +++ b/x-pack/test/security_solution_cypress/cypress/tasks/exceptions.ts @@ -6,6 +6,7 @@ */ import type { Exception } from '../objects/exception'; +import { TOASTER_CLOSE_ICON } from '../screens/alerts_detection_rules'; import { FIELD_INPUT, OPERATOR_INPUT, @@ -47,6 +48,7 @@ import { EXCEPTIONS_ITEM_ERROR_CALLOUT, EXCEPTIONS_ITEM_ERROR_DISMISS_BUTTON, } from '../screens/exceptions'; +import { closeErrorToast } from './alerts_detection_rules'; export const assertNumberOfExceptionItemsExists = (numberOfItems: number) => { cy.get(EXCEPTION_ITEM_VIEWER_CONTAINER).should('have.length', numberOfItems); @@ -185,6 +187,17 @@ export const validateEmptyExceptionConditionField = () => { }; export const submitNewExceptionItem = () => { cy.get(CONFIRM_BTN).should('exist'); + /* Sometimes a toaster error message unrelated with the test performed is displayed. + The toaster is blocking the confirm button we have to click. Using force true would solve the issue, but should not be used. + There are some tests that use the closeErrorToast() method to close error toasters before continuing with the interactions with the page. + In this case we check if a toaster is displayed and if so, close it to continue with the test. + */ + cy.root().then(($page) => { + const element = $page.find(TOASTER_CLOSE_ICON); + if (element.length > 0) { + closeErrorToast(); + } + }); cy.get(CONFIRM_BTN).click(); cy.get(CONFIRM_BTN).should('not.exist'); }; From c054a2d32ac9e6f3cca5ef5cdd61ca2a42b5e95a Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Tue, 31 Oct 2023 10:28:44 +0000 Subject: [PATCH 43/45] [SecuritySolution] Global search bar Edit Additional Filter not working (#168955) ## Summary The issue and steps to reproduce: https://github.com/elastic/kibana/issues/164406 Root cause: https://github.com/elastic/kibana/pull/168955/files#r1360612921 After: https://github.com/elastic/kibana/assets/6295984/dc338c53-2f04-4f8b-a794-ff188606332a ### 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 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../components/search_bar/index.test.tsx | 1 + .../common/components/search_bar/index.tsx | 27 ++++++++++++------- .../public/dashboards/pages/details/index.tsx | 4 +-- .../pages/rule_details/index.tsx | 4 +-- .../detection_engine/detection_engine.tsx | 4 +-- .../explore/hosts/pages/details/index.tsx | 5 ++-- .../public/explore/hosts/pages/hosts.tsx | 5 ++-- .../explore/network/pages/details/index.tsx | 5 ++-- .../public/explore/network/pages/network.tsx | 5 ++-- .../explore/users/pages/details/index.tsx | 5 ++-- .../public/explore/users/pages/users.tsx | 5 ++-- .../public/kubernetes/pages/index.tsx | 4 +-- .../overview/pages/detection_response.tsx | 4 +-- .../overview/pages/entity_analytics.tsx | 4 +-- .../public/overview/pages/overview.tsx | 5 ++-- .../hooks/use_aggregated_indicators.ts | 4 ++- .../indicators/hooks/use_indicators.ts | 4 ++- .../hooks/use_sourcerer_data_view.ts | 2 +- .../indicators/hooks/use_total_count.tsx | 4 ++- .../modules/indicators/pages/indicators.tsx | 3 ++- .../query_bar/components/query_bar.tsx | 7 ++--- .../cypress/e2e/header/search_bar.cy.ts | 11 ++++++-- 22 files changed, 77 insertions(+), 45 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/search_bar/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/search_bar/index.test.tsx index 71c57edd9191c..915a8fef984cd 100644 --- a/x-pack/plugins/security_solution/public/common/components/search_bar/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/search_bar/index.test.tsx @@ -68,6 +68,7 @@ describe('SearchBarComponent', () => { fields: [], title: '', }, + sourcererDataView: {}, updateSearch: jest.fn(), setSavedQuery: jest.fn(), setSearchBarFilter: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx index e30789bfe35bf..c9906c6e70eac 100644 --- a/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx @@ -14,11 +14,12 @@ import type { Dispatch } from 'redux'; import { Subscription } from 'rxjs'; import deepEqual from 'fast-deep-equal'; -import type { DataViewBase, Filter, Query, TimeRange } from '@kbn/es-query'; +import type { Filter, Query, TimeRange } from '@kbn/es-query'; import type { FilterManager, SavedQuery } from '@kbn/data-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; +import { DataView } from '@kbn/data-views-plugin/public'; import type { OnTimeChangeProps } from '@elastic/eui'; +import type { DataViewSpec } from '@kbn/data-views-plugin/public'; import { inputsActions } from '../../store/inputs'; import type { InputsRange } from '../../store/inputs/model'; import type { InputsModelId } from '../../store/inputs/constants'; @@ -44,8 +45,8 @@ import { useSyncTimerangeUrlParam } from '../../hooks/search_bar/use_sync_timera interface SiemSearchBarProps { id: InputsModelId.global | InputsModelId.timeline; - indexPattern: DataViewBase; pollForSignalIndex?: () => void; + sourcererDataView: DataViewSpec | undefined; timelineId?: string; dataTestSubj?: string; hideFilterBar?: boolean; @@ -60,13 +61,13 @@ export const SearchBarComponent = memo( hideFilterBar = false, hideQueryInput = false, id, - indexPattern, isLoading = false, pollForSignalIndex, queries, savedQuery, setSavedQuery, setSearchBarFilter, + sourcererDataView, start, toStr, updateSearch, @@ -82,6 +83,7 @@ export const SearchBarComponent = memo( unifiedSearch: { ui: { SearchBar }, }, + fieldFormats, } = useKibana().services; const dispatch = useDispatch(); @@ -294,7 +296,14 @@ export const SearchBarComponent = memo( // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const indexPatterns = useMemo(() => [indexPattern], [indexPattern]); + const dataViews: DataView[] | null = useMemo(() => { + if (sourcererDataView != null) { + return [new DataView({ spec: sourcererDataView, fieldFormats })]; + } else { + return null; + } + }, [sourcererDataView, fieldFormats]); + const onTimeRangeChange = useCallback( ({ query, dateRange }) => { const isQuickSelection = dateRange.from.includes('now') || dateRange.to.includes('now'); @@ -312,12 +321,12 @@ export const SearchBarComponent = memo( }, [filterManager, id, setTablesActivePageToZero, updateSearch] ); - return ( + return dataViews ? (
    ( dataTestSubj={dataTestSubj} />
    - ); + ) : null; }, (prevProps, nextProps) => prevProps.end === nextProps.end && prevProps.filterQuery === nextProps.filterQuery && prevProps.fromStr === nextProps.fromStr && + deepEqual(prevProps.sourcererDataView, nextProps.sourcererDataView) && prevProps.id === nextProps.id && prevProps.isLoading === nextProps.isLoading && prevProps.savedQuery === nextProps.savedQuery && @@ -348,7 +358,6 @@ export const SearchBarComponent = memo( prevProps.toStr === nextProps.toStr && prevProps.updateSearch === nextProps.updateSearch && prevProps.dataTestSubj === nextProps.dataTestSubj && - deepEqual(prevProps.indexPattern, nextProps.indexPattern) && deepEqual(prevProps.queries, nextProps.queries) ); diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx index 6f07b377a22d0..99346602d9453 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/details/index.tsx @@ -52,7 +52,7 @@ const DashboardViewComponent: React.FC = ({ ); const query = useDeepEqualSelector(getGlobalQuerySelector); const filters = useDeepEqualSelector(getGlobalFiltersQuerySelector); - const { indexPattern } = useSourcererDataView(); + const { sourcererDataView } = useSourcererDataView(); const { show: canReadDashboard } = useCapabilities(LEGACY_DASHBOARD_APP_ID); @@ -72,7 +72,7 @@ const DashboardViewComponent: React.FC = ({ return ( <> - + = ({ useListsConfig(); const { - indexPattern, + sourcererDataView, runtimeMappings, loading: isLoadingIndexPattern, } = useSourcererDataView(SourcererScopeName.detections); @@ -542,7 +542,7 @@ const RuleDetailsPageComponent: React.FC = ({ diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx index 5291c5326ae3f..8a8b269abf4ed 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/detection_engine.tsx @@ -151,7 +151,7 @@ const DetectionEnginePageComponent: React.FC = ({ >(); const { - indexPattern, + sourcererDataView, runtimeMappings, loading: isLoadingIndexPattern, } = useSourcererDataView(SourcererScopeName.detections); @@ -439,7 +439,7 @@ const DetectionEnginePageComponent: React.FC = ({ = ({ detailName, hostDeta [dispatch] ); - const { indexPattern, indicesExist, selectedPatterns } = useSourcererDataView(); + const { indexPattern, indicesExist, selectedPatterns, sourcererDataView } = + useSourcererDataView(); const [loading, { inspect, hostDetails: hostOverview, id, refetch }] = useHostDetails({ endDate: to, startDate: from, @@ -172,7 +173,7 @@ const HostDetailsComponent: React.FC = ({ detailName, hostDeta <> - + { }, [dispatch] ); - const { indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); + const { indicesExist, indexPattern, selectedPatterns, sourcererDataView } = + useSourcererDataView(); const [globalFilterQuery, kqlError] = useMemo( () => convertToBuildEsQuery({ @@ -189,7 +190,7 @@ const HostsComponent = () => { - + diff --git a/x-pack/plugins/security_solution/public/explore/network/pages/details/index.tsx b/x-pack/plugins/security_solution/public/explore/network/pages/details/index.tsx index 70f3dc56887fe..9b07b232ec050 100644 --- a/x-pack/plugins/security_solution/public/explore/network/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/pages/details/index.tsx @@ -103,7 +103,8 @@ const NetworkDetailsComponent: React.FC = () => { dispatch(setNetworkDetailsTablesActivePageToZero()); }, [detailName, dispatch]); - const { indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); + const { indicesExist, indexPattern, selectedPatterns, sourcererDataView } = + useSourcererDataView(); const ip = decodeIpv6(detailName); const networkDetailsFilter = useMemo(() => getNetworkDetailsPageFilter(ip), [ip]); @@ -164,7 +165,7 @@ const NetworkDetailsComponent: React.FC = () => { {indicesExist ? ( <> - + diff --git a/x-pack/plugins/security_solution/public/explore/network/pages/network.tsx b/x-pack/plugins/security_solution/public/explore/network/pages/network.tsx index fbc45396712ac..1caafa3ee182c 100644 --- a/x-pack/plugins/security_solution/public/explore/network/pages/network.tsx +++ b/x-pack/plugins/security_solution/public/explore/network/pages/network.tsx @@ -109,7 +109,8 @@ const NetworkComponent = React.memo( [dispatch] ); - const { indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); + const { indicesExist, indexPattern, selectedPatterns, sourcererDataView } = + useSourcererDataView(); const onSkipFocusBeforeEventsTable = useCallback(() => { containerElement.current @@ -157,7 +158,7 @@ const NetworkComponent = React.memo( - + diff --git a/x-pack/plugins/security_solution/public/explore/users/pages/details/index.tsx b/x-pack/plugins/security_solution/public/explore/users/pages/details/index.tsx index b751bc0e73b9f..2e5fc3b3dbbab 100644 --- a/x-pack/plugins/security_solution/public/explore/users/pages/details/index.tsx +++ b/x-pack/plugins/security_solution/public/explore/users/pages/details/index.tsx @@ -103,7 +103,8 @@ const UsersDetailsComponent: React.FC = ({ [detailName] ); - const { indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); + const { indicesExist, indexPattern, selectedPatterns, sourcererDataView } = + useSourcererDataView(); const [rawFilteredQuery, kqlError] = useMemo(() => { try { @@ -175,7 +176,7 @@ const UsersDetailsComponent: React.FC = ({ <> - + diff --git a/x-pack/plugins/security_solution/public/explore/users/pages/users.tsx b/x-pack/plugins/security_solution/public/explore/users/pages/users.tsx index 0666e34e6155e..68318e77da152 100644 --- a/x-pack/plugins/security_solution/public/explore/users/pages/users.tsx +++ b/x-pack/plugins/security_solution/public/explore/users/pages/users.tsx @@ -103,7 +103,8 @@ const UsersComponent = () => { return globalFilters; }, [severitySelection, tabName, globalFilters]); - const { indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); + const { indicesExist, indexPattern, selectedPatterns, sourcererDataView } = + useSourcererDataView(); const [globalFiltersQuery, kqlError] = useMemo( () => convertToBuildEsQuery({ @@ -188,7 +189,7 @@ const UsersComponent = () => { - + diff --git a/x-pack/plugins/security_solution/public/kubernetes/pages/index.tsx b/x-pack/plugins/security_solution/public/kubernetes/pages/index.tsx index 6388e8dd204d6..a7a74634b3c2c 100644 --- a/x-pack/plugins/security_solution/public/kubernetes/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/kubernetes/pages/index.tsx @@ -30,7 +30,7 @@ export const KubernetesContainer = React.memo(() => { const { kubernetesSecurity, uiSettings } = useKibana().services; const { globalFullScreen } = useGlobalFullScreen(); - const { indexPattern } = useSourcererDataView(); + const { indexPattern, sourcererDataView } = useSourcererDataView(); const { from, to } = useGlobalTime(); const getGlobalFiltersQuerySelector = useMemo( @@ -81,7 +81,7 @@ export const KubernetesContainer = React.memo(() => { {kubernetesSecurity.getKubernetesPage({ filter: ( - + ), indexPattern, diff --git a/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx b/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx index 067357734f640..8bdc95fc69aab 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/detection_response.tsx @@ -34,7 +34,7 @@ import { useGlobalFilterQuery } from '../../common/hooks/use_global_filter_query const DetectionResponseComponent = () => { const { filterQuery } = useGlobalFilterQuery(); - const { indicesExist, indexPattern, loading: isSourcererLoading } = useSourcererDataView(); + const { indicesExist, loading: isSourcererLoading, sourcererDataView } = useSourcererDataView(); const { signalIndexName } = useSignalIndex(); const { hasKibanaREAD, hasIndexRead } = useAlertsPrivileges(); const canReadCases = useGetUserCasesPermissions().read; @@ -49,7 +49,7 @@ const DetectionResponseComponent = () => { {indicesExist ? ( <> - + diff --git a/x-pack/plugins/security_solution/public/overview/pages/entity_analytics.tsx b/x-pack/plugins/security_solution/public/overview/pages/entity_analytics.tsx index 8e7863b46ba3d..96476c90aa180 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/entity_analytics.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/entity_analytics.tsx @@ -28,7 +28,7 @@ import { useHasSecurityCapability } from '../../helper_hooks'; const EntityAnalyticsComponent = () => { const { data: riskScoreEngineStatus } = useRiskEngineStatus(); - const { indicesExist, loading: isSourcererLoading, indexPattern } = useSourcererDataView(); + const { indicesExist, loading: isSourcererLoading, sourcererDataView } = useSourcererDataView(); const isRiskScoreModuleLicenseAvailable = useHasSecurityCapability('entity-analytics'); return ( @@ -36,7 +36,7 @@ const EntityAnalyticsComponent = () => { {indicesExist ? ( <> - + diff --git a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx index 6cccf353e4b1c..e0c50ae193903 100644 --- a/x-pack/plugins/security_solution/public/overview/pages/overview.tsx +++ b/x-pack/plugins/security_solution/public/overview/pages/overview.tsx @@ -43,7 +43,8 @@ const OverviewComponent = () => { const filters = useDeepEqualSelector(getGlobalFiltersQuerySelector); const { from, deleteQuery, setQuery, to } = useGlobalTime(); - const { indicesExist, indexPattern, selectedPatterns } = useSourcererDataView(); + const { indicesExist, sourcererDataView, indexPattern, selectedPatterns } = + useSourcererDataView(); const endpointMetadataIndex = useMemo(() => { return [ENDPOINT_METADATA_INDEX]; @@ -71,7 +72,7 @@ const OverviewComponent = () => { {indicesExist ? ( <> - + diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts index 3c3f7ca0ca617..5ee03382c665f 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_aggregated_indicators.ts @@ -80,7 +80,9 @@ export const useAggregatedIndicators = ({ const userTimeZone = useTimeZone(); const userFormat = useDateFormat(); - const { selectedPatterns } = useSourcererDataView(); + const { + sourcererDataView: { selectedPatterns }, + } = useSourcererDataView(); const { inspectorAdapters } = useInspector(); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts index ad18906a95f86..335150d3c19dc 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_indicators.ts @@ -64,7 +64,9 @@ export const useIndicators = ({ data: { search: searchService }, }, } = useKibana(); - const { selectedPatterns } = useSourcererDataView(); + const { + sourcererDataView: { selectedPatterns }, + } = useSourcererDataView(); const { inspectorAdapters } = useInspector(); diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_sourcerer_data_view.ts b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_sourcerer_data_view.ts index d2e12d545036c..947ac663a59cd 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_sourcerer_data_view.ts +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_sourcerer_data_view.ts @@ -54,7 +54,7 @@ export const useSourcererDataView = () => { return useMemo( () => ({ - ...sourcererDataView, + sourcererDataView, indexPatterns, indexPattern: updatedPattern, browserFields, diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_total_count.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_total_count.tsx index d9b7c22f83354..2e98154e620bd 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_total_count.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/hooks/use_total_count.tsx @@ -24,7 +24,9 @@ export const useIndicatorsTotalCount = () => { const [count, setCount] = useState(0); const [isLoading, setIsLoading] = useState(true); - const { selectedPatterns, loading: loadingDataView } = useSourcererDataView(); + const { + sourcererDataView: { selectedPatterns, loading: loadingDataView }, + } = useSourcererDataView(); useEffect(() => { const query = { diff --git a/x-pack/plugins/threat_intelligence/public/modules/indicators/pages/indicators.tsx b/x-pack/plugins/threat_intelligence/public/modules/indicators/pages/indicators.tsx index 827ad30aa4ea9..45f356ff455f2 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/indicators/pages/indicators.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/indicators/pages/indicators.tsx @@ -37,7 +37,7 @@ const IndicatorsPageProviders: FC = ({ children }) => ( const IndicatorsPageContent: VFC = () => { const { blockListIndicatorValue } = useBlockListContext(); - const { browserFields, indexPattern } = useSourcererDataView(); + const { browserFields, indexPattern, sourcererDataView } = useSourcererDataView(); const columnSettings = useColumnSettings(); @@ -84,6 +84,7 @@ const IndicatorsPageContent: VFC = () => { diff --git a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/query_bar.tsx b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/query_bar.tsx index eed55101e440b..784a25d597959 100644 --- a/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/query_bar.tsx +++ b/x-pack/plugins/threat_intelligence/public/modules/query_bar/components/query_bar.tsx @@ -7,7 +7,7 @@ import React, { useEffect, VFC } from 'react'; import { useSecurityContext } from '../../../hooks/use_security_context'; -import { SecuritySolutionDataViewBase } from '../../../types'; +import { SecuritySolutionDataViewBase, SourcererDataView } from '../../../types'; interface QueryBarProps { indexPattern: SecuritySolutionDataViewBase; @@ -16,9 +16,10 @@ interface QueryBarProps { refetch: VoidFunction; loading: boolean; }>; + sourcererDataView: SourcererDataView | undefined; } -export const QueryBar: VFC = ({ indexPattern, queries }) => { +export const QueryBar: VFC = ({ queries, sourcererDataView }) => { const { SiemSearchBar, registerQuery, deregisterQuery } = useSecurityContext(); useEffect(() => { @@ -27,5 +28,5 @@ export const QueryBar: VFC = ({ indexPattern, queries }) => { return () => queries.forEach(deregisterQuery); }, [queries, deregisterQuery, registerQuery]); - return ; + return ; }; diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/header/search_bar.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/header/search_bar.cy.ts index 203e966eef9ad..383a51592e91a 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/header/search_bar.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/header/search_bar.cy.ts @@ -6,7 +6,7 @@ */ import { login } from '../../tasks/login'; -import { visit } from '../../tasks/navigation'; +import { visitWithTimeRange } from '../../tasks/navigation'; import { openAddFilterPopover, fillAddFilterForm, @@ -25,11 +25,18 @@ import { waitForAllHostsToBeLoaded } from '../../tasks/hosts/all_hosts'; describe('SearchBar', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { + cy.task('esArchiverResetKibana'); + cy.task('esArchiverLoad', { archiveName: 'auditbeat' }); + login(); - visit(hostsUrl('allHosts')); + visitWithTimeRange(hostsUrl('allHosts')); waitForAllHostsToBeLoaded(); }); + afterEach(() => { + cy.task('esArchiverUnload', 'auditbeat'); + }); + it('adds correctly a filter to the global search bar', () => { openAddFilterPopover(); fillAddFilterForm(getHostIpFilter()); From 9eff64a39120e364a4f9a6b71d9501dbb1be0999 Mon Sep 17 00:00:00 2001 From: Juan Pablo Djeredjian Date: Tue, 31 Oct 2023 11:36:41 +0100 Subject: [PATCH 44/45] [Security Solution] Unskip tests for Detection Engine and Security Solution API Integration tests (#169262) ## Summary ### Changes - `x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/task_based/detection_rules.ts` **unskipped** - `x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts` **unskipped** ### Related flaky test issues **Tests already moved to `x-pack/test/security_solution_api_integration`** 1. https://github.com/elastic/kibana/issues/169055 2. https://github.com/elastic/kibana/issues/168775 This issue is reported for the legacy test, but is now moved to `x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/ips_text_array/text_array.ts` 3. https://github.com/elastic/kibana/issues/163511 This issue is reported for the legacy test, but is now moved to `x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions/operators_data_types/keyword_text_long/keyword.ts` - Flaky test runner for above tests: https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3631 **Test still in `x-pack/test/detection_engine_api_integration`** 1. https://github.com/elastic/kibana/issues/168415 2. https://github.com/elastic/kibana/issues/164318 3. https://github.com/elastic/kibana/issues/164313 4. https://github.com/elastic/kibana/issues/145776 **Other** 1. https://github.com/elastic/kibana/issues/151636 ## Flaky test runner link Part 1 - `x-pack/test/detection_engine_api_integration` - tests not yet migrated https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3597 Part 2 - `x-pack/test/security_solution_api_integration` - tests already moved https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3631 --- .../group4/telemetry/task_based/detection_rules.ts | 4 +--- .../rule_execution_logic/machine_learning.ts | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/task_based/detection_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/task_based/detection_rules.ts index 041033b96db86..684fc752c152e 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/task_based/detection_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group4/telemetry/task_based/detection_rules.ts @@ -34,9 +34,7 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); const retry = getService('retry'); - // Failing: See https://github.com/elastic/kibana/issues/164318 - // FLAKY: https://github.com/elastic/kibana/issues/164313 - describe.skip('Detection rule task telemetry', async () => { + describe('Detection rule task telemetry', async () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/security_solution/telemetry'); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts index b9fd707e3775b..792fcb30b6645 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/rule_execution_logic/machine_learning.ts @@ -66,8 +66,7 @@ export default ({ getService }: FtrProviderContext) => { rule_id: 'ml-rule-id', }; - // FLAKY: https://github.com/elastic/kibana/issues/145776 - describe.skip('Machine learning type rules', () => { + describe('Machine learning type rules', () => { before(async () => { // Order is critical here: auditbeat data must be loaded before attempting to start the ML job, // as the job looks for certain indices on start From 0bbcdaaee7ab114486fdb4861b4f3bd661120f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Tue, 31 Oct 2023 10:46:57 +0000 Subject: [PATCH 45/45] [Serverless nav] Tests for navigation panels (#169915) --- .../project_navigation.test.tsx.snap} | 2 +- .../navigation/__jest__/active_node.test.tsx | 232 +++++ .../__jest__/build_nav_tree.test.tsx | 738 +++++++++++++++ .../chrome/navigation/__jest__/links.test.tsx | 234 +++++ .../chrome/navigation/__jest__/panel.test.tsx | 546 +++++++++++ .../__jest__/project_navigation.test.tsx | 80 ++ .../navigation/__jest__/setup_jest_mocks.ts | 22 + .../chrome/navigation/__jest__/utils.tsx | 75 ++ packages/shared-ux/chrome/navigation/index.ts | 1 + .../navigation/src/ui/components/index.ts | 6 +- .../src/ui/components/navigation.test.tsx | 894 ------------------ .../src/ui/components/navigation.tsx | 4 +- .../ui/components/navigation_section_ui.tsx | 60 +- .../src/ui/components/panel/panel_group.tsx | 15 +- .../ui/components/panel/panel_nav_item.tsx | 2 +- .../src/ui/default_navigation.test.tsx | 871 ----------------- .../src/ui/hooks/use_init_navnode.ts | 13 +- .../chrome/navigation/src/ui/index.ts | 2 +- .../navigation_tree/navigation_tree.ts | 1 - 19 files changed, 1991 insertions(+), 1807 deletions(-) rename packages/shared-ux/chrome/navigation/{src/ui/__snapshots__/default_navigation.test.tsx.snap => __jest__/__snapshots__/project_navigation.test.tsx.snap} (99%) create mode 100644 packages/shared-ux/chrome/navigation/__jest__/active_node.test.tsx create mode 100644 packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx create mode 100644 packages/shared-ux/chrome/navigation/__jest__/links.test.tsx create mode 100644 packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx create mode 100644 packages/shared-ux/chrome/navigation/__jest__/project_navigation.test.tsx create mode 100644 packages/shared-ux/chrome/navigation/__jest__/setup_jest_mocks.ts create mode 100644 packages/shared-ux/chrome/navigation/__jest__/utils.tsx delete mode 100644 packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx delete mode 100644 packages/shared-ux/chrome/navigation/src/ui/default_navigation.test.tsx diff --git a/packages/shared-ux/chrome/navigation/src/ui/__snapshots__/default_navigation.test.tsx.snap b/packages/shared-ux/chrome/navigation/__jest__/__snapshots__/project_navigation.test.tsx.snap similarity index 99% rename from packages/shared-ux/chrome/navigation/src/ui/__snapshots__/default_navigation.test.tsx.snap rename to packages/shared-ux/chrome/navigation/__jest__/__snapshots__/project_navigation.test.tsx.snap index b4121ace0bf7f..65ed8b80aa8a3 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/__snapshots__/default_navigation.test.tsx.snap +++ b/packages/shared-ux/chrome/navigation/__jest__/__snapshots__/project_navigation.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` builds the full navigation tree when only custom project is provided reading the title from config or deeplink 1`] = ` +exports[`Default navigation builds the full navigation tree when only the project is provided 1`] = ` Array [ Object { "children": Array [ diff --git a/packages/shared-ux/chrome/navigation/__jest__/active_node.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/active_node.test.tsx new file mode 100644 index 0000000000000..a0c35ace442e4 --- /dev/null +++ b/packages/shared-ux/chrome/navigation/__jest__/active_node.test.tsx @@ -0,0 +1,232 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import './setup_jest_mocks'; +import React from 'react'; +import { type RenderResult, act } from '@testing-library/react'; +import { type Observable, of, BehaviorSubject } from 'rxjs'; +import type { + ChromeNavLink, + ChromeProjectNavigation, + ChromeProjectNavigationNode, +} from '@kbn/core-chrome-browser'; + +import { Navigation } from '../src/ui/components/navigation'; +import type { RootNavigationItemDefinition } from '../src/ui/types'; + +import { renderNavigation, errorHandler, TestType } from './utils'; + +describe('Active node', () => { + test('should set the active node', async () => { + const navLinks$: Observable = of([ + { + id: 'item1', + title: 'Item 1', + baseUrl: '', + url: '', + href: '', + }, + { + id: 'item2', + title: 'Item 2', + baseUrl: '', + url: '', + href: '', + }, + ]); + + let activeNodes$: BehaviorSubject; + + const getActiveNodes$ = () => { + activeNodes$ = new BehaviorSubject([ + [ + { + id: 'group1', + title: 'Group 1', + path: ['group1'], + }, + { + id: 'item1', + title: 'Item 1', + path: ['group1', 'item1'], + }, + ], + ]); + + return activeNodes$; + }; + + const runTests = async (type: TestType, { findByTestId }: RenderResult) => { + try { + expect((await findByTestId(/nav-item-group1.item1/)).dataset.testSubj).toMatch( + /nav-item-isActive/ + ); + expect((await findByTestId(/nav-item-group1.item2/)).dataset.testSubj).not.toMatch( + /nav-item-isActive/ + ); + + await act(async () => { + activeNodes$.next([ + [ + { + id: 'group1', + title: 'Group 1', + path: ['group1'], + }, + { + id: 'item2', + title: 'Item 2', + path: ['group1', 'item2'], + }, + ], + ]); + }); + + expect((await findByTestId(/nav-item-group1.item1/)).dataset.testSubj).not.toMatch( + /nav-item-isActive/ + ); + expect((await findByTestId(/nav-item-group1.item2/)).dataset.testSubj).toMatch( + /nav-item-isActive/ + ); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'group1', + children: [ + { link: 'item1', title: 'Item 1' }, + { link: 'item2', title: 'Item 2' }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + services: { navLinks$, activeNodes$: getActiveNodes$() }, + }); + + await runTests('treeDef', renderResult); + + renderResult.unmount(); + } + + // -- With UI Components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + link="item1" title="Item 1" /> + link="item2" title="Item 2" /> + + + ), + services: { navLinks$, activeNodes$: getActiveNodes$() }, + }); + + await runTests('uiComponents', renderResult); + } + }); + + test('should override the URL location to set the active node', async () => { + const navLinks$: Observable = of([ + { + id: 'item1', + title: 'Item 1', + baseUrl: '', + url: '', + href: '', + }, + ]); + + let activeNodes$: BehaviorSubject; + + const getActiveNodes$ = () => { + activeNodes$ = new BehaviorSubject([]); + + return activeNodes$; + }; + + const onProjectNavigationChange = (nav: ChromeProjectNavigation) => { + nav.navigationTree.forEach((node) => { + node.children?.forEach((child) => { + if (child.getIsActive?.({} as any)) { + activeNodes$.next([[child]]); + } + }); + }); + }; + + const runTests = async (type: TestType, { findByTestId }: RenderResult) => { + try { + expect((await findByTestId(/nav-item-group1.item1/)).dataset.testSubj).toMatch( + /nav-item-isActive/ + ); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'group1', + children: [ + { + link: 'item1', + title: 'Item 1', + getIsActive: () => { + return true; // Always active + }, + }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + services: { navLinks$, activeNodes$: getActiveNodes$() }, + onProjectNavigationChange, + }); + + await runTests('treeDef', renderResult); + + renderResult.unmount(); + } + + // -- With UI Components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + + link="item1" + title="Item 1" + getIsActive={() => { + return true; + }} + /> + + + ), + onProjectNavigationChange, + services: { navLinks$, activeNodes$: getActiveNodes$() }, + }); + + await runTests('uiComponents', renderResult); + } + }); +}); diff --git a/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx new file mode 100644 index 0000000000000..4947bcabac552 --- /dev/null +++ b/packages/shared-ux/chrome/navigation/__jest__/build_nav_tree.test.tsx @@ -0,0 +1,738 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import './setup_jest_mocks'; +import React from 'react'; +import { type RenderResult } from '@testing-library/react'; +import { type Observable, of } from 'rxjs'; +import type { ChromeNavLink } from '@kbn/core-chrome-browser'; + +import { navLinksMock } from '../mocks/src/navlinks'; +import { Navigation } from '../src/ui/components/navigation'; +import type { RootNavigationItemDefinition } from '../src/ui/types'; + +import { + getMockFn, + renderNavigation, + errorHandler, + type TestType, + type ProjectNavigationChangeListener, +} from './utils'; + +describe('builds navigation tree', () => { + test('render reference UI and build the navigation tree', async () => { + const onProjectNavigationChange = getMockFn(); + + const runTests = async (type: TestType, { findByTestId }: RenderResult) => { + try { + expect(await findByTestId(/nav-item-group1.item1\s/)).toBeVisible(); + expect(await findByTestId(/nav-item-group1.item2\s/)).toBeVisible(); + expect(await findByTestId(/nav-item-group1.group1A\s/)).toBeVisible(); + expect(await findByTestId(/nav-item-group1.group1A.item1\s/)).toBeVisible(); + expect(await findByTestId(/nav-item-group1.group1A.group1A_1\s/)).toBeVisible(); + + // Click the last group to expand and show the last depth + (await findByTestId(/nav-item-group1.group1A.group1A_1\s/)).click(); + + expect(await findByTestId(/nav-item-group1.group1A.group1A_1.item1/)).toBeVisible(); + + expect(onProjectNavigationChange).toHaveBeenCalled(); + const lastCall = + onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; + const [{ navigationTree }] = lastCall; + return navigationTree; + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const renderResult = renderNavigation({ + navTreeDef: { + body: [ + { + type: 'navGroup', + id: 'group1', + defaultIsCollapsed: false, + children: [ + { + id: 'item1', + title: 'Item 1', + href: 'https://foo', + }, + { + id: 'item2', + title: 'Item 2', + href: 'https://foo', + }, + { + id: 'group1A', + title: 'Group1A', + defaultIsCollapsed: false, + children: [ + { + id: 'item1', + title: 'Group 1A Item 1', + href: 'https://foo', + }, + { + id: 'group1A_1', + title: 'Group1A_1', + children: [ + { + id: 'item1', + title: 'Group 1A_1 Item 1', + href: 'https://foo', + }, + ], + }, + ], + }, + ], + }, + ], + }, + onProjectNavigationChange, + }); + + const navigationTree = await runTests('treeDef', renderResult); + + expect(navigationTree).toMatchInlineSnapshot(` + Array [ + Object { + "children": Array [ + Object { + "children": undefined, + "deepLink": undefined, + "href": "https://foo", + "id": "item1", + "isActive": false, + "isGroup": false, + "path": Array [ + "group1", + "item1", + ], + "sideNavStatus": "visible", + "title": "Item 1", + }, + Object { + "children": undefined, + "deepLink": undefined, + "href": "https://foo", + "id": "item2", + "isActive": false, + "isGroup": false, + "path": Array [ + "group1", + "item2", + ], + "sideNavStatus": "visible", + "title": "Item 2", + }, + Object { + "children": Array [ + Object { + "children": undefined, + "deepLink": undefined, + "href": "https://foo", + "id": "item1", + "isActive": false, + "isGroup": false, + "path": Array [ + "group1", + "group1A", + "item1", + ], + "sideNavStatus": "visible", + "title": "Group 1A Item 1", + }, + Object { + "children": Array [ + Object { + "children": undefined, + "deepLink": undefined, + "href": "https://foo", + "id": "item1", + "isActive": false, + "isGroup": false, + "path": Array [ + "group1", + "group1A", + "group1A_1", + "item1", + ], + "sideNavStatus": "visible", + "title": "Group 1A_1 Item 1", + }, + ], + "deepLink": undefined, + "href": undefined, + "id": "group1A_1", + "isActive": false, + "isGroup": true, + "path": Array [ + "group1", + "group1A", + "group1A_1", + ], + "sideNavStatus": "visible", + "title": "Group1A_1", + }, + ], + "deepLink": undefined, + "href": undefined, + "id": "group1A", + "isActive": true, + "isGroup": true, + "path": Array [ + "group1", + "group1A", + ], + "sideNavStatus": "visible", + "title": "Group1A", + }, + ], + "deepLink": undefined, + "href": undefined, + "id": "group1", + "isActive": true, + "isGroup": true, + "path": Array [ + "group1", + ], + "sideNavStatus": "visible", + "title": "", + "type": "navGroup", + }, + ] + `); + + onProjectNavigationChange.mockReset(); + renderResult.unmount(); + } + + // -- With UI Components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + + + + + + + + + + + ), + onProjectNavigationChange, + }); + + const navigationTree = await runTests('uiComponents', renderResult); + + expect(navigationTree).toMatchInlineSnapshot(` + Array [ + Object { + "children": Array [ + Object { + "children": undefined, + "deepLink": undefined, + "href": "https://foo", + "id": "item1", + "isActive": false, + "isGroup": false, + "path": Array [ + "group1", + "item1", + ], + "sideNavStatus": "visible", + "title": "Item 1", + }, + Object { + "children": undefined, + "deepLink": undefined, + "href": "https://foo", + "id": "item2", + "isActive": false, + "isGroup": false, + "path": Array [ + "group1", + "item2", + ], + "sideNavStatus": "visible", + "title": "Item 2", + }, + Object { + "children": Array [ + Object { + "children": undefined, + "deepLink": undefined, + "href": "https://foo", + "id": "item1", + "isActive": false, + "isGroup": false, + "path": Array [ + "group1", + "group1A", + "item1", + ], + "sideNavStatus": "visible", + "title": "Group 1A Item 1", + }, + Object { + "children": Array [ + Object { + "children": undefined, + "deepLink": undefined, + "href": "https://foo", + "id": "item1", + "isActive": false, + "isGroup": false, + "path": Array [ + "group1", + "group1A", + "group1A_1", + "item1", + ], + "sideNavStatus": "visible", + "title": "Group 1A_1 Item 1", + }, + ], + "deepLink": undefined, + "href": undefined, + "id": "group1A_1", + "isActive": false, + "isGroup": true, + "path": Array [ + "group1", + "group1A", + "group1A_1", + ], + "sideNavStatus": "visible", + "title": "Group1A_1", + }, + ], + "deepLink": undefined, + "href": undefined, + "id": "group1A", + "isActive": false, + "isGroup": true, + "path": Array [ + "group1", + "group1A", + ], + "sideNavStatus": "visible", + "title": "Group1A", + }, + ], + "deepLink": undefined, + "href": undefined, + "id": "group1", + "isActive": true, + "isGroup": true, + "path": Array [ + "group1", + ], + "sideNavStatus": "visible", + "title": "", + }, + ] + `); + } + }); + + test('should read the title from deeplink, prop or React children', async () => { + const navLinks$: Observable = of([ + ...navLinksMock, + { + id: 'item1', + title: 'Title from deeplink', + baseUrl: '', + url: '', + href: '', + }, + ]); + + const onProjectNavigationChange = getMockFn(); + + const runTests = (type: TestType) => { + expect(onProjectNavigationChange).toHaveBeenCalled(); + const lastCall = + onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; + const [{ navigationTree }] = lastCall; + + const groupChildren = navigationTree[0].children?.[0].children; + + if (!groupChildren) { + throw new Error('Expected group children to be defined'); + } + + try { + expect(groupChildren[0].title).toBe('Title from deeplink'); + expect(groupChildren[1].title).toBe('Overwrite deeplink title'); + expect(groupChildren[2].title).toBe('Title in props'); // Unknown deeplink, has not been rendered + } catch (e) { + errorHandler(type)(e); + } + + return groupChildren; + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'root', + children: [ + { + id: 'group1', + children: [ + { + id: 'item1', + link: 'item1', // Title from deeplink + }, + { + id: 'item2', + link: 'item1', // Overwrite title from deeplink + title: 'Overwrite deeplink title', + }, + { + id: 'item3', + title: 'Title in props', + }, + { + id: 'item4', + link: 'unknown', // Unknown deeplink + title: 'Should not be rendered', + }, + ], + }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + services: { navLinks$ }, + onProjectNavigationChange, + }); + + const groupChildren = runTests('treeDef'); + expect(groupChildren.length).toBe(3); + expect(groupChildren[3]).toBeUndefined(); // Unknown deeplink, has not been rendered + + onProjectNavigationChange.mockReset(); + renderResult.unmount(); + } + + // -- With UI components + { + renderNavigation({ + navigationElement: ( + + + + {/* Title from deeplink */} + id="item1" link="item1" /> + id="item2" link="item1" title="Overwrite deeplink title" /> + + id="item4" link="unknown" title="Should not be rendered" /> + Title in children + + + + ), + services: { navLinks$ }, + onProjectNavigationChange, + }); + + const groupChildren = runTests('uiComponents'); + expect(groupChildren.length).toBe(4); + // "item4" has been skipped as it is an unknown deeplink and we have the next item in the list + expect(groupChildren[3].title).toBe('Title in children'); + } + }); + + test('should not render the group if it does not have children AND no href or deeplink', async () => { + const navLinks$: Observable = of([ + { + id: 'item1', + title: 'Title from deeplink', + baseUrl: '', + url: '', + href: '', + }, + ]); + const onProjectNavigationChange = getMockFn(); + + const runTests = (type: TestType, { queryByTestId }: RenderResult) => { + expect(onProjectNavigationChange).toHaveBeenCalled(); + + try { + // Check the DOM + expect(queryByTestId(/nav-group-root.group1/)).toBeNull(); + expect(queryByTestId(/nav-item-root.group2.item1/)).toBeVisible(); + + // Check the navigation tree + const lastCall = + onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; + const [navTree] = lastCall; + const [rootNode] = navTree.navigationTree; + expect(rootNode.id).toBe('root'); + expect(rootNode.children?.length).toBe(2); + expect(rootNode.children?.[0]?.id).toBe('group1'); + expect(rootNode.children?.[0]?.children).toBeUndefined(); // No children mounted and registered itself + expect(rootNode.children?.[1]?.id).toBe('group2'); + return navTree; + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'root', + isCollapsible: false, + children: [ + { + id: 'group1', + children: [{ link: 'notRegistered' }], + }, + { + id: 'group2', + children: [{ link: 'item1' }], + }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + services: { navLinks$ }, + onProjectNavigationChange, + }); + + await runTests('treeDef', renderResult); + + onProjectNavigationChange.mockReset(); + renderResult.unmount(); + } + + // -- With UI components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + + link="notRegistered" /> + + + link="item1" /> + + + + ), + services: { navLinks$ }, + onProjectNavigationChange, + }); + + await runTests('uiComponents', renderResult); + } + }); + + test('should render group preset (analytics, ml...)', async () => { + const onProjectNavigationChange = getMockFn(); + + const runTests = async (type: TestType) => { + try { + expect(onProjectNavigationChange).toHaveBeenCalled(); + const lastCall = + onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; + const [navTreeGenerated] = lastCall; + + expect(navTreeGenerated).toEqual({ + navigationTree: expect.any(Array), + }); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'preset', + preset: 'analytics', + }, + { + type: 'preset', + preset: 'ml', + }, + { + type: 'preset', + preset: 'devtools', + }, + { + type: 'preset', + preset: 'management', + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + onProjectNavigationChange, + }); + + await runTests('treeDef'); + + renderResult.unmount(); + onProjectNavigationChange.mockReset(); + } + + // -- With UI Components + { + renderNavigation({ + navigationElement: ( + + + + + + + ), + onProjectNavigationChange, + }); + + await runTests('uiComponents'); + } + }); + + test('should render recently accessed items', async () => { + const recentlyAccessed$ = of([ + { label: 'This is an example', link: '/app/example/39859', id: '39850' }, + { label: 'Another example', link: '/app/example/5235', id: '5235' }, + ]); + + const runTests = async (type: TestType, { findByTestId }: RenderResult) => { + try { + expect(await findByTestId('nav-bucket-recentlyAccessed')).toBeVisible(); + expect((await findByTestId('nav-bucket-recentlyAccessed')).textContent).toBe( + 'RecentThis is an exampleAnother example' + ); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'recentlyAccessed', + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + services: { recentlyAccessed$ }, + }); + + await runTests('treeDef', renderResult); + renderResult.unmount(); + } + + // -- With UI Components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + + + + + + ), + services: { recentlyAccessed$ }, + }); + + await runTests('uiComponents', renderResult); + } + }); + + test('should render the cloud links', async () => { + const runTests = async (type: TestType, { findByTestId }: RenderResult) => { + try { + expect(await findByTestId(/nav-item-group1.cloudLink1/)).toBeVisible(); + expect(await findByTestId(/nav-item-group1.cloudLink2/)).toBeVisible(); + expect(await findByTestId(/nav-item-group1.cloudLink3/)).toBeVisible(); + + expect((await findByTestId(/nav-item-group1.cloudLink1/)).textContent).toBe( + 'Mock Users & RolesExternal link' + ); + expect((await findByTestId(/nav-item-group1.cloudLink2/)).textContent).toBe( + 'Mock PerformanceExternal link' + ); + expect((await findByTestId(/nav-item-group1.cloudLink3/)).textContent).toBe( + 'Mock Billing & SubscriptionsExternal link' + ); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'group1', + defaultIsCollapsed: false, + children: [ + { id: 'cloudLink1', cloudLink: 'userAndRoles' }, + { id: 'cloudLink2', cloudLink: 'performance' }, + { id: 'cloudLink3', cloudLink: 'billingAndSub' }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + }); + + await runTests('treeDef', renderResult); + renderResult.unmount(); + } + + // -- With UI Components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + + + + + + ), + }); + + await runTests('uiComponents', renderResult); + } + }); +}); diff --git a/packages/shared-ux/chrome/navigation/__jest__/links.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/links.test.tsx new file mode 100644 index 0000000000000..56da3d4494c89 --- /dev/null +++ b/packages/shared-ux/chrome/navigation/__jest__/links.test.tsx @@ -0,0 +1,234 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import './setup_jest_mocks'; +import React from 'react'; +import { type RenderResult } from '@testing-library/react'; +import { type Observable, of } from 'rxjs'; +import type { ChromeNavLink } from '@kbn/core-chrome-browser'; + +import { Navigation } from '../src/ui/components/navigation'; +import type { RootNavigationItemDefinition } from '../src/ui/types'; + +import { + getMockFn, + renderNavigation, + errorHandler, + TestType, + type ProjectNavigationChangeListener, +} from './utils'; + +describe('Links', () => { + test('should filter out unknown deeplinks', async () => { + const onProjectNavigationChange = getMockFn(); + const unknownLinkId = 'unknown'; + + const navLinks$: Observable = of([ + { + id: 'item1', + title: 'Title from deeplink', + baseUrl: '', + url: '', + href: '', + }, + ]); + + const runTests = async (type: TestType, { findByTestId, queryByTestId }: RenderResult) => { + try { + expect(await queryByTestId(new RegExp(`nav-item-root.group1.${unknownLinkId}`))).toBeNull(); + expect(await findByTestId(/nav-item-root.group1.item1/)).toBeVisible(); + expect(await findByTestId(/nav-item-root.group1.item1/)).toBeVisible(); + + expect(onProjectNavigationChange).toHaveBeenCalled(); + const lastCall = + onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; + const [{ navigationTree }] = lastCall; + const [root] = navigationTree; + expect(root.id).toBe('root'); + expect(root.children?.length).toBe(1); + expect(root.children?.[0].children?.length).toBe(1); + expect(root.children?.[0].children?.[0].id).toBe('item1'); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'root', + defaultIsCollapsed: false, + children: [ + { + id: 'group1', + defaultIsCollapsed: false, + children: [ + { + link: 'item1', + }, + { + link: unknownLinkId, + }, + ], + }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + onProjectNavigationChange, + services: { navLinks$ }, + }); + + await runTests('treeDef', renderResult); + + renderResult.unmount(); + onProjectNavigationChange.mockReset(); + } + + // -- With UI Components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + + link="item1" /> + {/* Should be removed */} + link={unknownLinkId} title="Should NOT be there" /> + + + + ), + onProjectNavigationChange, + services: { navLinks$ }, + }); + + await runTests('uiComponents', renderResult); + } + }); + + test('should allow href for absolute links', async () => { + const onProjectNavigationChange = getMockFn(); + + const runTests = async (type: TestType, { debug }: RenderResult) => { + try { + expect(onProjectNavigationChange).toHaveBeenCalled(); + const lastCall = + onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; + const [{ navigationTree }] = lastCall; + + const [root] = navigationTree; + expect(root.children?.[0].href).toBe('https://example.com'); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'root', + defaultIsCollapsed: false, + children: [ + { + id: 'item1', + title: 'Item 1', + href: 'https://example.com', + }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + onProjectNavigationChange, + }); + + await runTests('treeDef', renderResult); + + renderResult.unmount(); + onProjectNavigationChange.mockReset(); + } + + // -- With UI Components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + + + + ), + onProjectNavigationChange, + }); + + await runTests('uiComponents', renderResult); + } + }); + + test('should throw if href is not an absolute links', async () => { + // We'll mock the console.error to avoid dumping the (expected) error in the console + // source: https://github.com/jestjs/jest/pull/5267#issuecomment-356605468 + jest.spyOn(console, 'error'); + // @ts-expect-error we're mocking the console so "mockImplementation" exists + // eslint-disable-next-line no-console + console.error.mockImplementation(() => {}); + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'root', + defaultIsCollapsed: false, + children: [ + { + id: 'item1', + title: 'Item 1', + href: '../dashboards', + }, + ], + }, + ]; + + const expectToThrow = () => { + renderNavigation({ + navTreeDef: { body: navigationBody }, + }); + }; + + expect(expectToThrow).toThrowError('href must be an absolute URL. Node id [item1].'); + } + + // -- With UI Components + { + const expectToThrow = () => { + renderNavigation({ + navigationElement: ( + + + + + + ), + }); + }; + + expect(expectToThrow).toThrowError('href must be an absolute URL. Node id [item1].'); + // @ts-expect-error we're mocking the console so "mockImplementation" exists + // eslint-disable-next-line no-console + console.error.mockRestore(); + } + }); +}); diff --git a/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx new file mode 100644 index 0000000000000..40641eb31d2d2 --- /dev/null +++ b/packages/shared-ux/chrome/navigation/__jest__/panel.test.tsx @@ -0,0 +1,546 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import './setup_jest_mocks'; +import React from 'react'; +import { type RenderResult } from '@testing-library/react'; +import { BehaviorSubject } from 'rxjs'; +import type { ChromeProjectNavigationNode } from '@kbn/core-chrome-browser'; + +import { Navigation } from '../src/ui/components/navigation'; +import type { RootNavigationItemDefinition } from '../src/ui/types'; + +import { + renderNavigation, + errorHandler, + TestType, + getMockFn, + ProjectNavigationChangeListener, +} from './utils'; +import { PanelContentProvider } from '../src/ui'; + +describe('Panel', () => { + test('should render group as panel opener', async () => { + const onProjectNavigationChange = getMockFn(); + + const runTests = async (type: TestType, { findByTestId, queryByTestId }: RenderResult) => { + try { + expect(await findByTestId(/panelOpener-root.group1/)).toBeVisible(); + expect(queryByTestId(/sideNavPanel/)).toBeNull(); + (await findByTestId(/panelOpener-root.group1/)).click(); // open the panel + expect(queryByTestId(/sideNavPanel/)).toBeVisible(); + + expect(onProjectNavigationChange).toHaveBeenCalled(); + const lastCall = + onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; + const [{ navigationTree }] = lastCall; + + const [root] = navigationTree; + expect(root.id).toBe('root'); + expect(root.children?.[0]).toMatchObject({ + id: 'group1', + renderAs: 'panelOpener', + children: [ + { + id: 'management', + title: 'Deeplink management', + }, + ], + }); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: RootNavigationItemDefinition[] = [ + { + type: 'navGroup', + id: 'root', + isCollapsible: false, + children: [ + { + id: 'group1', + link: 'dashboards', + renderAs: 'panelOpener', + children: [{ link: 'management' }], + }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + onProjectNavigationChange, + }); + + await runTests('treeDef', renderResult); + + renderResult.unmount(); + } + + // -- With UI Components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + + + + + + ), + onProjectNavigationChange, + }); + await runTests('uiComponents', renderResult); + } + }); + + test('should not render group if all children are hidden', async () => { + const onProjectNavigationChange = getMockFn(); + + const runTests = async (type: TestType, { queryByTestId }: RenderResult) => { + try { + expect(queryByTestId(/panelOpener-root.group1/)).toBeNull(); + expect(queryByTestId(/panelOpener-root.group2/)).toBeNull(); + expect(queryByTestId(/panelOpener-root.group3/)).toBeVisible(); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'root', + isCollapsible: false, + children: [ + { + id: 'group1', + link: 'dashboards', + renderAs: 'panelOpener', + children: [{ link: 'unknown' }], + }, + { + id: 'group2', + link: 'dashboards', + renderAs: 'panelOpener', + children: [{ link: 'management', sideNavStatus: 'hidden' }], + }, + { + id: 'group3', + link: 'dashboards', + renderAs: 'panelOpener', + children: [{ link: 'management' }], // sideNavStatus is "visible" by default + }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + onProjectNavigationChange, + }); + + await runTests('treeDef', renderResult); + + renderResult.unmount(); + } + + // -- With UI Components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + + link="unknown" /> + + + + + + + + + + ), + onProjectNavigationChange, + }); + await runTests('uiComponents', renderResult); + } + }); + + describe('custom content', () => { + test('should render custom component inside the panel', async () => { + const panelContentProvider: PanelContentProvider = (id) => { + return { + content: ({ closePanel, selectedNode, activeNodes }) => { + const [path0 = []] = activeNodes; + return ( +
    +

    {selectedNode.id}

    +
      + {path0.map((node) => ( +
    • {node.id}
    • + ))} +
    + +
    + ); + }, + }; + }; + + const activeNodes$ = new BehaviorSubject([ + [ + { + id: 'activeGroup1', + title: 'Group 1', + path: ['activeGroup1'], + }, + { + id: 'activeItem1', + title: 'Item 1', + path: ['activeGroup1', 'activeItem1'], + }, + ], + ]); + + const runTests = async (type: TestType, { queryByTestId }: RenderResult) => { + try { + queryByTestId(/panelOpener-root.group1/)?.click(); // open the panel + + expect(queryByTestId(/customPanelContent/)).toBeVisible(); + // Test that the selected node is correclty passed + expect(queryByTestId(/customPanelSelectedNode/)?.textContent).toBe('root.group1'); + // Test that the active nodes are correclty passed + expect(queryByTestId(/customPanelActiveNodes/)?.textContent).toBe( + 'activeGroup1activeItem1' + ); + // Test that handler to close the panel is correctly passed + queryByTestId(/customPanelCloseBtn/)?.click(); // close the panel + expect(queryByTestId(/customPanelContent/)).toBeNull(); + expect(queryByTestId(/sideNavPanel/)).toBeNull(); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'root', + isCollapsible: false, + children: [ + { + id: 'group1', + link: 'dashboards', + renderAs: 'panelOpener', + children: [{ link: 'management' }], + }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + panelContentProvider, + services: { activeNodes$ }, + }); + + await runTests('treeDef', renderResult); + + renderResult.unmount(); + } + + // -- With UI Components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + + + + + + ), + services: { activeNodes$ }, + }); + await runTests('uiComponents', renderResult); + } + }); + }); + + describe('auto generated content', () => { + test('should rendre block groups with title', async () => { + const runTests = async ( + type: TestType, + { queryByTestId, queryAllByTestId }: RenderResult + ) => { + try { + queryByTestId(/panelOpener-root.group1/)?.click(); // open the panel + + expect(queryByTestId(/panelGroupId-foo/)).toBeVisible(); + expect(queryByTestId(/panelGroupTitleId-foo/)?.textContent).toBe('Foo'); + + const panelNavItems = queryAllByTestId(/panelNavItem/); + expect(panelNavItems.length).toBe(2); // "item2" has been filtered out as it is hidden + expect(panelNavItems.map(({ textContent }) => textContent?.trim())).toEqual([ + 'Item 1', + 'Item 3', + ]); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'root', + isCollapsible: false, + children: [ + { + id: 'group1', + link: 'dashboards', + renderAs: 'panelOpener', + children: [ + { + id: 'foo', + title: 'Foo', + children: [ + { id: 'item1', link: 'management', title: 'Item 1' }, + { id: 'item2', link: 'management', title: 'Item 2', sideNavStatus: 'hidden' }, + { id: 'item3', link: 'management', title: 'Item 3' }, + ], + }, + ], + }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + }); + + await runTests('treeDef', renderResult); + + renderResult.unmount(); + } + + // -- With UI Components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + + + + + + + + + + ), + }); + await runTests('uiComponents', renderResult); + } + }); + + test('should rendre block groups without title', async () => { + const runTests = async ( + type: TestType, + { queryByTestId, queryAllByTestId }: RenderResult + ) => { + try { + queryByTestId(/panelOpener-root.group1/)?.click(); // open the panel + + expect(queryByTestId(/panelGroupTitleId-foo/)).toBeNull(); // No title rendered + + const panelNavItems = queryAllByTestId(/panelNavItem/); + expect(panelNavItems.length).toBe(2); // "item2" has been filtered out as it is hidden + expect(panelNavItems.map(({ textContent }) => textContent?.trim())).toEqual([ + 'Item 1', + 'Item 3', + ]); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'root', + isCollapsible: false, + children: [ + { + id: 'group1', + link: 'dashboards', + renderAs: 'panelOpener', + children: [ + { + id: 'foo', + children: [ + { id: 'item1', link: 'management', title: 'Item 1' }, + { id: 'item2', link: 'management', title: 'Item 2', sideNavStatus: 'hidden' }, + { id: 'item3', link: 'management', title: 'Item 3' }, + ], + }, + ], + }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + }); + + await runTests('treeDef', renderResult); + + renderResult.unmount(); + } + + // -- With UI Components + { + // const renderResult = renderNavigation({ + // navigationElement: ( + // + // + // + // + // + // + // + // + // + // + // + // ), + // }); + // await runTests('uiComponents', renderResult); + } + }); + + test('should rendre accordion groups', async () => { + const runTests = async ( + type: TestType, + { queryByTestId, queryAllByTestId, findByRole }: RenderResult + ) => { + try { + queryByTestId(/panelOpener-root.group1/)?.click(); // open the panel + + expect(queryByTestId(/panelGroupId-foo/)).toBeVisible(); + + const panelNavItems = queryAllByTestId(/panelNavItem/); + expect(panelNavItems.length).toBe(2); // "item2" has been filtered out as it is hidden + + expect(queryByTestId(/panelNavItem-id-item1/)).not.toBeVisible(); // Accordion is collapsed + expect(queryByTestId(/panelNavItem-id-item3/)).not.toBeVisible(); // Accordion is collapsed + + queryByTestId(/panelAccordionBtnId-foo/)?.click(); // Expand accordion + + expect(queryByTestId(/panelNavItem-id-item1/)).toBeVisible(); + expect(queryByTestId(/panelNavItem-id-item3/)).toBeVisible(); + } catch (e) { + errorHandler(type)(e); + } + }; + + // -- Default navigation + { + const navigationBody: Array> = [ + { + type: 'navGroup', + id: 'root', + isCollapsible: false, + children: [ + { + id: 'group1', + link: 'dashboards', + renderAs: 'panelOpener', + children: [ + { + id: 'foo', + title: 'Foo', + renderAs: 'accordion', + children: [ + { id: 'item1', link: 'management', title: 'Item 1' }, + { id: 'item2', link: 'management', title: 'Item 2', sideNavStatus: 'hidden' }, + { id: 'item3', link: 'management', title: 'Item 3' }, + ], + }, + ], + }, + ], + }, + ]; + + const renderResult = renderNavigation({ + navTreeDef: { body: navigationBody }, + }); + + await runTests('treeDef', renderResult); + + renderResult.unmount(); + } + + // -- With UI Components + { + const renderResult = renderNavigation({ + navigationElement: ( + + + + + + + + + + + + ), + }); + await runTests('uiComponents', renderResult); + } + }); + }); +}); diff --git a/packages/shared-ux/chrome/navigation/__jest__/project_navigation.test.tsx b/packages/shared-ux/chrome/navigation/__jest__/project_navigation.test.tsx new file mode 100644 index 0000000000000..7b47c466b838b --- /dev/null +++ b/packages/shared-ux/chrome/navigation/__jest__/project_navigation.test.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import './setup_jest_mocks'; +import { type Observable, of } from 'rxjs'; +import type { ChromeNavLink } from '@kbn/core-chrome-browser'; + +import { navLinksMock } from '../mocks/src/navlinks'; +import type { ProjectNavigationTreeDefinition } from '../src/ui/types'; + +import { getMockFn, renderNavigation, type ProjectNavigationChangeListener } from './utils'; + +describe('Default navigation', () => { + /** + * INFO: the navigation system support providing "just" the serverless project navigation and we + * render all the rest (other sections, footer, recently accessed...) + * For now, none of the serverless project uses this feature as they all have completely different navs + */ + test('builds the full navigation tree when only the project is provided', async () => { + const onProjectNavigationChange = getMockFn(); + const navLinks$: Observable = of([ + ...navLinksMock, + { + id: 'item2', + title: 'Title from deeplink!', + baseUrl: '', + url: '', + href: '', + }, + ]); + + const projectNavigationTree: ProjectNavigationTreeDefinition = [ + { + id: 'group1', + title: 'Group 1', + children: [ + { + id: 'item1', + title: 'Item 1', + }, + { + id: 'item2', + link: 'item2', // Title from deeplink + }, + { + id: 'item3', + link: 'item2', + title: 'Deeplink title overriden', // Override title from deeplink + }, + { + link: 'disabled', + title: 'Should NOT be there', + }, + ], + }, + ]; + + renderNavigation({ + projectNavigationTree, + onProjectNavigationChange, + services: { navLinks$ }, + }); + + expect(onProjectNavigationChange).toHaveBeenCalled(); + const lastCall = + onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; + const [navTreeGenerated] = lastCall; + + expect(navTreeGenerated).toEqual({ + navigationTree: expect.any(Array), + }); + + // The project navigation tree passed + expect(navTreeGenerated.navigationTree).toMatchSnapshot(); + }); +}); diff --git a/packages/shared-ux/chrome/navigation/__jest__/setup_jest_mocks.ts b/packages/shared-ux/chrome/navigation/__jest__/setup_jest_mocks.ts new file mode 100644 index 0000000000000..9d41d7f29236a --- /dev/null +++ b/packages/shared-ux/chrome/navigation/__jest__/setup_jest_mocks.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useEffect } from 'react'; + +const mockUseEffect = useEffect; + +// Replace useDebounce() with a normal useEffect() +jest.mock('react-use/lib/useDebounce', () => { + return (cb: () => void, ms: number, deps: any[]) => { + mockUseEffect(() => { + cb(); + }, deps); + }; +}); + +export {}; diff --git a/packages/shared-ux/chrome/navigation/__jest__/utils.tsx b/packages/shared-ux/chrome/navigation/__jest__/utils.tsx new file mode 100644 index 0000000000000..547f71277683b --- /dev/null +++ b/packages/shared-ux/chrome/navigation/__jest__/utils.tsx @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React from 'react'; +import { render, type RenderResult } from '@testing-library/react'; +import type { ChromeProjectNavigation } from '@kbn/core-chrome-browser'; + +import { EuiThemeProvider } from '@elastic/eui'; +import { getServicesMock } from '../mocks/src/jest'; +import { NavigationProvider } from '../src/services'; +import { DefaultNavigation } from '../src/ui/default_navigation'; +import type { PanelContentProvider } from '../src/ui'; +import type { NavigationTreeDefinition, ProjectNavigationTreeDefinition } from '../src/ui/types'; +import { NavigationServices } from '../types'; + +const services = getServicesMock(); + +export type ProjectNavigationChangeListener = (projectNavigation: ChromeProjectNavigation) => void; +export type TestType = 'treeDef' | 'uiComponents'; + +export const renderNavigation = ({ + navTreeDef, + projectNavigationTree, + navigationElement, + services: overrideServices = {}, + onProjectNavigationChange = () => undefined, + panelContentProvider, +}: { + navTreeDef?: NavigationTreeDefinition; + projectNavigationTree?: ProjectNavigationTreeDefinition; + navigationElement?: React.ReactElement; + services?: Partial; + onProjectNavigationChange?: ProjectNavigationChangeListener; + panelContentProvider?: PanelContentProvider; +}): RenderResult => { + const element = navigationElement ?? ( + + ); + + const renderResult = render( + + + {element} + + + ); + + return renderResult; +}; + +export const errorHandler = (type: TestType) => (e: Error) => { + const err = new Error(`Failed to run tests for ${type}.`); + err.stack = e.stack; + // eslint-disable-next-line no-console + console.error(err.message); + throw err; +}; + +type ArgsType = T extends (...args: infer A) => any ? A : never; + +export function getMockFn any>() { + return jest.fn() as jest.Mock>; +} diff --git a/packages/shared-ux/chrome/navigation/index.ts b/packages/shared-ux/chrome/navigation/index.ts index 0659fe0461664..1d2c89f25e4d6 100644 --- a/packages/shared-ux/chrome/navigation/index.ts +++ b/packages/shared-ux/chrome/navigation/index.ts @@ -21,6 +21,7 @@ export type { RootNavigationItemDefinition, PanelComponentProps, PanelContent, + PanelContentProvider, } from './src/ui'; export type { NavigationServices } from './types'; diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/index.ts b/packages/shared-ux/chrome/navigation/src/ui/components/index.ts index 29de9b8d21dad..c4e66fcb72e75 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/index.ts +++ b/packages/shared-ux/chrome/navigation/src/ui/components/index.ts @@ -8,4 +8,8 @@ export { Navigation } from './navigation'; export type { Props as RecentlyAccessedProps } from './recently_accessed'; -export type { PanelContent, PanelComponentProps } from './panel'; +export type { + PanelContent, + PanelComponentProps, + ContentProvider as PanelContentProvider, +} from './panel'; diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx deleted file mode 100644 index 16a952281aec2..0000000000000 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.test.tsx +++ /dev/null @@ -1,894 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import type { - ChromeNavLink, - ChromeProjectNavigation, - ChromeProjectNavigationNode, -} from '@kbn/core-chrome-browser'; -import { render } from '@testing-library/react'; -import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { BehaviorSubject, of, type Observable } from 'rxjs'; -import { EuiThemeProvider } from '@elastic/eui'; - -import { getServicesMock } from '../../../mocks/src/jest'; -import { NavigationProvider } from '../../services'; -import { Navigation } from './navigation'; - -// There is a 100ms debounce to update project navigation tree -const SET_NAVIGATION_DELAY = 100; - -describe('', () => { - const services = getServicesMock(); - - beforeAll(() => { - jest.useFakeTimers(); - }); - - afterAll(() => { - jest.useRealTimers(); - }); - - describe('builds the navigation tree', () => { - test('render reference UI and build the navigation tree', async () => { - const onProjectNavigationChange = jest.fn(); - - const { findByTestId } = render( - - - - - - - - - - - - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect(await findByTestId(/nav-item-group1.item1/)).toBeVisible(); - expect(await findByTestId(/nav-item-group1.item2/)).toBeVisible(); - expect(await findByTestId(/nav-item-group1.group1A\s/)).toBeVisible(); - expect(await findByTestId(/nav-item-group1.group1A.item1/)).toBeVisible(); - expect(await findByTestId(/nav-item-group1.group1A.group1A_1\s/)).toBeVisible(); - - // Click the last group to expand and show the last depth - (await findByTestId(/nav-item-group1.group1A.group1A_1\s/)).click(); - - expect(await findByTestId(/nav-item-group1.group1A.group1A_1.item1/)).toBeVisible(); - - expect(onProjectNavigationChange).toHaveBeenCalled(); - const lastCall = - onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; - const [navTree] = lastCall; - - expect(navTree.navigationTree).toMatchInlineSnapshot(` - Array [ - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": undefined, - "href": "https://foo", - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "item1", - ], - "sideNavStatus": "visible", - "title": "Item 1", - }, - Object { - "children": undefined, - "deepLink": undefined, - "href": "https://foo", - "id": "item2", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "item2", - ], - "sideNavStatus": "visible", - "title": "Item 2", - }, - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": undefined, - "href": "https://foo", - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "group1A", - "item1", - ], - "sideNavStatus": "visible", - "title": "Group 1A Item 1", - }, - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": undefined, - "href": "https://foo", - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "group1A", - "group1A_1", - "item1", - ], - "sideNavStatus": "visible", - "title": "Group 1A_1 Item 1", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group1A_1", - "isActive": true, - "isGroup": true, - "path": Array [ - "group1", - "group1A", - "group1A_1", - ], - "renderAs": "accordion", - "sideNavStatus": "visible", - "title": "Group1A_1", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group1A", - "isActive": true, - "isGroup": true, - "path": Array [ - "group1", - "group1A", - ], - "renderAs": "accordion", - "sideNavStatus": "visible", - "title": "Group1A", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group1", - "isActive": true, - "isGroup": true, - "path": Array [ - "group1", - ], - "renderAs": "accordion", - "sideNavStatus": "visible", - "title": "", - }, - ] - `); - }); - - test('should read the title from props, children or deeplink', async () => { - const navLinks$: Observable = of([ - { - id: 'item1', - title: 'Title from deeplink', - baseUrl: '', - url: '', - href: '', - }, - ]); - - const onProjectNavigationChange = jest.fn(); - - render( - - - - - - {/* Title from deeplink */} - id="item1" link="item1" /> - id="item2" link="item1" title="Overwrite deeplink title" /> - - Title in children - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect(onProjectNavigationChange).toHaveBeenCalled(); - const lastCall = - onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; - const [navTree] = lastCall; - - expect(navTree.navigationTree).toMatchInlineSnapshot(` - Array [ - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": Object { - "baseUrl": "", - "href": "", - "id": "item1", - "title": "Title from deeplink", - "url": "", - }, - "href": undefined, - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "root", - "group1", - "item1", - ], - "sideNavStatus": "visible", - "title": "Title from deeplink", - }, - Object { - "children": undefined, - "deepLink": Object { - "baseUrl": "", - "href": "", - "id": "item1", - "title": "Title from deeplink", - "url": "", - }, - "href": undefined, - "id": "item2", - "isActive": false, - "isGroup": false, - "path": Array [ - "root", - "group1", - "item2", - ], - "sideNavStatus": "visible", - "title": "Overwrite deeplink title", - }, - Object { - "children": undefined, - "deepLink": undefined, - "href": undefined, - "id": "item3", - "isActive": false, - "isGroup": false, - "path": Array [ - "root", - "group1", - "item3", - ], - "sideNavStatus": "visible", - "title": "Title in props", - }, - Object { - "children": undefined, - "deepLink": undefined, - "href": undefined, - "id": "item4", - "isActive": false, - "isGroup": false, - "path": Array [ - "root", - "group1", - "item4", - ], - "sideNavStatus": "visible", - "title": "Title in children", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group1", - "isActive": false, - "isGroup": true, - "path": Array [ - "root", - "group1", - ], - "sideNavStatus": "visible", - "title": "", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "root", - "isActive": false, - "isGroup": true, - "path": Array [ - "root", - ], - "sideNavStatus": "visible", - "title": "", - }, - ] - `); - }); - - test('should filter out unknown deeplinks', async () => { - const navLinks$: Observable = of([ - { - id: 'item1', - title: 'Title from deeplink', - baseUrl: '', - url: '', - href: '', - }, - ]); - - const onProjectNavigationChange = jest.fn(); - - const { findByTestId } = render( - - - - - - {/* Title from deeplink */} - id="item1" link="item1" /> - {/* Should not appear */} - - id="unknownLink" - link="unknown" - title="Should NOT be there" - /> - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect(await findByTestId(/nav-item-root.group1.item1/)).toBeVisible(); - expect(await findByTestId(/nav-item-root.group1.item1/)).toBeVisible(); - - expect(onProjectNavigationChange).toHaveBeenCalled(); - const lastCall = - onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; - const [navTree] = lastCall; - - expect(navTree.navigationTree).toMatchInlineSnapshot(` - Array [ - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": Object { - "baseUrl": "", - "href": "", - "id": "item1", - "title": "Title from deeplink", - "url": "", - }, - "href": undefined, - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "root", - "group1", - "item1", - ], - "sideNavStatus": "visible", - "title": "Title from deeplink", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group1", - "isActive": true, - "isGroup": true, - "path": Array [ - "root", - "group1", - ], - "sideNavStatus": "visible", - "title": "", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "root", - "isActive": true, - "isGroup": true, - "path": Array [ - "root", - ], - "sideNavStatus": "visible", - "title": "", - }, - ] - `); - }); - - test('should not render the group if it does not have children AND no href or deeplink', async () => { - const navLinks$: Observable = of([ - { - id: 'item1', - title: 'Title from deeplink', - baseUrl: '', - url: '', - href: '', - }, - ]); - const onProjectNavigationChange = jest.fn(); - - const { queryByTestId } = render( - - - - - - id="item1" link="notRegistered" /> - - - id="item1" link="item1" /> - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect(queryByTestId(/nav-group-root.group1/)).toBeNull(); - expect(queryByTestId(/nav-item-root.group2.item1/)).toBeVisible(); - - expect(onProjectNavigationChange).toHaveBeenCalled(); - const lastCall = - onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; - const [navTree] = lastCall; - - expect(navTree.navigationTree).toMatchInlineSnapshot(` - Array [ - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": undefined, - "href": undefined, - "id": "group1", - "isActive": true, - "isGroup": true, - "path": Array [ - "root", - "group1", - ], - "sideNavStatus": "visible", - "title": "", - }, - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": Object { - "baseUrl": "", - "href": "", - "id": "item1", - "title": "Title from deeplink", - "url": "", - }, - "href": undefined, - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "root", - "group2", - "item1", - ], - "sideNavStatus": "visible", - "title": "Title from deeplink", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group2", - "isActive": true, - "isGroup": true, - "path": Array [ - "root", - "group2", - ], - "sideNavStatus": "visible", - "title": "", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "root", - "isActive": true, - "isGroup": true, - "path": Array [ - "root", - ], - "sideNavStatus": "visible", - "title": "", - }, - ] - `); - }); - - test('should render group preset (analytics, ml...)', async () => { - const onProjectNavigationChange = jest.fn(); - - render( - - - - - - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect(onProjectNavigationChange).toHaveBeenCalled(); - const lastCall = - onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; - const [navTreeGenerated] = lastCall; - - expect(navTreeGenerated).toEqual({ - navigationTree: expect.any(Array), - }); - }); - - test('should render recently accessed items', async () => { - const recentlyAccessed$ = of([ - { label: 'This is an example', link: '/app/example/39859', id: '39850' }, - { label: 'Another example', link: '/app/example/5235', id: '5235' }, - ]); - - const { findByTestId } = render( - - - - - - - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect(await findByTestId('nav-bucket-recentlyAccessed')).toBeVisible(); - expect((await findByTestId('nav-bucket-recentlyAccessed')).textContent).toBe( - 'RecentThis is an exampleAnother example' - ); - }); - - test('should allow href for absolute links', async () => { - const onProjectNavigationChange = jest.fn(); - - render( - - - - - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect(onProjectNavigationChange).toHaveBeenCalled(); - const lastCall = - onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; - const [navTreeGenerated] = lastCall; - - expect(navTreeGenerated.navigationTree).toMatchInlineSnapshot(` - Array [ - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": undefined, - "href": "https://example.com", - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "item1", - ], - "sideNavStatus": "visible", - "title": "Item 1", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group1", - "isActive": false, - "isGroup": true, - "path": Array [ - "group1", - ], - "sideNavStatus": "visible", - "title": "", - }, - ] - `); - }); - - test('should throw if href is not an absolute links', async () => { - // We'll mock the console.error to avoid dumping the (expected) error in the console - // source: https://github.com/jestjs/jest/pull/5267#issuecomment-356605468 - jest.spyOn(console, 'error'); - // @ts-expect-error we're mocking the console so "mockImplementation" exists - // eslint-disable-next-line no-console - console.error.mockImplementation(() => {}); - - const onProjectNavigationChange = jest.fn(); - - const expectToThrow = () => { - render( - - - - - - - - - - ); - }; - - expect(expectToThrow).toThrowError('href must be an absolute URL. Node id [item1].'); - // @ts-expect-error we're mocking the console so "mockImplementation" exists - // eslint-disable-next-line no-console - console.error.mockRestore(); - }); - - test('should set the active node', async () => { - const navLinks$: Observable = of([ - { - id: 'item1', - title: 'Item 1', - baseUrl: '', - url: '', - href: '', - }, - { - id: 'item2', - title: 'Item 2', - baseUrl: '', - url: '', - href: '', - }, - ]); - - const activeNodes$ = new BehaviorSubject([ - [ - { - id: 'group1', - title: 'Group 1', - path: ['group1'], - }, - { - id: 'item1', - title: 'Item 1', - path: ['group1', 'item1'], - }, - ], - ]); - - const getActiveNodes$ = () => activeNodes$; - - const { findByTestId } = render( - - - - - link="item1" title="Item 1" /> - link="item2" title="Item 2" /> - - - - - ); - - expect((await findByTestId(/nav-item-group1.item1/)).dataset.testSubj).toMatch( - /nav-item-isActive/ - ); - expect((await findByTestId(/nav-item-group1.item2/)).dataset.testSubj).not.toMatch( - /nav-item-isActive/ - ); - - await act(async () => { - activeNodes$.next([ - [ - { - id: 'group1', - title: 'Group 1', - path: ['group1'], - }, - { - id: 'item2', - title: 'Item 2', - path: ['group1', 'item2'], - }, - ], - ]); - }); - - expect((await findByTestId(/nav-item-group1.item1/)).dataset.testSubj).not.toMatch( - /nav-item-isActive/ - ); - expect((await findByTestId(/nav-item-group1.item2/)).dataset.testSubj).toMatch( - /nav-item-isActive/ - ); - }); - - test('should override the history behaviour to set the active node', async () => { - const navLinks$: Observable = of([ - { - id: 'item1', - title: 'Item 1', - baseUrl: '', - url: '', - href: '', - }, - ]); - - const activeNodes$ = new BehaviorSubject([]); - const getActiveNodes$ = () => activeNodes$; - - const onProjectNavigationChange = (nav: ChromeProjectNavigation) => { - nav.navigationTree.forEach((node) => { - if (node.children) { - node.children.forEach((child) => { - if (child.getIsActive?.('mockLocation' as any)) { - activeNodes$.next([[child]]); - } - }); - } - }); - }; - - const { findByTestId } = render( - - - - - - link="item1" - title="Item 1" - getIsActive={() => { - return true; - }} - /> - - - - - ); - - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - - expect((await findByTestId(/nav-item-group1.item1/)).dataset.testSubj).toMatch( - /nav-item-isActive/ - ); - }); - }); - - describe('cloud links', () => { - test('render the cloud link', async () => { - const onProjectNavigationChange = jest.fn(); - - const { findByTestId } = render( - - - - - - - - - - - - ); - - expect(await findByTestId(/nav-item-group1.cloudLink1/)).toBeVisible(); - expect(await findByTestId(/nav-item-group1.cloudLink2/)).toBeVisible(); - expect(await findByTestId(/nav-item-group1.cloudLink3/)).toBeVisible(); - - expect((await findByTestId(/nav-item-group1.cloudLink1/)).textContent).toBe( - 'Mock Users & RolesExternal link' - ); - expect((await findByTestId(/nav-item-group1.cloudLink2/)).textContent).toBe( - 'Mock PerformanceExternal link' - ); - expect((await findByTestId(/nav-item-group1.cloudLink3/)).textContent).toBe( - 'Mock Billing & SubscriptionsExternal link' - ); - }); - }); -}); diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.tsx index 83dc748047a93..8f74abee6a110 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation.tsx @@ -93,8 +93,8 @@ export function Navigation({ }); }, []); - const register = useCallback( - (navNode: ChromeProjectNavigationNode) => { + const register = useCallback( + (navNode) => { if (orderChildrenRef.current[navNode.id] === undefined) { orderChildrenRef.current[navNode.id] = idx.current++; } diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_section_ui.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_section_ui.tsx index 8fc2a2adf800a..d91c59d6988ef 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/navigation_section_ui.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/navigation_section_ui.tsx @@ -60,6 +60,15 @@ const getRenderAs = (navNode: ChromeProjectNavigationNode): RenderAs => { return 'block'; }; +const getTestSubj = (navNode: ChromeProjectNavigationNode, isActive = false): string => { + const { id, deepLink } = navNode; + return classnames(`nav-item`, `nav-item-${id}`, { + [`nav-item-deepLinkId-${deepLink?.id}`]: !!deepLink, + [`nav-item-id-${id}`]: id, + [`nav-item-isActive`]: isActive, + }); +}; + const filterChildren = ( children?: ChromeProjectNavigationNode[] ): ChromeProjectNavigationNode[] | undefined => { @@ -94,15 +103,18 @@ const isEuiCollapsibleNavItemProps = ( }; const renderBlockTitle: ( - { title }: ChromeProjectNavigationNode, + navNode: ChromeProjectNavigationNode, { spaceBefore }: { spaceBefore: EuiThemeSize | null } ) => Required['renderItem'] = - ({ title }, { spaceBefore }) => - () => - ( + (navNode, { spaceBefore }) => + () => { + const { title } = navNode; + const dataTestSubj = getTestSubj(navNode); + return ( { return { marginTop: spaceBefore ? euiTheme.size[spaceBefore] : undefined, @@ -114,6 +126,7 @@ const renderBlockTitle: (
    {title}
    ); + }; const renderGroup = ( navGroup: ChromeProjectNavigationNode, @@ -165,27 +178,14 @@ const nodeToEuiCollapsibleNavProps = ( } => { const { navNode, isItem, hasChildren, hasLink } = serializeNavNode(_navNode); - const { - id, - title, - href, - icon, - renderAs, - isActive, - deepLink, - spaceBefore: _spaceBefore, - } = navNode; + const { id, title, href, icon, renderAs, isActive, spaceBefore: _spaceBefore } = navNode; const isExternal = Boolean(href) && isAbsoluteLink(href!); const isAccordion = hasChildren && !isItem; const isAccordionExpanded = (itemsState[id]?.isCollapsed ?? DEFAULT_IS_COLLAPSED) === false; const isSelected = isAccordion && isAccordionExpanded ? false : isActive; - const dataTestSubj = classnames(`nav-item`, `nav-item-${id}`, { - [`nav-item-deepLinkId-${deepLink?.id}`]: !!deepLink, - [`nav-item-id-${id}`]: id, - [`nav-item-isActive`]: isSelected, - }); + const dataTestSubj = getTestSubj(navNode, isSelected); let spaceBefore = _spaceBefore; if (spaceBefore === undefined && treeDepth === 1 && hasChildren) { @@ -291,6 +291,18 @@ const nodeToEuiCollapsibleNavProps = ( return { items, isVisible }; }; +// Temporary solution to prevent showing the outline when the page load when the +// accordion is auto-expanded if one of its children is active +// Once https://github.com/elastic/eui/pull/7314 is released in Kibana we can +// safely remove this CSS class. +const className = css` + .euiAccordion__childWrapper, + .euiAccordion__children, + .euiCollapsibleNavAccordion__children { + outline: none; + } +`; + interface AccordionItemsState { [navNodeId: string]: { isCollapsible: boolean; @@ -453,15 +465,7 @@ export const NavigationSectionUI: FC = ({ navNode }) => { return ( diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/panel/panel_group.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/panel/panel_group.tsx index d3f39f291f25d..56c27124a4a06 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/panel/panel_group.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/panel/panel_group.tsx @@ -69,6 +69,7 @@ export const PanelGroup: FC = ({ navNode, isFirstInList, hasHorizontalRul const removePaddingTop = !hasTitle && !isFirstInList; const someChildIsGroup = filteredChildren?.some((child) => !!child.children); const firstChildIsGroup = !!filteredChildren?.[0]?.children; + const groupTestSubj = `panelGroup panelGroupId-${navNode.id}`; let spaceBefore = _spaceBefore; if (spaceBefore === undefined) { @@ -106,6 +107,10 @@ export const PanelGroup: FC = ({ navNode, isFirstInList, hasHorizontalRul buttonContent={title} className={classNames.accordion} buttonClassName={accordionButtonClassName} + data-test-subj={groupTestSubj} + buttonProps={{ + 'data-test-subj': `panelAccordionBtnId-${navNode.id}`, + }} > <> {!firstChildIsGroup && } @@ -118,10 +123,14 @@ export const PanelGroup: FC = ({ navNode, isFirstInList, hasHorizontalRul } return ( - <> +
    {spaceBefore !== null && } {hasTitle && ( - +

    {title}

    )} @@ -139,6 +148,6 @@ export const PanelGroup: FC = ({ navNode, isFirstInList, hasHorizontalRul {renderChildren()} {appendHorizontalRule && } - +
    ); }; diff --git a/packages/shared-ux/chrome/navigation/src/ui/components/panel/panel_nav_item.tsx b/packages/shared-ux/chrome/navigation/src/ui/components/panel/panel_nav_item.tsx index 651408697169a..aa87582497b07 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/components/panel/panel_nav_item.tsx +++ b/packages/shared-ux/chrome/navigation/src/ui/components/panel/panel_nav_item.tsx @@ -42,7 +42,7 @@ export const PanelNavItem: FC = ({ item }) => { wrapText className="sideNavPanelLink" size="s" - data-test-subj={`nav-item-id-${item.id}`} + data-test-subj={`panelNavItem panelNavItem-id-${item.id}`} href={href} iconType={icon} onClick={onClick} diff --git a/packages/shared-ux/chrome/navigation/src/ui/default_navigation.test.tsx b/packages/shared-ux/chrome/navigation/src/ui/default_navigation.test.tsx deleted file mode 100644 index 9cc7d72525f92..0000000000000 --- a/packages/shared-ux/chrome/navigation/src/ui/default_navigation.test.tsx +++ /dev/null @@ -1,871 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { render } from '@testing-library/react'; -import { type Observable, of, BehaviorSubject } from 'rxjs'; -import type { - ChromeNavLink, - ChromeProjectNavigation, - ChromeProjectNavigationNode, -} from '@kbn/core-chrome-browser'; -import { EuiThemeProvider } from '@elastic/eui'; - -import { getServicesMock } from '../../mocks/src/jest'; -import { NavigationProvider } from '../services'; -import { DefaultNavigation } from './default_navigation'; -import type { ProjectNavigationTreeDefinition, RootNavigationItemDefinition } from './types'; -import { navLinksMock } from '../../mocks/src/navlinks'; -import { act } from 'react-dom/test-utils'; - -// There is a 100ms debounce to update project navigation tree -const SET_NAVIGATION_DELAY = 100; - -describe('', () => { - const services = getServicesMock(); - beforeAll(() => { - jest.useFakeTimers(); - }); - - afterAll(() => { - jest.useRealTimers(); - }); - - describe('builds custom navigation tree', () => { - test('render reference UI and build the navigation tree', async () => { - const onProjectNavigationChange = jest.fn(); - - const navigationBody: RootNavigationItemDefinition[] = [ - { - type: 'navGroup', - id: 'group1', - children: [ - { - id: 'item1', - title: 'Item 1', - href: 'http://foo', - }, - { - id: 'item2', - title: 'Item 2', - href: 'http://foo', - }, - { - id: 'group1A', - title: 'Group1A', - children: [ - { - id: 'item1', - title: 'Group 1A Item 1', - href: 'http://foo', - }, - { - id: 'group1A_1', - title: 'Group1A_1', - children: [ - { - id: 'item1', - title: 'Group 1A_1 Item 1', - href: 'http://foo', - }, - ], - }, - ], - }, - ], - }, - ]; - - const { findAllByTestId } = render( - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - // Click the last group to expand and show the last depth - (await findAllByTestId(/nav-item-group1.group1A.group1A_1/))[0].click(); - - expect(onProjectNavigationChange).toHaveBeenCalled(); - const lastCall = - onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; - const [navTreeGenerated] = lastCall; - - expect(navTreeGenerated.navigationTree).toMatchInlineSnapshot(` - Array [ - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": undefined, - "href": "http://foo", - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "item1", - ], - "sideNavStatus": "visible", - "title": "Item 1", - }, - Object { - "children": undefined, - "deepLink": undefined, - "href": "http://foo", - "id": "item2", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "item2", - ], - "sideNavStatus": "visible", - "title": "Item 2", - }, - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": undefined, - "href": "http://foo", - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "group1A", - "item1", - ], - "sideNavStatus": "visible", - "title": "Group 1A Item 1", - }, - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": undefined, - "href": "http://foo", - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "group1", - "group1A", - "group1A_1", - "item1", - ], - "sideNavStatus": "visible", - "title": "Group 1A_1 Item 1", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group1A_1", - "isActive": false, - "isGroup": true, - "path": Array [ - "group1", - "group1A", - "group1A_1", - ], - "sideNavStatus": "visible", - "title": "Group1A_1", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group1A", - "isActive": false, - "isGroup": true, - "path": Array [ - "group1", - "group1A", - ], - "sideNavStatus": "visible", - "title": "Group1A", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group1", - "isActive": false, - "isGroup": true, - "path": Array [ - "group1", - ], - "sideNavStatus": "visible", - "title": "", - "type": "navGroup", - }, - ] - `); - }); - - test('should read the title from deeplink', async () => { - const navLinks$: Observable = of([ - ...navLinksMock, - { - id: 'item1', - title: 'Title from deeplink', - baseUrl: '', - url: '', - href: '', - }, - ]); - - const onProjectNavigationChange = jest.fn(); - - const navigationBody: Array> = [ - { - type: 'navGroup', - id: 'root', - children: [ - { - id: 'group1', - children: [ - { - id: 'item1', - link: 'item1', // Title from deeplink - }, - { - id: 'item2', - link: 'item1', // Overwrite title from deeplink - title: 'Overwrite deeplink title', - }, - { - id: 'item3', - link: 'unknown', // Unknown deeplink - title: 'Should not be rendered', - }, - ], - }, - ], - }, - ]; - - render( - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect(onProjectNavigationChange).toHaveBeenCalled(); - const lastCall = - onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; - const [navTreeGenerated] = lastCall; - - expect(navTreeGenerated.navigationTree).toMatchInlineSnapshot(` - Array [ - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": Object { - "baseUrl": "", - "href": "", - "id": "item1", - "title": "Title from deeplink", - "url": "", - }, - "href": undefined, - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "root", - "group1", - "item1", - ], - "sideNavStatus": "visible", - "title": "Title from deeplink", - }, - Object { - "children": undefined, - "deepLink": Object { - "baseUrl": "", - "href": "", - "id": "item1", - "title": "Title from deeplink", - "url": "", - }, - "href": undefined, - "id": "item2", - "isActive": false, - "isGroup": false, - "path": Array [ - "root", - "group1", - "item2", - ], - "sideNavStatus": "visible", - "title": "Overwrite deeplink title", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group1", - "isActive": false, - "isGroup": true, - "path": Array [ - "root", - "group1", - ], - "sideNavStatus": "visible", - "title": "", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "root", - "isActive": false, - "isGroup": true, - "path": Array [ - "root", - ], - "sideNavStatus": "visible", - "title": "", - "type": "navGroup", - }, - ] - `); - }); - - test("shouldn't render hidden deeplink", async () => { - const navLinks$: Observable = of([ - ...navLinksMock, - { - id: 'item1', - title: 'Item 1', - baseUrl: '', - url: '', - href: '', - }, - { - id: 'item', - title: 'Item 2', - hidden: true, - baseUrl: '', - url: '', - href: '', - }, - ]); - - const onProjectNavigationChange = jest.fn(); - - const navigationBody: Array> = [ - { - type: 'navGroup', - id: 'root', - children: [ - { - id: 'group1', - children: [ - { - id: 'item1', - link: 'item1', - }, - { - id: 'item2', - link: 'item2', // this should be hidden from sidenav - }, - ], - }, - ], - }, - ]; - - const { queryByTestId } = render( - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect(onProjectNavigationChange).toHaveBeenCalled(); - const lastCall = - onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; - const [navTreeGenerated] = lastCall; - - expect(navTreeGenerated.navigationTree).toMatchInlineSnapshot(` - Array [ - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": Object { - "baseUrl": "", - "href": "", - "id": "item1", - "title": "Item 1", - "url": "", - }, - "href": undefined, - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "root", - "group1", - "item1", - ], - "sideNavStatus": "visible", - "title": "Item 1", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group1", - "isActive": false, - "isGroup": true, - "path": Array [ - "root", - "group1", - ], - "sideNavStatus": "visible", - "title": "", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "root", - "isActive": false, - "isGroup": true, - "path": Array [ - "root", - ], - "sideNavStatus": "visible", - "title": "", - "type": "navGroup", - }, - ] - `); - - expect(await queryByTestId(/nav-item-deepLinkId-item1/)).not.toBeNull(); - expect(await queryByTestId(/nav-item-deepLinkId-item2/)).toBeNull(); - }); - - test('should allow href for absolute links', async () => { - const onProjectNavigationChange = jest.fn(); - - const navigationBody: Array> = [ - { - type: 'navGroup', - id: 'root', - children: [ - { - id: 'group1', - children: [ - { - id: 'item1', - title: 'Absolute link', - href: 'https://example.com', - }, - ], - }, - ], - }, - ]; - - render( - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect(onProjectNavigationChange).toHaveBeenCalled(); - const lastCall = - onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; - const [navTreeGenerated] = lastCall; - - expect(navTreeGenerated.navigationTree).toMatchInlineSnapshot(` - Array [ - Object { - "children": Array [ - Object { - "children": Array [ - Object { - "children": undefined, - "deepLink": undefined, - "href": "https://example.com", - "id": "item1", - "isActive": false, - "isGroup": false, - "path": Array [ - "root", - "group1", - "item1", - ], - "sideNavStatus": "visible", - "title": "Absolute link", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "group1", - "isActive": false, - "isGroup": true, - "path": Array [ - "root", - "group1", - ], - "sideNavStatus": "visible", - "title": "", - }, - ], - "deepLink": undefined, - "href": undefined, - "id": "root", - "isActive": false, - "isGroup": true, - "path": Array [ - "root", - ], - "sideNavStatus": "visible", - "title": "", - "type": "navGroup", - }, - ] - `); - }); - - test('should throw if href is not an absolute links', async () => { - // We'll mock the console.error to avoid dumping the (expected) error in the console - // source: https://github.com/jestjs/jest/pull/5267#issuecomment-356605468 - jest.spyOn(console, 'error'); - // @ts-expect-error we're mocking the console so "mockImplementation" exists - // eslint-disable-next-line no-console - console.error.mockImplementation(() => {}); - - const onProjectNavigationChange = jest.fn(); - - const navigationBody: Array> = [ - { - type: 'navGroup', - id: 'root', - children: [ - { - id: 'group1', - children: [ - { - id: 'item1', - title: 'Absolute link', - href: '../dashboards', - }, - ], - }, - ], - }, - ]; - - const expectToThrow = () => { - render( - - - - - - ); - }; - - expect(expectToThrow).toThrowError('href must be an absolute URL. Node id [item1].'); - // @ts-expect-error we're mocking the console so "mockImplementation" exists - // eslint-disable-next-line no-console - console.error.mockRestore(); - }); - - test('should render recently accessed items', async () => { - const recentlyAccessed$ = of([ - { label: 'This is an example', link: '/app/example/39859', id: '39850' }, - { label: 'Another example', link: '/app/example/5235', id: '5235' }, - ]); - - const navigationBody: RootNavigationItemDefinition[] = [ - { - type: 'recentlyAccessed', - }, - ]; - - const { findByTestId } = render( - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect(await findByTestId('nav-bucket-recentlyAccessed')).toBeVisible(); - expect((await findByTestId('nav-bucket-recentlyAccessed')).textContent).toBe( - 'RecentThis is an exampleAnother example' - ); - }); - - test('should set the active node', async () => { - const navLinks$: Observable = of([ - { - id: 'item1', - title: 'Item 1', - baseUrl: '', - url: '', - href: '', - }, - { - id: 'item2', - title: 'Item 2', - baseUrl: '', - url: '', - href: '', - }, - ]); - - const navigationBody: RootNavigationItemDefinition[] = [ - { - type: 'navGroup', - id: 'group1', - children: [ - { - link: 'item1' as any, - title: 'Item 1', - }, - { - link: 'item2' as any, - title: 'Item 2', - }, - ], - }, - ]; - - const activeNodes$ = new BehaviorSubject([ - [ - { - id: 'group1', - title: 'Group 1', - path: ['group1'], - }, - { - id: 'item1', - title: 'Item 1', - path: ['group1', 'item1'], - }, - ], - ]); - - const getActiveNodes$ = () => activeNodes$; - - const { findByTestId } = render( - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect((await findByTestId(/nav-item-group1.item1/)).dataset.testSubj).toMatch( - /nav-item-isActive/ - ); - expect((await findByTestId(/nav-item-group1.item2/)).dataset.testSubj).not.toMatch( - /nav-item-isActive/ - ); - }); - - test('should override the history behaviour to set the active node', async () => { - const navLinks$: Observable = of([ - { - id: 'item1', - title: 'Item 1', - baseUrl: '', - url: '', - href: '', - }, - ]); - - const navigationBody: RootNavigationItemDefinition[] = [ - { - type: 'navGroup', - id: 'group1', - children: [ - { - link: 'item1' as any, - title: 'Item 1', - getIsActive: () => true, - }, - ], - }, - ]; - - const activeNodes$ = new BehaviorSubject([[]]); - const getActiveNodes$ = () => activeNodes$; - - const onProjectNavigationChange = (nav: ChromeProjectNavigation) => { - nav.navigationTree.forEach((node) => { - if (node.children) { - node.children.forEach((child) => { - if (child.getIsActive) { - activeNodes$.next([[child]]); - } - }); - } - }); - }; - - const { findByTestId } = render( - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect((await findByTestId(/nav-item-group1.item1/)).dataset.testSubj).toMatch( - /nav-item-isActive/ - ); - }); - }); - - describe('builds the full navigation tree when only custom project is provided', () => { - test('reading the title from config or deeplink', async () => { - const navLinks$: Observable = of([ - ...navLinksMock, - { - id: 'item2', - title: 'Title from deeplink!', - baseUrl: '', - url: '', - href: '', - }, - ]); - - const onProjectNavigationChange = jest.fn(); - - // Custom project navigation tree definition - const projectNavigationTree: ProjectNavigationTreeDefinition = [ - { - id: 'group1', - title: 'Group 1', - children: [ - { - id: 'item1', - title: 'Item 1', - }, - { - id: 'item2', - link: 'item2', // Title from deeplink - }, - { - id: 'item3', - link: 'item2', - title: 'Deeplink title overriden', // Override title from deeplink - }, - { - link: 'disabled', - title: 'Should NOT be there', - }, - ], - }, - ]; - - render( - - - - - - ); - - await act(async () => { - jest.advanceTimersByTime(SET_NAVIGATION_DELAY); - }); - - expect(onProjectNavigationChange).toHaveBeenCalled(); - const lastCall = - onProjectNavigationChange.mock.calls[onProjectNavigationChange.mock.calls.length - 1]; - const [navTreeGenerated] = lastCall; - - expect(navTreeGenerated).toEqual({ - navigationTree: expect.any(Array), - }); - - // The project navigation tree passed - expect(navTreeGenerated.navigationTree).toMatchSnapshot(); - }); - - describe('cloud links', () => { - test('render the cloud link', async () => { - const { findByTestId } = render( - - - - - - ); - - expect( - (await findByTestId(/nav-item-project_settings_project_nav.cloudLinkUserAndRoles/)) - .textContent - ).toBe('Mock Users & RolesExternal link'); - - expect( - (await findByTestId(/nav-item-project_settings_project_nav.cloudLinkBilling/)).textContent - ).toBe('Mock Billing & SubscriptionsExternal link'); - }); - }); - }); -}); diff --git a/packages/shared-ux/chrome/navigation/src/ui/hooks/use_init_navnode.ts b/packages/shared-ux/chrome/navigation/src/ui/hooks/use_init_navnode.ts index 4e24c887b1d02..f8d56bc785269 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/hooks/use_init_navnode.ts +++ b/packages/shared-ux/chrome/navigation/src/ui/hooks/use_init_navnode.ts @@ -120,6 +120,11 @@ function validateNodeProps< `[Chrome navigation] Error in node [${id}]. If renderAs is set to "panelOpener", a "link" must also be provided.` ); } + if (renderAs === 'item' && !link) { + throw new Error( + `[Chrome navigation] Error in node [${id}]. If renderAs is set to "item", a "link" must also be provided.` + ); + } if (appendHorizontalRule && !isGroup) { throw new Error( `[Chrome navigation] Error in node [${id}]. "appendHorizontalRule" can only be added for group with children.` @@ -279,6 +284,10 @@ export const useInitNavNode = < const registerChildNode = useCallback( (childNode) => { + if (orderChildrenRef.current[childNode.id] === undefined) { + orderChildrenRef.current[childNode.id] = idx.current++; + } + const childPath = nodePath ? [...nodePath, childNode.id] : []; setChildrenNodes((prev) => { @@ -291,10 +300,6 @@ export const useInitNavNode = < }; }); - if (orderChildrenRef.current[childNode.id] === undefined) { - orderChildrenRef.current[childNode.id] = idx.current++; - } - return { unregister: (childId: string) => { setChildrenNodes((prev) => { diff --git a/packages/shared-ux/chrome/navigation/src/ui/index.ts b/packages/shared-ux/chrome/navigation/src/ui/index.ts index 60b55fc5d200a..2e94b144161dd 100644 --- a/packages/shared-ux/chrome/navigation/src/ui/index.ts +++ b/packages/shared-ux/chrome/navigation/src/ui/index.ts @@ -7,7 +7,7 @@ */ export { Navigation } from './components'; -export type { PanelContent, PanelComponentProps } from './components'; +export type { PanelContent, PanelComponentProps, PanelContentProvider } from './components'; export { DefaultNavigation } from './default_navigation'; diff --git a/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree/navigation_tree.ts b/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree/navigation_tree.ts index bb8f610f7275d..f717a19e371c2 100644 --- a/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree/navigation_tree.ts +++ b/x-pack/plugins/security_solution_serverless/public/navigation/navigation_tree/navigation_tree.ts @@ -163,7 +163,6 @@ const formatFooterNodesFromLinks = ( title: category.label, icon: category.iconType, breadcrumbStatus: 'hidden', - defaultIsCollapsed: true, children: category.linkIds?.reduce((acc, linkId) => { const projectNavLink = projectNavLinks.find(({ id }) => id === linkId);